Package org.ungoverned.osgi.bundle.bundlerepository

Source Code of org.ungoverned.osgi.bundle.bundlerepository.BundleRepositoryServiceImpl

/*
* Oscar Bundle Repository
* Copyright (c) 2004, Richard S. Hall
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*   * Neither the name of the ungoverned.org nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Contact: Richard S. Hall (heavy@ungoverned.org)
* Contributor(s):
*
**/
package org.ungoverned.osgi.bundle.bundlerepository;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

import kxml.sax.KXmlSAXParser;

import org.osgi.framework.*;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.ungoverned.osgi.service.bundlerepository.*;

import fr.imag.adele.metadataparser.MultivalueMap;
import fr.imag.adele.metadataparser.XmlCommonHandler;

public class BundleRepositoryServiceImpl implements BundleRepositoryService
{
    private BundleContext m_context = null;
    private boolean m_initialized = false;
    private String[] m_urls = null;
    private List m_bundleList = new ArrayList();
    private Map m_exportPackageMap = new HashMap();

    private static final int EXPORT_PACKAGE_IDX = 0;
    private static final int EXPORT_BUNDLE_IDX = 1;

    private int m_hopCount = 1;

    private static final String[] DEFAULT_REPOSITORY_URL = {
      //"http://oscar-osgi.sf.net/repository.xml",
      "http://www.knopflerfish.org/releases/current/repository.xml",
    };

    public static final String REPOSITORY_URL_PROP
        = "oscar.repository.url";

    public static final String EXTERN_REPOSITORY_TAG = "extern-repositories";

    // These repositories are no more. Silently ignore them.
    private String[] noMoreHosts = new String[]{
        "domoware.isti.cnr.it",
        "update.cainenable.org",
    };


    public BundleRepositoryServiceImpl(BundleContext context)
    {
        m_context = context;
        String urlStr = 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[] getRepositoryURLs()
    {
        if (m_urls != null)
        {
            // Return a copy because the array is mutable.
            return (String[]) m_urls.clone();
        }
        return null;
    }

    public synchronized void setRepositoryURLs(String[] urls)
    {
        if (urls != null)
        {
            m_urls = urls;
            initialize();
        }
    }

    /**
     * Get the number of bundles available in the repository.
     * @return the number of available bundles.
    **/
    public synchronized int getBundleRecordCount()
    {
        if (!m_initialized)
        {
            initialize();
        }

        return m_bundleList.size();
    }

    /**
     * Get the specified bundle record from the repository.
     * @param i the bundle record index to retrieve.
     * @return the associated bundle record or <tt>null</tt>.
    **/
    public synchronized BundleRecord getBundleRecord(int i)
    {
        if (!m_initialized)
        {
            initialize();
        }

        if ((i < 0) || (i >= getBundleRecordCount()))
        {
            return null;
        }

        return (BundleRecord) m_bundleList.get(i);
    }

    public synchronized BundleRecord getBundleRecord(String name,
                                                     int[] version)
    {
      return getBundleRecord(name,
                             new Version(version[0], version[1], version[2]));
    }

  /**
   * Get bundle record for the bundle with the specified name
   * and version from the repository.
   * @param name the name of the bundle to get the bundle record for.
   * @param version The version of the bundle to retrieve the bundle
   *                record of.
   * @return the associated bundle record or <tt>null</tt>.
   **/
  public synchronized BundleRecord getBundleRecord(String name,
                                                   Version version)
  {
    if (!m_initialized) {
      initialize();
    }

    BundleRecord[] records = getBundleRecords(name);

    if (records.length > 0) {
      for (int i = 0; i < records.length; i++) {
        String targetName =
          (String) records[i].getAttribute(BundleRecord.BUNDLE_NAME);
        Version targetVersion = new Version((String) records[i].getAttribute(BundleRecord.BUNDLE_VERSION));

        if ((targetName != null) && targetName.equalsIgnoreCase(name) &&
            (targetVersion.compareTo(version) == 0)) {
          return records[i];
        }
      }
    }

    return null;
  }

  /**
   * Get all versions of bundle records for the specified name
   * from the repository.
   * @param name the bundle record name to retrieve.
   * @return an array of all versions of bundle records having the
   *         specified name or <tt>null</tt>.
   **/
  public synchronized BundleRecord[] getBundleRecords(String name)
  {
    if (!m_initialized) {
      initialize();
    }

    BundleRecord[] records = new BundleRecord[0];

    for (int i = 0; i < m_bundleList.size(); i++) {
      String targetName = (String)
        getBundleRecord(i).getAttribute(BundleRecord.BUNDLE_NAME);
      if ((targetName != null) && targetName.equalsIgnoreCase(name)) {
        BundleRecord[] newRecords = new BundleRecord[records.length + 1];
        System.arraycopy(records, 0, newRecords, 0, records.length);
        newRecords[records.length] = getBundleRecord(i);
        records = newRecords;
      }
    }

    return records;
  }

    public boolean deployBundle(
        PrintStream out, PrintStream err, String updateLocation,
        boolean isResolve, boolean isStart)
    {
        // List to hole bundles that need to be started.
        List startList = null;

        // The first thing to do is to see if the bundle to be
        // deployed is locally installed. If so, then we have to
        // check if an update is available for it. If no update
        // is available then we just return. NOTE: This is sort
        // of a hack that is necessary because the resolvePackages()
        // method doesn't do this sort of checking, so we have
        // to do it here first.
        Bundle localBundle = findLocalBundleByUpdate(updateLocation);
        if (localBundle != null)
        {
            if (!isUpdateAvailable(out, err, localBundle))
            {
                out.println("No update available: "
                    + Util.getBundleName(localBundle));
                return false;
            }
        }

        // Find the appropriate bundle record in the repository,
        // then calculate the transitive closure of its dependencies.
        BundleRecord record = findBundleRecordByUpdate(updateLocation);
        if (record != null)
        {
            // This set will contain all bundles that must be
            // deployed as a result of deploying the target bundle;
            // use a list because this will keep everything in order.
            List deployList = new ArrayList();
            // Add the target bundle to the set, since it will
            // ultimately be deployed as a result of this request.
            deployList.add(updateLocation);
            // If the resolve flag is set, then get its imports to
            // calculate the transitive closure of its dependencies.
            if (isResolve)
            {
                PackageDeclaration[] imports = (PackageDeclaration[])
                    record.getAttribute(BundleRecord.IMPORT_PACKAGE);
                try
                {
                    resolvePackages(imports, deployList);
                }
                catch (ResolveException ex)
                {
                    err.println("Resolve error: " + ex.getPackageDeclaration());
                    return false;
                }
            }

            // Now iterate through all bundles in the deploy list
            // in reverse order and deploy each; the order is not
            // so important, but by reversing them at least the
            // dependencies will be printed first and perhaps it
            // will avoid some ordering issues when we are starting
            // bundles.
            for (int i = deployList.size() - 1; i >= 0; i--)
            {
                String deployLocation = (String) deployList.get(i);

                // See if the bundle is locally installed, in which case
                // we will be performing an update.
                localBundle = findLocalBundleByUpdate(deployLocation);
                if (localBundle != null)
                {
                    // Verify that the locally installed bundle has an
                    // update available for it; if not, then ignore it.
                    if (!isUpdateAvailable(out, err, localBundle))
                    {
                        continue;
                    }

                    // Print out an "updating" message.
                    if (!deployLocation.equals(updateLocation))
                    {
                        out.print("Updating dependency: ");
                    }
                    else
                    {
                        out.print("Updating: ");
                    }
                    out.println(Util.getBundleName(localBundle));

                    // Actually perform the update.
                    try
                    {
                        localBundle.update();
                    }
                    catch (BundleException ex)
                    {
                        err.println("Update error: " + Util.getBundleName(localBundle));
                        ex.printStackTrace(err);
                        return false;
                    }
                }
                else
                {
                    // Print out an "installing" message.
                    if (!deployLocation.equals(updateLocation))
                    {
                        out.print("Installing dependency: ");
                    }
                    else
                    {
                        out.print("Installing: ");
                    }
                    record = findBundleRecordByUpdate(deployLocation);
                    out.println(record.getAttribute(BundleRecord.BUNDLE_NAME));

                    // Actually perform the install.
                    try
                    {
                        Bundle bundle = m_context.installBundle(deployLocation);

                        // If necessary, save the installed bundle to be
                        // started later.
                        if (isStart)
                        {
                            if (startList == null)
                            {
                                startList = new ArrayList();
                            }
                            startList.add(bundle);
                        }
                    }
                    catch (BundleException ex)
                    {
                        err.println("Install error: "
                            + record.getAttribute(BundleRecord.BUNDLE_NAME));
                        ex.printStackTrace(err);
                        return false;
                    }
                }
            }

            // If necessary, start bundles after installing them all.
            if (isStart)
            {
                for (int i = 0; (startList != null) && (i < startList.size()); i++)
                {
                    localBundle = (Bundle) startList.get(i);
                    try
                    {
                        localBundle.start();
                    }
                    catch (BundleException ex)
                    {
                        err.println("Update error: " + Util.getBundleName(localBundle));
                        ex.printStackTrace();
                    }
                }
            }

            return true;
        }

        return false;
    }

    public BundleRecord[] resolvePackages(PackageDeclaration[] pkgs)
        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();
        resolvePackages(pkgs, deployList);

        // Convert list of update locations to an array of bundle
        // records and return it.
        BundleRecord[] records = new BundleRecord[deployList.size()];
        for (int i = 0; i < deployList.size(); i++)
        {
            String updateLocation = (String) deployList.get(i);
            records[i] = findBundleRecordByUpdate(updateLocation);
        }
        return records;
    }

    public void resolvePackages(
        PackageDeclaration[] pkgs, List deployList)
        throws ResolveException
    {
        for (int pkgIdx = 0;
            (pkgs != null) && (pkgIdx < pkgs.length);
            pkgIdx++)
        {
            // If the package can be locally resolved, then
            // it can be completely ignored; otherwise, try
            // to find a resolving bundle.
            if (!isLocallyResolvable(pkgs[pkgIdx]))
            {
                // Select resolving bundle for current package.
                BundleRecord source = selectResolvingBundle(pkgs[pkgIdx]);
                // If there is no resolving bundle, then throw a
                // resolve exception.
                if (source == null)
                {
                    throw new ResolveException(pkgs[pkgIdx]);
                }
                // Get the update location of the resolving bundle.
                String updateLocation = (String)
                    source.getAttribute(BundleRecord.BUNDLE_UPDATELOCATION);
                // If the resolving bundle's update location is already
                // in the deploy list, then just ignore it; otherwise,
                // add it to the deploy list and resolve its packages.
                if (!deployList.contains(updateLocation))
                {
                    deployList.add(updateLocation);
                    PackageDeclaration[] imports = (PackageDeclaration[])
                        source.getAttribute(BundleRecord.IMPORT_PACKAGE);
                    resolvePackages(imports, deployList);
                }
            }
        }
    }

    /**
     * Returns a locally installed bundle that has an update location
     * manifest attribute that matches the specified update location
     * value.
     * @param updateLocation the update location attribute for which to search.
     * @return a bundle with a matching update location attribute or
     *         <tt>null</tt> if one could not be found.
    **/
    private Bundle findLocalBundleByUpdate(String updateLocation)
    {
        Bundle[] locals = m_context.getBundles();
        for (int i = 0; i < locals.length; i++)
        {
            String localUpdateLocation = (String)
                locals[i].getHeaders().get(BundleRecord.BUNDLE_UPDATELOCATION);
            if ((localUpdateLocation != null) &&
                localUpdateLocation.equals(updateLocation))
            {
                return locals[i];
            }
        }
        return null;
    }

  private boolean isUpdateAvailable(PrintStream out,
                                    PrintStream err,
                                    Bundle bundle)
  {
    // Get the bundle's update location.
    String updateLocation =
      (String) bundle.getHeaders().get(Constants.BUNDLE_UPDATELOCATION);

    // Get associated repository bundle recorded for the
    // local bundle and see if an update is necessary.
    BundleRecord record = findBundleRecordByUpdate(updateLocation);
    if (record == null) {
      err.println(Util.getBundleName(bundle) + " not in repository.");
      return false;
    }

    // Check bundle version againts bundle record version.
    Version bundleVersion = bundle.getVersion();
    Version recordVersion = new Version((String) record.getAttribute(BundleRecord.BUNDLE_VERSION));
    if (recordVersion.compareTo(bundleVersion) > 0) {
      return true;
    }
    return false;
  }

    /**
     * Returns the bundle record corresponding to the specified update
     * location string; update location strings are assumed to be
     * unique.
     * @param updateLocation the update location of the bundle record
     *        to retrieve.
     * @return the corresponding bundle record or <tt>null</tt>.
    **/
    private synchronized BundleRecord findBundleRecordByUpdate(String updateLocation)
    {
        if (!m_initialized)
        {
            initialize();
        }

        for (int i = 0; i < m_bundleList.size(); i++)
        {
            String location = (String)
                getBundleRecord(i).getAttribute(BundleRecord.BUNDLE_UPDATELOCATION);
            if ((location != null) && location.equalsIgnoreCase(updateLocation))
            {
                return getBundleRecord(i);
            }
        }
        return null;
    }

    /**
     * Determines whether a package is resolvable at the local framework.
     * @param target the package declaration to check for availability.
     * @return <tt>true</tt> if the package is available locally,
     *         <tt>false</tt> otherwise.
    **/
    private synchronized boolean isLocallyResolvable(PackageDeclaration target)
    {
      // Get package admin service.
      ServiceReference ref = m_context.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
      if (ref == null) {
        return false;
      }

      PackageAdmin pa = (PackageAdmin) m_context.getService(ref);
      if (pa == null) {
        return false;
      }

      ExportedPackage[] exports = pa.getExportedPackages((Bundle)null);
      if (exports != null) {
        for (int i = 0; (exports != null) && (i < exports.length); i++) {
          PackageDeclaration source =
            new PackageDeclaration(exports[i].getName(),
                                   exports[i].getVersion());
          if (source.doesSatisfy(target)) {
            return true;
          }
        }
      }

      return false;
    }

    /**
     * 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(PackageDeclaration targetPkg)
    {
        BundleRecord[] sources = findResolvingBundles(targetPkg);

        if (sources == null)
        {
            return null;
        }

        // Try to select a source bundle record that is already
        // installed locally.
        for (int i = 0; i < sources.length; i++)
        {
            String updateLocation = (String)
                sources[i].getAttribute(BundleRecord.BUNDLE_UPDATELOCATION);
            if (updateLocation != null)
            {
                Bundle bundle = findLocalBundleByUpdate(updateLocation);
                if (bundle != null)
                {
                    return sources[i];
                }
            }
        }

        // If none of the sources are installed locally, then
        // just pick the first one.
        return sources[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 synchronized BundleRecord[] findResolvingBundles(PackageDeclaration targetPkg)
    {
        // Create a list for storing bundles that can resolve package.
        ArrayList resolveList = new ArrayList();
        // Get the exporter list from the export package map for this package.
        ArrayList exporterList = (ArrayList) m_exportPackageMap.get(targetPkg.getName());
        // Loop through each exporter and see if it satisfies the target.
        for (int i = 0; (exporterList != null) && (i < exporterList.size()); i++)
        {
            // Get the export info from the exporter list.
            Object[] exportInfo = (Object[]) exporterList.get(i);
            // Get the export package from the export info.
            PackageDeclaration exportPkg = (PackageDeclaration)
                exportInfo[EXPORT_PACKAGE_IDX];
            // Get the export bundle from the export info.
            BundleRecord exportBundle = (BundleRecord)
                exportInfo[EXPORT_BUNDLE_IDX];
            // See if the export package satisfies the target package.
            if (exportPkg.doesSatisfy(targetPkg))
            {
                // Add it to the list of resolving bundles.
                resolveList.add(exportBundle);
            }
        }
        // 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 void initialize()
    {
        m_initialized = true;
        m_bundleList.clear();
        m_exportPackageMap.clear();

        for (int urlIdx = 0; (m_urls != null) && (urlIdx < m_urls.length); urlIdx++)
        {
            parseRepositoryFile(m_hopCount, m_urls[urlIdx]);
        }
    }

    private String getUserAgentForBundle(Bundle bundle)
    {
      String uaName = bundle.getSymbolicName();
      String uaVers = (String) bundle.getHeaders().get("Bundle-Version");
      String uaComm = "";

      if (0==bundle.getBundleId()) { // The system bundle
        uaComm = uaVers;
        uaName = m_context.getProperty(Constants.FRAMEWORK_VENDOR);
        uaVers = m_context.getProperty(Constants.FRAMEWORK_VERSION);
      }
      return uaName
        + (uaVers!=null && uaVers.length()>0 ? ("/" + uaVers)      : "")
        + (uaComm!=null && uaComm.length()>0 ? (" (" +uaComm +")") : "");
    }

    private void parseRepositoryFile(int hopCount, String urlStr)
    {
        InputStream is = null;
        InputStreamReader isr = 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);

            // This repository is no more. Silently ignore it.
            for (int i=0; i<noMoreHosts.length; i++) {
                if(noMoreHosts[i].equals(url.getHost())) {
                    return;
                }
            }

            URLConnection conn = url.openConnection();

            // Support for http proxy authentication
            String auth = m_context.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);
                }
            }

            // Support for http basic authentication
            String basicAuth = m_context.getProperty("http.basicAuth");
            if (basicAuth != null && !"".equals(basicAuth)) {
              if ("http".equals(url.getProtocol()) ||
                  "https".equals(url.getProtocol())) {
                String base64 = Util.base64Encode(basicAuth);
                conn.setRequestProperty("Authorization",
                                        "Basic " +base64);
              }
            }

            // Identify us (via User-Agent) with bundlename and the
            // framework vendor name and version.
            conn.setRequestProperty("User-Agent",
                                    getUserAgentForBundle(m_context.getBundle())
                                    +" "
                                    +getUserAgentForBundle(m_context.getBundle(0))
                                    );
            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("import-package", HashMap.class);
            handler.addType("export-package", 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)
            {
              System.err.println("Failed to parse '"+url+"': "+ex.getMessage());
              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 any import package declarations
                    // to PackageDeclaration objects.
                    Object target = bundleMap.get(BundleRecord.IMPORT_PACKAGE);
                    if (target != null) {
                      // Overwrite the original package declarations.
                      bundleMap.put(BundleRecord.IMPORT_PACKAGE,
                                    convertPackageDeclarations(target,true));
                    }

                    // Convert any export package declarations
                    // to PackageDeclaration objects.
                    target = bundleMap.get(BundleRecord.EXPORT_PACKAGE);
                    if (target != null)
                    {
                        // Overwrite the original package declarations.
                        bundleMap.put(BundleRecord.EXPORT_PACKAGE,
                                      convertPackageDeclarations(target,false));
                    }

                    // Create a bundle record using the map.
                    BundleRecord record = new BundleRecord(bundleMap);

                    // Try to put all exported packages from this bundle
                    // into the export package map to simplify access.
                    try
                    {
                        PackageDeclaration[] exportPkgs = (PackageDeclaration[])
                            record.getAttribute(BundleRecord.EXPORT_PACKAGE);
                        for (int exportIdx = 0;
                            (exportPkgs != null) && (exportIdx < exportPkgs.length);
                            exportIdx++)
                        {
                            // Check to see if this package is already in the
                            // export package map.
                            ArrayList exporterList = (ArrayList)
                                m_exportPackageMap.get(exportPkgs[exportIdx].getName());
                            // If the package is not in the map, create an
                            // array list for it.
                            if (exporterList == null)
                            {
                                exporterList = new ArrayList();
                            }
                            // Add the export info to the array list.
                            Object[] exportInfo = new Object[2];
                            exportInfo[EXPORT_PACKAGE_IDX] = exportPkgs[exportIdx];
                            exportInfo[EXPORT_BUNDLE_IDX] = record;
                            exporterList.add(exportInfo);
                            // Put the array list containing the export info
                            // into the export map, which will make it easy
                            // to search for which bundles export what. Note,
                            // if the exporterList already was in the map, this
                            // will just overwrite it with the same value.
                            m_exportPackageMap.put(
                                exportPkgs[exportIdx].getName(), exporterList);
                        }
                        // TODO: Filter duplicates.
                        m_bundleList.add(record);
                    }
                    catch (IllegalArgumentException ex)
                    {
                        // Ignore.
                    }
                }
            }
        }
        catch (MalformedURLException ex)
        {
            System.err.println("Error malformed URL, '" +urlStr +"': " + ex);
        }
        catch (IOException ex)
        {
            System.err.println("Error when connecting to '" +urlStr +"': " +ex);
        }
        finally
        {
            try
            {
                if (is != null) is.close();
            }
            catch (IOException ex)
            {
                // Not much we can do.
            }
        }

        Collections.sort(m_bundleList, 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 PackageDeclaration[] convertPackageDeclarations(Object target,
                                                          boolean versionRange)
    {
      PackageDeclaration[] decls = null;

      // If there is only one package it will be a
      // Map as specified above when parsing.
      if (target instanceof Map) {
        // Put package declaration into an array.
        decls = new PackageDeclaration[1];
        decls[0] = convertPackageMap((Map) target, versionRange);
      }
      // If there is more than one package, then the
      // MultivalueMap will convert them to a list of maps.
      else if (target instanceof List) {
        List pkgList = (List) target;
        decls = new PackageDeclaration[pkgList.size()];
        for (int pkgIdx = 0; pkgIdx < decls.length; pkgIdx++) {
          decls[pkgIdx] = convertPackageMap((Map) pkgList.get(pkgIdx),
                                            versionRange);
        }
      }

      return decls;
    }

    private PackageDeclaration convertPackageMap(Map map,
                                                 boolean versionRange)
    {
      // Create a case-insensitive map.
      Map pkgMap = new TreeMap(new Comparator() {
          public int compare(Object o1, Object o2)
          {
            return o1.toString().compareToIgnoreCase(o2.toString());
          }
        });
      pkgMap.putAll((Map) map);

      // Get package name and version.
      String name = (String) pkgMap.get(PackageDeclaration.PACKAGE_ATTR);
      String version = (String) pkgMap.get(PackageDeclaration.VERSION_ATTR);
      if (null==version)
        version = (String) pkgMap.get(PackageDeclaration.SPEC_VERSION_ATTR);

      if (name != null) {
        return new PackageDeclaration(name, version, versionRange);
      }

      return null;
    }
}
TOP

Related Classes of org.ungoverned.osgi.bundle.bundlerepository.BundleRepositoryServiceImpl

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.