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 }