/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.itql;
// Java 2 standard packages
import java.io.*;
import java.net.*;
import java.util.*;
import javax.xml.parsers.*;
// Third party packages
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
// Automatically generated packages (SableCC)
import org.mulgara.query.Answer;
import org.mulgara.query.QueryException;
import org.mulgara.query.TuplesException;
import javax.swing.*;
/**
* Interactive TQL session command line shell.
*
* @created 2001-Aug-17
*
* @author Simon Raboczi
* @author Tom Adams
* @author Andrew Newman
*
* @version $Revision: 1.8 $
*
* @modified $Date: 2005/01/05 04:58:15 $ by $Author: newmana $
*
* @maintenanceAuthor $Author: newmana $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright ©2001 <a href="http://www.pisoftware.com/">Plugged In
* Software Pty Ltd</a>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class ItqlSession {
/**
* Get line separator.
*/
private static final String eol = System.getProperty("line.separator");
/**
* The prompt.
*/
public final static String PROMPT = "iTQL> ";
/**
* The secondary prompt, indicating an incomplete command.
*/
public final static String PROMPT2 = " ";
//
// Constants
//
/**
* the log4j configuration file path (withing the JAR file)
*/
private final static String LOG4J_CONFIG_PATH = "log4j-itql.xml";
/**
* the logging category to log to
*/
private final static Logger log = Logger.getLogger(ItqlSession.class);
/**
* the default path to the pre-loading script
*/
private final static String PRE_LOADING_SCRIPT_PATH = "default-pre.itql";
/**
* the default path to the post-loading script
*/
private final static String POST_LOADING_SCRIPT_PATH = "default-post.itql";
//
// Private state
//
/**
* The graphical UI.
*/
private static ItqlSessionUI ui;
/**
* The messages from the previous queries.
*/
private List<String> messages = new ArrayList<String>();
/**
* The answers from the previous queries.
*/
private List<Answer> answers = new ArrayList<Answer>();
/**
* The file name (URL) of the script to execute if -s is given.
*/
private String script = null;
//
// Members
//
/**
* the iTQL interpreter associated with this session
*/
private final ItqlInterpreterBean itqlBean;
/**
* the URL of the post-loading script
*/
private URL postLoadingScriptURL = null;
/**
* the URL of the pre-loading script
*/
private URL preLoadingScriptURL = null;
//
// Constructors
//
/**
* Creates a new ITQL session.
*
* @throws IOException if the logging configuration can't be read
* @throws QueryException if a connection can't be established
* to the server
*/
public ItqlSession() throws IOException, QueryException {
// load the default logging configuration
this.loadLoggingConfig();
itqlBean = new ItqlInterpreterBean();
}
/**
* Initiates a session using the given <code>session</code>
*
* @param session the interactive session to issue commands to
* @param in the stream to read commands from
* @param out the stream to print responses to
*/
public static void session(ItqlSession session, InputStream in,
PrintStream out) {
// ui.print("@@build.label@@" + eol);
// display the copyright notice
ui.print("iTQL Command Line Interface" + eol +
"Copyright (C) 2001-2004 Tucana Technologies, Inc." + eol);
// print a help message
ui.print(eol + "Type \"help ;\", then enter for help." + eol + eol);
// print a prompt
ui.print(PROMPT);
}
/**
* Start an interactive TQL session.
*
* @param args command line parameters
* @throws IOException EXCEPTION TO DO
* @throws QueryException EXCEPTION TO DO
*/
public static void main(String[] args) throws IOException, QueryException {
// create a new session to work with
ItqlSession itqlSession = new ItqlSession();
try {
// set the default pre- and post-loading scripts
itqlSession.retrieveDefaultLoadingScripts();
// create a parser to parse the command line options
ItqlOptionParser optsParser = new ItqlOptionParser(args);
// parse the
optsParser.parse();
// process the options
boolean startSession = itqlSession.processOptions(optsParser);
// log that we've processed the options
if (log.isDebugEnabled()) {
log.debug("Processed command line options");
}
// execute the pre-loading script - we need to do this after we get the
// command line options as we can override the defaults
itqlSession.executeLoadingScript(itqlSession.getPreLoadingScriptURL());
if (itqlSession.getScript() != null) {
itqlSession.executeScript(new URL(itqlSession.getScript()));
} // end if
// log that we've executed the pre-loading script
if (log.isDebugEnabled()) {
log.debug("Executed pre-loading script");
}
// if we can, execute this session using std in and std out
if (startSession) {
// Create the UI.
JFrame app = new JFrame("iTQL Shell");
app.setSize(640, 480);
app.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
ui = new ItqlSessionUI(itqlSession);
app.getContentPane().add(ui);
if (log.isInfoEnabled()) {
log.info("Starting iTQL interpreter");
}
ItqlSession.session(itqlSession, System.in, System.out);
// Make the UI visible
app.setVisible(true);
if (log.isInfoEnabled()) {
log.info("Stopping iTQL interpreter");
}
// log that we've executed the pre-loading script
if (log.isDebugEnabled()) {
log.debug("Executed post-loading script");
}
} else {
// execute the post-loading script
itqlSession.executeLoadingScript(itqlSession.getPostLoadingScriptURL());
// Close the session and exit.
itqlSession.close();
System.exit(0);
}
} catch (ItqlOptionParser.UnknownOptionException uoe) {
// log that the option we received was invalid
log.warn("Invalid command line option specified: " + uoe.getOptionName());
// let the user know about the invalid option
System.err.println("Invalid option: " + uoe.getOptionName());
// print the usage instructions
itqlSession.printUsage();
} catch (ItqlOptionParser.IllegalOptionValueException iove) {
// format the incorrect option string
String optionMsg =
"-" + iove.getOption().shortForm() + ", --" +
iove.getOption().longForm() + " = " + iove.getValue();
// log that the option value we received was invalid
log.warn("Invalid command line option value specified: " + optionMsg);
// let the user know about the invalid option
System.err.println("Invalid option value: " + optionMsg);
// print the usage instructions
itqlSession.printUsage();
}
}
// ItqlSession()
//
// Methods overriding the AbstractSession superclass
//
/**
* Returns a list of messages (Strings) from the execution of the last
* command or series of commands. Successful and unsuccessful commands will
* have valid string objects.
*
* @return the message from the execution of the last command.
*/
public List<String> getLastMessages() {
return messages;
}
/**
* Returns a list of Answers from the execution of the last command or series
* of commands. Failures will have null answers in place.
*/
public List<Answer> getLastAnswers() {
return answers;
}
/**
* Returns the script to run.
*
* @return the script to run.
*/
public String getScript() {
return script;
}
/**
* Executes a series of commands the given command. Makes the result availabe
* via getLastMessages and getLastAnswers.
*
* @param command the command to execute
*/
public void executeCommand(String command) {
// Reset answers and messages
answers.clear();
messages.clear();
String message = null;
Answer answer = null;
String[] queries = ItqlInterpreterBean.splitQuery(command);
for (int index = 0; index < queries.length; index++) {
try {
// log the command we're executing
if (log.isDebugEnabled()) {
log.debug("Starting execution of command \"" + command + "\"");
}
// execute the command
answer = itqlBean.executeQuery(queries[index]);
// close the session if requested
if (itqlBean.isQuitRequested()) {
close();
}
message = itqlBean.getLastMessage();
// log that we've executed the command
if (log.isDebugEnabled()) {
log.debug("Completed execution of commmand \"" + command + "\"");
}
} catch (UnsupportedOperationException uoe) {
// Known exception get the last message.
message = itqlBean.getLastMessage();
log.warn("Couldn't execute command", uoe);
} catch (ItqlInterpreterException iee) {
// Known exception get the last message.
message = itqlBean.getLastMessage();
log.warn("Couldn't execute command", iee);
} catch (Exception e) {
// Unknown exception get the exception's message.
message = e.getMessage();
log.error("Couldn't execute command", e);
} catch (Error e) {
// Unknown exception get the exception's message.
message = e.getMessage();
log.error("Couldn't execute command", e);
}
// Add the message and answer
messages.add(message);
answers.add(answer);
}
}
/**
* Sets the URL of the pre-loading script.
*
* @param preLoadingScriptURL the URL of the pre-loading script
*/
private void setPreLoadingScriptURL(URL preLoadingScriptURL) {
this.preLoadingScriptURL = preLoadingScriptURL;
}
// getPreLoadingScriptURL()
/**
* Sets the URL of the post-loading script.
*
* @param postLoadingScriptURL the URL of the post-loading script
*/
private void setPostLoadingScriptURL(URL postLoadingScriptURL) {
this.postLoadingScriptURL = postLoadingScriptURL;
}
// setInterpreter()
/**
* Returns the URL of the pre-loading script.
*
* @return the URL of the pre-loading script
*/
private URL getPreLoadingScriptURL() {
return this.preLoadingScriptURL;
}
// setPostLoadingScriptURL()
/**
* Returns the URL of the post-loading script.
*
* @return the URL of the post-loading script
*/
private URL getPostLoadingScriptURL() {
return this.postLoadingScriptURL;
}
// command()
/**
* Closes the session associated with this interpreter. Subclasses that
* override this method <strong>must</strong> call <code>super.close()</code>.
*/
@SuppressWarnings("deprecation")
private void close() {
// Close the session, if any
itqlBean.setServerURI(null);
itqlBean.close();
// Exit program
System.exit(0);
}
// main()
//
// Internal methods
//
/**
* Retrieves the default loading scripts.
*/
private void retrieveDefaultLoadingScripts() {
// locate the pre-loading script
URL preScriptURL = this.locateScript(PRE_LOADING_SCRIPT_PATH);
if (preScriptURL != null) {
this.setPreLoadingScriptURL(preScriptURL);
}
// locate the post-loading script
URL postScriptURL = this.locateScript(POST_LOADING_SCRIPT_PATH);
if (postScriptURL != null) {
this.setPostLoadingScriptURL(postScriptURL);
}
}
/**
* Locates the loading script with the given path. <p>
*
* This locates scripts in the following order:</p>
* <ol>
* <li> Current working directory;</li>
* <li> System classpath (if embedded in a JAR).</li>
* </ol>
* <p>
*
* Note. These could be overwritten by the command-line options <code>-o</code>
* and <code>-p</code>. </p>
*
* @param scriptPath the path to the script to locate
* @return a URL to the script, null if the script could not be found
*/
private URL locateScript(String scriptPath) {
URL scriptURL = null;
// find the current directory
String currentDirectory = System.getProperty("user.dir", ".");
// append a "/" if we need to
if (!currentDirectory.endsWith("/")) {
currentDirectory += File.separator;
}
// end if
// log that we're looking for scripts
log.debug("Looking for script " + scriptPath + " in " + currentDirectory);
// try to find the script
File loadingScript = new File(currentDirectory + scriptPath);
if (loadingScript.exists() && loadingScript.isFile()) {
// log that we've found the file
log.debug("Found loading script - " + loadingScript);
// return the URL!!!
try {
scriptURL = loadingScript.toURI().toURL();
} catch (MalformedURLException mue) {
// log the error
log.warn("Unable to convert loading script filename to URL - " +
mue.getMessage());
System.err.println("Unable to convert loading script filename " +
"to URL - " + loadingScript);
}
// try-catch
} else {
// log that we're now looking in the classpath
log.debug("Looking for loading script " + scriptPath + " in classpath");
// try to obtain from the classpath
URL loadingScriptURL = ClassLoader.getSystemResource(scriptPath);
// set it
if (loadingScriptURL != null) {
log.debug("Found loading script at - " + loadingScriptURL);
scriptURL = loadingScriptURL;
}
// end if
}
// end if
// return the URL
return scriptURL;
}
// locateScript()
/**
* Executes the pre-loading script.
*
* @param loadingScriptURL the URL of the loading (pre/post) script to execute
*/
private void executeLoadingScript(URL loadingScriptURL) {
// execute it
if (loadingScriptURL != null) {
// log that we're executing the script
log.debug("Executing loading script " + loadingScriptURL);
// execute the script
this.executeScript(loadingScriptURL);
}
// end if
}
// executeLoadingScript()
/**
* Processes the command line options passed to the interpreter.
*
* @param parser the command line option parser to use to parse the command
* line options
* @return RETURNED VALUE TO DO
*/
private boolean processOptions(ItqlOptionParser parser) {
// log that we're processing command line options
log.debug("Processing command line options");
// flag to indicate whether we can start the interpreter
boolean startInterpreter = true;
try {
// find out if the user wants help
if (parser.getOptionValue(ItqlOptionParser.HELP) != null) {
// print the help
this.printUsage();
// don't start the interpreter
startInterpreter = false;
} else {
// dump the interpreter configuration
Object dumpConfig = parser.getOptionValue(ItqlOptionParser.DUMP_CONFIG);
if (dumpConfig != null) {
this.dumpConfig();
}
// end if
// load an external interpreter configuration file
Object itqlConf = parser.getOptionValue(ItqlOptionParser.ITQL_CONFIG);
if (itqlConf != null) {
this.loadItqlConfig(new URL( (String) itqlConf));
}
// end if
// load an external logging configuration file
Object logConf = parser.getOptionValue(ItqlOptionParser.LOG_CONFIG);
if (logConf != null) {
this.loadLoggingConfig(new URL( (String) logConf));
}
// end if
// find out whether to execute pre-and post loading scripts
Object defaultLoadingScripts =
parser.getOptionValue(ItqlOptionParser.NO_LOAD);
if (defaultLoadingScripts == null) {
// override the default pre-loading script
Object preScript = parser.getOptionValue(ItqlOptionParser.PRE_SCRIPT);
if (preScript != null) {
this.setPreLoadingScriptURL(new URL( (String) preScript));
}
// end if
// override the default post-loading script
Object postScript =
parser.getOptionValue(ItqlOptionParser.POST_SCRIPT);
if (postScript != null) {
this.setPostLoadingScriptURL(new URL( (String) preScript));
}
// end if
} else {
// log that we've turned off pre- and post-loading scripts
log.debug("Pre- and post-loading scripts disabled");
// unset default pre- and post-loading scripts
this.setPreLoadingScriptURL(null);
this.setPostLoadingScriptURL(null);
}
// execute an iTQL script and quit
script = (String) parser.getOptionValue(ItqlOptionParser.SCRIPT);
if (script != null) {
startInterpreter = false;
} // end if
} // end if
} catch (IOException ioe) {
// log the error
log.warn("Invalid URL on command line - " + ioe.getMessage());
// print the usage
System.err.println("Invalid URL - " + ioe.getMessage());
this.printUsage();
// don't start the interpreter
startInterpreter = false;
} catch (Exception e) {
// log the error
log.warn("Could not start interpreter - " + e.getMessage());
// let the user know
System.err.println("Error - " + e.getMessage());
// don't start the interpreter
startInterpreter = false;
}
// try-catch
// return the continue flag
return startInterpreter;
}
// processOptions()
/**
* Prints the usage instructions for the interpreter.
*/
private void printUsage() {
// build the usage message
StringBuffer usage = new StringBuffer();
usage.append("Usage: java -jar <jarfile> ");
//usage.append("[-d|");
//usage.append("-g|");
//usage.append("-h] ");
usage.append("[-h|");
usage.append("-n] ");
//usage.append("[-i <url>] ");
usage.append("[-l <url>] ");
usage.append("[-o <url>] ");
usage.append("[-p <url>] ");
usage.append("[-s <url>]");
usage.append(eol);
usage.append(eol);
//usage.append("-d, --dumpconfig dump the interpreter configuration to " +
// "the current directory\n");
//usage.append("-g, --gui display the iTQL GUI ");
usage.append("-h, --help display this help screen" + eol);
usage.append("-n, --noload do not execute pre- and post-loading " +
"scripts (useful with -s)" + eol);
//usage.append("-i, --itqlconfig use an external configuration file\n");
usage.append("-l, --logconfig use an external logging configuration " +
"file" + eol);
usage.append("-o, --postload execute an iTQL script after " +
"interpreter stops," + eol);
usage.append(" overriding default post-loading script" +
eol);
usage.append("-p, --preload execute an iTQL script before " +
"interpreter starts," + eol);
usage.append(" overriding default pre-loading script" +
eol);
usage.append("-s, --script execute an iTQL script and quit" + eol);
usage.append(eol);
usage.append("The intepreter executes default pre- and post-loading " +
"scripts. These can be" + eol);
usage.append("used to load aliases etc. into the interpreter to simplify " +
"commands. The" + eol);
usage.append("default scripts are contained within the JAR file, " +
"however you can overide" + eol);
usage.append("these by placing files named default-pre.itql and " +
"default-post.itql in" + eol);
usage.append("the directory from which you run the interpreter, or by " +
"using the -p and" + eol);
usage.append("-o options." + eol);
// print the usage
System.out.println(usage.toString());
}
// printUsage()
/**
* Dunps the current interpreter configuration to the current directory. This
* will dump the entire interpreter configuration including the logging and
* application logging.
*
*/
private void dumpConfig() {
// we don't support this feature yet
throw new UnsupportedOperationException();
}
// dumpConfig()
/**
* Loads an external iTQL interpreter configuration file. This will use the
* configuration in the file located at <code>itqlConfURL</code>, instead of
* the configuration contained within the distribution JAR file.
*
* @param itqlConfURL the URL of the external iTQL interpreter configuration
* file
* @return RETURNED VALUE TO DO
*/
private boolean loadItqlConfig(URL itqlConfURL) {
// we don't support this feature yet
throw new UnsupportedOperationException();
}
// loadItqlConfig()
/**
* Executes a script.
*
* @param scriptURL the URL of the script to load
*/
private void executeScript(URL scriptURL) {
// log that we're executing the script
log.debug("Executing script from " + scriptURL);
// keep a record of the line number
int line = 0;
try {
// create a reader to read the contents of the script
BufferedReader scriptIn =
new BufferedReader(new InputStreamReader(scriptURL.openStream(),
"UTF-8"));
// execute the script!
String command = scriptIn.readLine();
while (command != null) {
// increment the line number
line++;
if (!command.equals("")) {
// execute the command
Answer answer = itqlBean.executeQuery(command);
// print the results
if (answer != null) {
answer.beforeFirst();
if (answer.isUnconstrained()) {
System.out.println("[ true ]");
} else {
while (answer.next()) {
System.out.print("[ ");
for (int index = 0; index < answer.getNumberOfVariables();
index++) {
System.out.print(String.valueOf(answer.getObject(index)));
if (index < (answer.getNumberOfVariables() - 1)) {
System.out.print(", ");
}
}
System.out.println(" ]");
}
}
}
if (answer != null) {
answer.close();
}
String lastMessage = itqlBean.getLastMessage();
if ((lastMessage != null) && (!lastMessage.equals("")) && (ui != null)) {
System.out.println(lastMessage);
}
} // end if
// get the next command
command = scriptIn.readLine();
} // end if
} catch (ItqlInterpreterException pe) {
// let the user know the problem
System.err.println("Syntax error (line " + line + "): " +
pe.getMessage());
log.warn("Unable to execute script - " + scriptURL + " - " + pe);
} catch (TuplesException te) {
// let the user know the problem
System.err.println("Syntax error (line " + line + "): " +
te.getMessage());
log.warn("Unable to execute script - " + scriptURL + " - " + te);
} catch (IOException ioe) {
// let the user know the problem
System.err.println("Could not execute script - " + ioe);
log.warn("Unable to execute script - " + scriptURL + " - " + ioe);
} // try-catch
} // executeScript()
/**
* Loads an external XML log4j configuration file. This will use the
* configuration in the file located at <code>logConfURL</code>, instead of
* the configuration contained within the distribution JAR file.
*
* @param logConfURL the URL of the external XML log4j configuration file
* @throws Exception if unable to complete the method sucessfully
*/
private void loadLoggingConfig(URL logConfURL) throws Exception {
// configure the logging service
DOMConfigurator.configure(logConfURL);
log.info("Using new logging configuration from " + logConfURL);
}
// loadLoggingConfig()
/**
* Loads the embedded logging configuration (from the JAR file).
*
*/
private void loadLoggingConfig() {
// get a URL from the classloader for the logging configuration
URL log4jConfigURL = ClassLoader.getSystemResource(LOG4J_CONFIG_PATH);
// if we didn't get a URL, tell the user that something went wrong
if (log4jConfigURL == null) {
System.err.println("Unable to find logging configuration file in JAR " +
"with " + LOG4J_CONFIG_PATH + ", reverting to default configuration.");
BasicConfigurator.configure();
} else {
try {
// configure the logging service
DOMConfigurator.configure(log4jConfigURL);
log.info("Using logging configuration from " + log4jConfigURL);
} catch (FactoryConfigurationError e) {
System.err.println("Unable to configure logging service");
}
// try-catch
}
// end if
}
// getPostLoadingScriptURL()
}