001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.example.dna.repository; 023 024 import java.io.BufferedReader; 025 import java.io.File; 026 import java.io.IOException; 027 import java.io.InputStreamReader; 028 import java.util.ArrayList; 029 import java.util.Arrays; 030 import java.util.HashMap; 031 import java.util.List; 032 import java.util.Map; 033 import javax.security.auth.callback.CallbackHandler; 034 import org.jboss.dna.common.util.StringUtil; 035 import com.sun.security.auth.callback.TextCallbackHandler; 036 037 /** 038 * @author Randall Hauch 039 */ 040 public class ConsoleInput implements UserInterface { 041 042 protected static BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 043 044 private final RepositoryClient repositoryClient; 045 private final Map<Integer, String> selectionToSourceName = new HashMap<Integer, String>(); 046 047 /** 048 * Construct the console input and prompt for user input to interact with the RepositoryClient. 049 * 050 * @param client the client that should be used; may not be null 051 * @param args the command-line arguments; may not be null but may be empty 052 */ 053 public ConsoleInput( final RepositoryClient client, 054 final String[] args ) { 055 assert client != null; 056 this.repositoryClient = client; 057 for (String arg : args) { 058 arg = arg.trim().toLowerCase(); 059 if (arg.equals("--help")) { 060 System.out.println(); 061 System.out.println("Usage: run.sh [options]"); 062 System.out.println(); 063 System.out.println("Options:"); 064 System.out.println(" --api=value Specify which API should be used to obtain the content."); 065 System.out.println(" The 'value' must be either 'jcr' or 'dna', and defaults"); 066 System.out.println(" to 'jcr'."); 067 System.out.println(" --jaas Specify that JAAS should be used to authenticate the user."); 068 System.out.println(" --jaas=name With no 'name', use JAAS with an application context"); 069 System.out.println(" named \"" + RepositoryClient.JAAS_LOGIN_CONTEXT_NAME + "\"."); 070 System.out.println(" If another application context is to be used, then specify"); 071 System.out.println(" the name."); 072 System.out.println(" --help Print these instructions and exit."); 073 System.out.println(); 074 return; 075 } 076 } 077 try { 078 Thread eventThread = new Thread(new Runnable() { 079 080 private boolean quit = false; 081 082 @SuppressWarnings( "synthetic-access" ) 083 public void run() { 084 try { 085 System.out.println(); 086 System.out.print("Starting repositories ... "); 087 client.startRepositories(); 088 System.out.println("done."); 089 System.out.println(); 090 displayMainMenu(); 091 092 while (!quit) { 093 System.out.print(">"); 094 try { 095 String input = in.readLine().trim(); 096 if ("?".equals(input) || "h".equals(input)) displayMainMenu(); 097 else if ("q".equals(input)) quit = true; 098 else { 099 try { 100 int selection = Integer.parseInt(input); 101 String sourceName = selectionToSourceName.get(selection); 102 displayNavigationMenu(sourceName); 103 displayMainMenu(); 104 } catch (NumberFormatException e) { 105 System.out.println("Invalid option."); 106 displayMainMenu(); 107 } 108 } 109 } catch (NumberFormatException e) { 110 System.out.println("Invalid integer " + e.getMessage()); 111 } catch (IllegalArgumentException e) { 112 System.out.println(e.getMessage()); 113 } catch (IOException e) { 114 e.printStackTrace(); 115 } catch (Throwable e) { 116 e.printStackTrace(); 117 } 118 } 119 } catch (Exception err) { 120 System.out.println("Error: " + err.getLocalizedMessage()); 121 err.printStackTrace(System.err); 122 } finally { 123 try { 124 // Terminate ... 125 System.out.println(); 126 System.out.print("done.\nShutting down repositories and services ... "); 127 client.shutdown(); 128 System.out.print("done."); 129 System.out.println(); 130 System.out.println(); 131 } catch (Exception err) { 132 System.out.println("Error shutting down sequencing service and repository: " 133 + err.getLocalizedMessage()); 134 err.printStackTrace(System.err); 135 } 136 } 137 } 138 }); 139 140 eventThread.start(); 141 } catch (Exception err) { 142 System.out.println("Error: " + err.getLocalizedMessage()); 143 err.printStackTrace(System.err); 144 } 145 } 146 147 /** 148 * Generate the main menu for the console-based application. 149 */ 150 protected void displayMainMenu() { 151 selectionToSourceName.clear(); 152 System.out.println("-----------------------------------"); 153 System.out.println("Menu:"); 154 System.out.println(); 155 System.out.println("Select a repository to view:"); 156 int selection = 1; 157 for (String sourceName : repositoryClient.getNamesOfRepositories()) { 158 selectionToSourceName.put(selection, sourceName); 159 System.out.println(StringUtil.justifyRight("" + selection++, 3, ' ') + ") " + sourceName); 160 } 161 System.out.println("or"); 162 System.out.println(" ?) Show this menu"); 163 System.out.println(" q) Quit"); 164 } 165 166 /** 167 * Display the menu for navigating the source with the supplied name. This method returns as soon as the user exits the 168 * source. 169 * 170 * @param sourceName the source to be navigated; may not be null 171 */ 172 protected void displayNavigationMenu( String sourceName ) { 173 assert sourceName != null; 174 String currentPath = "/"; 175 System.out.println(); 176 System.out.println("Entering the \"" + sourceName + "\" repository."); 177 displayNavigationHelp(); 178 while (true) { 179 try { 180 // Print the prompt and read the input command ... 181 System.out.print(sourceName + "> "); 182 String input = in.readLine().trim(); 183 184 // Process the command ... 185 if (input.length() == 0) continue; 186 if ("?".equals(input) || "help".equals(input) || "h".equals(input)) { 187 displayNavigationHelp(); 188 } else if ("pwd".equals(input)) { 189 System.out.println(currentPath); 190 } else if ("exit".equals(input)) { 191 return; 192 } else if (input.startsWith("ls") || input.startsWith("ll")) { 193 input = input.substring(2).trim(); 194 String path = repositoryClient.buildPath(currentPath, input); 195 displayNode(sourceName, path); 196 } else if (input.startsWith("cd ")) { 197 input = input.substring("cd ".length()).trim(); 198 if (input.length() == 0) continue; 199 // Change the current path to the new location 200 String oldPath = currentPath; 201 currentPath = repositoryClient.buildPath(currentPath, input); 202 // If the current path does not exist, then go back to the previous path ... 203 if (!repositoryClient.getNodeInfo(sourceName, currentPath, null, null)) { 204 System.out.println("\"" + currentPath + "\" does not exist"); 205 currentPath = oldPath; 206 } else { 207 System.out.println(currentPath); 208 } 209 } 210 } catch (Throwable e) { 211 displayError(" processing your command", e); 212 } 213 } 214 } 215 216 protected void displayNavigationHelp() { 217 System.out.println(); 218 System.out.println("Enter one of the following commands followed by RETURN:"); 219 System.out.println(" pwd print the current node's path"); 220 System.out.println(" ls [path] to list the details of the node at the specified absolute or relative path"); 221 System.out.println(" (or the current path if none is supplied)"); 222 System.out.println(" cd path to change to the node at the specified absolute or relative path"); 223 System.out.println(" exit to exit this repository and return to the main menu"); 224 System.out.println(); 225 } 226 227 /** 228 * Display the node with the given path found in the supplied source. 229 * 230 * @param sourceName the name of the source; may not be null 231 * @param path the path to the node; may not be null 232 */ 233 protected void displayNode( String sourceName, 234 String path ) { 235 assert sourceName != null; 236 assert path != null; 237 238 // Retrieve the node information from the client ... 239 Map<String, Object[]> properties = new HashMap<String, Object[]>(); 240 List<String> children = new ArrayList<String>(); 241 try { 242 repositoryClient.getNodeInfo(sourceName, path, properties, children); 243 } catch (Throwable t) { 244 displayError(" displaying node \"" + path + "\"", t); 245 } 246 247 // Print the './' and '../' options ... 248 System.out.println(" ./"); 249 System.out.println(" ../"); 250 251 // Display the children ... 252 for (String childName : children) { 253 System.out.println(" " + childName + "/"); 254 } 255 // Determine the maximum length of the properties so that we can left-justify the values 256 int maxLength = 5; 257 for (String propertyName : properties.keySet()) { 258 maxLength = Math.max(maxLength, propertyName.length()); 259 } 260 // Display the properties ... 261 for (Map.Entry<String, Object[]> property : properties.entrySet()) { 262 String name = StringUtil.justifyLeft(property.getKey(), maxLength, ' '); 263 Object[] values = property.getValue(); 264 String valueStr = values.length == 1 ? values[0].toString() : Arrays.asList(values).toString(); 265 System.out.println(" " + name + " = " + valueStr); 266 } 267 } 268 269 /** 270 * Display the supplied error that happened during the activity. 271 * 272 * @param activity the activity; may not be null but may be empty 273 * @param t the exception; may not be null 274 */ 275 protected void displayError( String activity, 276 Throwable t ) { 277 assert activity != null; 278 assert t != null; 279 System.err.println(); 280 System.err.println("There has been an error" + activity); 281 System.err.println(" " + t.getMessage()); 282 t.printStackTrace(System.err); 283 System.err.println(); 284 System.err.println("Press any key to continue:"); 285 try { 286 in.readLine(); 287 } catch (IOException err) { 288 err.printStackTrace(System.err); 289 } 290 } 291 292 /** 293 * {@inheritDoc} 294 * 295 * @see org.jboss.example.dna.repository.UserInterface#getLocationOfRepositoryFiles() 296 */ 297 public String getLocationOfRepositoryFiles() { 298 return new File("").getAbsolutePath(); 299 } 300 301 /** 302 * {@inheritDoc} 303 * 304 * @see org.jboss.example.dna.repository.UserInterface#getCallbackHandler() 305 */ 306 public CallbackHandler getCallbackHandler() { 307 return new TextCallbackHandler(); 308 } 309 310 }