/*
* 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"
*
* <pre>
* urls [<repository-file-url> ...]
* list [<string> ...]
* info <bundle-name>[;<version>] ...
* deploy [-nodeps] <bundle-name>[;<version>] ... | <bundle-id> ...
* install [-nodeps] <bundle-name>[;<version>] ...
* start [-nodeps] <bundle-name>[;<version>] ...
* update -check
* update [-nodeps] <bundle-name>[;<version>] ... | <bundle-id> ...
* source [-x] <local-dir> <bundle-name>[;<version>] ...
* </pre>
*/
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 = "[<url>] ...";
public final static String [] HELP_URLS = new String [] {
"List or set repository URLs",
"<url> 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] [<name>]";
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 = "<name;version> ...";
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),
new Version(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] <name;version> ...";
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] <name;version> ...";
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] <name;version> ...";
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] <name;version> ...";
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.
Version localVersion = bundles[bundleIdx].getVersion();
// 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 remoteVersionS = (String)
records[recordIdx].getAttribute(BundleRecord.BUNDLE_VERSION);
if (remoteVersionS != null) {
Version remoteVersion = new Version(remoteVersionS);
int result = remoteVersion.compareTo(localVersion);
if (result > 0) {
out.println(records[recordIdx].getAttribute(BundleRecord.BUNDLE_NAME)
+ " update available.");
break;
}
}
}
}
}
}
/*
public final static String USAGE_SOURCE = "[-x] <localDir> <name;version> ...";
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, new Version(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, new Version(versionString));
}
return bundle;
}
private Bundle findLocalBundleByVersion(String name, Version 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);
Version targetVersion = targets[i].getVersion();
if ((targetName != null) && targetName.equalsIgnoreCase(name) &&
0==targetVersion.compareTo(version)) {
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 <tt>true</tt> close the underlying writer
* when <tt>PrintWriterStream.close()</tt> 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 <tt>PrintWriterStream(pw, false)</tt>
*/
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:
* <pre>
* write((int) byte)
* </pre>
*/
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);
}
}
}