/* * Oscar Bundle Repository * Copyright (c) 2004, Richard S. Hall * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of the ungoverned.org nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Contact: Richard S. Hall (heavy@ungoverned.org) * Contributor(s): * **/ package org.ungoverned.osgi.bundle.bundlerepository; import java.io.*; import java.util.StringTokenizer; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.ungoverned.osgi.service.bundlerepository.BundleRecord; import org.ungoverned.osgi.service.bundlerepository.BundleRepositoryService; import org.ungoverned.osgi.service.shell.Command; public class ObrCommandImpl implements Command { private static final String HELP_CMD = "help"; private static final String URLS_CMD = "urls"; private static final String LIST_CMD = "list"; private static final String INFO_CMD = "info"; private static final String DEPLOY_CMD = "deploy"; private static final String INSTALL_CMD = "install"; private static final String START_CMD = "start"; private static final String UPDATE_CMD = "update"; private static final String SOURCE_CMD = "source"; private static final String NODEPS_SWITCH = "-nodeps"; private static final String CHECK_SWITCH = "-check"; private static final String EXTRACT_SWITCH = "-x"; private BundleContext m_context = null; private BundleRepositoryService m_brs = null; public ObrCommandImpl(BundleContext context, BundleRepositoryService brs) { m_context = context; m_brs = brs; } public String getName() { return "obr"; } public String getUsage() { return "obr help"; } public String getShortDescription() { return "Oscar bundle repository."; } public synchronized void execute(String commandLine, PrintStream out, PrintStream err) { try { // Parse the commandLine to get the OBR command. StringTokenizer st = new StringTokenizer(commandLine); // Ignore the invoking command. st.nextToken(); // Try to get the OBR command, default is HELP command. String command = HELP_CMD; try { command = st.nextToken(); } catch (Exception ex) { // Ignore. } // Perform the specified command. if ((command == null) || (command.equals(HELP_CMD))) { help(out, st); } else { if (command.equals(URLS_CMD)) { urls(commandLine, command, out, err); } else if (command.equals(LIST_CMD)) { list(commandLine, command, out, err); } else if (command.equals(INFO_CMD)) { info(commandLine, command, out, err); } else if (command.equals(DEPLOY_CMD)) { deploy(commandLine, command, out, err); } else if (command.equals(INSTALL_CMD) || command.equals(START_CMD)) { install(commandLine, command, out, err); } else if (command.equals(UPDATE_CMD)) { update(commandLine, command, out, err); } else if (command.equals(SOURCE_CMD)) { source(commandLine, command, out, err); } else { err.println("Unknown command: " + command); } } } catch (InvalidSyntaxException ex) { err.println("Syntax error: " + ex.getMessage()); } catch (IOException ex) { err.println("Error: " + ex); } } private void urls( String commandLine, String command, PrintStream out, PrintStream err) throws IOException { // Parse the commandLine. StringTokenizer st = new StringTokenizer(commandLine); // Ignore the "obr" command. st.nextToken(); // Ignore the "urls" command. st.nextToken(); int count = st.countTokens(); String[] urls = new String[count]; for (int i = 0; i < count; i++) { urls[i] = st.nextToken(); } if (count > 0) { m_brs.setRepositoryURLs(urls); } else { urls = m_brs.getRepositoryURLs(); if (urls != null) { for (int i = 0; i < urls.length; i++) { out.println(urls[i]); } } else { out.println("No repository URLs are set."); } } } private void list( String commandLine, String command, PrintStream out, PrintStream err) throws IOException { // Create a stream tokenizer for the command line string, // since the syntax for install/start is more sophisticated. StringReader sr = new StringReader(commandLine); StreamTokenizer tokenizer = new StreamTokenizer(sr); tokenizer.resetSyntax(); tokenizer.quoteChar('\''); tokenizer.quoteChar('\"'); tokenizer.whitespaceChars('\u0000', '\u0020'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('0', '9'); tokenizer.wordChars('\u00A0', '\u00FF'); tokenizer.wordChars('.', '.'); tokenizer.wordChars('-', '-'); tokenizer.wordChars('_', '_'); // Ignore the invoking command name and the OBR command. int type = tokenizer.nextToken(); type = tokenizer.nextToken(); String substr = null; for (type = tokenizer.nextToken(); type != StreamTokenizer.TT_EOF; type = tokenizer.nextToken()) { // Add a space in between tokens. if (substr == null) { substr = ""; } else { substr += " "; } if ((type == StreamTokenizer.TT_WORD) || (type == '\'') || (type == '"')) { substr += tokenizer.sval.toLowerCase(); } } boolean printed = false; for (int i = 0; i < m_brs.getBundleRecordCount(); i++) { BundleRecord record = m_brs.getBundleRecord(i); String name = (String) record.getAttribute(BundleRecord.BUNDLE_NAME); if (name != null) { if ((substr == null) || (name.toLowerCase().indexOf(substr) >= 0)) { if (!printed) { printed = true; out.println(""); } String version = (String) record.getAttribute(BundleRecord.BUNDLE_VERSION); if (version != null) { out.println(name + " (" + version + ")"); } else { out.println(name); } } } } if (printed) { out.println(""); } else { out.println("No matching bundles."); } } private void info( String commandLine, String command, PrintStream out, PrintStream err) throws IOException, InvalidSyntaxException { ParsedCommand pc = parseInfo(commandLine); for (int i = 0; (pc != null) && (i < pc.getTargetCount()); i++) { out.println(""); BundleRecord record = null; // If there is no version, then try to retrieve by // name, but error if there are multiple versions. if (pc.getTargetVersion(i) == null) { BundleRecord[] records = m_brs.getBundleRecords(pc.getTargetName(i)); if (records.length == 1) { record = records[0]; } } else { record = m_brs.getBundleRecord( pc.getTargetName(i), Util.parseVersionString( pc.getTargetVersion(i))); } if (record != null) { record.printAttributes(out); } else { err.println("Unknown bundle or amiguous version: " + pc.getTargetName(i)); } } out.println(""); } private void deploy( String commandLine, String command, PrintStream out, PrintStream err) throws IOException, InvalidSyntaxException { ParsedCommand pc = parseInstallStart(commandLine); for (int i = 0; (pc != null) && (i < pc.getTargetCount()); i++) { // Find either the local bundle or the bundle // record so we can get the update location attribute. String updateLocation = null; // First look for update location locally. Bundle bundle = findLocalBundle(pc.getTargetName(i), pc.getTargetVersion(i)); if (bundle != null) { updateLocation = (String) bundle.getHeaders().get(Constants.BUNDLE_UPDATELOCATION); } // If update location wasn't found locally, look in repository. if (updateLocation == null) { BundleRecord record = findBundleRecord(pc.getTargetName(i), pc.getTargetVersion(i)); if (record != null) { updateLocation = (String) record.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION); } } if (updateLocation != null) { m_brs.deployBundle( out, // Output stream. err, // Error stream. updateLocation, // Update location. pc.isResolve(), // Resolve dependencies. false); // Start. } else { err.println("Unknown bundle or amiguous version: " + pc.getTargetName(i)); } } } private void install( String commandLine, String command, PrintStream out, PrintStream err) throws IOException, InvalidSyntaxException { // Parse the command line to get all local targets to install. ParsedCommand pc = parseInstallStart(commandLine); // Loop through each local target and try to find // the corresponding bundle record from the repository. for (int targetIdx = 0; (pc != null) && (targetIdx < pc.getTargetCount()); targetIdx++) { // Get the current target's name and version. String targetName = pc.getTargetName(targetIdx); String targetVersionString = pc.getTargetVersion(targetIdx); // Make sure the bundle is not already installed. Bundle bundle = findLocalBundle(targetName, targetVersionString); if (bundle == null) { // Find the targets bundle record. BundleRecord record = findBundleRecord(targetName, targetVersionString); // If we found a record, try to install it. if (record != null) { m_brs.deployBundle( out, // Output stream. err, // Error stream. (String) record.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION), // Update location. pc.isResolve(), // Resolve dependencies. command.equals(START_CMD)); // Start. } else { err.println("Not in repository: " + targetName); } } else { err.println("Already installed: " + targetName); } } } private void update( String commandLine, String command, PrintStream out, PrintStream err) throws IOException, InvalidSyntaxException { // Parse the command line to get all local targets to update. ParsedCommand pc = parseUpdate(commandLine); if (pc.isCheck()) { updateCheck(out, err); } else { // Loop through each local target and try to find // the corresponding locally installed bundle. for (int targetIdx = 0; (pc != null) && (targetIdx < pc.getTargetCount()); targetIdx++) { // Get the current target's name and version. String targetName = pc.getTargetName(targetIdx); String targetVersionString = pc.getTargetVersion(targetIdx); // Find corresponding locally installed bundle. Bundle bundle = findLocalBundle(targetName, targetVersionString); // If we found a locally installed bundle, then // try to update it. if (bundle != null) { m_brs.deployBundle( out, // Output stream. err, // Error stream. (String) bundle.getHeaders().get(Constants.BUNDLE_UPDATELOCATION), // Local bundle to update. pc.isResolve(), // Resolve dependencies. false); // Start. } else { err.println("Not installed: " + targetName); } } } } private void updateCheck(PrintStream out, PrintStream err) throws IOException { Bundle[] bundles = m_context.getBundles(); // Loop through each local target and try to find // the corresponding locally installed bundle. for (int bundleIdx = 0; (bundles != null) && (bundleIdx < bundles.length); bundleIdx++) { // Ignore the system bundle. if (bundles[bundleIdx].getBundleId() == 0) { continue; } // Get the local bundle's update location. String localLoc = (String) bundles[bundleIdx].getHeaders().get(Constants.BUNDLE_UPDATELOCATION); if (localLoc == null) { // Without an update location, there is no way to // check for an update, so ignore the bundle. continue; } // Get the local bundle's version. String localVersion = (String) bundles[bundleIdx].getHeaders().get(Constants.BUNDLE_VERSION); localVersion = (localVersion == null) ? "0.0.0" : localVersion; // Get the matching repository bundle records. BundleRecord[] records = m_brs.getBundleRecords( (String) bundles[bundleIdx].getHeaders().get(Constants.BUNDLE_NAME)); // Loop through all records to see if there is an update. for (int recordIdx = 0; (records != null) && (recordIdx < records.length); recordIdx++) { String remoteLoc = (String) records[recordIdx].getAttribute(BundleRecord.BUNDLE_UPDATELOCATION); if (remoteLoc == null) { continue; } // If the update locations are equal, then compare versions. if (remoteLoc.equals(localLoc)) { String remoteVersion = (String) records[recordIdx].getAttribute(BundleRecord.BUNDLE_VERSION); if (remoteVersion != null) { int result = Util.compareVersion( Util.parseVersionString(remoteVersion), Util.parseVersionString(localVersion)); if (result > 0) { out.println( records[recordIdx].getAttribute(BundleRecord.BUNDLE_NAME) + " update available."); break; } } } } } } private void source( String commandLine, String command, PrintStream out, PrintStream err) throws IOException, InvalidSyntaxException { // Parse the command line to get all local targets to update. ParsedCommand pc = parseSource(commandLine); for (int i = 0; i < pc.getTargetCount(); i++) { BundleRecord record = findBundleRecord( pc.getTargetName(i), pc.getTargetVersion(i)); if (record != null) { String srcURL = (String) record.getAttribute(BundleRecord.BUNDLE_SOURCEURL); if (srcURL != null) { FileUtil.downloadSource( out, err, srcURL, pc.getDirectory(), pc.isExtract()); } else { err.println("Missing source URL: " + pc.getTargetName(i)); } } else { err.println("Not in repository: " + pc.getTargetName(i)); } } } private BundleRecord findBundleRecord(String name, String versionString) { BundleRecord record = null; // If there is no version, then try to retrieve by // name, but error if there are multiple versions. if (versionString == null) { BundleRecord[] records = m_brs.getBundleRecords(name); if (records.length == 1) { record = records[0]; } } else { record = m_brs.getBundleRecord( name, Util.parseVersionString(versionString)); } return record; } private Bundle findLocalBundle(String name, String versionString) { Bundle bundle = null; // Get the name only if there is no version, but error // if there are multiple matches for the same name. if (versionString == null) { // Perhaps the target name is a bundle ID and // not a name, so try to interpret as a long. try { bundle = m_context.getBundle(Long.parseLong(name)); } catch (NumberFormatException ex) { // The bundle is not a number, so look for a local // bundle with the same name. Bundle[] matchingBundles = findLocalBundlesByName(name); // If only one matches, then select is. if (matchingBundles.length == 1) { bundle = matchingBundles[0]; } } } else { // Find the local bundle by name and version. bundle = findLocalBundleByVersion( name, Util.parseVersionString(versionString)); } return bundle; } private Bundle findLocalBundleByVersion(String name, int[] version) { // Get bundles with matching name. Bundle[] targets = findLocalBundlesByName(name); // Find bundle with matching version. if (targets.length > 0) { for (int i = 0; i < targets.length; i++) { String targetName = (String) targets[i].getHeaders().get(BundleRecord.BUNDLE_NAME); int[] targetVersion = Util.parseVersionString((String) targets[i].getHeaders().get(BundleRecord.BUNDLE_VERSION)); if ((targetName != null) && targetName.equalsIgnoreCase(name) && (Util.compareVersion(targetVersion, version) == 0)) { return targets[i]; } } } return null; } private Bundle[] findLocalBundlesByName(String name) { // Get local bundles. Bundle[] bundles = m_context.getBundles(); // Find bundles with matching name. Bundle[] targets = new Bundle[0]; for (int i = 0; i < bundles.length; i++) { String targetName = (String) bundles[i].getHeaders().get(BundleRecord.BUNDLE_NAME); if (targetName == null) { targetName = bundles[i].getLocation(); } if ((targetName != null) && targetName.equalsIgnoreCase(name)) { Bundle[] newTargets = new Bundle[targets.length + 1]; System.arraycopy(targets, 0, newTargets, 0, targets.length); newTargets[targets.length] = bundles[i]; targets = newTargets; } } return targets; } private ParsedCommand parseInfo(String commandLine) throws IOException, InvalidSyntaxException { // Create a stream tokenizer for the command line string, // since the syntax for install/start is more sophisticated. StringReader sr = new StringReader(commandLine); StreamTokenizer tokenizer = new StreamTokenizer(sr); tokenizer.resetSyntax(); tokenizer.quoteChar('\''); tokenizer.quoteChar('\"'); tokenizer.whitespaceChars('\u0000', '\u0020'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('0', '9'); tokenizer.wordChars('\u00A0', '\u00FF'); tokenizer.wordChars('.', '.'); tokenizer.wordChars('-', '-'); tokenizer.wordChars('_', '_'); // Ignore the invoking command name and the OBR command. int type = tokenizer.nextToken(); type = tokenizer.nextToken(); int EOF = 1; int SWITCH = 2; int TARGET = 4; int VERSION = 8; int VERSION_VALUE = 16; // Construct an install record. ParsedCommand pc = new ParsedCommand(); String currentTargetName = null; // The state machine starts by expecting either a // SWITCH or a TARGET. int expecting = (TARGET); while (true) { // Get the next token type. type = tokenizer.nextToken(); switch (type) { // EOF received. case StreamTokenizer.TT_EOF: // Error if we weren't expecting EOF. if ((expecting & EOF) == 0) { throw new InvalidSyntaxException( "Expecting more arguments.", null); } // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Return cleanly. return pc; // WORD or quoted WORD received. case StreamTokenizer.TT_WORD: case '\'': case '\"': // If we are expecting a target, the record it. if ((expecting & TARGET) > 0) { // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Set the new target as the current target. currentTargetName = tokenizer.sval; expecting = (EOF | TARGET | VERSION); } else if ((expecting & VERSION_VALUE) > 0) { pc.addTarget(currentTargetName, tokenizer.sval); currentTargetName = null; expecting = (EOF | TARGET); } else { throw new InvalidSyntaxException( "Not expecting '" + tokenizer.sval + "'.", null); } break; // Version separator character received. case ';': // Error if we weren't expecting the version separator. if ((expecting & VERSION) == 0) { throw new InvalidSyntaxException( "Not expecting version.", null); } // Otherwise, we will only expect a version value next. expecting = (VERSION_VALUE); break; } } } private ParsedCommand parseInstallStart(String commandLine) throws IOException, InvalidSyntaxException { // Create a stream tokenizer for the command line string, // since the syntax for install/start is more sophisticated. StringReader sr = new StringReader(commandLine); StreamTokenizer tokenizer = new StreamTokenizer(sr); tokenizer.resetSyntax(); tokenizer.quoteChar('\''); tokenizer.quoteChar('\"'); tokenizer.whitespaceChars('\u0000', '\u0020'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('0', '9'); tokenizer.wordChars('\u00A0', '\u00FF'); tokenizer.wordChars('.', '.'); tokenizer.wordChars('-', '-'); tokenizer.wordChars('_', '_'); // Ignore the invoking command name and the OBR command. int type = tokenizer.nextToken(); type = tokenizer.nextToken(); int EOF = 1; int SWITCH = 2; int TARGET = 4; int VERSION = 8; int VERSION_VALUE = 16; // Construct an install record. ParsedCommand pc = new ParsedCommand(); String currentTargetName = null; // The state machine starts by expecting either a // SWITCH or a TARGET. int expecting = (SWITCH | TARGET); while (true) { // Get the next token type. type = tokenizer.nextToken(); switch (type) { // EOF received. case StreamTokenizer.TT_EOF: // Error if we weren't expecting EOF. if ((expecting & EOF) == 0) { throw new InvalidSyntaxException( "Expecting more arguments.", null); } // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Return cleanly. return pc; // WORD or quoted WORD received. case StreamTokenizer.TT_WORD: case '\'': case '\"': // If we are expecting a command SWITCH and the token // equals a command SWITCH, then record it. if (((expecting & SWITCH) > 0) && tokenizer.sval.equals(NODEPS_SWITCH)) { pc.setResolve(false); expecting = (EOF | TARGET); } // If we are expecting a target, the record it. else if ((expecting & TARGET) > 0) { // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Set the new target as the current target. currentTargetName = tokenizer.sval; expecting = (EOF | TARGET | VERSION); } else if ((expecting & VERSION_VALUE) > 0) { pc.addTarget(currentTargetName, tokenizer.sval); currentTargetName = null; expecting = (EOF | TARGET); } else { throw new InvalidSyntaxException( "Not expecting '" + tokenizer.sval + "'.", null); } break; // Version separator character received. case ';': // Error if we weren't expecting the version separator. if ((expecting & VERSION) == 0) { throw new InvalidSyntaxException( "Not expecting version.", null); } // Otherwise, we will only expect a version value next. expecting = (VERSION_VALUE); break; } } } private ParsedCommand parseUpdate(String commandLine) throws IOException, InvalidSyntaxException { // Create a stream tokenizer for the command line string, // since the syntax for install/start is more sophisticated. StringReader sr = new StringReader(commandLine); StreamTokenizer tokenizer = new StreamTokenizer(sr); tokenizer.resetSyntax(); tokenizer.quoteChar('\''); tokenizer.quoteChar('\"'); tokenizer.whitespaceChars('\u0000', '\u0020'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('0', '9'); tokenizer.wordChars('\u00A0', '\u00FF'); tokenizer.wordChars('.', '.'); tokenizer.wordChars('-', '-'); tokenizer.wordChars('_', '_'); // Ignore the invoking command name and the OBR command. int type = tokenizer.nextToken(); type = tokenizer.nextToken(); int EOF = 1; int SWITCH = 2; int TARGET = 4; int VERSION = 8; int VERSION_VALUE = 16; // Construct an install record. ParsedCommand pc = new ParsedCommand(); String currentTargetName = null; // The state machine starts by expecting either a // SWITCH or a TARGET. int expecting = (SWITCH | TARGET); while (true) { // Get the next token type. type = tokenizer.nextToken(); switch (type) { // EOF received. case StreamTokenizer.TT_EOF: // Error if we weren't expecting EOF. if ((expecting & EOF) == 0) { throw new InvalidSyntaxException( "Expecting more arguments.", null); } // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Return cleanly. return pc; // WORD or quoted WORD received. case StreamTokenizer.TT_WORD: case '\'': case '\"': // If we are expecting a command SWITCH and the token // equals a NODEPS switch, then record it. if (((expecting & SWITCH) > 0) && tokenizer.sval.equals(NODEPS_SWITCH)) { pc.setResolve(false); expecting = (EOF | TARGET); } // If we are expecting a command SWITCH and the token // equals a CHECK swithc, then record it. else if (((expecting & SWITCH) > 0) && tokenizer.sval.equals(CHECK_SWITCH)) { pc.setCheck(true); expecting = (EOF); } // If we are expecting a target, the record it. else if ((expecting & TARGET) > 0) { // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Set the new target as the current target. currentTargetName = tokenizer.sval; expecting = (EOF | TARGET | VERSION); } else if ((expecting & VERSION_VALUE) > 0) { pc.addTarget(currentTargetName, tokenizer.sval); currentTargetName = null; expecting = (EOF | TARGET); } else { throw new InvalidSyntaxException( "Not expecting '" + tokenizer.sval + "'.", null); } break; // Version separator character received. case ';': // Error if we weren't expecting the version separator. if ((expecting & VERSION) == 0) { throw new InvalidSyntaxException( "Not expecting version.", null); } // Otherwise, we will only expect a version value next. expecting = (VERSION_VALUE); break; } } } private ParsedCommand parseSource(String commandLine) throws IOException, InvalidSyntaxException { // Create a stream tokenizer for the command line string, // since the syntax for install/start is more sophisticated. StringReader sr = new StringReader(commandLine); StreamTokenizer tokenizer = new StreamTokenizer(sr); tokenizer.resetSyntax(); tokenizer.quoteChar('\''); tokenizer.quoteChar('\"'); tokenizer.whitespaceChars('\u0000', '\u0020'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('0', '9'); tokenizer.wordChars('\u00A0', '\u00FF'); tokenizer.wordChars('.', '.'); tokenizer.wordChars('-', '-'); tokenizer.wordChars('_', '_'); tokenizer.wordChars('/', '/'); // Ignore the invoking command name and the OBR command. int type = tokenizer.nextToken(); type = tokenizer.nextToken(); int EOF = 1; int SWITCH = 2; int DIRECTORY = 4; int TARGET = 8; int VERSION = 16; int VERSION_VALUE = 32; // Construct an install record. ParsedCommand pc = new ParsedCommand(); String currentTargetName = null; // The state machine starts by expecting either a // SWITCH or a DIRECTORY. int expecting = (SWITCH | DIRECTORY); while (true) { // Get the next token type. type = tokenizer.nextToken(); switch (type) { // EOF received. case StreamTokenizer.TT_EOF: // Error if we weren't expecting EOF. if ((expecting & EOF) == 0) { throw new InvalidSyntaxException( "Expecting more arguments.", null); } // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Return cleanly. return pc; // WORD or quoted WORD received. case StreamTokenizer.TT_WORD: case '\'': case '\"': // If we are expecting a command SWITCH and the token // equals a command SWITCH, then record it. if (((expecting & SWITCH) > 0) && tokenizer.sval.equals(EXTRACT_SWITCH)) { pc.setExtract(true); expecting = (DIRECTORY); } // If we are expecting a directory, the record it. else if ((expecting & DIRECTORY) > 0) { // Set the directory for the command. pc.setDirectory(tokenizer.sval); expecting = (TARGET); } // If we are expecting a target, the record it. else if ((expecting & TARGET) > 0) { // Add current target if there is one. if (currentTargetName != null) { pc.addTarget(currentTargetName, null); } // Set the new target as the current target. currentTargetName = tokenizer.sval; expecting = (EOF | TARGET | VERSION); } else if ((expecting & VERSION_VALUE) > 0) { pc.addTarget(currentTargetName, tokenizer.sval); currentTargetName = null; expecting = (EOF | TARGET); } else { throw new InvalidSyntaxException( "Not expecting '" + tokenizer.sval + "'.", null); } break; // Version separator character received. case ';': // Error if we weren't expecting the version separator. if ((expecting & VERSION) == 0) { throw new InvalidSyntaxException( "Not expecting version.", null); } // Otherwise, we will only expect a version value next. expecting = (VERSION_VALUE); break; } } } private void help(PrintStream out, StringTokenizer st) { String command = HELP_CMD; if (st.hasMoreTokens()) { command = st.nextToken(); } if (command.equals(URLS_CMD)) { out.println(""); out.println("obr " + URLS_CMD + " [ ...]"); out.println(""); out.println( "This command gets or sets the URLs to the repository files\n" + "used by OBR. Specify no arguments to get the current repository\n" + "URLs or specify a space-delimited list of URLs to change the\n" + "URLs. Each URL should point to a file containing meta-data about\n" + "available bundles in XML format."); out.println(""); } else if (command.equals(LIST_CMD)) { out.println(""); out.println("obr " + LIST_CMD + " [ ...]"); out.println(""); out.println( "This command lists bundles available in the bundle repository.\n" + "If no arguments are specified, then all available bundles are\n" + "listed, otherwise any arguments are concatenated with spaces\n" + "and used as a substring filter on the bundle names."); out.println(""); } else if (command.equals(INFO_CMD)) { out.println(""); out.println("obr " + INFO_CMD + " [;] ..."); out.println(""); out.println( "This command displays the meta-data for the specified bundles.\n" + "If a bundle's name contains spaces, then it must be surrounded\n" + "by quotes. It is also possible to specify a precise version\n" + "if more than one version exists, such as:\n" + "\n" + " obr info \"Bundle Repository\";1.0.0\n" + "\n" + "The above example retrieves the meta-data for version \"1.0.0\"\n" + "of the bundle named \"Bundle Repository\"."); out.println(""); } else if (command.equals(DEPLOY_CMD)) { out.println(""); out.println("obr " + DEPLOY_CMD + " [" + NODEPS_SWITCH + "] [;] ... | ..."); out.println(""); out.println( "This command tries to install or update the specified bundles\n" + "and all of their dependencies by default; use the \"" + NODEPS_SWITCH + "\" switch\n" + "to ignore dependencies. You can specify either the bundle name or\n" + "the bundle identifier. If a bundle's name contains spaces, then\n" + "it must be surrounded by quotes. It is also possible to specify a\n" + "precise version if more than one version exists, such as:\n" + "\n" + " obr deploy \"Bundle Repository\";1.0.0\n" + "\n" + "For the above example, if version \"1.0.0\" of \"Bundle Repository\" is\n" + "already installed locally, then the command will attempt to update it\n" + "and all of its dependencies; otherwise, the command will install it\n" + "and all of its dependencies."); out.println(""); } else if (command.equals(INSTALL_CMD)) { out.println(""); out.println("obr " + INSTALL_CMD + " [" + NODEPS_SWITCH + "] [;] ..."); out.println(""); out.println( "This command installs the specified bundles and all of their\n" + "dependencies by default; use the \"" + NODEPS_SWITCH + "\" switch to ignore\n" + "dependencies. If a bundle's name contains spaces, then it\n" + "must be surrounded by quotes. If a specified bundle is already\n" + "installed, then this command has no effect. It is also possible\n" + "to specify a precise version if more than one version exists,\n" + "such as:\n" + "\n" + " obr install \"Bundle Repository\";1.0.0\n" + "\n" + "The above example installs version \"1.0.0\" of the bundle\n" + "named \"Bundle Repository\" and its dependencies. "); out.println(""); } else if (command.equals(START_CMD)) { out.println(""); out.println("obr " + START_CMD + " [" + NODEPS_SWITCH + "] [;] ..."); out.println(""); out.println( "This command installs and starts the specified bundles and all\n" + "of their dependencies by default; use the \"" + NODEPS_SWITCH + "\" switch to\n" + "ignore dependencies. If a bundle's name contains spaces, then\n" + "it must be surrounded by quotes. If a specified bundle is already\n" + "installed, then this command has no effect. It is also possible\n" + "to specify a precise version if more than one version exists,\n" + "such as:\n" + "\n" + " obr start \"Bundle Repository\";1.0.0\n" + "\n" + "The above example installs and starts version \"1.0.0\" of the\n" + "bundle named \"Bundle Repository\" and its dependencies."); out.println(""); } else if (command.equals(UPDATE_CMD)) { out.println(""); out.println("obr " + UPDATE_CMD + " " + CHECK_SWITCH); out.println(""); out.println("obr " + UPDATE_CMD + " [" + NODEPS_SWITCH + "] [;] ... | ..."); out.println(""); out.println( "The first form of the command above checks for available updates\n" + "and the second updates the specified locally installed bundles\n" + "and all of their dependencies by default; use the \"" + NODEPS_SWITCH + "\" switch\n" + "to ignore dependencies. You can specify either the bundle name or\n" + "the bundle identifier. If a bundle's name contains spaces, then\n" + "it must be surrounded by quotes. If a specified bundle is not\n" + "already installed, then this command has no effect. It is also\n" + "possible to specify a precise version if more than one version\n" + "exists, such as:\n" + "\n" + " obr update \"Bundle Repository\";1.0.0\n" + "\n" + "The above example updates version \"1.0.0\" of the bundle named\n" + "\"Bundle Repository\" and its dependencies. The update command may\n" + "install new bundles if the updated bundles have new dependencies."); out.println(""); } else if (command.equals(SOURCE_CMD)) { out.println(""); out.println("obr " + SOURCE_CMD + " [" + EXTRACT_SWITCH + "] [;] ..."); out.println(""); out.println( "This command retrieves the source archives of the specified\n" + "bundles and saves them to the specified local directory; use\n" + "the \"" + EXTRACT_SWITCH + "\" switch to automatically extract the source archives.\n" + "If a bundle name contains spaces, then it must be surrounded\n" + "by quotes. It is also possible to specify a precise version if\n" + "more than one version exists, such as:\n" + "\n" + " obr source /home/rickhall/tmp \"Bundle Repository\";1.0.0\n" + "\n" + "The above example retrieves the source archive of version \"1.0.0\"\n" + "of the bundle named \"Bundle Repository\" and saves it to the\n" + "specified local directory."); out.println(""); } else { out.println("obr " + HELP_CMD + " [" + URLS_CMD + " | " + LIST_CMD + " | " + INFO_CMD + " | " + INSTALL_CMD + " | " + DEPLOY_CMD + " | " + START_CMD + " | " + UPDATE_CMD + " | " + SOURCE_CMD + "]"); out.println("obr " + URLS_CMD + " [ ...]"); out.println("obr " + LIST_CMD + " [ ...]"); out.println("obr " + INFO_CMD + " [;] ..."); out.println("obr " + DEPLOY_CMD + " [" + NODEPS_SWITCH + "] [;] ... | ..."); out.println("obr " + INSTALL_CMD + " [" + NODEPS_SWITCH + "] [;] ..."); out.println("obr " + START_CMD + " [" + NODEPS_SWITCH + "] [;] ..."); out.println("obr " + UPDATE_CMD + " " + CHECK_SWITCH); out.println("obr " + UPDATE_CMD + " [" + NODEPS_SWITCH + "] [;] ... | ..."); out.println("obr " + SOURCE_CMD + " [" + EXTRACT_SWITCH + "] [;] ..."); } } private static class ParsedCommand { private static final int NAME_IDX = 0; private static final int VERSION_IDX = 1; private boolean m_isResolve = true; private boolean m_isCheck = false; private boolean m_isExtract = false; private String m_dir = null; private String[][] m_targets = new String[0][]; public boolean isResolve() { return m_isResolve; } public void setResolve(boolean b) { m_isResolve = b; } public boolean isCheck() { return m_isCheck; } public void setCheck(boolean b) { m_isCheck = b; } public boolean isExtract() { return m_isExtract; } public void setExtract(boolean b) { m_isExtract = b; } public String getDirectory() { return m_dir; } public void setDirectory(String s) { m_dir = s; } public int getTargetCount() { return m_targets.length; } public String getTargetName(int i) { if ((i < 0) || (i >= getTargetCount())) { return null; } return m_targets[i][NAME_IDX]; } public String getTargetVersion(int i) { if ((i < 0) || (i >= getTargetCount())) { return null; } return m_targets[i][VERSION_IDX]; } public void addTarget(String name, String version) { String[][] newTargets = new String[m_targets.length + 1][]; System.arraycopy(m_targets, 0, newTargets, 0, m_targets.length); newTargets[m_targets.length] = new String[] { name, version }; m_targets = newTargets; } } }