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 }