/* * 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. * * Original source: Richard S. Hall (heavy@ungoverned.org) * KF Console API adaption: Erik Wistrand (wistrand@knopflerfish.org) * */ package org.knopflerfish.osgi.bundle.bundlerepository; import java.io.*; import java.util.Dictionary; import java.util.Hashtable; import org.osgi.framework.*; import org.ungoverned.osgi.service.bundlerepository.BundleRecord; import org.ungoverned.osgi.service.bundlerepository.BundleRepositoryService; import org.ungoverned.osgi.bundle.bundlerepository.Util; import org.ungoverned.osgi.bundle.bundlerepository.FileUtil; import org.knopflerfish.service.console.*; /** * Export the following OBR commands as a KF console command group "obr" * *
 *  urls [ ...]
 *  list [ ...]
 *  info [;] ...
 *  deploy [-nodeps] [;] ... |  ...
 *  install [-nodeps] [;] ...
 *  start [-nodeps] [;] ...
 *  update -check
 *  update [-nodeps] [;] ... |  ...
 *  source [-x]  [;] ...
 * 
*/ public class ObrCommandGroup extends CommandGroupAdapter { private BundleContext bc = null; private BundleRepositoryService brs = null; protected ServiceRegistration reg = null; public ObrCommandGroup(BundleContext bc) { super("obr", "OBR commands"); this.bc = bc; ServiceReference sr = bc.getServiceReference(BundleRepositoryService.class.getName()); this.brs = (BundleRepositoryService)bc.getService(sr); if(this.brs == null) { throw new RuntimeException("BundleRepositoryService must be available"); } } public void register() { if(reg == null) { Hashtable props = new Hashtable(); props.put("groupName", getGroupName()); reg = bc.registerService(CommandGroup.class.getName(), this, props); } } void unregister() { if(reg != null) { reg.unregister(); reg = null; } } public final static String USAGE_URLS = "[] ..."; public final static String [] HELP_URLS = new String [] { "List or set repository URLs", " repository URL" }; public int cmdUrls(Dictionary opts, Reader in, PrintWriter out, Session session) { String[] urls = (String[])opts.get("url"); if(urls != null && urls.length > 0) { brs.setRepositoryURLs(urls); } else { urls = 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."); } } return 0; } public final static String USAGE_LIST = "[-l] []"; public final static String [] HELP_LIST = new String [] { "List contents of repository", "-l - long format", "name - name (or substring) for bundles to list", " If no name is given, list all bundles.", }; public int cmdList(Dictionary opts, Reader in, PrintWriter out, Session session) { String substr = (String)opts.get("name"); boolean bLong = null != opts.get("-l"); int nCount = 0; int count = brs.getBundleRecordCount(); if(count == 0) { out.println("No bundles in repositories"); } else { if(bLong) { out.println("No Name Update-location"); } else { out.println("No Name"); } for (int i = 0; i < brs.getBundleRecordCount(); i++) { BundleRecord record = brs.getBundleRecord(i); String name = (String) record.getAttribute(BundleRecord.BUNDLE_NAME); if (name != null) { if ((substr == null) || (name.toLowerCase().indexOf(substr) >= 0)) { nCount++; String version = (String) record.getAttribute(BundleRecord.BUNDLE_VERSION); boolean bCit = true; // name.indexOf(" ") != -1; StringBuffer sb = new StringBuffer(); sb.append(" "); sb.append(Integer.toString(i + 1)); pad(sb, 5); if(bCit) { sb.append("\""); } if (version != null) { sb.append(name + ";" + version); } else { sb.append(name); } if(bCit) { sb.append("\""); } if(bLong) { sb.append(" "); pad(sb, 25); sb.append(record.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION)); } out.println(sb.toString()); } } } } if (nCount == 0) { out.println("No matching bundles."); } return 0; } static StringBuffer pad(StringBuffer sb, int len) { while(sb.length() < len) { sb.append(" "); } return sb; } public final static String USAGE_INFO = " ..."; public final static String [] HELP_INFO = new String [] { "Show bundle info", "name - name (or substring) for bundles to list", " if name is an integer, use bundle with", " this index (from obr list)"}; public int cmdInfo(Dictionary opts, Reader in, PrintWriter outPW, Session session) { String[] infos = (String[])opts.get("name;version"); ParsedCommand pc = parseInfo(infos); for (int i = 0; (pc != null) && (i < pc.getTargetCount()); i++) { 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 = brs.getBundleRecords(pc.getTargetName(i)); if (records.length == 1) { record = records[0]; } else { } } else { record = brs.getBundleRecord(pc.getTargetName(i), Util.parseVersionString( pc.getTargetVersion(i))); } if (record != null) { PrintStream outStream = new PrintWriterStream(outPW); record.printAttributes(outStream); } else { outPW.println("Unknown bundle or ambiguous version: " + pc.getTargetName(i)); for (int j = 0; j < brs.getBundleRecordCount(); j++) { BundleRecord r2 = brs.getBundleRecord(j); String name = (String) r2.getAttribute(BundleRecord.BUNDLE_NAME); String version = (String) r2.getAttribute(BundleRecord.BUNDLE_VERSION); if(name.equals(pc.getTargetName(i))) { outPW.println(" \"" + name + ";" + version + "\""); } } } outPW.println(""); } return 0; } public final static String USAGE_DEPLOY = "[-nodeps] ..."; public final static String [] HELP_DEPLOY = new String [] { "Deploy bundle(s)", "name;version - name (and optional version)" }; public int cmdDeploy(Dictionary opts, Reader in, PrintWriter outPW, Session session) { String[] infos = (String[])opts.get("name;version"); ParsedCommand pc = parseInfo(infos); boolean bResolve = (null == opts.get("-nodeps")); 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) { PrintStream outStream = new PrintWriterStream(outPW); brs.deployBundle( outStream, // Output stream. outStream, // Error stream. updateLocation, // Update location. bResolve, // Resolve dependencies. false); // Start. } else { outPW.println("Unknown bundle or ambiguous version: " + pc.getTargetName(i)); return 1; } } return 0; } public final static String USAGE_INSTALL = "[-nodeps] ..."; public final static String [] HELP_INSTALL = new String [] { "Install bundle", "name;version - name and optional version.", " If name starts with '=', use number from obr list", }; public int cmdInstall(Dictionary opts, Reader in, PrintWriter out, Session session) { return doInstallOrStart(opts, in, out, session, false); } public final static String USAGE_START = "[-nodeps] ..."; public final static String [] HELP_START = new String [] { "Install and start bundle", "name;version - name and optional version.", " If name starts with =, use number from obr list", }; public int cmdStart(Dictionary opts, Reader in, PrintWriter out, Session session) { return doInstallOrStart(opts, in, out, session, true); } public int doInstallOrStart(Dictionary opts, Reader in, PrintWriter outPW, Session session, boolean bStart) { String[] infos = (String[])opts.get("name;version"); ParsedCommand pc = parseInfo(infos); boolean bResolve = (null == opts.get("-nodeps")); // 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) { PrintStream outStream = new PrintWriterStream(outPW); brs.deployBundle( outStream, // Output stream. outStream, // Error stream. (String) record.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION), // Update location. bResolve, // Resolve dependencies. bStart); } else { outPW.println("Not in repository: " + targetName); return 1; } } else { outPW.println("Already installed: " + targetName); } } return 0; } public final static String USAGE_UPDATE = "[-nodeps] [-check] ..."; public final static String [] HELP_UPDATE = new String [] { "Update bundle", "name;version - name and optional version" }; public int cmdUpdate(Dictionary opts, Reader in, PrintWriter outPW, Session session) throws IOException { String[] infos = (String[])opts.get("name;version"); boolean bCheck = (null == opts.get("-check")); boolean bResolve = (null == opts.get("-nodeps")); ParsedCommand pc = parseInfo(infos); PrintStream out = new PrintWriterStream(outPW); if (bCheck) { updateCheck(out, out); } 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) { brs.deployBundle( out, // Output stream. out, // Error stream. (String) bundle.getHeaders().get(Constants.BUNDLE_UPDATELOCATION), // Local bundle to update. bResolve, // Resolve dependencies. false); // Start. } else { outPW.println("Not installed: " + targetName); } } } return 0; } private void updateCheck(PrintStream out, PrintStream err) throws IOException { Bundle[] bundles = bc.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 = 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; } } } } } } /* public final static String USAGE_SOURCE = "[-x] ..."; public final static String [] HELP_SOURCE = new String [] { "Get source for a bundle", "name;version - name and optional version" }; public int cmdSource(Dictionary opts, Reader in, PrintWriter outPW, Session session) { String[] infos = (String[])opts.get("name;version"); String localDir = (String)opts.get("localDir"); boolean bExtract = opts.get("-x") != null; if(localDir == null) { localDir = "."; } ParsedCommand pc = parseInfo(infos); PrintStream out = new PrintWriterStream(outPW); 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, out, srcURL, localDir, bExtract); } else { outPW.println("Missing source URL: " + pc.getTargetName(i)); return 1; } } else { outPW.println("Not in repository: " + pc.getTargetName(i)); return 1; } } return 0; } */ private BundleRecord findBundleRecord(String name, String versionString) { BundleRecord record = null; if(name.startsWith("=")) { int id = -1; try { id = Integer.parseInt(name.substring(1)); record = brs.getBundleRecord(id - 1); if(record != null) { System.out.println(id + " " + record.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION)); return record; } } catch (Exception ignored) { } } // If there is no version, then try to retrieve by // name, but error if there are multiple versions. if (versionString == null) { BundleRecord[] records = brs.getBundleRecords(name); if (records.length == 1) { record = records[0]; } } else { record = 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 = bc.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 = bc.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; } ParsedCommand parseInfo(String[] infos) { ParsedCommand pc = new ParsedCommand(); for(int i = 0; infos != null && i < infos.length; i++) { String name = infos[i]; String version = null; int ix = infos[i].indexOf(";"); if(ix != -1) { name = infos[i].substring(0, ix); version = infos[i].substring(ix + 1); } pc.addTarget(name, version); } return pc; } private static class ParsedCommand { private static final int NAME_IDX = 0; private static final int VERSION_IDX = 1; private String[][] m_targets = new String[0][]; 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; } } /** * Wrap a PrintWriter into a PrintStream by overriding all methods. */ public static class PrintWriterStream extends PrintStream { PrintWriter pw; boolean bClose = false; /** * @param pw underlying writer to which all data is send to * @param bClose if true close the underlying writer * when PrintWriterStream.close() is called. */ public PrintWriterStream(PrintWriter pw, boolean bClose) { super(new ByteArrayOutputStream()); // This is really a dummy stream this.pw = pw; this.bClose = bClose; } /** * Same as PrintWriterStream(pw, false) */ public PrintWriterStream(PrintWriter pw) { this(pw, false); } /** * Only closes the underlying stream if * constructued with the close flag. */ public void close() { super.close(); if(bClose) { pw.close(); } } /** * Write using the trivial, but possibly not always correct translation: *
     *  write((int) byte)
     * 
*/ public void write(byte[] buf, int off, int len) { for(int i = off; i < off + len; i++) { write((int)buf[i]); } } public void write(int b) { pw.write(b); } public boolean checkError() { return pw.checkError(); } public void flush() { pw.flush(); } public void print(boolean b) { pw.print(b); } public void print(char c) { pw.print(c); } public void print(char[] s) { pw.print(s); } public void print(double d) { pw.print(d); } public void print(float f) { pw.print(f); } public void print(int i) { pw.print(i); } public void print(long l) { pw.print(l); } public void print(Object obj) { pw.print(obj); } public void print(String s) { pw.print(s); } public void println() { pw.println(); } public void println(boolean x) { pw.println(x); } public void println(char x) { pw.println(x); } public void println(char[] x) { pw.println(x); } public void println(double x) { pw.println(x); } public void println(float x) { pw.println(x); } public void println(int x) { pw.println(x); } public void println(long x) { pw.println(x); } public void println(Object x) { pw.println(x); } public void println(String x) { pw.println(x); } } }