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.HashMap; 030 import java.util.List; 031 import java.util.Map; 032 import javax.security.auth.callback.CallbackHandler; 033 import org.jboss.dna.common.util.StringUtil; 034 import com.sun.security.auth.callback.TextCallbackHandler; 035 036 /** 037 * @author Randall Hauch 038 */ 039 public class ConsoleInput implements UserInterface { 040 041 protected static BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 042 043 private final RepositoryClient repositoryClient; 044 private final Map<Integer, String> selectionToSourceName = new HashMap<Integer, String>(); 045 046 /** 047 * Construct the console input and prompt for user input to interact with the RepositoryClient. 048 * 049 * @param client the client that should be used; may not be null 050 * @param args the command-line arguments; may not be null but may be empty 051 */ 052 public ConsoleInput( final RepositoryClient client, 053 final String[] args ) { 054 assert client != null; 055 this.repositoryClient = client; 056 for (String arg : args) { 057 arg = arg.trim().toLowerCase(); 058 if (arg.equals("--help")) { 059 System.out.println(); 060 System.out.println("Usage: run.sh [options]"); 061 System.out.println(); 062 System.out.println("Options:"); 063 System.out.println(" --api=value Specify which API should be used to obtain the content."); 064 System.out.println(" The 'value' must be either 'jcr' or 'dna', and defaults"); 065 System.out.println(" to 'jcr'."); 066 System.out.println(" --jaas Specify that JAAS should be used to authenticate the user."); 067 System.out.println(" --jaas=name With no 'name', use JAAS with an application context"); 068 System.out.println(" named \"" + RepositoryClient.JAAS_LOGIN_CONTEXT_NAME + "\"."); 069 System.out.println(" If another application context is to be used, then specify"); 070 System.out.println(" the name."); 071 System.out.println(" --help Print these instructions and exit."); 072 System.out.println(); 073 return; 074 } 075 } 076 try { 077 Thread eventThread = new Thread(new Runnable() { 078 079 private boolean quit = false; 080 081 @SuppressWarnings( "synthetic-access" ) 082 public void run() { 083 try { 084 System.out.println(); 085 System.out.print("Starting repositories ... "); 086 client.startRepositories(); 087 System.out.println("done."); 088 System.out.println(); 089 displayMainMenu(); 090 091 while (!quit) { 092 System.out.print(">"); 093 try { 094 String input = in.readLine().trim(); 095 if ("?".equals(input) || "h".equals(input)) displayMainMenu(); 096 else if ("q".equals(input)) quit = true; 097 else { 098 try { 099 int selection = Integer.parseInt(input); 100 String sourceName = selectionToSourceName.get(selection); 101 displayNavigationMenu(sourceName); 102 displayMainMenu(); 103 } catch (NumberFormatException e) { 104 System.out.println("Invalid option."); 105 displayMainMenu(); 106 } 107 } 108 } catch (NumberFormatException e) { 109 System.out.println("Invalid integer " + e.getMessage()); 110 } catch (IllegalArgumentException e) { 111 System.out.println(e.getMessage()); 112 } catch (IOException e) { 113 e.printStackTrace(); 114 } catch (Throwable e) { 115 e.printStackTrace(); 116 } 117 } 118 } catch (Exception err) { 119 System.out.println("Error: " + err.getLocalizedMessage()); 120 err.printStackTrace(System.err); 121 } finally { 122 try { 123 // Terminate ... 124 System.out.println(); 125 System.out.print("done.\nShutting down repositories and services ... "); 126 client.shutdown(); 127 System.out.print("done."); 128 System.out.println(); 129 System.out.println(); 130 } catch (Exception err) { 131 System.out.println("Error shutting down sequencing service and repository: " 132 + err.getLocalizedMessage()); 133 err.printStackTrace(System.err); 134 } 135 } 136 } 137 }); 138 139 eventThread.start(); 140 } catch (Exception err) { 141 System.out.println("Error: " + err.getLocalizedMessage()); 142 err.printStackTrace(System.err); 143 } 144 } 145 146 /** 147 * Generate the main menu for the console-based application. 148 */ 149 protected void displayMainMenu() { 150 selectionToSourceName.clear(); 151 System.out.println("-----------------------------------"); 152 System.out.println("Menu:"); 153 System.out.println(); 154 System.out.println("Select a repository to view:"); 155 int selection = 1; 156 for (String sourceName : repositoryClient.getNamesOfRepositories()) { 157 selectionToSourceName.put(selection, sourceName); 158 System.out.println(StringUtil.justifyRight("" + selection++, 3, ' ') + ") " + sourceName); 159 } 160 System.out.println("or"); 161 System.out.println(" ?) Show this menu"); 162 System.out.println(" q) Quit"); 163 } 164 165 /** 166 * Display the menu for navigating the source with the supplied name. This method returns as soon as the user exits the 167 * source. 168 * 169 * @param sourceName the source to be navigated; may not be null 170 */ 171 protected void displayNavigationMenu( String sourceName ) { 172 assert sourceName != null; 173 String currentPath = "/"; 174 System.out.println(); 175 System.out.println("Entering the \"" + sourceName + "\" repository."); 176 displayNavigationHelp(); 177 while (true) { 178 try { 179 // Print the prompt and read the input command ... 180 System.out.print(sourceName + "> "); 181 String input = in.readLine().trim(); 182 183 // Process the command ... 184 if (input.length() == 0) continue; 185 if ("?".equals(input) || "help".equals(input) || "h".equals(input)) { 186 displayNavigationHelp(); 187 } else if ("pwd".equals(input)) { 188 System.out.println(currentPath); 189 } else if ("exit".equals(input)) { 190 return; 191 } else if (input.startsWith("ls") || input.startsWith("ll")) { 192 input = input.substring(2).trim(); 193 String path = repositoryClient.buildPath(currentPath, input); 194 displayNode(sourceName, path); 195 } else if (input.startsWith("cd ")) { 196 input = input.substring("cd ".length()).trim(); 197 if (input.length() == 0) continue; 198 // Change the current path to the new location 199 String oldPath = currentPath; 200 currentPath = repositoryClient.buildPath(currentPath, input); 201 // If the current path does not exist, then go back to the previous path ... 202 if (!repositoryClient.getNodeInfo(sourceName, currentPath, null, null)) { 203 System.out.println("\"" + currentPath + "\" does not exist"); 204 currentPath = oldPath; 205 } else { 206 System.out.println(currentPath); 207 } 208 } 209 } catch (Throwable e) { 210 displayError(" processing your command", e); 211 } 212 } 213 } 214 215 protected void displayNavigationHelp() { 216 System.out.println(); 217 System.out.println("Enter one of the following commands followed by RETURN:"); 218 System.out.println(" pwd print the current node's path"); 219 System.out.println(" ls [path] to list the details of the node at the specified absolute or relative path"); 220 System.out.println(" (or the current path if none is supplied)"); 221 System.out.println(" cd path to change to the node at the specified absolute or relative path"); 222 System.out.println(" exit to exit this repository and return to the main menu"); 223 System.out.println(); 224 } 225 226 /** 227 * Display the node with the given path found in the supplied source. 228 * 229 * @param sourceName the name of the source; may not be null 230 * @param path the path to the node; may not be null 231 */ 232 protected void displayNode( String sourceName, 233 String path ) { 234 assert sourceName != null; 235 assert path != null; 236 237 // Retrieve the node information from the client ... 238 Map<String, Object[]> properties = new HashMap<String, Object[]>(); 239 List<String> children = new ArrayList<String>(); 240 try { 241 repositoryClient.getNodeInfo(sourceName, path, properties, children); 242 } catch (Throwable t) { 243 displayError(" displaying node \"" + path + "\"", t); 244 } 245 246 // Print the './' and '../' options ... 247 System.out.println(" ./"); 248 System.out.println(" ../"); 249 250 // Display the children ... 251 for (String childName : children) { 252 System.out.println(" " + childName + "/"); 253 } 254 // Determine the maximum length of the properties so that we can left-justify the values 255 int maxLength = 5; 256 for (String propertyName : properties.keySet()) { 257 maxLength = Math.max(maxLength, propertyName.length()); 258 } 259 // Display the properties ... 260 for (Map.Entry<String, Object[]> property : properties.entrySet()) { 261 String name = StringUtil.justifyLeft(property.getKey(), maxLength, ' '); 262 Object[] values = property.getValue(); 263 String valueStr = StringUtil.readableString(values); 264 if (values.length == 1) valueStr = StringUtil.readableString(values[0]); 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 }