Package org.knopflerfish.bundle.frameworkcommands

Source Code of org.knopflerfish.bundle.frameworkcommands.FrameworkCommandGroup

/*
* Copyright (c) 2003-2010, KNOPFLERFISH project
* 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 KNOPFLERFISH project 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.
*/

package org.knopflerfish.bundle.frameworkcommands;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.cert.Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;

import org.knopflerfish.service.console.CommandGroupAdapter;
import org.knopflerfish.service.console.Session;
import org.knopflerfish.service.console.Util;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.packageadmin.RequiredBundle;
import org.osgi.service.permissionadmin.PermissionAdmin;
import org.osgi.service.permissionadmin.PermissionInfo;
import org.osgi.service.condpermadmin.ConditionInfo;
import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
import org.osgi.service.startlevel.StartLevel;

// ******************** FrameworkCommandGroup ********************

/**
* Console commands for interaction with the framework.
*
* @author Gatespace AB
*/

public class FrameworkCommandGroup
  extends CommandGroupAdapter
{
  final BundleContext bc;

  private final PackageAdmin packageAdmin;
  private final PermissionAdmin permissionAdmin;
  private final ConditionalPermissionAdmin condPermAdmin;
  private final StartLevel startLevel;

  /**
   * The default directories for bundle jar files.
   *
   * <p>
   *
   * The framework property <code>org.knopflerfish.gosg.jars</code>
   * holds a semicolon separated path of URLs that is used to complete
   * the location when it is given as a partial URL.
   *
   * </p>
   */
  private List/*<URL>*/ baseURLs = new ArrayList();


  FrameworkCommandGroup(BundleContext bc) {
    super("framework", "Framework commands");
    this.bc = bc;

    // all of these services are framework singleton internal services
    // thus, we take a shortcut and skip the service tracking

    ServiceReference sr = bc.getServiceReference(PackageAdmin.class
                                                 .getName());
    packageAdmin = null==sr ? null : (PackageAdmin) bc.getService(sr);

    sr = bc.getServiceReference(PermissionAdmin.class.getName());
    permissionAdmin = null==sr ? null : (PermissionAdmin) bc.getService(sr);

    sr = bc.getServiceReference(ConditionalPermissionAdmin.class.getName());
    condPermAdmin = null==sr ? null
      : (ConditionalPermissionAdmin) bc.getService(sr);

    sr = bc.getServiceReference(StartLevel.class.getName());
    startLevel = null==sr ? null : (StartLevel) bc.getService(sr);

    try {
      setupJars();
    } catch (MalformedURLException mfe) {
    }
  }

  void setupJars()
    throws MalformedURLException
  {
    String jars = bc.getProperty("org.knopflerfish.gosg.jars");
    jars = null==jars || jars.length()==0 ? "file:jars/" : jars;

    final StringTokenizer st = new StringTokenizer(jars, ";");
    final String[] prefixes = new String[st.countTokens()];

    for (int i=0; st.hasMoreTokens(); i++) {
      prefixes[i] = st.nextToken();
    }

    setupJars(prefixes, false);
  }

  void setupJars(final String[] prefixes, boolean append)
    throws MalformedURLException
  {
    MalformedURLException firstMFE = null;
    if (!append) {
      baseURLs.clear();
    }

    for (int i=0; i<prefixes.length; i++) {
      try {
        baseURLs.add(new URL(prefixes[i]));
      } catch (MalformedURLException mfe) {
        if (null==firstMFE){
          firstMFE = mfe;
        }
      }
    }
    if (null!=firstMFE) {
      throw firstMFE;
    }
  }

  /**
   * Completes a partial bundle location using the bundles dir path.
   * The result is the first combination of a directory URL (as
   * returned by <code>getBundleDirs()</code>) and the specified
   * location that results in a valid URL with accessible data.
   *
   * @param location the bundle location to complete
   */
  public String completeLocation(String location) {
    final int ic = location.indexOf(":");
    if (ic < 2 || ic > location.indexOf("/")) {
      // URL wihtout protocol complete it.
      for (Iterator it=baseURLs.iterator(); it.hasNext(); ) {
        final URL baseURL = (URL) it.next();

        try {
          final URL url = new URL(baseURL, location);
          if ("file".equals(url.getProtocol())) {
            final File f = new File(url.getFile());
            if (!f.exists() || !f.canRead()) {
              continue; // Noope; try next.
            }
          } else if ("http".equals(url.getProtocol())) {
            final HttpURLConnection uc = (HttpURLConnection)
              url.openConnection();
            uc.connect();
            final int rc = uc.getResponseCode();
            uc.disconnect();
            if (rc != HttpURLConnection.HTTP_OK) {
              continue; // Noope; try next.
            }
          } else {
            // Generic case; Check if we can read data from this URL
            InputStream is = null;
            try {
              is = url.openStream();
            } finally {
              if (is != null)
                is.close();
            }
          }
          location = url.toString();
          break; // Found.
        } catch (Exception _e) {
        }
      }
    }
    return location;
  }

  //
  // Addpermission command
  //

  public final static String USAGE_ADDPERMISSION
    = "-b #bundle# | -d | -l #location# <type> [<name> [<actions>]]";

  public final static String[] HELP_ADDPERMISSION = new String[] {
    "Add permissions to bundle",
    "-d            Add default permissions",
    "-b #bundle#   Add permission for bundle name or id",
    "-l #location# Add permission for location",
    "<type>        Permission type", "<name>        Permission name",
    "<actions>     Permission actions" };

  public int cmdAddpermission(Dictionary opts, Reader in, PrintWriter out,
                              Session session) {
    if (permissionAdmin == null) {
      out.println("Permission Admin service is not available");
      return 1;
    }
    String loc = null;
    PermissionInfo[] pi;
    String selection = (String) opts.get("-b");
    if (selection != null) {
      Bundle[] b = bc.getBundles();
      Util.selectBundles(b, new String[] { selection });
      for (int i = 0; i < b.length; i++) {
        if (b[i] != null) {
          if (loc == null) {
            loc = b[i].getLocation();
          } else {
            out.println("ERROR! Multiple bundles selected");
            return 1;
          }
        }
      }
      if (loc == null) {
        out.println("ERROR! No matching bundle");
        return 1;
      }
      pi = permissionAdmin.getPermissions(loc);
    } else if (opts.get("-d") != null) {
      pi = permissionAdmin.getDefaultPermissions();
    } else {
      loc = (String) opts.get("-l");
      pi = permissionAdmin.getPermissions(loc);
    }
    PermissionInfo pia;
    try {
      pia = new PermissionInfo((String)opts.get("type"),
                               (String)opts.get("name"),
                               (String)opts.get("actions"));
    } catch (IllegalArgumentException e) {
      out.println("ERROR! " + e.getMessage());
      out.println("PermissionInfo type = " + opts.get("type"));
      out.println("PermissionInfo name = " + opts.get("name"));
      out.println("PermissionInfo actions = " + opts.get("actions"));
      return 1;
    }
    if (pi != null) {
      PermissionInfo[] npi = new PermissionInfo[pi.length + 1];
      System.arraycopy(pi, 0, npi, 0, pi.length);
      pi = npi;
    } else {
      pi = new PermissionInfo[1];
    }
    pi[pi.length - 1] = pia;
    if (loc != null) {
      permissionAdmin.setPermissions(loc, pi);
    } else {
      permissionAdmin.setDefaultPermissions(pi);
    }
    return 0;
  }

  //
  // Bundles command
  //

  public final static String USAGE_BUNDLES
    = "[-1] [-i] [-l] [-s] [-t] [<bundle>] ...";

  public final static String[] HELP_BUNDLES = new String[] {
    "List bundles",
    "-1       One column output",
    "-i       Sort on bundle id",
    "-s       Sort on bundle start level",
    "-t       Sort on last modified time",
    "-l       Verbose output",
    "<bundle> Name or id of bundle"
  };

  public int cmdBundles(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    Bundle[] b = getBundles((String[]) opts.get("bundle"),
                            opts.get("-i") != null,
                            opts.get("-s") != null,
                            opts.get("-t") != null);
    boolean verbose = (opts.get("-l") != null);
    boolean oneColumn = (opts.get("-1") != null);

    Vector tmp = new Vector();
    for (int i = 0; i < b.length; i++) {
      if (b[i] != null) {
        tmp.add(b[i]);
      }
    }

    if (tmp.size() == 0) {
      out.println("ERROR! No matching bundle");
      return 1;
    } else {
      printBundles(out, (Bundle[])tmp.toArray(new Bundle[tmp.size()]), verbose, oneColumn);
      return 0;
    }
  }

  private void printBundles(PrintWriter out, Bundle[] b, boolean verbose, boolean oneColumn) {
    boolean needNl = false;
    // .println("12 5/active CM Commands 2 1/active CM Service");
    String[] lastModified = null;
    if (verbose) {
      lastModified = new String[b.length];
      int longestLM = 0;
      SimpleDateFormat dateFormat = new SimpleDateFormat();
      for (int i = 0; i < b.length; i++) { // Or just look at the first one...
        lastModified[i] = dateFormat.format(new Date(b[i].getLastModified()));
        if (lastModified[i].length() > longestLM) {
          longestLM = lastModified[i].length();
        }
      }
      String lmHeader = "modified";
      if (longestLM > lmHeader.length()) {
        String blank = "                                    ";
        lmHeader += blank.substring(blank.length() - (longestLM - lmHeader.length()));
      }
      out.println("   id  level/state  " + lmHeader + "  location");
      out.println("   ----------------------------------------------");
    } else {
      out.println("   id  level/state name");
      out.println("   --------------------");
    }
    for (int i = 0; i < b.length; i++) {
      String level = null;
      try {
        level = "" + startLevel.getBundleStartLevel(b[i]);
        if (level.length() < 2) {
          level = " " + level;
        }
      } catch (Exception e) {
        // no start level set.
      }

      if (b[i] == null) {
        break;
      }
      if (verbose) {
        out.println(Util.showId(b[i]) + showState(b[i])
                    + " " + lastModified[i]
                    + "  " + b[i].getLocation()
                    + getBundleSpeciality(b[i]));
      } else {
        String s = Util.showId(b[i]) + showState(b[i])
          + Util.shortName(b[i]) + getBundleSpeciality(b[i]);
        if ((i & 1) == 0 && !oneColumn) {
          out.print(s);
          int l = 40 - s.length();
          if (l > 0) {
            String blank = "                                    ";
            out.print(blank.substring(blank.length() - l));
          }
          needNl = true;
        } else {
          out.println(s);
          needNl = false;
        }
      }
    }
    if (needNl) {
      out.println("");
    }
  }

  private String getBundleSpeciality(Bundle bundle) {
    if (packageAdmin == null) {
      return "";
    }
    StringBuffer sb = new StringBuffer();
    Bundle[] fragments = packageAdmin.getFragments(bundle);
    if (fragments != null && fragments.length > 0) {
      sb.append("h:"); // host
      for (int i=0; i<fragments.length;i++){
        if (i>0) sb.append(",");
        sb.append(fragments[i].getBundleId());
      }
    }
    Bundle[] hosts = packageAdmin.getHosts(bundle);
    if (hosts != null && hosts.length > 0) {
      sb.append("f:"); // fragment
      for (int i=0; i<hosts.length;i++){
        if (i>0) sb.append(",");
        sb.append(hosts[i].getBundleId());
      }
    }
    return sb.length()>0 ? (" (" +sb.toString() + ")" ) : "";
  }


  //
  // Call command
  //

  public final static String USAGE_CALL
    = "[-f #filter#] <interface> <method> [<args>] ...";

  public final static String[] HELP_CALL = new String[] {
    "Call a method with zero or more java.lang.String",
    "arguments in a registered service.",
    "-f #filter# Filter to select service, the condition",
    "            '(objectClass=<interface>) is allways added to the filter",
    "<interface> Service interface",
    "<method>    Method in service to call",
    "<args>      Arguments to method. If arguments",
    "            are on the form \"value::type\", the value",
    "            will be attempted to created as the",
    "            specified type", };

  public int cmdCall(final Dictionary opts,
                     final Reader in,
                     final PrintWriter out,
                     final Session session)
  {
    int res = 1;
    final String si = (String) opts.get("interface");
    final String filterArg = (String) opts.get("-f");
    final String filter = null==filterArg
      ? null
      : "(&(objectClass=" +si +")" +filterArg +")";
    final ServiceReference sr = (ServiceReference) AccessController
      .doPrivileged(new PrivilegedAction() {
          public Object run() {
            ServiceReference res = null;
            if (null==filter) {
              res = bc.getServiceReference(si);
              if (null==res) {
                out.println("No service with interface '" + si +"'.");
              }
            } else {
              try {
                ServiceReference[] srs = bc.getServiceReferences(si,filter);
                if (null==srs) {
                  out.println("No service that matches the filter '"
                              +filter +"'.");
                } else if (1==srs.length) {
                  res = srs[0];
                } else {
                  out.println("Multiple service matches the filter '"
                              +filter +"' please narrow it down.");
                }
              } catch (InvalidSyntaxException ise) {
                out.println("Invalid filter '" +filter +"': "
                            +ise.getMessage());
              }
            }
            return res;
          }
        });
    if (sr == null) {
      return 1;
    }
    Object s = AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
          return bc.getService(sr);
        }
      });
    if (s == null) {
      out.println("No such service: " + si);
      return 1;
    }

    String method = (String) opts.get("method");
    Class[] parameterTypes = null;
    Object[] methodArgs = null;
    String[] args = (String[]) opts.get("args");
    if (args == null) {
      args = new String[0];
    }

    methodArgs = new Object[args.length];

    try {
      Method m = findMethod(si, s.getClass(), method, args.length);

      if (m != null) {
        parameterTypes = m.getParameterTypes();
      } else {
        parameterTypes = new Class[args.length];
      }
      for (int i = 0; i < args.length; i++) {
        String val = args[i];
        String className = String.class.getName();
        int ix = val.indexOf("::");
        if (ix != -1) {
          className = val.substring(ix + 2);
          val = val.substring(0, ix);
        }
        if (m == null) {
          methodArgs[i] = makeObject(val, className);
          parameterTypes[i] = getClass(className);
        } else {
          methodArgs[i] = makeObject(val, parameterTypes[i].getName());
        }
      }

      if (m == null) {
        m = s.getClass().getMethod(method, parameterTypes);
      }

      out.println("Result: " + Util.showObject(m.invoke(s, methodArgs)));
      res = 0;
    } catch (InvocationTargetException e) {
      out.println("Exception thrown by call");
      e.getTargetException().printStackTrace(out);
    } catch (IllegalAccessException e) {
      out.println("Call method not accessible (must be public)");
    } catch (NullPointerException e) {
      out.println("Internal error: " + e);
    } catch (IllegalArgumentException e) {
      out.println("Internal error: " + e);
    } catch (NoSuchMethodException e) {
      out.println("No method '" + method + "' with matching arguments: "
                  + e);
    }

    bc.ungetService(sr);
    return res;
  }

  Class getClass(String className) {
    try {
      if ("int".equals(className)) {
        return Integer.TYPE;
      } else if ("boolean".equals(className)) {
        return Boolean.TYPE;
      } else if ("long".equals(className)) {
        return Long.TYPE;
      } else if ("string".equals(className)) {
        return String.class;
      }

      if (-1 == className.indexOf(".")) {
        className = "java.lang." + className;
      }
      return Class.forName(className);
    } catch (Exception e) {
      throw new IllegalArgumentException("Unknown class " + className);
    }
  }

  /**
   * Find the class object for the named interface/class that the
   * specified class implements.
   * @param type  The full name of the class / interface that we are
   *              looking for.
   * @param clazz The class to investigate.
   * @return class object for the specified type.
   */
  Class findClass(final String type, final Class clazz)
  {
    if (type.equals(clazz.getName())) return clazz;

    Class[] clazzes = clazz.getInterfaces();
    for (int i=0; i<clazzes.length; i++) {
      if (type.equals(clazzes[i].getName())) return clazzes[i];
    }
    return findClass(type, clazz.getSuperclass());
  }

  Method findMethod(final String si,
                    final Class clazz,
                    final String name,
                    final int nArgs)
  {
    Class ifClass = findClass(si, clazz);
    // Fallback to use the original class if the one given by si was not found.
    if (null==ifClass) ifClass = clazz;

    Method[] methods = ifClass.getDeclaredMethods();
    Vector v = new Vector();
    for (int i = 0; i < methods.length; i++) {
      if (methods[i].getName().equals(name)
          && methods[i].getParameterTypes().length == nArgs) {
        v.addElement(methods[i]);
      }
    }
    if (v.size() == 1) {
      return (Method) v.elementAt(0);
    }
    return null;
  }

  Object makeObject(String val, String className) {
    try {
      Class clazz = getClass(className);

      if (clazz == Integer.TYPE) {
        return new Integer(val);
      }

      if (clazz == Long.TYPE) {
        return new Long(val);
      }

      if (clazz == Boolean.TYPE) {
        return "true".equals(val) ? Boolean.TRUE : Boolean.FALSE;
      }

      if (clazz == String.class) {
        return val;
      }

      Constructor cons = clazz
        .getConstructor(new Class[] { String.class });
      Object r = cons.newInstance(new Object[] { val });
      return r;
    } catch (Exception e) {
      throw new IllegalArgumentException("makeObject(" + val + ", "
                                         + className + "): " + e);
    }
  }

  //
  // Certificates command
  //

  public final static String USAGE_CERTIFICATES
    = "[ -i ] <bundle> ...";

  public final static String[] HELP_CERTIFICATES = new String[] {
    "List certificates for bundles",
    "-i           Sort on bundle id",
    "<bundle>     Name or id of bundle" };

  public int cmdCertificates(Dictionary opts, Reader in, PrintWriter out,
                              Session session) {
    Bundle[] b = getBundles((String[]) opts.get("bundle"),
                            opts.get("-i") != null);
    boolean match = false;
    for (int i = 0; i < b.length; i++) {
      if (b[i] != null) {
        try {
          boolean found = false;
          Method m = b[i].getClass().getMethod("getCertificates", null);
          Certificate [] cs = (Certificate [])m.invoke(b[i], null);
          out.println("Bundle: " + showBundle(b[i]));
          if (cs != null) {
            for (int j = 0; j < cs.length; j++) {
              out.println("Certificate " + j + ":");
              out.println(cs[j].toString());
              found = true;
            }
          }
          if (!found) {
            out.println("  Not a signed bundle.");
          }
        } catch (Exception e) {
          out.println("This command only works on a Knopflefish framework");
          return 1;
        }
        match = true;
      }
    }
    if (!match) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    return 0;
  }


  //
  // Closure command
  //

  public final static String USAGE_CLOSURE = "<bundle>";

  public final static String[] HELP_CLOSURE = new String[] {
    "Display the closure for a bundle",
    "<bundle> - Name or id of bundle" };

  public int cmdClosure(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {

    if (packageAdmin == null) {
      out.println("Package Admin service is not available");
      return 1;
    }

    String bname = (String) opts.get("bundle");
    Bundle[] bl = getBundles(new String[] { bname }, true);
    Bundle bundle = bl[0];
    if (bundle == null) {
      out.println("ERROR! No matching bundle for '" + bname + "'");
      return 1;
    }

    bl = getBundles(null, false, false, false);

    // Package

    Vector pkgClosure = new Vector();
    // This is O(n2) at least, possibly O(n3). Should be improved
    for(int b = 0; b < bl.length; b++) {
      ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bl[b]);
      if (pkgs == null) continue;
      for(int p = 0; p < pkgs.length; p++) {
        Bundle[] bl2 = pkgs[p].getImportingBundles();
        if (bl2 == null) continue;
        for(int ib = 0; ib < bl2.length;  ib++) {
          if(bl2[ib].getBundleId() == bundle.getBundleId() && !pkgClosure.contains(bl[b])) {
            pkgClosure.add(bl[b]);
          }
        }
      }
    }
    pkgClosure.remove(bundle);
    if (pkgClosure.size() == 0) {
      out.println("No package dependencies");
    } else {
      out.println("Static dependencies via packages:");
      Bundle[] bundles = (Bundle[]) pkgClosure.toArray(new Bundle[pkgClosure.size()]);
      printBundles(out, bundles, false, true);
    }

    // Service

    Vector serviceClosure = new Vector();
    ServiceReference[] srl = bundle.getServicesInUse();
    for (int i = 0; srl != null && i < srl.length; i++) {
      if (!serviceClosure.contains(srl[i].getBundle())) {
        serviceClosure.add(srl[i].getBundle());
      }
    }
    serviceClosure.remove(bundle);
    if(serviceClosure.size() == 0) {
      out.println("No service dependencies");
    } else {
      out.println("Runtime dependencies via services:");
      Bundle[] bundles = (Bundle[]) serviceClosure.toArray(new Bundle[serviceClosure.size()]);
      printBundles(out, bundles, false, true);
    }

    // Fragment

    Bundle[] fragmentBundles = packageAdmin.getFragments(bundle);
    if (fragmentBundles == null) {
      out.println("No fragments");
    } else {
      out.println("Fragments:");
      printBundles(out, fragmentBundles, false, true);
    }

    // Host

    Bundle[] hostBundles = packageAdmin.getHosts(bundle);
    if (hostBundles == null) {
      out.println("No hosts");
    } else {
      out.println("Hosts:");
      printBundles(out, hostBundles, false, true);
    }

    // Required

    Vector required = new Vector();
    Vector requiredBy = new Vector();

    try { // untested code
      RequiredBundle[] requiredBundles = packageAdmin.getRequiredBundles(null);
      if (requiredBundles != null) {
        for (int reqd = 0; reqd < requiredBundles.length; reqd++) {
          Bundle[] requiringBundles = requiredBundles[reqd].getRequiringBundles();
          if (requiringBundles == null) continue;
          if (requiredBundles[reqd].getBundle().equals(bundle)) {
            for (int ring = 0; ring < requiringBundles.length; ring++) {
              requiredBy.add(requiringBundles[ring]);
            }
          } else {
            for (int ring = 0; ring < requiringBundles.length; ring++) {
              if (requiringBundles[ring].equals(bundle)) {
                required.add(requiredBundles[reqd].getBundle());
              }
            }
          }
        }
      }
    } catch (Throwable ignored) {}
    if (required.size() == 0) {
      out.println("No required bundles");
    } else {
      out.println("Required bundles:");
      Bundle[] bundles = (Bundle[]) required.toArray(new Bundle[required.size()]);
      printBundles(out, bundles, false, true);
    }
    if (requiredBy.size() == 0) {
      out.println("No requiring bundles");
    } else {
      out.println("Requiring bundles:");
      Bundle[] bundles = (Bundle[]) requiredBy.toArray(new Bundle[requiredBy.size()]);
      printBundles(out, bundles, false, true);
    }

    return 0;
  }


  //
  // CondPermission command
  //

  public final static String USAGE_CONDPERMISSION
    = "[<name>] ...";

  public final static String[] HELP_CONDPERMISSION = new String[] {
    "Get conditional permissions",
    "<name>               Name of conditional permission" };

  public int cmdCondpermission(Dictionary opts, Reader in, PrintWriter out,
                                  Session session) {
    if (condPermAdmin == null) {
      out.println("Conditional Permission Admin service is not available");
      return 1;
    }
    String [] names = (String []) opts.get("name");
    Enumeration e;
    if (names != null) {
      Vector cpis = new Vector();
      for (int i = 0; i < names.length; i++ ) {
        ConditionalPermissionInfo cpi = condPermAdmin.getConditionalPermissionInfo(names[i]);
        if (cpi != null) {
          cpis.addElement(cpi);
        } else {
          out.println("Didn't find ConditionalPermissionInfo named: " + names[i]);
        }
      }
      e = cpis.elements();
    } else {
      e = condPermAdmin.getConditionalPermissionInfos();
    }
    while (e.hasMoreElements()) {
      // NYI! pretty print
      out.println(e.nextElement().toString());
    }
    return 0;
  }


  //
  // Deletepermission command
  //

  public final static String USAGE_DELETEPERMISSION
    = "[-r] -b #bundle# | -d | -l #location# <type> <name> <actions>";

  public final static String[] HELP_DELETEPERMISSION = new String[] {
    "Delete permissions from a bundle",
    "-b #bundle#   Delete permission for bundle name or id",
    "-d            Delete default permissions",
    "-l #location# Delete permission for location",
    "-r            Remove entry if empty",
    "<type>        Permission type (*, match all)",
    "<name>        Permission name (*, match all)",
    "<actions>     Permission actions (*, match all)" };

  public int cmdDeletepermission(Dictionary opts, Reader in, PrintWriter out,
                                 Session session) {
    if (permissionAdmin == null) {
      out.println("Permission Admin service is not available");
      return 1;
    }
    String loc = null;
    PermissionInfo[] pi;
    String selection = (String) opts.get("-b");
    if (selection != null) {
      Bundle[] b = bc.getBundles();
      Util.selectBundles(b, new String[] { selection });
      for (int i = 0; i < b.length; i++) {
        if (b[i] != null) {
          if (loc == null) {
            loc = b[i].getLocation();
          } else {
            out.println("ERROR! Multiple bundles selected");
            return 1;
          }
        }
      }
      if (loc == null) {
        out.println("ERROR! No matching bundle");
        return 1;
      }
      pi = permissionAdmin.getPermissions(loc);
    } else if (opts.get("-d") != null) {
      pi = permissionAdmin.getDefaultPermissions();
    } else {
      loc = (String) opts.get("-l");
      pi = permissionAdmin.getPermissions(loc);
    }
    if (pi != null) {
      String type = (String) opts.get("type");
      String name = (String) opts.get("name");
      String actions = (String) opts.get("actions");
      int size = 0;
      for (int i = 0; i < pi.length; i++) {
        if (("*".equals(type) || pi[i].getType().equals(type))
            && ("*".equals(name) || pi[i].getName().equals(name))
            && ("*".equals(actions) || pi[i].getActions().equals(
                                                                 actions))) {
          pi[i] = null;
        } else {
          size++;
        }
      }
      if (size == 0) {
        if (opts.get("-r") != null) {
          pi = null;
        } else {
          pi = new PermissionInfo[0];
        }
      } else {
        PermissionInfo[] npi = new PermissionInfo[size];
        for (int i = pi.length - 1; i >= 0; i--) {
          if (pi[i] != null) {
            npi[--size] = pi[i];
          }
        }
        pi = npi;
      }
      if (loc != null) {
        permissionAdmin.setPermissions(loc, pi);
      } else {
        permissionAdmin.setDefaultPermissions(pi);
      }
    }
    return 0;
  }

  //
  // Findbundles command
  //

  public final static String USAGE_FINDBUNDLES = "<symbolic name>";

  public final static String[] HELP_FINDBUNDLES = new String[] {
    "Find bundles with a given symbolic name",
    "<symbolic name>  Symbolic name" };

  public int cmdFindbundles(Dictionary opts, Reader in, PrintWriter out,
                            Session session) {
    if (packageAdmin == null) {
      out.println("Package Admin service is not available");
      return 1;
    }
    String symbolicName = (String) opts.get("symbolic name");
    Bundle[] bundles = packageAdmin.getBundles(symbolicName, null);
    if (bundles == null) {
      out.println("No bundles found.");
    } else {
      printBundles(out, bundles, true, true);
    }
    return 0;
  }

  //
  // Headers command
  //

  public final static String USAGE_HEADERS = "[-i] [-l #locale#] <bundle> ...";

  public final static String[] HELP_HEADERS = new String[] {
    "Show bundle header values",
    "-i           Sort on bundle id",
    "-l #locale#  Get localized headers for a given locale",
    "<bundle>     Name or id of bundle" };

  public int cmdHeaders(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    Bundle[] b = getBundles((String[]) opts.get("bundle"),
                            opts.get("-i") != null);
    String locale = (String) opts.get("-l");
    boolean found = false;
    for (int i = 0; i < b.length; i++) {
      if (b[i] != null) {
        out.println("Bundle: " + showBundle(b[i]));
        Dictionary d = (locale == null ? b[i].getHeaders()
                        : b[i].getHeaders(locale));
        for (Enumeration e = d.keys(); e.hasMoreElements();) {
          String key = (String) e.nextElement();
          out.println("  " + key + " = "
                      + Util.showObject(d.get(key)));
        }
        found = true;
      }
    }
    if (!found) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    return 0;
  }

  //
  // Install command
  //

  public final static String USAGE_INSTALL = "[-s] <location> ...";
  public final static String[] HELP_INSTALL = new String[] {
    "Install one or more bundles",
    "-s         Persistently start bundle(s) according to activation policy",
    "<location> Location of bundle archive (URL).",
    "Note: The base URLs used to complete partial URLs may be set using the cd command"
  };

  public int cmdInstall(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    String[] loc = (String[]) opts.get("location");
    String url = null;
    try {
      for (int i = 0; i < loc.length; i++) {
        url = completeLocation(loc[i]);
        Bundle b = bc.installBundle(url);
        out.println("Installed: " + showBundle(b));
        if (opts.get("-s") != null) {
          b.start(Bundle.START_ACTIVATION_POLICY);
          out.println("Started: " + showBundle(b));
        }
      }
    } catch (BundleException e) {
      Throwable t = e;
      while (t instanceof BundleException
             && ((BundleException) t).getNestedException() != null)
        t = ((BundleException) t).getNestedException();
      if (t instanceof FileNotFoundException) {
        out.println("Couldn't install/start bundle: URL not found: "
                    + url);
      } else {
        out.println("Couldn't install/start bundle: " + url
                    + " (due to: " + t + ")");
        t.printStackTrace(out);
      }
      return 1;
    }
    return 0;
  }

 
  //
  // Meminfo command
  //
  public final static String USAGE_MEMINFO = "[-gc] [-b | -m]";

  public final static String[] HELP_MEMINFO = new String[] {
    "Display java memory information, in kilobytes",
    "-gc  Run garbage collector first",
    "-b   Display using bytes",
    "-m   Display using megabytes" };

  public int cmdMeminfo(Dictionary opts, Reader in, PrintWriter out,
                         Session session) {
    if (opts.get("-gc") != null) {
      System.gc();
    }
   
    int d = 1024;
    String unit = "kB";
    if (opts.get("-b") != null) {
      d = 1;
      unit = "bytes";
    } else if (opts.get("-m") != null) {
      d = d * 1024;
      unit = "MB";
    }
    Runtime r = Runtime.getRuntime();
    out.println("Total: " + (r.totalMemory() + d/2) / d
        + "  Free: " + (r.freeMemory() + d/2) / d
        + "  Max: " + (r.maxMemory() + d/2) / d + "  (" + unit + ")");
    return 0;
  }

 
  //
  // Package command
  //

  public final static String USAGE_PACKAGE = "[-l] -b | -p [<selection>] ...";

  public final static String[] HELP_PACKAGE = new String[] {
    "Show java package information",
    "If no package or bundle is specified show all packages",
    "-l         Verbose output",
    "-b         Only look at selected bundles",
    "-p         Only look at selected packages",
    "<selection>  Package or bundle" };

  public int cmdPackage(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    if (packageAdmin == null) {
      out.println("Package Admin service is not available");
      return 1;
    }
    boolean verbose = opts.get("-l") != null;
    ExportedPackage[] epkgs;
    String[] selection = (String[]) opts.get("selection");
    if (opts.get("-b") != null) {
      Bundle[] b = getBundles(selection, false);
      epkgs = new ExportedPackage[0];
      for (int i = 0; i < b.length; i++) {
        if (b[i] != null) {
          ExportedPackage[] e = packageAdmin
            .getExportedPackages(b[i]);
          if (e != null) {
            if (verbose) {
              ExportedPackage[] ne = new ExportedPackage[e.length
                                                         + epkgs.length];
              System.arraycopy(epkgs, 0, ne, 0, epkgs.length);
              System.arraycopy(e, 0, ne, epkgs.length, e.length);
              epkgs = ne;
            } else {
              out.println("Exported by " + showBundle(b[i]));
              out.println("   Package: " + e[0].getName());
              for (int j = 1; j < e.length; j++) {
                out.println("            " + e[j].getName());
              }
            }
          }
        }
      }
      if (!verbose) {
        return 0;
      }
    } else {
      if (selection != null) {
        epkgs = new ExportedPackage[selection.length];
        for (int i = 0; i < selection.length; i++) {
          epkgs[i] = packageAdmin.getExportedPackage(selection[i]);
        }
      } else {
        epkgs = packageAdmin.getExportedPackages((Bundle)null);
        // TODO: We should sort here
      }
    }
    for (int i = 0; i < epkgs.length; i++) {
      if (epkgs[i] != null) {
        out.print("Package: " + epkgs[i].getName());
        Bundle b = epkgs[i].getExportingBundle();
        if (verbose) {
          out.println();
          out.println("   specification version: "
                      + epkgs[i].getVersion());
          out.println("   removal pending: "
                      + epkgs[i].isRemovalPending());
          out.println("   exporting bundle: " + showBundle(b));
          Bundle[] ib = epkgs[i].getImportingBundles();
          if (ib != null && ib.length > 0) {
            out.println("   importing bundle: "
                        + showBundle(ib[0]));
            for (int j = 1; j < ib.length; j++) {
              out.println("                     "
                          + showBundle(ib[j]));
            }
          }
          out.println();
        } else {
          out.println(" exported by " + showBundle(b));
        }
      } else {
        if (verbose) {
          out.println("Package not found: " + selection[i]);
          out.println();
        }
      }
    }
    return 0;
  }

  //
  // Permissions command
  //

  public final static String USAGE_PERMISSIONS = "[-d] [<selection>] ...";

  public final static String[] HELP_PERMISSIONS = new String[] {
    "Show permission information",
    "If no parameters is given show all entries",
    "-d           Show default permissions",
    "<selection>  Name or id of bundle or an unknown location" };

  public int cmdPermissions(Dictionary opts, Reader in, PrintWriter out,
                            Session session) {
    if (permissionAdmin == null) {
      out.println("Permission Admin service is not available");
      return 1;
    }
    String[] loclist = permissionAdmin.getLocations();
    String[] selection = (String[]) opts.get("selection");
    if (loclist != null && selection != null) {
      Bundle[] b = bc.getBundles();
      Util.selectBundles(b, selection);
      lloop: for (int i = 0; i < loclist.length; i++) {
        for (int j = 0; j < selection.length; j++) {
          if (loclist[i].equals(selection[j])) {
            continue lloop;
          }
        }
        for (int j = 0; j < b.length; j++) {
          if (b[j] != null && loclist[i].equals(b[j].getLocation())) {
            continue lloop;
          }
        }
        loclist[i] = null;
      }
    }

    if (opts.get("-d") != null) {
      out.println("Default permissions");
      showPerms(out, permissionAdmin.getDefaultPermissions());
    }

    if (loclist != null) {
      Bundle[] b = bc.getBundles();
      for (int i = 0; i < loclist.length; i++) {
        if (loclist[i] != null) {
          int j = b.length;
          while (--j >= 0) {
            if (loclist[i].equals(b[j].getLocation())) {
              break;
            }
          }
          out.println("Location: "
                      + loclist[i]
                      + (j >= 0 ? " (Bundle #" + b[j].getBundleId() + ")"
                         : ""));
          showPerms(out, permissionAdmin.getPermissions(loclist[i]));
        }
      }
    }
    return 0;
  }

  //
  // Refresh command
  //

  public final static String USAGE_REFRESH = "[<bundle>] ...";

  public final static String[] HELP_REFRESH = new String[] {
    "Refresh all exported java packages belong to specified bundle",
    "If no bundle is specified refresh all bundles",
    "<bundle> Name or id of bundle" };

  public int cmdRefresh(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    if (packageAdmin == null) {
      out.println("Package Admin service is not available");
      return 1;
    }
    String[] bs = (String[]) opts.get("bundle");
    if (bs != null) {
      Bundle[] b = getBundles(bs, true);
      for (int i = 0; i < b.length; i++) {
        if (b[i] == null) {
          Bundle[] nb = new Bundle[i];
          System.arraycopy(b, 0, nb, 0, nb.length);
          b = nb;
          break;
        }
      }
      if (b.length == 0) {
        out.println("ERROR! No matching bundle");
        return 1;
      }
      packageAdmin.refreshPackages(b);
    } else {
      packageAdmin.refreshPackages(null);
    }
    return 0;
  }

  //
  // Resolve command
  //

  public final static String USAGE_RESOLVE = "[<bundle>] ...";

  public final static String[] HELP_RESOLVE = new String[] {
    "Resolve one or more bundles",
    "If no bundle is specified resolve all bundles",
    "<bundle> Name or id of bundle" };

  public int cmdResolve(Dictionary opts, Reader in, PrintWriter out,
                        Session session) {
    if (packageAdmin == null) {
      out.println("Package Admin service is not available");
      return 1;
    }
    String[] bs = (String[]) opts.get("bundle");
    Bundle[] b = null;
    if (bs != null) {
      b = getBundles(bs, true);
      for (int i = 0; i < b.length; i++) {
        if (b[i] == null) {
          Bundle[] nb = new Bundle[i];
          System.arraycopy(b, 0, nb, 0, nb.length);
          b = nb;
          break;
        }
      }
      if (b.length == 0) {
        out.println("ERROR! No matching bundle");
        return 1;
      }
    }
    packageAdmin.resolveBundles(b);
    return 0;
  }

  //
  // Services command
  //

  public final static String USAGE_SERVICES
    = "[-i] [-l] [-sid #id#] [-f #filter#] [-r] [-u] [<bundle>] ...";

  public final static String[] HELP_SERVICES = new String[] {
    "List registered services",
    "-i          Sort on bundle id",
    "-l          Verbose output",
    "-r          Show services registered by named bundles (default)",
    "-sid #id#   Show the service with the specified service id",
    "-u          Show services used by named bundles",
    "-f #filter# Show all services that matches the specified filter.",
    "<bundle>    Name or id of bundle" };

  public int cmdServices(final Dictionary opts, Reader in,
                         final PrintWriter out, Session session) {
    final Bundle[] b = getBundles((String[]) opts.get("bundle"), opts
                                  .get("-i") != null);
    Integer res = (Integer) AccessController.doPrivileged(new PrivilegedAction()
      {
        public Object run() {
          boolean useDefaultOper = opts.get("-r") == null
            && opts.get("-u") == null;
          ServiceReference[] fs = null;
          String filter = null;
          if (opts.get("-f") != null) {
            filter = (String) opts.get("-f");
            if ('(' != filter.charAt(0)) {
              filter = "(" +filter +")";
            }
          }
          if (opts.get("-sid") != null) {
            if (filter!=null) {
              filter = "(&(service.id=" +opts.get("-sid") +")" +filter +")";
            } else {
              filter = "(service.id=" +opts.get("-sid") +")";
            }
          }
          if (filter != null) {
            try {
              fs = bc.getServiceReferences(null, filter);
              if (null==fs) {
                out.println("No services matching '"+filter +"'.");
                return new Integer(0);
              }
            } catch (InvalidSyntaxException ise) {
              out.println("Invalid filter '" +filter +"' found: "
                          +ise.getMessage());
              return new Integer(1);
            }
          }

          for (int i = 0; i < b.length; i++) {
            if (b[i] != null) {
              final String heading = "Bundle: " + showBundle(b[i]);
              boolean headingPrinted = false;
              if (opts.get("-r") != null || useDefaultOper) {
                headingPrinted =
                  showServices(getServicesRegisteredBy(b[i],fs),
                               out,
                               heading,
                               headingPrinted,
                               "  registered:",
                               opts.get("-l") != null);
              }
              if (opts.get("-u") != null) {
                headingPrinted =
                  showServices(getServicesUsedBy(b[i],fs),
                               out,
                               heading,
                               headingPrinted,
                               "  uses:",
                               opts.get("-l") != null);
              }
            }
          }
          return new Integer(0);
        }
      });
    return res.intValue();
  }

  ServiceReference[] getServicesRegisteredBy(Bundle b,
                                             ServiceReference[] services)
  {
    if (services==null) {
      return b.getRegisteredServices();
    }
    // Filter the given services on registered by.
    long bid = b.getBundleId();
    int count = 0;
    for (int j = 0; j<services.length; j++) {
      if (bid==services[j].getBundle().getBundleId()) {
        count++;
      }
    }
    if (0==count) return null;
    ServiceReference[] res = new ServiceReference[count];
    int ix = 0;
    for (int j = 0; j<services.length; j++) {
      if (bid==services[j].getBundle().getBundleId()) {
        res[ix++] = services[j];
      }
    }
    return res;
  }

  ServiceReference[] getServicesUsedBy(Bundle b,
                                       ServiceReference[] services)
  {
    if (null==services) {
      return b.getServicesInUse();
    }
    // Filter the given services on using bundle.
    long bid = b.getBundleId();
    int count = 0;
    for (int j = 0; j<services.length; j++) {
      Bundle[] usingBundles = services[j].getUsingBundles();
      for (int k=0; usingBundles!=null && k<usingBundles.length; k++) {
        if (bid==usingBundles[k].getBundleId()) {
          count++;
          break;
        }
      }
    }
    if (0==count) return null;
    ServiceReference[] res = new ServiceReference[count];
    int ix = 0;
    for (int j = 0; j<services.length; j++) {
      Bundle[] usingBundles = services[j].getUsingBundles();
      for (int k=0; usingBundles!=null && k<usingBundles.length; k++) {
        if (bid==usingBundles[k].getBundleId()) {
          res[ix++] = services[j];
          break;
        }
      }
    }
    return res;
  }

  boolean showServices(final ServiceReference[] services,
                       final PrintWriter out,
                       final String heading,
                       final boolean headinPrinted,
                       final String title,
                       final boolean detailed)
  {
    if (services != null && services.length > 0) {
      if (!headinPrinted) {
        out.println(heading);
      }
      out.print(title);
      for (int j = 0; j<services.length; j++) {
        if (null!=services[j]) {
          if (detailed) {
            out.print("\n    ");
            showLongService(services[j], "    ", out);
          } else {
            out.print(" "+ Util.showServiceClasses(services[j]));
          }
        }
      }
      out.println("");
      return true;
    }
    return false;
  }

  void showLongService(ServiceReference s, String pad, PrintWriter out) {
    out.print(Util.showServiceClasses(s));
    String[] k = s.getPropertyKeys();
    for (int i = 0; i < k.length; i++) {
      out.print("\n  " + pad + k[i] + " = "
                + Util.showObject(s.getProperty(k[i])));
    }
  }


  //
  // SetCondPermission command
  //

  public final static String USAGE_SETCONDPERMISSION
    = "[-name #name] <conditional_permission_info>...";

  public final static String[] HELP_SETCONDPERMISSION = new String[] {
    "Set conditional permission",
    "-name #name                     Name of conditional permission",
    "<conditional_permission_info>   ConditionalPermissionInfo string",
    "",
    "Example that grants all bundles installed with a file-url all permissions:",
    "> setcondpermission '[org.osgi.service.condpermadmin.BundleLocationCondition \"file:*\"]' (java.security.AllPermission)" };

  public int cmdSetcondpermission(Dictionary opts, Reader in, PrintWriter out,
                                  Session session) {
    if (condPermAdmin == null) {
      out.println("Conditional Permission Admin service is not available");
      return 1;
    }
    String loc = null;
    Vector /* ConditionInfo */ cis = new Vector();
    Vector /* PermissionInfo */ pis = new Vector();
    String name = (String) opts.get("-name");
    String [] cpis = (String []) opts.get("conditional_permission_info");
    String endChar = null;
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < cpis.length; ) {
      String cpi = cpis[i];
      if (endChar != null) {
        buf.append(cpi);
        i++;
        if (cpi.endsWith(endChar)) {
          try {
            if (endChar == "]") {
              cis.addElement(new ConditionInfo(buf.toString()));
            } else {
              pis.addElement(new PermissionInfo(buf.toString()));
            }
          } catch (IllegalArgumentException e) {
            out.println("ERROR! Failed to instanciate: " + buf.toString()
                + " " + e.getMessage());
            return 1;
          }
          endChar = null;
          buf.setLength(0);
        } else {
          buf.append(' ');
        }
      } else if (cpi.startsWith("[")) {
        endChar = "]";
      } else if (cpi.startsWith("(")) {
        endChar = ")";
      } else {
        out.println("ERROR! Expected start char '(' or '[', got: " + cpi);
        return 1;
      }
    }
    ConditionInfo [] cia = (ConditionInfo []) cis.toArray(new ConditionInfo [cis.size()]);
    PermissionInfo [] pia = (PermissionInfo []) pis.toArray(new PermissionInfo [pis.size()]);
    condPermAdmin.setConditionalPermissionInfo(name, cia, pia);
    return 0;
  }

  //
  // Start command
  //

  public final static String USAGE_START = "[-t] [-e] <bundle> ...";
  public final static String[] HELP_START = new String[] {
    "Persistently start one or more bundles according to their ",
    "activation policy.",
    "-t       Perform a transient start. I.e., non-persisten start.",
    "-e       Eagerly start the bundles, ignoring their activation policy.",
    "<bundle> Name or id of bundle"
  };

  public int cmdStart(Dictionary opts, Reader in, PrintWriter out,
                      Session session) {
    int startOptions = 0;
    if (opts.get("-t") != null) {
      startOptions |= Bundle.START_TRANSIENT;
    }
    if (opts.get("-e") == null) {
      startOptions |= Bundle.START_ACTIVATION_POLICY;
    }

    Bundle[] b = getBundles((String[]) opts.get("bundle"), true);
    boolean found = false;
    for (int i = 0; i < b.length; i++) {
      if (b[i] != null) {
        try {
          b[i].start(startOptions);
          out.println("Started: " + showBundle(b[i]));
        } catch (BundleException e) {
          Throwable t = e;
          while (t instanceof BundleException
                 && ((BundleException) t).getNestedException() != null)
            t = ((BundleException) t).getNestedException();
          out.println("Couldn't start bundle: " + showBundle(b[i])
                      + " (due to: " + t + ")");
          t.printStackTrace(out);
        }
        found = true;
      }
    }
    if (!found) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    return 0;
  }

  //
  // Stop command
  //
  public final static String USAGE_STOP = "[-t] <bundle> ...";
  public final static String[] HELP_STOP = new String[] {
    "Persitently stop one or more bundles",
    "-t       Perform a transient stop. I.e., non-persisten stop.",
    "<bundle> Name or id of bundle"
  };

  public int cmdStop(Dictionary opts, Reader in, PrintWriter out,
                     Session session) {
    int stopOptions = 0;
    if (opts.get("-t") != null) {
      stopOptions |= Bundle.STOP_TRANSIENT;
    }

    Bundle[] b = getBundles((String[]) opts.get("bundle"), true);
    boolean found = false;
    for (int i = b.length - 1; i >= 0; i--) {
      if (b[i] != null) {
        try {
          b[i].stop(stopOptions);
          out.println("Stopped: " + showBundle(b[i]));
        } catch (BundleException e) {
          Throwable t = e;
          while (t instanceof BundleException
                 && ((BundleException) t).getNestedException() != null)
            t = ((BundleException) t).getNestedException();
          out.println("Couldn't stop bundle: " + showBundle(b[i])
                      + " (due to: " + t + ")");
        }
        found = true;
      }
    }
    if (!found) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    return 0;
  }

  //
  // Showstate command
  //

  public final static String USAGE_SHOWSTATE = "[<pid>] ...";

  public final static String[] HELP_SHOWSTATE = new String[] {
    "Show the state of a service, if the service provides state information",
    "<pid>     The service pid(s) of interest" };

  public int cmdShowstate(Dictionary opts, Reader in, PrintWriter out,
                          Session session) {
    String[] pids = (String[]) opts.get("pid");
    try {
      if (pids != null && pids.length > 0) {
        for (int i = 0; i < pids.length; i++) {
          showstate(out, bc.getServiceReferences(null, "(service.id="
                                                 + pids[i] + ")"));
        }
      } else
        showstate(out, bc.getServiceReferences(null, "(state=*)"));
    } catch (Exception e) {
      out.println("Error: " + e);
    }
    return 0;
  }

  //
  // Shutdown command
  //

  public final static String USAGE_SHUTDOWN = "[-r]";

  public final static String[] HELP_SHUTDOWN = new String[] {
    "Shutdown framework", "-r Restart framework" };

  public int cmdShutdown(Dictionary opts, Reader in, PrintWriter out,
                         Session session) {

    boolean restart = opts.get("-r") != null;

      Bundle sysBundle = bc.getBundle(0);
      if (restart) {
        try {
          sysBundle.update(); // restart the framework
        } catch (Exception e) {
          out.println("Failed to restart the framework " + e);
          return 1;
        }
      } else {
        try {
          sysBundle.stop(); // shut down the framework
        } catch (Exception e) {
          out.println("Failed to stop using system bundle " + e);
          try {
            System.exit(0);
          } catch (Exception e2) {
            out.println("Failed to exit using system exit " + e2);
            return 1;
          }
        }
      }
    return 0;
  }

  //
  // Threads command
  //

  public final static String USAGE_THREADS = "[-a]";

  public final static String[] HELP_THREADS = new String[] {
    "Display threads within this framework", "-a  List all threads in this JVM" };

  public int cmdThreads(Dictionary opts, Reader in, PrintWriter out,
                         Session session) {
    final boolean showAll = opts.get("-a") != null;
    ThreadGroup tg = Thread.currentThread().getThreadGroup();

    for (ThreadGroup ctg = tg; ctg != null; ctg = ctg.getParent()) {
      if (showAll) {
        tg = ctg;
      } else if (ctg.getName().startsWith("FW#")) {
        tg = ctg;
        break;
      }
    }
   
    Thread [] threads;
    int count;
    while (true) {
      int acount = tg.activeCount() + 5;
      threads = new Thread[acount];
      count = tg.enumerate(threads);
      if (count < acount) {
        break;
      }
    }
    int groupCols = tg.getName().length();
    boolean sameGroup = true;
    for (int i = 0; i < count; i++) {
      ThreadGroup itg = threads[i].getThreadGroup();
      if (!tg.equals(itg)) {
        int cols = itg.getName().length();
        if (groupCols < cols) {
          groupCols = cols;
        }
        sameGroup = false;
      }
    }
    out.print("Pri ");
    if (!sameGroup) {
      String glabel = "Group                              ";
      if (groupCols < 4) {
        groupCols = 4;
      }
      if (++groupCols > glabel.length()) {
        groupCols = glabel.length();
        out.print(glabel);
      } else {
        out.print(glabel.substring(0, groupCols));
      }
    }
    out.println("Name");
    for (int i = 0; i < count; i++) {
      try {
        StringBuffer sb = new StringBuffer();
        int p = threads[i].getPriority();
        if (p < 10) {
          sb.append(' ');
        }
        sb.append(p);
        do {
          sb.append(' ');
        } while (sb.length() < 4);
        if (!sameGroup) {
          String g = threads[i].getThreadGroup().getName();
          sb.append(g);
          int l = g.length();
          do {
            sb.append(' ');
            l++;
          } while (l < groupCols);
        }
        sb.append(threads[i].getName());
        out.println(sb.toString());
      } catch (NullPointerException _ignore) {
        // Handle disappering thread
      }
    }
    return 0;
  }


  //
  // Uninstall command
  //

  public final static String USAGE_UNINSTALL = "<bundle> ...";

  public final static String[] HELP_UNINSTALL = new String[] {
    "Uninstall one or more bundles", "<bundle> Name or id of bundle" };

  public int cmdUninstall(Dictionary opts, Reader in, PrintWriter out,
                          Session session) {
    Bundle[] b = getBundles((String[]) opts.get("bundle"), true);
    boolean found = false;
    for (int i = 0; i < b.length; i++) {
      if (b[i] != null) {
        try {
          b[i].uninstall();
          out.println("Uninstalled: " + showBundle(b[i]));
        } catch (BundleException e) {
          Throwable t = e;
          while (t instanceof BundleException
                 && ((BundleException) t).getNestedException() != null)
            t = ((BundleException) t).getNestedException();
          out.println("Couldn't uninstall: " + showBundle(b[i])
                      + " (due to: " + t + ")");
        }
        found = true;
      }
    }
    if (!found) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    return 0;
  }

  //
  // Update command
  //

  public final static String USAGE_UPDATE = "<bundle> ...";

  public final static String[] HELP_UPDATE = new String[] {
    "Update one or more bundles",
    "<bundle> Name or id of bundle",
    "Note: Use refresh command to force the framework to do a package update",
    "of exported packages used by running bundles." };

  public int cmdUpdate(Dictionary opts, Reader in, PrintWriter out,
                       Session session) {
    Bundle[] b = getBundles((String[]) opts.get("bundle"), true);
    boolean found = false;
    for (int i = b.length - 1; i >= 0; i--) {
      if (b[i] != null) {
        try {
          b[i].update();
          out.println("Updated: " + showBundle(b[i]));
        } catch (BundleException e) {
          Throwable t = e;
          while (t instanceof BundleException
                 && ((BundleException) t).getNestedException() != null)
            t = ((BundleException) t).getNestedException();
          out.println("Couldn't update: " + showBundle(b[i])
                      + " (due to: " + t + ")");
        }
        found = true;
      }
    }
    if (!found) {
      out.println("ERROR! No matching bundle");
      return 1;
    }
    out
      .println("Note: Use refresh command to update exported packages in running bundles");
    return 0;
  }

  public final static String USAGE_FROMUPDATE = "<bundle> <url>";

  public final static String[] HELP_FROMUPDATE = new String[] {
    "Update a bundle from a specific URL",
    "<bundle> - Name or id of bundle",
    "<url>    - URL to update from",
    "Note 1: Use refresh command to force the framework to do a package update",
    "of exported packages used by running bundles.",
    "Note 2: The base URLs used to complete partial URLs may be set using the cd command"
  };

  public int cmdFromupdate(Dictionary opts, Reader in, PrintWriter out,
                           Session session) {
    String bname = (String) opts.get("bundle");
    Bundle[] bl = getBundles(new String[] { bname }, true);
    String fromURL = completeLocation((String) opts.get("url"));

    Bundle b = bl[0];
    if (b == null) {
      out.println("ERROR! No matching bundle for '" + bname + "'");
      return 1;
    }

    try {
      URL url = new URL(fromURL);
      URLConnection conn = url.openConnection();
      InputStream inStream = conn.getInputStream();
      b.update(inStream);
      out.println("Updated: " + showBundle(b));
    } catch (BundleException e) {
      Throwable t = e;
      while (t instanceof BundleException
             && ((BundleException) t).getNestedException() != null)
        t = ((BundleException) t).getNestedException();
      out.println("Couldn't update: " + showBundle(b) + " (due to: " + t
                  + ")");
    } catch (Exception e) {
      out.println("Couldn't update: " + showBundle(b) + " (due to: " + e
                  + ")");
    }

    out.println("Note: Use refresh command to update exported packages "
                +"in running bundles");
    return 0;
  }

  public final static String USAGE_FROMINSTALL = "<url> [<location>]";

  public final static String[] HELP_FROMINSTALL = new String[] {
    "Install a bundle with a specific location from an URL",
    "<url>      - URL to bundle jar file",
    "<location> - Optional location string to use for installation",
    "Note: The base URLs used to complete partial URLs may be set using the cd command"
  };

  public int cmdFrominstall(Dictionary opts, Reader in, PrintWriter out,
                            Session session) {
    final String fromURL = completeLocation( (String) opts.get("url"));
    String loc = (String) opts.get("location");

    if (loc == null) {
      loc = fromURL;
    }

    try {
      URL url = new URL(fromURL);
      URLConnection conn = url.openConnection();
      InputStream inStream = conn.getInputStream();
      Bundle b = bc.installBundle(loc, inStream);
      out.println("Installed: " + showBundle(b));
    } catch (BundleException e) {
      Throwable t = e;
      while (t instanceof BundleException
             && ((BundleException) t).getNestedException() != null)
        t = ((BundleException) t).getNestedException();
      out.println("Couldn't install: url=" + fromURL + ", location="
                  + loc + " (due to: " + t + ")");
    } catch (Exception e) {
      out.println("Couldn't install: url=" + fromURL + ", location="
                  + loc + " (due to: " + e + ")");
    }

    return 0;
  }

  //
  // Private methods
  //

  private void showstate(PrintWriter out, ServiceReference[] srs) {
    if (srs != null) {
      for (int i = 0; i < srs.length; i++) {
        Object state = srs[i].getProperty("state");
        if (state != null) {
          out.println("State for " + srs[i].getProperty("service.id")
                      + ":");
          out.println(state.toString());
        }
      }
    }
  }

  private Bundle[] getBundles(String[] selection, boolean sortNumeric) {
    return getBundles(selection, sortNumeric, false, false);
  }

  private Bundle[] getBundles(String[] selection,
                              boolean sortNumeric,
                              boolean sortStartLevel) {
    return getBundles(selection, sortNumeric, sortStartLevel, false);
  }

  private Bundle[] getBundles(String[] selection,
                              boolean sortNumeric,
                              boolean sortStartLevel,
                              boolean sortTime) {
    Bundle[] b = bc.getBundles();
    Util.selectBundles(b, selection);
    if (sortNumeric) {
      Util.sortBundlesId(b);
    } else {
      Util.sortBundles(b, false);
    }
    if (sortStartLevel) {
      sortBundlesStartLevel(b);
    }
    if (sortTime) {
      Util.sortBundlesTime(b);
    }

    return b;
  }

  /**
   * Sort an array of bundle objects based on their start level All entries
   * with no start level is placed at the end of the array.
   *
   * @param b
   *            array of bundles to be sorted, modified with result
   */
  protected void sortBundlesStartLevel(Bundle[] b) {
    int x = b.length;

    for (int l = x; x > 0;) {
      x = 0;
      int p = Integer.MAX_VALUE;
      try {
        p = startLevel.getBundleStartLevel(b[0]);
      } catch (Exception ignored) {
      }
      for (int i = 1; i < l; i++) {
        int n = Integer.MAX_VALUE;
        try {
          n = startLevel.getBundleStartLevel(b[i]);
        } catch (Exception ignored) {
        }
        if (p > n) {
          x = i - 1;
          Bundle t = b[x];
          b[x] = b[i];
          b[i] = t;
        } else {
          p = n;
        }
      }
    }
  }

  public String showState(Bundle bundle) {
    StringBuffer sb = new StringBuffer();

    try {
      StringBuffer s = new StringBuffer
        (Integer.toString(startLevel.getBundleStartLevel(bundle)));
      while (s.length() < 2) {
        s.insert(0, " ");
      }
      sb.append(s.toString());
    } catch (Exception ignored) {
      sb.append("--");
    }

    sb.append("/");

    switch (bundle.getState()) {
    case Bundle.INSTALLED:
      sb.append("installed");
      break;
    case Bundle.RESOLVED:
      sb.append("resolved");
      break;
    case Bundle.STARTING:
      sb.append("starting");
      break;
    case Bundle.ACTIVE:
      sb.append("active");
      break;
    case Bundle.STOPPING:
      sb.append("stopping");
      break;
    case Bundle.UNINSTALLED:
      sb.append("uninstalled");
      break;
    default:
      sb.append("ILLEGAL <" + bundle.getState() + "> ");
      break;
    }
    while (sb.length() < 13) {
      sb.append(" ");
    }

    return sb.toString();
  }

  String showBundle(Bundle b) {
    return Util.shortName(b) + " (#" + b.getBundleId() + ")";
  }

  private void showPerms(PrintWriter out, PermissionInfo[] pi) {
    final String shift = "    ";
    if (pi == null) {
      out.println(shift + "DEFAULT");
    } else if (pi.length == 0) {
      out.println(shift + "NONE");
    } else {
      for (int i = 0; i < pi.length; i++) {
        out.println(shift + pi[i]);
      }
    }
  }

  //
  // Set start level command
  //
  public final static String USAGE_STARTLEVEL = "[<level>]";

  public final static String[] HELP_STARTLEVEL = new String[] {
    "Shows or sets the global startlevel", "[<level>] new start level",
    "          if no <level> is provided, show current level", };

  public int cmdStartlevel(Dictionary opts, Reader in, PrintWriter out,
                           Session session) {

    String levelStr = (String) opts.get("level");

    try {
      if (levelStr != null) {
        int level = Integer.parseInt(levelStr);
        startLevel.setStartLevel(level);
      } else {
        out.println("current start level:        "
                    + startLevel.getStartLevel());
        out.println("initial bundle start level: "
                    + startLevel.getInitialBundleStartLevel());
      }
      return 0;
    } catch (Exception e) {
      out.println("Failed to show/set startlevel=" + levelStr);
      e.printStackTrace(out);
      return -1;
    }
  }

  //
  // CD command
  //
  public final static String USAGE_CD = "[-reset] [-a] [<base URL>] ...";

  public final static String[] HELP_CD = new String[] {
    "Shows or sets the base URLs used to complete bundle location",
    "when installing bundles.",
    "[-reset]       reset the base URL list to the startup value.",
    "[-a]           append given base URLs to the current list.",
    "[<base URL>] ... new list of base URLs to be used.", };

  public int cmdCd(Dictionary opts, Reader in, PrintWriter out,
                   Session session) {

    final String[] baseURLsArg = (String[]) opts.get("base URL");
    final boolean append = opts.get("-a") != null;

    try {
      if (opts.get("-reset") != null) {
        setupJars();
      }
      if (baseURLsArg == null) {
        for (Iterator it = baseURLs.iterator(); it.hasNext(); ) {
          out.println(" " + it.next());
        }
      } else {
        setupJars(baseURLsArg, append);
      }
      return 0;
    } catch (Exception e) {
      out.println("Failed to cd: "+e);
      e.printStackTrace(out);
      return -1;
    }
  }

  //
  // Set bundle start level
  //
  public final static String USAGE_BUNDLELEVEL = "<level> [<bundle>] ...";

  public final static String[] HELP_BUNDLELEVEL = new String[] {
    "Set startlevel(s) for bundles", "<level>   new start level",
    "<bundle>  Name or id of bundles",
    "          If bundle list is empty, set initial",
    "          start level for new bundles", };

  public int cmdBundlelevel(Dictionary opts, Reader in, PrintWriter out,
                            Session session) {
    int level = -1;
    try {
      level = Integer.parseInt((String) opts.get("level"));
      String[] bls = (String[]) opts.get("bundle");
      Bundle[] bl = getBundles(bls, false, false);

      if (bls == null || bls.length == 0) {
        startLevel.setInitialBundleStartLevel(level);
        out.println("initial bundle start level set to " + level);
      } else {
        for (int i = 0; i < bl.length; i++) {
          if (bl[i] != null) {
            System.out.println("set " + i + " " + bl[i] + " "
                               + level);
            startLevel.setBundleStartLevel(bl[i], level);
          }
        }
      }
      return 0;
    } catch (Exception e) {
      out.println("Failed to set bundle startlevel=" + level);
      e.printStackTrace(out);
      return -1;
    }
  }

  //
  // Property command
  //
  public final static String USAGE_PROPERTY = "[-s] [-f] [<property>] ...";

  public final static String[] HELP_PROPERTY = new String[] {
    "Lists Framework and System properties with values.",
    "If no property name is specified all properties will be listed.",
    "[-f]             Only list framework properties, i.e., those mentioned in",
    "                 the OSGi specification and other properties that are",
    "                 known to be present in the frameworks property map.",
    "[-s]             Show the value returned by System.getProperty().",
    "                 The default is to show the value returned by",
    "                 BundleContext.getProperty().",
    "[<property>] ... Property keys to include in the list.", };

  public int cmdProperty(Dictionary opts, Reader in, PrintWriter out,
                         Session session)
  {
    final boolean sysProps = opts.get("-s") != null;
    final boolean includeFwPros = opts.get("-f") != null;
    final String[] propNamesA = (String[]) opts.get("property");

    try {
      final Set propNames = new TreeSet();
      if (includeFwPros) {
        // -f
        propNames.addAll(getAllFrameworkPropKeys());
      } else if (null!=propNamesA) {
        // List specified props
        propNames.addAll(Arrays.asList(propNamesA));
      } else {
        // List all props
        propNames.addAll(getAllFrameworkPropKeys());
        propNames.addAll(getAllSystemPropKeys());
      }

      for(Iterator it = propNames.iterator(); it.hasNext();) {
        final String key = (String) it.next();
        final String val = sysProps
          ? (String) System.getProperty(key)
          : (String) bc.getProperty(key);
        if (null!=val) {
          out.println("  " + key + " : " + val);
        }
      }

      return 0;
    } catch (Exception e) {
      out.println("Failed to print props values: "+e);
      e.printStackTrace(out);
      return -1;
    }
  }
  // The key under which the KF-framework keeps a comma-separated list
  // of all framework property keys.
  public static final String fwPropKeysKey
    = "org.knopflerfish.framework.bundleprops.keys";
  public static final Set FW_PROP_NAMES = new HashSet() {{
    add(Constants.FRAMEWORK_VENDOR);
    add(Constants.FRAMEWORK_VERSION);
    add(Constants.FRAMEWORK_LANGUAGE);
    add(Constants.FRAMEWORK_OS_NAME);
    add(Constants.FRAMEWORK_OS_VERSION);
    add(Constants.FRAMEWORK_PROCESSOR);
    add(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
    add(Constants.FRAMEWORK_BOOTDELEGATION);
    add(Constants.FRAMEWORK_STORAGE);
    add(Constants.FRAMEWORK_STORAGE_CLEAN);
    add(Constants.FRAMEWORK_TRUST_REPOSITORIES);
    add(Constants.FRAMEWORK_EXECPERMISSION);
    add(Constants.FRAMEWORK_LIBRARY_EXTENSIONS);
    add(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
    add(Constants.FRAMEWORK_BUNDLE_PARENT);
    add(Constants.FRAMEWORK_WINDOWSYSTEM);
    add(Constants.FRAMEWORK_SECURITY);
    add(Constants.SUPPORTS_FRAMEWORK_EXTENSION);
    add(Constants.SUPPORTS_FRAMEWORK_FRAGMENT);
    add(Constants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE);
  }};
  // The set of keys for all Framework properties
  private Set getAllFrameworkPropKeys()
  {
    final HashSet res = new HashSet();

    // Keys of properites mentioned in the OSGi specification.
    res.addAll(FW_PROP_NAMES);

    // All available keys from a property mainained by the
    // Knopflerfish Framewwork implementation for this purpose.
    final String fwPropKeys = bc.getProperty(fwPropKeysKey);
    if (null!=fwPropKeys) {
      final StringTokenizer st = new StringTokenizer(fwPropKeys,",");
      while (st.hasMoreTokens()) {
        final String key = ((String) st.nextToken()).trim();
        res.add(key);
      }
    }
    return res;
  }
  // The set of keys for all System properties
  private Set getAllSystemPropKeys()
  {
    final HashSet res = new HashSet();

    final Properties properties = System.getProperties();
    for (Enumeration pke = properties.propertyNames(); pke.hasMoreElements();){
      final String key = (String) pke.nextElement();
      res.add(key);
    }
    return res;
  }


}
TOP

Related Classes of org.knopflerfish.bundle.frameworkcommands.FrameworkCommandGroup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.