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