You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2005/08/16 20:34:41 UTC

svn commit: r233031 [3/21] - in /incubator/oscar/trunk: ./ etc/ lib/ src/ src/org/ src/org/apache/ src/org/apache/osgi/ src/org/apache/osgi/bundle/ src/org/apache/osgi/bundle/bundlerepository/ src/org/apache/osgi/bundle/bundlerepository/kxmlsax/ src/or...

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/RepositoryState.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/RepositoryState.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/RepositoryState.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/RepositoryState.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,563 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import org.apache.osgi.bundle.bundlerepository.kxmlsax.KXmlSAXParser;
+import org.apache.osgi.bundle.bundlerepository.metadataparser.MultivalueMap;
+import org.apache.osgi.bundle.bundlerepository.metadataparser.XmlCommonHandler;
+import org.apache.osgi.service.bundlerepository.BundleRecord;
+import org.apache.osgi.service.bundlerepository.ResolveException;
+import org.osgi.framework.*;
+
+public class RepositoryState
+{
+    private BundleContext m_context = null;
+    private String[] m_urls = null;
+    private Map m_recordMap = new HashMap();
+    private BundleRecord[] m_recordArray = null;
+    private boolean m_initialized = false;
+
+    private int m_hopCount = 1;
+
+    private static final String[] DEFAULT_REPOSITORY_URL = {
+        "http://oscar-osgi.sf.net/alpha/repository.xml"
+    };
+    public static final String REPOSITORY_URL_PROP = "osgi.repository.url";
+    public static final String EXTERN_REPOSITORY_TAG = "extern-repositories";
+
+    public RepositoryState(BundleContext context)
+    {
+        m_context = context;
+
+        String urlStr = m_context.getProperty(REPOSITORY_URL_PROP);
+        if (urlStr != null)
+        {
+            StringTokenizer st = new StringTokenizer(urlStr);
+            if (st.countTokens() > 0)
+            {
+                m_urls = new String[st.countTokens()];
+                for (int i = 0; i < m_urls.length; i++)
+                {
+                    m_urls[i] = st.nextToken();
+                }
+            }
+        }
+
+        // Use the default URL if none were specified.
+        if (m_urls == null)
+        {
+            m_urls = DEFAULT_REPOSITORY_URL;
+        }
+    }
+
+    public String[] getURLs()
+    {
+        // Return a copy because the array is mutable.
+        return (m_urls == null) ? null : (String[]) m_urls.clone();
+    }
+
+    public void setURLs(String[] urls)
+    {
+        if (urls != null)
+        {
+            m_urls = urls;
+            initialize();
+        }
+    }
+    
+    public BundleRecord[] getRecords()
+    {
+        if (!m_initialized)
+        {
+            initialize();
+        }
+
+        // Returned cached array of bundle records.
+        return m_recordArray;
+    }
+
+    public BundleRecord[] getRecords(String symName)
+    {
+        if (!m_initialized)
+        {
+            initialize();
+        }
+
+        // Return a copy of the array, since it would be mutable
+        // otherwise.
+        BundleRecord[] records = (BundleRecord[]) m_recordMap.get(symName);
+        // Return a copy because the array is mutable.
+        return (records == null) ? null : (BundleRecord[]) records.clone();
+    }
+
+    public BundleRecord getRecord(String symName, int[] version)
+    {
+        if (!m_initialized)
+        {
+            initialize();
+        }
+
+        BundleRecord[] records = (BundleRecord[]) m_recordMap.get(symName);
+        if ((records != null) && (records.length > 0))
+        {
+            for (int i = 0; i < records.length; i++)
+            {
+                int[] targetVersion = Util.parseVersionString(
+                    (String) records[i].getAttribute(BundleRecord.BUNDLE_VERSION));
+            
+                if (Util.compareVersion(targetVersion, version) == 0)
+                {
+                    return records[i];
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public BundleRecord[] resolvePackages(LocalState localState, Filter[] reqFilters)
+        throws ResolveException
+    {
+        // Create a list that will contain the transitive closure of
+        // all import dependencies; use a list because this will keep
+        // everything in order.
+        List deployList = new ArrayList();
+        // Add the target bundle
+        resolvePackages(localState, reqFilters, deployList);
+        
+        // Convert list of symbolic names to an array of bundle
+        // records and return it.
+        BundleRecord[] records = new BundleRecord[deployList.size()];
+        return (BundleRecord[]) deployList.toArray(records);
+    }
+
+    private void resolvePackages(
+        LocalState localState, Filter[] reqFilters, List deployList)
+        throws ResolveException
+    {
+        for (int reqIdx = 0;
+            (reqFilters != null) && (reqIdx < reqFilters.length);
+            reqIdx++)
+        {
+            // If the package can be locally resolved, then
+            // it can be completely ignored; otherwise, try
+            // to find a resolving bundle.
+            if (!localState.isResolvable(reqFilters[reqIdx]))
+            {
+                // Select resolving bundle for current package.
+                BundleRecord source = selectResolvingBundle(
+                    deployList, localState, reqFilters[reqIdx]);
+                // If there is no resolving bundle, then throw a
+                // resolve exception.
+                if (source == null)
+                {
+throw new IllegalArgumentException("HACK: SHOULD THROW RESOLVE EXCEPTION: " + reqFilters[reqIdx]);
+//                    throw new ResolveException(reqFilters[reqIdx]);
+                }
+                // If the resolving bundle is already in the deploy list,
+                // then just ignore it; otherwise, add it to the deploy
+                // list and resolve its packages.
+                if (!deployList.contains(source))
+                {
+                    deployList.add(source);
+                    Filter[] filters = (Filter[])
+                        source.getAttribute("requirements");
+                    resolvePackages(localState, filters, deployList);
+                }
+            }
+        }
+    }
+
+    /**
+     * Selects a single source bundle record for the target package from
+     * the repository. The algorithm tries to select a source bundle record
+     * if it is already installed locally in the framework; this approach
+     * favors updating already installed bundles rather than installing
+     * new ones. If no matching bundles are installed locally, then the
+     * first bundle record providing the target package is returned.
+     * @param targetPkg the target package for which to select a source
+     *        bundle record.
+     * @return the selected bundle record or <tt>null</tt> if no sources
+     *         could be found.
+    **/
+    private BundleRecord selectResolvingBundle(
+        List deployList, LocalState localState, Filter targetFilter)
+    {
+        BundleRecord[] exporters = findExporters(targetFilter);
+        if (exporters == null)
+        {
+            return null;
+        }
+
+        // Try to select a source bundle record that is already
+        // in the deployed list to minimize the number of bundles
+        // that need to be deployed. If this is not possible, then
+        // try to select a bundle that is already installed locally,
+        // since it might be possible to update this bundle to
+        // minimize the number of bundles installed in the framework.
+        for (int i = 0; i < exporters.length; i++)
+        {
+            if (deployList.contains(exporters[i]))
+            {
+                return exporters[i];
+            }
+            else
+            {
+                String symName = (String)
+                    exporters[i].getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME);
+                if (symName != null)
+                {
+                    BundleRecord[] records = localState.findBundles(symName);
+                    if (records != null)
+                    {
+                        return exporters[i];
+                    }
+                }
+            }
+        }
+            
+        // If none of the sources are installed locally, then
+        // just pick the first one.
+        return exporters[0];
+    }
+
+    /**
+     * Returns an array of bundle records that resolve the supplied
+     * package declaration.
+     * @param target the package declaration to resolve.
+     * @return an array of bundle records that resolve the package
+     *         declaration or <tt>null</tt> if none are found.
+    **/
+    private BundleRecord[] findExporters(Filter targetFilter)
+    {
+        MapToDictionary mapDict = new MapToDictionary(null);
+
+        // Create a list for storing bundles that can resolve package.
+        List resolveList = new ArrayList();
+        for (int recIdx = 0; recIdx < m_recordArray.length; recIdx++)
+        {
+            Map[] capMaps = (Map[]) m_recordArray[recIdx].getAttribute("capability");
+            for (int capIdx = 0; capIdx < capMaps.length; capIdx++)
+            {
+                mapDict.setSourceMap(capMaps[capIdx]);
+                if (targetFilter.match(mapDict))
+                {
+                    resolveList.add(m_recordArray[recIdx]);
+                }
+            }
+        }
+
+        // If no resolving bundles were found, return null.
+        if (resolveList.size() == 0)
+        {
+            return null;
+        }
+
+        // Otherwise, return an array containing resolving bundles.
+        return (BundleRecord[]) resolveList.toArray(new BundleRecord[resolveList.size()]);
+    }
+
+    private boolean isUpdateAvailable(
+        PrintStream out, PrintStream err, Bundle bundle)
+    {
+        // Get the bundle's update location.
+        String symname =
+            (String) bundle.getHeaders().get(BundleRecord.BUNDLE_SYMBOLICNAME);
+
+        // Get associated repository bundle recorded for the
+        // local bundle and see if an update is necessary.
+        BundleRecord[] records = getRecords(symname);
+        if (records == null)
+        {
+            err.println(Util.getBundleName(bundle) + " not in repository.");
+            return false;
+        }
+        
+        // Check bundle version againts bundle record version.
+        for (int i = 0; i < records.length; i++)
+        {
+            int[] bundleVersion = Util.parseVersionString(
+                (String) bundle.getHeaders().get(BundleRecord.BUNDLE_VERSION));
+            int[] recordVersion = Util.parseVersionString(
+                (String) records[i].getAttribute(BundleRecord.BUNDLE_VERSION));
+            if (Util.compareVersion(recordVersion, bundleVersion) > 0)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void initialize()
+    {
+        m_initialized = true;
+        m_recordMap.clear();
+
+        for (int urlIdx = 0; (m_urls != null) && (urlIdx < m_urls.length); urlIdx++)
+        {
+            parseRepositoryFile(m_hopCount, m_urls[urlIdx]);
+        }
+        
+        // Cache a sorted array of all bundle records.
+        List list = new ArrayList();
+        for (Iterator i = m_recordMap.entrySet().iterator(); i.hasNext(); )
+        {
+            BundleRecord[] records = (BundleRecord[]) ((Map.Entry) i.next()).getValue();
+            for (int recIdx = 0; recIdx < records.length; recIdx++)
+            {
+                list.add(records[recIdx]);
+            }
+        }
+        m_recordArray = (BundleRecord[]) list.toArray(new BundleRecord[list.size()]);
+        Arrays.sort(m_recordArray, new Comparator() {
+            public int compare(Object o1, Object o2)
+            {
+                BundleRecord r1 = (BundleRecord) o1;
+                BundleRecord r2 = (BundleRecord) o2;
+                String name1 = (String) r1.getAttribute(BundleRecord.BUNDLE_NAME);
+                String name2 = (String) r2.getAttribute(BundleRecord.BUNDLE_NAME);
+                return name1.compareToIgnoreCase(name2);
+            }
+        });
+    }
+
+    private void parseRepositoryFile(int hopCount, String urlStr)
+    {
+        InputStream is = null;
+        BufferedReader br = null;
+
+        try
+        {
+            // Do it the manual way to have a chance to 
+            // set request properties as proxy auth (EW).
+            URL url = new URL(urlStr);
+            URLConnection conn = url.openConnection(); 
+
+            // Support for http proxy authentication
+            String auth = System.getProperty("http.proxyAuth");
+            if ((auth != null) && (auth.length() > 0))
+            {
+                if ("http".equals(url.getProtocol()) ||
+                    "https".equals(url.getProtocol()))
+                {
+                    String base64 = Util.base64Encode(auth);
+                    conn.setRequestProperty(
+                        "Proxy-Authorization", "Basic " + base64);
+                }
+            }
+            is = conn.getInputStream();
+
+            // Create the parser Kxml
+            XmlCommonHandler handler = new XmlCommonHandler();
+            handler.addType("bundles", ArrayList.class);
+            handler.addType("repository", HashMap.class);
+            handler.addType("extern-repositories", ArrayList.class);
+            handler.addType("bundle", MultivalueMap.class);
+            handler.addType("requirement", String.class);
+            handler.addType("capability", ArrayList.class);
+            handler.addType("property", HashMap.class);
+            handler.setDefaultType(String.class);
+
+            br = new BufferedReader(new InputStreamReader(is));
+            KXmlSAXParser parser;
+            parser = new KXmlSAXParser(br);
+            try
+            {
+                parser.parseXML(handler);
+            }
+            catch (Exception ex)
+            {
+                ex.printStackTrace();
+                return;
+            }
+
+            List root = (List) handler.getRoot();
+            for (int bundleIdx = 0; bundleIdx < root.size(); bundleIdx++)
+            {
+                Object obj = root.get(bundleIdx);
+                
+                // The elements of the root will either be a HashMap for
+                // the repository tag or a MultivalueMap for the bundle
+                // tag, as indicated above when we parsed the file.
+                
+                // If HashMap, then read repository information.
+                if (obj instanceof HashMap)
+                {
+                    // Create a case-insensitive map.
+                    Map repoMap = new TreeMap(new Comparator() {
+                        public int compare(Object o1, Object o2)
+                        {
+                            return o1.toString().compareToIgnoreCase(o2.toString());
+                        }
+                    });
+                    repoMap.putAll((Map) obj);
+
+                    // Process external repositories if hop count is
+                    // greater than zero.
+                    if (hopCount > 0)
+                    {
+                        // Get the external repository list.
+                        List externList = (List) repoMap.get(EXTERN_REPOSITORY_TAG);
+                        for (int i = 0; (externList != null) && (i < externList.size()); i++)
+                        {
+                            parseRepositoryFile(hopCount - 1, (String) externList.get(i));
+                        }
+                    }
+                }
+                // Else if mulitvalue map, then create a bundle record
+                // for the associated bundle meta-data.
+                else if (obj instanceof MultivalueMap)
+                {
+                    // Create a case-insensitive map.
+                    Map bundleMap = new TreeMap(new Comparator() {
+                        public int compare(Object o1, Object o2)
+                        {
+                            return o1.toString().compareToIgnoreCase(o2.toString());
+                        }
+                    });
+                    bundleMap.putAll((Map) obj);
+
+                    // Convert capabilities into case-insensitive maps.
+                    List list = (List) bundleMap.get("capability");
+                    Map[] capabilityMaps = convertCapabilities(list);
+                    bundleMap.put("capability", capabilityMaps);
+
+                    // Convert requirements info filters.
+                    list = (List) bundleMap.get("requirement");
+                    Filter[] filters = convertRequirements(list);
+                    bundleMap.put("requirement", filters);
+
+                    // Convert any remaining single-element lists into
+                    // the element itself.
+                    for (Iterator i = bundleMap.keySet().iterator(); i.hasNext(); )
+                    {
+                        Object key = i.next();
+                        Object value = bundleMap.get(key);
+                        if ((value instanceof List) &&
+                            (((List) value).size() == 1))
+                        {
+                            bundleMap.put(key, ((List) value).get(0));
+                        }
+                    }
+
+                    // Create a bundle record using the map.
+                    BundleRecord record = new BundleRecord(bundleMap);
+                    // TODO: Filter duplicates.
+                    BundleRecord[] records =
+                        (BundleRecord[]) m_recordMap.get(
+                            record.getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME));
+                    if (records == null)
+                    {
+                        records = new BundleRecord[] { record };
+                    }
+                    else
+                    {
+                        BundleRecord[] newRecords = new BundleRecord[records.length + 1];
+                        System.arraycopy(records, 0, newRecords, 0, records.length);
+                        newRecords[records.length] = record;
+                        records = newRecords;
+                    }
+                    m_recordMap.put(
+                        record.getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME), records);
+                }
+            }
+        }
+        catch (MalformedURLException ex)
+        {
+            ex.printStackTrace(System.err);
+//            System.err.println("Error: " + ex);
+        }
+        catch (IOException ex)
+        {
+            ex.printStackTrace(System.err);
+//            System.err.println("Error: " + ex);
+        }
+        finally
+        {
+            try
+            {
+                if (is != null) is.close();
+            }
+            catch (IOException ex)
+            {
+                // Not much we can do.
+            }
+        }
+    }
+
+    private Map[] convertCapabilities(List capLists)
+    {
+        Map[] capabilityMaps = new Map[(capLists == null) ? 0 : capLists.size()];
+        for (int capIdx = 0; (capLists != null) && (capIdx < capLists.size()); capIdx++)
+        {
+            // Create a case-insensitive map.
+            capabilityMaps[capIdx] = new TreeMap(new Comparator() {
+                public int compare(Object o1, Object o2)
+                {
+                    return o1.toString().compareToIgnoreCase(o2.toString());
+                }
+            });
+
+            List capList = (List) capLists.get(capIdx);
+            
+            for (int propIdx = 0; propIdx < capList.size(); propIdx++)
+            {
+                Map propMap = (Map) capList.get(propIdx);
+                String name = (String) propMap.get("name");
+                String type = (String) propMap.get("type");
+                String value = (String) propMap.get("value");
+                try
+                {
+                    Class clazz = this.getClass().getClassLoader().loadClass(type);
+                    Object o = clazz
+                        .getConstructor(new Class[] { String.class })
+                            .newInstance(new Object[] { value });
+                    capabilityMaps[capIdx].put(name, o);
+                }
+                catch (Exception ex)
+                {
+// TODO: DETERMINE WHAT TO DO HERE.
+                    // Two options here, we can either ignore the
+                    // entire capability or we can just ignore the
+                    // property. For now, just ignore the property.
+                    continue;
+                }
+            }
+        }
+        return capabilityMaps;
+    }
+
+    private Filter[] convertRequirements(List reqsList)
+    {
+        Filter[] filters = new Filter[(reqsList == null) ? 0 : reqsList.size()];
+        for (int i = 0; (reqsList != null) && (i < reqsList.size()); i++)
+        {
+            try
+            {
+                filters[i] = m_context.createFilter((String) reqsList.get(i));
+            }
+            catch (InvalidSyntaxException ex)
+            {
+            }
+        }
+        return filters;
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/Util.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/Util.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/Util.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/Util.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,208 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository;
+
+import java.io.*;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+public class Util
+{
+    public static String getBundleName(Bundle bundle)
+    {
+        String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+        return (name == null)
+            ? "Bundle " + Long.toString(bundle.getBundleId())
+            : name;
+    }
+
+    public static int compareVersion(int[] v1, int[] v2)
+    {
+        if (v1[0] > v2[0])
+        {
+            return 1;
+        }
+        else if (v1[0] < v2[0])
+        {
+            return -1;
+        }
+        else if (v1[1] > v2[1])
+        {
+            return 1;
+        }
+        else if (v1[1] < v2[1])
+        {
+            return -1;
+        }
+        else if (v1[2] > v2[2])
+        {
+            return 1;
+        }
+        else if (v1[2] < v2[2])
+        {
+            return -1;
+        }
+        return 0;
+    }
+
+    public static int[] parseVersionString(String s)
+    {
+        int[] version = new int[] { 0, 0, 0 };
+
+        if (s != null)
+        {
+            StringTokenizer st = new StringTokenizer(s, ".");
+            if (st.hasMoreTokens())
+            {
+                try
+                {
+                    version[0] = Integer.parseInt(st.nextToken());
+                    if (st.hasMoreTokens())
+                    {
+                        version[1] = Integer.parseInt(st.nextToken());
+                        if (st.hasMoreTokens())
+                        {
+                            version[2] = Integer.parseInt(st.nextToken());
+                        }
+                    }
+                    return version;
+                }
+                catch (NumberFormatException ex)
+                {
+                    throw new IllegalArgumentException(
+                        "Improper version number.");
+                }
+            }
+        }
+
+        return version;
+    }
+
+    private static final byte encTab[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+        0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+        0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
+        0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+        0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
+        0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f };
+
+    private static final byte decTab[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+        -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+        -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+        18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
+        30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+        48, 49, 50, 51, -1, -1, -1, -1, -1 };
+
+    public static String base64Encode(String s) throws IOException
+    {
+        return encode(s.getBytes(), 0);
+    }
+
+    /**
+     * Encode a raw byte array to a Base64 String.
+     * 
+     * @param in Byte array to encode.
+     * @param len Length of Base64 lines. 0 means no line breaks.
+    **/
+    public static String encode(byte[] in, int len) throws IOException
+    {
+        ByteArrayOutputStream baos = null;
+        ByteArrayInputStream bais = null;
+        try
+        {
+            baos = new ByteArrayOutputStream();
+            bais = new ByteArrayInputStream(in);
+            encode(bais, baos, len);
+            // ASCII byte array to String
+            return (new String(baos.toByteArray()));
+        }
+        finally
+        {
+            if (baos != null)
+            {
+                baos.close();
+            }
+            if (bais != null)
+            {
+                bais.close();
+            }
+        }
+    }
+
+    public static void encode(InputStream in, OutputStream out, int len)
+        throws IOException
+    {
+
+        // Check that length is a multiple of 4 bytes
+        if (len % 4 != 0)
+        {
+            throw new IllegalArgumentException("Length must be a multiple of 4");
+        }
+
+        // Read input stream until end of file
+        int bits = 0;
+        int nbits = 0;
+        int nbytes = 0;
+        int b;
+
+        while ((b = in.read()) != -1)
+        {
+            bits = (bits << 8) | b;
+            nbits += 8;
+            while (nbits >= 6)
+            {
+                nbits -= 6;
+                out.write(encTab[0x3f & (bits >> nbits)]);
+                nbytes++;
+                // New line
+                if (len != 0 && nbytes >= len)
+                {
+                    out.write(0x0d);
+                    out.write(0x0a);
+                    nbytes -= len;
+                }
+            }
+        }
+
+        switch (nbits)
+        {
+            case 2:
+                out.write(encTab[0x3f & (bits << 4)]);
+                out.write(0x3d); // 0x3d = '='
+                out.write(0x3d);
+                break;
+            case 4:
+                out.write(encTab[0x3f & (bits << 2)]);
+                out.write(0x3d);
+                break;
+        }
+
+        if (len != 0)
+        {
+            if (nbytes != 0)
+            {
+                out.write(0x0d);
+                out.write(0x0a);
+            }
+            out.write(0x0d);
+            out.write(0x0a);
+        }
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXHandler.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXHandler.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXHandler.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXHandler.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,68 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.kxmlsax;
+
+import java.util.Properties;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Interface for SAX handler with kXML
+ *
+ * @author Didier Donsez (didier.donsez@imag.fr)
+ */
+public interface KXmlSAXHandler {
+
+	/**
+	* Method called when parsing text
+	*
+	* @param   ch
+	* @param   offset
+	* @param   length
+	* @exception   SAXException
+	*/
+	public void characters(char[] ch, int offset, int length) throws Exception;
+
+	/**
+	* Method called when a tag opens
+	*
+	* @param   uri
+	* @param   localName
+	* @param   qName
+	* @param   attrib
+	* @exception   SAXException
+	**/
+	public void startElement(
+		String uri,
+		String localName,
+		String qName,
+		Properties attrib)
+		throws Exception;
+	/**
+	* Method called when a tag closes
+	*
+	* @param   uri
+	* @param   localName
+	* @param   qName
+	* @exception   SAXException
+	*/
+	public void endElement(
+		java.lang.String uri,
+		java.lang.String localName,
+		java.lang.String qName)
+		throws Exception;
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXParser.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXParser.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXParser.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/kxmlsax/KXmlSAXParser.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,79 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.kxmlsax;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Properties;
+
+import org.kxml.Attribute;
+import org.kxml.Xml;
+import org.kxml.parser.ParseEvent;
+import org.kxml.parser.XmlParser;
+
+/**
+ * The KXmlSAXParser extends the XmlParser from kxml. This is a very
+ * simple parser that does not take into account the DTD
+ *
+ * @version 	1.0 08 Nov 2002
+ * @version 	1.1 24 Apr 2004
+ * @author 	Humberto Cervantes, Didier Donsez
+ */
+public class KXmlSAXParser extends XmlParser {
+	/**
+	* The constructor for a parser, it receives a java.io.Reader.
+	*
+	* @param   reader  The reader
+	* @exception   IOException thrown by the superclass
+	*/
+	public KXmlSAXParser(Reader r) throws IOException {
+		super(r);
+	}
+
+	/**
+	* Parser from the reader provided in the constructor, and call
+	* the startElement and endElement in a KxmlHandler
+	*
+	* @param   reader  The reader
+	* @exception   Exception thrown by the superclass
+	*/
+	public void parseXML(KXmlSAXHandler handler) throws Exception {
+		ParseEvent evt = null;
+		do {
+			evt = read();
+			if (evt.getType() == Xml.START_TAG) {
+				Properties props = new Properties();
+				for (int i = 0; i < evt.getAttributeCount(); i++) {
+					Attribute attr = evt.getAttribute(i);
+					props.put(attr.getName(), attr.getValue());
+				}
+				handler.startElement(
+					"uri",
+					evt.getName(),
+					evt.getName(),
+					props);
+			} else if (evt.getType() == Xml.END_TAG) {
+				handler.endElement("uri", evt.getName(), evt.getName());
+			} else if (evt.getType() == Xml.TEXT) {
+				String text = evt.getText();
+				handler.characters(text.toCharArray(),0,text.length());
+			} else {
+				// do nothing
+			}
+		} while (evt.getType() != Xml.END_DOCUMENT);
+	}
+}

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/manifest.mf
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/manifest.mf?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/manifest.mf (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/manifest.mf Tue Aug 16 11:33:34 2005
@@ -0,0 +1,10 @@
+Bundle-Name: Bundle Repository
+Bundle-SymbolicName: org.apache.osgi.bundle.bundlerepository
+Bundle-Description: A simple bundle repository for Felix.
+Bundle-Activator: org.apache.osgi.bundle.bundlerepository.Activator
+Bundle-ClassPath: .,org/apache/osgi/bundle/bundlerepository/kxml.jar
+Bundle-Version: 2.0.0.alpha2
+Import-Package: org.osgi.framework
+DynamicImport-Package: org.apache.osgi.service.shell
+Export-Package: 
+ org.apache.osgi.service.bundlerepository; specification-version="1.1.0"

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/ClassUtility.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/ClassUtility.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/ClassUtility.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/ClassUtility.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,97 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.metadataparser;
+
+/**
+ * This class provides methods to process class name
+ */
+
+public class ClassUtility {
+
+	/**
+	 * This method capitalizes the first character in the provided string.
+	 * @return resulted string
+	 */
+	public static String capitalize(String name) {
+
+		int len=name.length();
+		StringBuffer sb=new StringBuffer(len);
+		boolean setCap=true;
+		for(int i=0; i<len; i++){
+			char c=name.charAt(i);
+			if(c=='-' || c=='_') {
+				setCap=true;			
+			} else {
+				if(setCap){
+					sb.append(Character.toUpperCase(c));
+					setCap=false;
+				} else {
+					sb.append(c);
+				}
+			}
+		} 
+ 
+		return sb.toString();
+	}
+
+	/**
+	 * This method capitalizes all characters in the provided string.
+	 * @return resulted string
+	 */
+	public static String finalstaticOf(String membername) {
+		int len=membername.length();
+		StringBuffer sb=new StringBuffer(len+2);
+		for(int i=0; i<len; i++){
+			char c=membername.charAt(i);
+			if(Character.isLowerCase(c) ) {
+				sb.append(Character.toUpperCase(c));
+			} else if(Character.isUpperCase(c) ) {
+				sb.append('_').append(c);
+			} else {
+				sb.append(c);				
+			}
+		} 
+ 
+		return sb.toString();
+	}
+	
+	/**
+	 * This method returns the package name in a full class name
+	 * @return resulted string
+	 */
+	public static String packageOf(String fullclassname) {
+		int index=fullclassname.lastIndexOf(".");
+		if(index>0) {
+			return fullclassname.substring(0,index);
+		} else {
+			return "";	
+		}
+	}
+
+	/**
+	 * This method returns the package name in a full class name
+	 * @return resulted string
+	 */
+	public static String classOf(String fullclassname) {
+		int index=fullclassname.lastIndexOf(".");
+		if(index>0) {
+			return fullclassname.substring(index+1);
+		} else {
+			return fullclassname;	
+		}
+	}
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/KXmlMetadataHandler.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/KXmlMetadataHandler.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/KXmlMetadataHandler.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/KXmlMetadataHandler.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,63 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.metadataparser;
+
+import java.io.*;
+
+import org.apache.osgi.bundle.bundlerepository.kxmlsax.KXmlSAXParser;
+
+
+/**
+ * handles the metadata in XML format
+ * (use kXML (http://kxml.enhydra.org/) a open-source very light weight XML parser
+ * @version 	1.00 11 Nov 2003
+ * @author 	Didier Donsez
+ */
+public class KXmlMetadataHandler /*implements MetadataHandler*/ {
+
+	private XmlCommonHandler handler;
+
+	public KXmlMetadataHandler() {
+		handler = new XmlCommonHandler();
+	}
+
+	/**
+	* Called to parse the InputStream and set bundle list and package hash map
+	*/
+	public void parse(InputStream is) throws Exception {
+		BufferedReader br = new BufferedReader(new InputStreamReader(is));
+		KXmlSAXParser parser;
+		parser = new KXmlSAXParser(br);
+		parser.parseXML(handler);
+	}
+
+	/**
+	 * return the metadata
+	 * @return a Objet
+	 */
+	public Object getMetadata() {
+		return handler.getRoot();
+	}
+
+	public void addType(String qname, Class clazz) {
+		handler.addType(qname, clazz);
+	}
+
+	public void setDefaultType(Class clazz) {
+		handler.setDefaultType(clazz);
+	}
+}

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/MultivalueMap.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/MultivalueMap.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/MultivalueMap.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/MultivalueMap.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,142 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.metadataparser;
+
+import java.util.*;
+
+public class MultivalueMap implements Map {
+	private Map m_map = null;
+
+	public MultivalueMap() {
+		m_map = new HashMap();
+	}
+
+	public MultivalueMap(Map map) {
+		m_map = map;
+	}
+
+	/**
+	 * @see java.util.Map#size()
+	 */
+	public int size() {
+		return m_map.size();
+	}
+
+	/**
+	 * @see java.util.Map#clear()
+	 */
+	public void clear() {
+		m_map.clear();
+	}
+
+	/**
+	 * @see java.util.Map#isEmpty()
+	 */
+	public boolean isEmpty() {
+		return m_map.isEmpty();
+	}
+
+	/**
+	 * @see java.util.Map#containsKey(java.lang.Object)
+	 */
+	public boolean containsKey(Object arg0) {
+		return m_map.containsKey(arg0);
+	}
+
+	/**
+	 * @see java.util.Map#containsValue(java.lang.Object)
+	 */
+	public boolean containsValue(Object arg0) {
+		return false;
+	}
+
+	/**
+	 * @see java.util.Map#values()
+	 */
+	public Collection values() {
+		return null;
+	}
+
+	/**
+	 * @see java.util.Map#putAll(java.util.Map)
+	 */
+	public void putAll(Map arg0) {
+	}
+
+	/**
+	 * @see java.util.Map#entrySet()
+	 */
+	public Set entrySet() {
+		return m_map.entrySet();
+	}
+
+	/**
+	 * @see java.util.Map#keySet()
+	 */
+	public Set keySet() {
+		return m_map.keySet();
+	}
+
+	/**
+	 * @see java.util.Map#get(java.lang.Object)
+	 */
+	public Object get(Object key) {
+		return m_map.get(key);
+	}
+
+	/**
+	 * @see java.util.Map#remove(java.lang.Object)
+	 */
+	public Object remove(Object arg0) {
+		return m_map.remove(arg0);
+	}
+
+	/**
+	 * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+	 */
+	public Object put(Object key, Object value) {
+		Object prev = m_map.get(key);
+		if (prev == null) {
+            List list = new ArrayList();
+            list.add(value);
+			m_map.put(key, list);
+			return list;
+		} else {
+            ((List) prev).add(value);
+            return prev;
+		}
+	}
+
+	public String toString() {
+		StringBuffer sb=new StringBuffer();
+		sb.append("[MultivalueMap:");
+		if(m_map.isEmpty()) {
+			sb.append("empty");
+		} else {
+			Set keys=m_map.keySet();
+			Iterator iter=keys.iterator();
+			while(iter.hasNext()){
+				String key=(String)iter.next();
+				sb.append("\n\"").append(key).append("\":");
+				sb.append(m_map.get(key).toString());		
+			}
+			sb.append('\n');
+		}
+		sb.append(']');
+		return sb.toString();
+	}
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/XmlCommonHandler.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/XmlCommonHandler.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/XmlCommonHandler.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/bundlerepository/metadataparser/XmlCommonHandler.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,405 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.bundlerepository.metadataparser;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.apache.osgi.bundle.bundlerepository.kxmlsax.KXmlSAXHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * SAX handler for the XML OBR file
+ *
+ * @author Didier Donsez (didier.donsez@imag.fr)
+ */
+public class XmlCommonHandler implements KXmlSAXHandler {
+
+    private static final String PI_MAPPING="mapping";
+
+    private int columnNumber;
+
+    private int lineNumber;
+
+    //
+    // Data
+    //
+
+    private Object root;
+
+    private Stack objectStack;
+    private Stack qnameStack;
+
+    private Map types;
+    private Class defaultType;
+
+    private StringBuffer currentText;
+
+    public XmlCommonHandler() {
+        objectStack = new Stack();
+        qnameStack = new Stack();
+        types = new HashMap();
+    }
+
+    public void addType(String qname, Class clazz) {
+        types.put(qname, clazz);
+    }
+
+    public void setDefaultType(Class clazz) {
+        defaultType=clazz;
+    }
+
+    public Object getRoot() {
+        return root;
+    }
+
+    /* for PCDATA */
+    public void characters(char[] ch, int offset, int length)
+        throws Exception {
+        if (currentText != null)
+            currentText.append(ch, offset, length);
+    }
+
+    private String adderOf(Class clazz) {
+        return "add"
+            + ClassUtility.capitalize(ClassUtility.classOf(clazz.getName()));
+    }
+
+    private String adderOf(String key) {
+        return "add" + ClassUtility.capitalize(key);
+    }
+
+    private String setterOf(Class clazz) {
+        return "set"
+            + ClassUtility.capitalize(ClassUtility.classOf(clazz.getName()));
+    }
+
+    private String setterOf(String key) {
+        return "set" + ClassUtility.capitalize(key);
+    }
+
+    /**
+    * Method called when a tag opens
+    *
+    * @param   uri
+    * @param   localName
+    * @param   qName
+    * @param   attrib
+    * @exception   SAXException
+    **/
+    public void startElement(
+        String uri,
+        String localName,
+        String qName,
+        Properties attrib)
+        throws Exception {
+
+        trace("START ("+lineNumber+","+columnNumber+"):" + uri + ":" + qName);
+
+        Class clazz = (Class) types.get(qName);
+        // TODO: should add uri in the future
+
+        if(clazz==null && defaultType!=null)
+            clazz=defaultType;
+
+        Object obj;
+        if (clazz != null) {
+
+            try {
+                obj = clazz.newInstance();
+            } catch (InstantiationException e) {
+                throw new Exception(lineNumber+","+columnNumber+":"+
+                    "class "+clazz.getName()+" for element " + qName + " should have an empty constructor");
+            } catch (IllegalAccessException e) {
+                throw new Exception(lineNumber+","+columnNumber+":"+
+                    "illegal access on the empty constructor of class "+clazz.getName()+" for element " + qName);
+            }
+
+            Set keyset = attrib.keySet();
+            Iterator iter = keyset.iterator();
+            while (iter.hasNext()) {
+                String key = (String) iter.next();
+
+                if (obj instanceof Map) {
+                    ((Map) obj).put(key, attrib.get(key));
+                } else if (obj instanceof List) {
+                    throw new Exception(lineNumber+","+columnNumber+":"+
+                        "List element " + qName + " cannot have any attribute");
+                } else if (obj instanceof String) {
+                    if(key.equals("value")){
+                        obj=(String)attrib.get(key);
+                    } else {
+                        throw new Exception(lineNumber+","+columnNumber+":"+
+                            "String element " + qName + " cannot have other attribute than value");
+                    }
+                } else {
+                    Method method = null;
+                    try {
+                        method =
+                            clazz.getMethod(
+                                setterOf(key),
+                                new Class[] { String.class });
+                    } catch (NoSuchMethodException e) {
+                        // do nothing
+                    }
+                    if (method == null)
+                        try {
+                            method =
+                                clazz.getMethod(
+                                    adderOf(key),
+                                    new Class[] { String.class });
+
+                        } catch (NoSuchMethodException e) {
+                            throw new Exception(lineNumber+","+columnNumber+":"+
+                                "element "
+                                    + qName
+                                    + " does not support the attribute "
+                                    + key);
+                        }
+                    if (method != null)
+                        method.invoke(
+                            obj,
+                            new String[] {(String) attrib.get(key)});
+                }
+
+            }
+
+        } else {
+            throw new Exception(lineNumber+","+columnNumber+":"+
+                "this element " + qName + " has not corresponding class");
+        }
+
+        if (root == null)
+            root = obj;
+        objectStack.push(obj);
+        qnameStack.push(qName);
+        currentText = new StringBuffer();
+
+        trace("START/ ("+lineNumber+","+columnNumber+"):" + uri + ":" + qName);
+    }
+
+    /**
+    * Method called when a tag closes
+    *
+    * @param   uri
+    * @param   localName
+    * @param   qName
+    * @exception   SAXException
+    */
+    public void endElement(
+        java.lang.String uri,
+        java.lang.String localName,
+        java.lang.String qName)
+        throws Exception {
+
+        trace("END ("+lineNumber+","+columnNumber+"):" + uri + ":" + qName);
+
+        Object obj = objectStack.pop();
+
+        if (currentText != null && currentText.length() != 0) {
+            if (obj instanceof Map) {
+                ((Map) obj).put(qName, currentText.toString().trim());
+            } else if (obj instanceof List) {
+                throw new Exception(lineNumber+","+columnNumber+":"+
+                    "List element " + qName + " cannot have PCDATAs");
+            } else if (obj instanceof String) {
+                String str=(String)obj;
+                if(str.length()!=0){
+                    throw new Exception(lineNumber+","+columnNumber+":"+
+                        "String element " + qName + " cannot have both PCDATA and an attribute value");
+                } else {
+                    obj=currentText.toString().trim();
+                }
+            } else {
+                Method method = null;
+                try {
+                    method =
+                        obj.getClass().getMethod(
+                            "addText",
+                            new Class[] { String.class });
+                } catch (NoSuchMethodException e) {
+                    // do nothing
+                }
+                if (method != null) {
+                    method.invoke(obj, new String[] { currentText.toString().trim()});
+                }
+            }
+        }
+
+        currentText = null;
+
+        if (!objectStack.isEmpty()) {
+
+            Object parent = objectStack.peek();
+            String parentName = (String) qnameStack.peek();
+
+            if (parent instanceof Map) {
+                ((Map) parent).put(qName, obj);
+            } else if (parent instanceof List) {
+                ((List) parent).add(obj);
+            } else {
+                Method method = null;
+                try {
+                    method =
+                        parent.getClass().getMethod(
+                            adderOf(ClassUtility.capitalize(qName)),
+                            new Class[] { obj.getClass()});
+                } catch (NoSuchMethodException e) {
+                    trace(
+                        "NoSuchMethodException: "
+                            + adderOf(ClassUtility.capitalize(qName)));
+                    // do nothing
+                }
+                if (method == null)
+                    try {
+                        method =
+                            parent.getClass().getMethod(
+                                setterOf(ClassUtility.capitalize(qName)),
+                                new Class[] { obj.getClass()});
+                    } catch (NoSuchMethodException e) {
+                        trace(
+                            "NoSuchMethodException: "
+                                + setterOf(ClassUtility.capitalize(qName)));
+                        // do nothing
+                    }
+                if (method == null)
+                    try {
+                        method =
+                            parent.getClass().getMethod(
+                                adderOf(obj.getClass()),
+                                new Class[] { obj.getClass()});
+                    } catch (NoSuchMethodException e) {
+                        trace(
+                            "NoSuchMethodException: "
+                                + adderOf(obj.getClass()));
+                        // do nothing
+                    }
+                if (method == null)
+                    try {
+                        method =
+                            parent.getClass().getMethod(
+                                setterOf(obj.getClass()),
+                                new Class[] { obj.getClass()});
+                    } catch (NoSuchMethodException e) {
+                        trace(
+                            "NoSuchMethodException: "
+                                + setterOf(obj.getClass()));
+                        // do nothing
+                    }
+
+                if (method != null) {
+                    trace(method.getName());
+                    method.invoke(parent, new Object[] { obj });
+                } else {
+                    throw new Exception(lineNumber+","+columnNumber+":"+
+                        " element " + parentName + " cannot have an attribute " + qName + " of type " + obj.getClass());
+                }
+            }
+
+        }
+
+        trace("END/ ("+lineNumber+","+columnNumber+"):" + uri + ":" + qName);
+
+    }
+
+    private void trace(String msg) {
+        if (false)
+            System.err.println(msg);
+    }
+
+    /**
+     * @see kxml.sax.KXmlSAXHandler#setLineNumber(int)
+     */
+    public void setLineNumber(int lineNumber) {
+        this.lineNumber=lineNumber;
+    }
+
+    /**
+     * @see kxml.sax.KXmlSAXHandler#setColumnNumber(int)
+     */
+    public void setColumnNumber(int columnNumber) {
+        this.columnNumber=columnNumber;
+
+    }
+
+    /**
+     * @see kxml.sax.KXmlSAXHandler#processingInstruction(java.lang.String, java.lang.String)
+     */
+    public void processingInstruction(String target, String data) throws Exception {
+        trace("pi:"+target+";"+data);
+        if(target==null){ // TODO kXML
+            if(!data.startsWith(PI_MAPPING)) return;
+        } else if(!target.equals(PI_MAPPING))return;
+
+
+        // defaultclass attribute
+        String datt="defaultclass=\"";
+        int dstart=data.indexOf(datt);
+        if(dstart!=-1) {
+            int dend=data.indexOf("\"",dstart+datt.length());
+            if(dend==-1)
+                throw new Exception(lineNumber+","+columnNumber+":"+
+                    " \"defaultclass\" attribute in \"mapping\" PI is not quoted");
+
+            String classname=data.substring(dstart+datt.length(),dend);
+            Class clazz=null;
+            try {
+                clazz=getClass().getClassLoader().loadClass(classname);
+            } catch (ClassNotFoundException e) {
+                throw new Exception(lineNumber+","+columnNumber+":"+
+                    " cannot found class "+ classname+" for \"mapping\" PI");
+            }
+            setDefaultType(clazz);
+            return;
+        }
+
+        // element attribute
+        String eatt="element=\"";
+        int estart=data.indexOf(eatt);
+        if(estart==-1)
+            throw new Exception(lineNumber+","+columnNumber+":"+
+                " missing \"element\" attribute in \"mapping\" PI");
+        int eend=data.indexOf("\"",estart+eatt.length());
+        if(eend==-1)
+        throw new Exception(lineNumber+","+columnNumber+":"+
+            " \"element\" attribute in \"mapping\" PI is not quoted");
+
+        String element=data.substring(estart+eatt.length(),eend);
+
+        // element class
+        String catt="class=\"";
+        int cstart=data.indexOf(catt);
+        if(cstart==-1)
+            throw new Exception(lineNumber+","+columnNumber+":"+
+                " missing \"class\" attribute in \"mapping\" PI");
+        int cend=data.indexOf("\"",cstart+catt.length());
+        if(cend==-1)
+        throw new Exception(lineNumber+","+columnNumber+":"+
+            " \"class\" attribute in \"mapping\" PI is not quoted");
+
+        String classname=data.substring(cstart+catt.length(),cend);
+
+        Class clazz=null;
+        try {
+            clazz=getClass().getClassLoader().loadClass(classname);
+        } catch (ClassNotFoundException e) {
+            throw new Exception(lineNumber+","+columnNumber+":"+
+                " cannot found class "+ classname+" for \"mapping\" PI");
+        }
+        addType(element,clazz);
+    }
+}

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/Activator.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/Activator.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/Activator.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/Activator.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,351 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.shell;
+
+import java.io.PrintStream;
+import java.security.*;
+import java.util.*;
+
+import org.apache.osgi.service.shell.Command;
+import org.osgi.framework.*;
+
+public class Activator implements BundleActivator
+{
+    private transient BundleContext m_context = null;
+    private transient ShellServiceImpl m_shell = null;
+
+    public void start(BundleContext context)
+    {
+        m_context = context;
+
+        // Register shell service implementation.
+        String[] classes = {
+            org.apache.osgi.service.shell.ShellService.class.getName(),
+            org.ungoverned.osgi.service.shell.ShellService.class.getName()
+        };
+        context.registerService(classes, m_shell = new ShellServiceImpl(), null);
+
+        // Listen for registering/unregistering of shell command
+        // services so that we can automatically add/remove them
+        // from our list of available commands.
+        ServiceListener sl = new ServiceListener() {
+            public void serviceChanged(ServiceEvent event)
+            {
+                if (event.getType() == ServiceEvent.REGISTERED)
+                {
+                    m_shell.addCommand(event.getServiceReference());
+                }
+                else if (event.getType() == ServiceEvent.UNREGISTERING)
+                {
+                    m_shell.removeCommand(event.getServiceReference());
+                }
+                else
+                {
+                }
+            }
+        };
+
+        try
+        {
+            m_context.addServiceListener(sl,
+                "(|(objectClass="
+                + org.apache.osgi.service.shell.Command.class.getName()
+                + ")(objectClass="
+                + org.ungoverned.osgi.service.shell.Command.class.getName()
+                + "))");
+        }
+        catch (InvalidSyntaxException ex)
+        {
+            System.err.println("Activator: Cannot register service listener.");
+            System.err.println("Activator: " + ex);
+        }
+
+        // Now manually try to find any commands that have already
+        // been registered (i.e., we didn't see their service events).
+        initializeCommands();
+
+        // Register "exports" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new BundleLevelCommandImpl(m_context), null);
+
+        // Register "cd" command service.
+        classes = new String[2];
+        classes[0] = org.apache.osgi.service.shell.Command.class.getName();
+        classes[1] = org.apache.osgi.service.shell.CdCommand.class.getName();
+        context.registerService(
+            classes, new CdCommandImpl(m_context), null);
+
+        // Register "exports" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new PackagesCommandImpl(m_context), null);
+
+        // Register "headers" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new HeadersCommandImpl(m_context), null);
+
+        // Register "help" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new HelpCommandImpl(m_context), null);
+
+        // Register "install" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new InstallCommandImpl(m_context), null);
+
+        // Register "ps" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new PsCommandImpl(m_context), null);
+
+        // Register "refresh" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new RefreshCommandImpl(m_context), null);
+
+        // Register "services" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new ServicesCommandImpl(m_context), null);
+
+        // Register "startlevel" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new StartLevelCommandImpl(m_context), null);
+
+        // Register "shutdown" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new ShutdownCommandImpl(m_context), null);
+
+        // Register "start" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new StartCommandImpl(m_context), null);
+
+        // Register "stop" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new StopCommandImpl(m_context), null);
+
+        // Register "uninstall" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new UninstallCommandImpl(m_context), null);
+
+        // Register "update" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new UpdateCommandImpl(m_context), null);
+
+        // Register "version" command service.
+        context.registerService(
+            org.apache.osgi.service.shell.Command.class.getName(),
+            new VersionCommandImpl(m_context), null);
+    }
+
+    public void stop(BundleContext context)
+    {
+        m_shell.clearCommands();
+    }
+
+    private void initializeCommands()
+    {
+        synchronized (m_shell)
+        {
+            try
+            {
+                ServiceReference[] refs = m_context.getServiceReferences(
+                    org.apache.osgi.service.shell.Command.class.getName(), null);
+                if (refs != null)
+                {
+                    for (int i = 0; i < refs.length; i++)
+                    {
+                        m_shell.addCommand(refs[i]);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                System.err.println("Activator: " + ex);
+            }
+        }
+    }
+
+    private class ShellServiceImpl implements
+        org.apache.osgi.service.shell.ShellService,
+        org.ungoverned.osgi.service.shell.ShellService
+    {
+        private HashMap m_commandRefMap = new HashMap();
+        private TreeMap m_commandNameMap = new TreeMap();
+
+        public synchronized String[] getCommands()
+        {
+            Set ks = m_commandNameMap.keySet();
+            String[] cmds = (ks == null)
+                ? new String[0] : (String[]) ks.toArray(new String[ks.size()]);
+            return cmds;
+        }
+
+        public synchronized String getCommandUsage(String name)
+        {
+            Command command = (Command) m_commandNameMap.get(name);
+            return (command == null) ? null : command.getUsage();
+        }
+
+        public synchronized String getCommandDescription(String name)
+        {
+            Command command = (Command) m_commandNameMap.get(name);
+            return (command == null) ? null : command.getShortDescription();
+        }
+
+        public synchronized ServiceReference getCommandReference(String name)
+        {
+            return (ServiceReference) m_commandNameMap.get(name);
+        }
+
+        public synchronized void removeCommand(ServiceReference ref)
+        {
+            Command command = (Command) m_commandRefMap.remove(ref);
+            if (command != null)
+            {
+                m_commandNameMap.remove(command.getName());
+            }
+        }
+
+        public synchronized void executeCommand(
+            String commandLine, PrintStream out, PrintStream err) throws Exception
+        {
+            commandLine = commandLine.trim();
+            String commandName = (commandLine.indexOf(' ') >= 0)
+                ? commandLine.substring(0, commandLine.indexOf(' ')) : commandLine;
+            Command command = getCommand(commandName);
+            if (command != null)
+            {
+                if (System.getSecurityManager() != null)
+                {
+                    try
+                    {
+                        AccessController.doPrivileged(
+                            new ExecutePrivileged(command, commandLine, out, err));
+                    }
+                    catch (PrivilegedActionException ex)
+                    {
+                        throw ex.getException();
+                    }
+                }
+                else
+                {
+                    try
+                    {
+                        command.execute(commandLine, out, err);
+                    }
+                    catch (Throwable ex)
+                    {
+                        err.println("Unable to execute command: " + ex);
+                        ex.printStackTrace(err);
+                    }
+                }
+            }
+            else
+            {
+                err.println("Command not found.");
+            }
+        }
+
+        protected synchronized Command getCommand(String name)
+        {
+            Command command = (Command) m_commandNameMap.get(name);
+            return (command == null) ? null : command;
+        }
+
+        protected synchronized void addCommand(ServiceReference ref)
+        {
+            Object cmdObj = m_context.getService(ref);
+            Command command =
+                (cmdObj instanceof org.ungoverned.osgi.service.shell.Command)
+                ? new OldCommandWrapper((org.ungoverned.osgi.service.shell.Command) cmdObj)
+                : (Command) cmdObj;
+            m_commandRefMap.put(ref, command);
+            m_commandNameMap.put(command.getName(), command);
+        }
+
+        protected synchronized void clearCommands()
+        {
+            m_commandRefMap.clear();
+            m_commandNameMap.clear();
+        }
+    }
+
+    private static class OldCommandWrapper implements Command
+    {
+        private org.ungoverned.osgi.service.shell.Command m_oldCommand = null;
+
+        public OldCommandWrapper(org.ungoverned.osgi.service.shell.Command oldCommand)
+        {
+            m_oldCommand = oldCommand;
+        }
+
+        public String getName()
+        {
+            return m_oldCommand.getName();
+        }
+
+        public String getUsage()
+        {
+            return m_oldCommand.getUsage();
+        }
+
+        public String getShortDescription()
+        {
+            return m_oldCommand.getShortDescription();
+        }
+
+        public void execute(String line, PrintStream out, PrintStream err)
+        {
+            m_oldCommand.execute(line, out, err);
+        }
+    }
+
+    public static class ExecutePrivileged implements PrivilegedExceptionAction
+    {
+        private Command m_command = null;
+        private String m_commandLine = null;
+        private PrintStream m_out = null;
+        private PrintStream m_err = null;
+
+        public ExecutePrivileged(
+            Command command, String commandLine,
+            PrintStream out, PrintStream err)
+            throws Exception
+        {
+            m_command = command;
+            m_commandLine = commandLine;
+            m_out = out;
+            m_err = err;
+        }
+
+        public Object run() throws Exception
+        {
+            m_command.execute(m_commandLine, m_out, m_err);
+            return null;
+        }
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/BundleLevelCommandImpl.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/BundleLevelCommandImpl.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/BundleLevelCommandImpl.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/BundleLevelCommandImpl.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,167 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.shell;
+
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+import org.apache.osgi.service.shell.Command;
+import org.osgi.framework.*;
+import org.osgi.service.startlevel.StartLevel;
+
+public class BundleLevelCommandImpl implements Command
+{
+    private BundleContext m_context = null;
+
+    public BundleLevelCommandImpl(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public String getName()
+    {
+        return "bundlelevel";
+    }
+
+    public String getUsage()
+    {
+        return "bundlelevel <level> <id> ... | <id>";
+    }
+
+    public String getShortDescription()
+    {
+        return "set or get bundle start level.";
+    }
+
+    public void execute(String s, PrintStream out, PrintStream err)
+    {
+        // Get start level service.
+        ServiceReference ref = m_context.getServiceReference(
+            org.osgi.service.startlevel.StartLevel.class.getName());
+        if (ref == null)
+        {
+            out.println("StartLevel service is unavailable.");
+            return;
+        }
+
+        StartLevel sl = (StartLevel) m_context.getService(ref);
+        if (sl == null)
+        {
+            out.println("StartLevel service is unavailable.");
+            return;
+        }
+
+        // Parse command line.
+        StringTokenizer st = new StringTokenizer(s, " ");
+
+        // Ignore the command name.
+        st.nextToken();
+
+        // If there is only one token, then assume it is
+        // a bundle ID for which we must retrieve the bundle
+        // level.
+        if (st.countTokens() == 1)
+        {
+            // Get the bundle and display start level.
+            Bundle bundle = null;
+            String token = null;
+            try
+            {
+                token = st.nextToken();
+                long id = Long.parseLong(token);
+                bundle = m_context.getBundle(id);
+                if (bundle != null)
+                {
+                    out.println("Bundle " + token + " is level "
+                        + sl.getBundleStartLevel(bundle));
+                }
+                else
+                {
+                    err.println("Bundle ID " + token + " is invalid.");
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse integer '" + token + "'.");
+            }
+            catch (Exception ex)
+            {
+                err.println(ex.toString());
+            }
+        }
+        // If there is more than one token, assume the first
+        // token is the new start level and the remaining
+        // tokens are the bundle IDs whose start levels should
+        // be changed.
+        else if (st.countTokens() > 1)
+        {
+            // Get the bundle.
+            Bundle bundle = null;
+            String token = null;
+            int startLevel = -1;
+
+            try
+            {
+                token = st.nextToken();
+                startLevel = Integer.parseInt(token);
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse start level '" + token + "'.");
+            }
+
+            // Ignore invalid start levels.
+            if (startLevel > 0)
+            {
+                // Set the start level for each specified bundle.
+                while (st.hasMoreTokens())
+                {
+                    try
+                    {
+                        token = st.nextToken();
+                        long id = Long.parseLong(token);
+                        bundle = m_context.getBundle(id);
+                        if (bundle != null)
+                        {
+                            sl.setBundleStartLevel(bundle, startLevel);
+                        }
+                        else
+                        {
+                            err.println("Bundle ID '" + token + "' is invalid.");
+                        }
+                    }
+                    catch (NumberFormatException ex)
+                    {
+                        err.println("Unable to parse bundle ID '" + token + "'.");
+                    }
+                    catch (Exception ex)
+                    {
+                        err.println(ex.toString());
+                    }
+                }
+            }
+            else
+            {
+                err.println("Invalid start level.");
+            }
+        }
+        else
+        {
+            err.println("Incorrect number of arguments.");
+        }
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/CdCommandImpl.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/CdCommandImpl.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/CdCommandImpl.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/CdCommandImpl.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,86 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.shell;
+
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+import org.apache.osgi.service.shell.CdCommand;
+import org.osgi.framework.BundleContext;
+
+public class CdCommandImpl implements CdCommand
+{
+    private BundleContext m_context = null;
+    private String m_baseURL = "";
+
+    public CdCommandImpl(BundleContext context)
+    {
+        m_context = context;
+
+        // See if the initial base URL is specified.
+        String baseURL = m_context.getProperty(BASE_URL_PROPERTY);
+        setBaseURL(baseURL);
+    }
+
+    public String getName()
+    {
+        return "cd";
+    }
+
+    public String getUsage()
+    {
+        return "cd [<base-URL>]";
+    }
+
+    public String getShortDescription()
+    {
+        return "change or display base URL.";
+    }
+
+    public void execute(String s, PrintStream out, PrintStream err)
+    {
+        StringTokenizer st = new StringTokenizer(s, " ");
+
+        // Ignore the command name.
+        st.nextToken();
+
+        // No more tokens means to display the base URL,
+        // otherwise set the base URL.
+        if (st.countTokens() == 0)
+        {
+            out.println(m_baseURL);
+        }
+        else if (st.countTokens() == 1)
+        {
+            setBaseURL(st.nextToken());
+        }
+        else
+        {
+            err.println("Incorrect number of arguments");
+        }
+    }
+
+    public String getBaseURL()
+    {
+        return m_baseURL;
+    }
+
+    public void setBaseURL(String s)
+    {
+        m_baseURL = (s == null) ? "" : s;
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HeadersCommandImpl.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HeadersCommandImpl.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HeadersCommandImpl.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HeadersCommandImpl.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,111 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.shell;
+
+import java.io.PrintStream;
+import java.util.*;
+
+import org.apache.osgi.service.shell.Command;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class HeadersCommandImpl implements Command
+{
+    private BundleContext m_context = null;
+
+    public HeadersCommandImpl(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public String getName()
+    {
+        return "headers";
+    }
+
+    public String getUsage()
+    {
+        return "headers [<id> ...]";
+    }
+
+    public String getShortDescription()
+    {
+        return "display bundle header properties.";
+    }
+
+    public void execute(String s, PrintStream out, PrintStream err)
+    {
+        StringTokenizer st = new StringTokenizer(s, " ");
+
+        // Ignore the command name.
+        st.nextToken();
+
+        // Print the specified bundles or all if none are specified.
+        if (st.hasMoreTokens())
+        {
+            while (st.hasMoreTokens())
+            {
+                String id = st.nextToken().trim();
+
+                try
+                {
+                    long l = Long.parseLong(id);
+                    Bundle bundle = m_context.getBundle(l);
+                    if (bundle != null)
+                    {
+                        printHeaders(out, bundle);
+                    }
+                    else
+                    {
+                        err.println("Bundle ID " + id + " is invalid.");
+                    }
+                }
+                catch (NumberFormatException ex)
+                {
+                    err.println("Unable to parse id '" + id + "'.");
+                }
+                catch (Exception ex)
+                {
+                    err.println(ex.toString());
+                }
+            }
+        }
+        else
+        {
+            Bundle[] bundles = m_context.getBundles();
+            for (int i = 0; i < bundles.length; i++)
+            {
+                printHeaders(out, bundles[i]);
+            }
+        }
+    }
+
+    private void printHeaders(PrintStream out, Bundle bundle)
+    {
+        String title = Util.getBundleName(bundle);
+        out.println("\n" + title);
+        out.println(Util.getUnderlineString(title));
+        Dictionary dict = bundle.getHeaders();
+        Enumeration keys = dict.keys();
+        while (keys.hasMoreElements())
+        {
+            Object k = (String) keys.nextElement();
+            Object v = dict.get(k);
+            out.println(k + " = " + Util.getValueString(v));
+        }
+    }
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HelpCommandImpl.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HelpCommandImpl.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HelpCommandImpl.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/bundle/shell/HelpCommandImpl.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,97 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.osgi.bundle.shell;
+
+import java.io.PrintStream;
+
+import org.apache.osgi.service.shell.Command;
+import org.apache.osgi.service.shell.ShellService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class HelpCommandImpl implements Command
+{
+    private BundleContext m_context = null;
+
+    public HelpCommandImpl(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public String getName()
+    {
+        return "help";
+    }
+
+    public String getUsage()
+    {
+        return "help";
+    }
+
+    public String getShortDescription()
+    {
+        return "display shell commands.";
+    }
+
+    public void execute(String s, PrintStream out, PrintStream err)
+    {
+        try {
+            // Get a reference to the shell service.
+            ServiceReference ref = m_context.getServiceReference(
+                org.apache.osgi.service.shell.ShellService.class.getName());
+
+            if (ref != null)
+            {
+                ShellService ss = (ShellService) m_context.getService(ref);
+                String[] cmds = ss.getCommands();
+                String[] usage = new String[cmds.length];
+                String[] desc = new String[cmds.length];
+                int maxUsage = 0;
+                for (int i = 0; i < cmds.length; i++)
+                {
+                    usage[i] = ss.getCommandUsage(cmds[i]);
+                    desc[i] = ss.getCommandDescription(cmds[i]);
+                    // Just in case the command has gone away.
+                    if ((usage[i] != null) && (desc[i] != null))
+                    {
+                        maxUsage = Math.max(maxUsage, usage[i].length());
+                    }
+                }
+                StringBuffer sb = new StringBuffer();
+                for (int i = 0; i < cmds.length; i++)
+                {
+                    // Just in case the command has gone away.
+                    if ((usage[i] != null) && (desc[i] != null))
+                    {
+                        sb.delete(0, sb.length());
+                        for (int j = 0; j < (maxUsage - usage[i].length()); j++)
+                        {
+                            sb.append(' ');
+                        }
+                        out.println(usage[i] + sb + " - " + desc[i]);
+                    }
+                }
+            }
+            else
+            {
+                err.println("No ShellService is unavailable.");
+            }
+        } catch (Exception ex) {
+            err.println(ex.toString());
+        }
+    }
+}