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 [8/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/framework/StartLevelImpl.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/StartLevelImpl.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/StartLevelImpl.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/StartLevelImpl.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,147 @@
+/*
+ * 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.framework;
+
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * @author rickhall
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+**/
+public class StartLevelImpl implements StartLevel, Runnable
+{
+ private Felix m_felix = null;
+ private List m_requestList = null;
+ // Reusable admin permission.
+ private static AdminPermission m_adminPerm = new AdminPermission();
+
+ public StartLevelImpl(Felix felix)
+ {
+ m_felix = felix;
+ m_requestList = new ArrayList();
+
+ // Start a thread to perform asynchronous package refreshes.
+ Thread t = new Thread(this, "FelixStartLevel");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#getStartLevel()
+ **/
+ public int getStartLevel()
+ {
+ return m_felix.getStartLevel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#setStartLevel(int)
+ **/
+ public void setStartLevel(int startlevel)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ AccessController.checkPermission(m_adminPerm);
+ }
+ else if (startlevel <= 0)
+ {
+ throw new IllegalArgumentException(
+ "Start level must be greater than zero.");
+ }
+ synchronized (m_requestList)
+ {
+ m_requestList.add(new Integer(startlevel));
+ m_requestList.notifyAll();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#getBundleStartLevel(org.osgi.framework.Bundle)
+ **/
+ public int getBundleStartLevel(Bundle bundle)
+ {
+ return m_felix.getBundleStartLevel(bundle);
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#setBundleStartLevel(org.osgi.framework.Bundle, int)
+ **/
+ public void setBundleStartLevel(Bundle bundle, int startlevel)
+ {
+ m_felix.setBundleStartLevel(bundle, startlevel);
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#getInitialBundleStartLevel()
+ **/
+ public int getInitialBundleStartLevel()
+ {
+ return m_felix.getInitialBundleStartLevel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#setInitialBundleStartLevel(int)
+ **/
+ public void setInitialBundleStartLevel(int startlevel)
+ {
+ m_felix.setInitialBundleStartLevel(startlevel);
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.startlevel.StartLevel#isBundlePersistentlyStarted(org.osgi.framework.Bundle)
+ **/
+ public boolean isBundlePersistentlyStarted(Bundle bundle)
+ {
+ return m_felix.isBundlePersistentlyStarted(bundle);
+ }
+
+ public void run()
+ {
+ int startLevel = 0;
+
+ // This thread loops forever, thus it should
+ // be a daemon thread.
+ while (true)
+ {
+ synchronized (m_requestList)
+ {
+ // Wait for a request.
+ while (m_requestList.size() == 0)
+ {
+ try {
+ m_requestList.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+
+ // Get the requested start level.
+ startLevel = ((Integer) m_requestList.remove(0)).intValue();
+ }
+
+ // Set the new start level.
+ m_felix.setFrameworkStartLevel(startLevel);
+ }
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundle.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundle.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundle.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundle.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,277 @@
+/*
+ * 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.framework;
+
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+import org.apache.osgi.framework.searchpolicy.*;
+import org.apache.osgi.framework.util.CaseInsensitiveMap;
+import org.apache.osgi.framework.util.FelixConstants;
+import org.apache.osgi.moduleloader.LibrarySource;
+import org.apache.osgi.moduleloader.ResourceSource;
+import org.osgi.framework.*;
+
+
+class SystemBundle extends BundleImpl
+{
+ private List m_activatorList = null;
+ private BundleActivator m_activator = null;
+ private Thread m_shutdownThread = null;
+ private Object[][] m_attributes = null;
+ private ResourceSource[] m_resSources = null;
+ private LibrarySource[] m_libSources = null;
+
+ protected SystemBundle(Felix felix, BundleInfo info, List activatorList)
+ throws BundleException
+ {
+ super(felix, info);
+
+ // Create an activator list if necessary.
+ if (activatorList == null)
+ {
+ activatorList = new ArrayList();
+ }
+
+ // Add the bundle activator for the package admin service.
+ activatorList.add(new PackageAdminActivator(felix));
+
+ // Add the bundle activator for the start level service.
+ activatorList.add(new StartLevelActivator(felix));
+
+ m_activatorList = activatorList;
+
+ // The system bundle exports framework packages as well as
+ // arbitrary user-defined packages from the system class path.
+ // We must construct the system bundle's export metadata.
+
+ // Get system property that specifies which class path
+ // packages should be exported by the system bundle.
+ R4Package[] classPathPkgs = null;
+ try
+ {
+ classPathPkgs = R4Package.parseImportOrExportHeader(
+ getFelix().getConfig().get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES));
+ }
+ catch (Exception ex)
+ {
+ getFelix().getLogger().log(
+ LogWrapper.LOG_ERROR,
+ "Error parsing system bundle export statement.", ex);
+ }
+
+ // Now, create the list of standard framework exports for
+ // the system bundle.
+ R4Package[] exports = new R4Package[classPathPkgs.length + 3];
+
+ exports[0] = new R4Package(
+ "org.osgi.framework",
+ new R4Directive[0],
+ new R4Attribute[] { new R4Attribute("version", "1.2.0", false) });
+
+ exports[1] = new R4Package(
+ "org.osgi.service.packageadmin",
+ new R4Directive[0],
+ new R4Attribute[] { new R4Attribute("version", "1.2.0", false) });
+
+ exports[2] = new R4Package(
+ "org.osgi.service.startlevel",
+ new R4Directive[0],
+ new R4Attribute[] { new R4Attribute("version", "1.0.0", false) });
+
+ // Copy the class path exported packages.
+ System.arraycopy(classPathPkgs, 0, exports, 3, classPathPkgs.length);
+
+ m_attributes = new Object[][] {
+ new Object[] { R4SearchPolicy.EXPORTS_ATTR, exports },
+ new Object[] { R4SearchPolicy.IMPORTS_ATTR, new R4Package[0] }
+ };
+
+ m_resSources = new ResourceSource[0];
+
+ m_libSources = null;
+
+ String exportString = "";
+ for (int i = 0; i < exports.length; i++)
+ {
+ exportString = exportString +
+ exports[i].getId()
+ + "; specification-version=\""
+ + exports[i].getVersionLow().toString() + "\"";
+
+ if (i < (exports.length - 1))
+ {
+ exportString = exportString + ", ";
+ exportString = exportString +
+ exports[i].getId()
+ + "; specification-version=\""
+ + exports[i].getVersionLow().toString() + "\", ";
+ }
+ }
+
+ // Initialize header map as a case insensitive map.
+ Map map = new CaseInsensitiveMap();
+ map.put(FelixConstants.BUNDLE_VERSION, FelixConstants.FELIX_VERSION_VALUE);
+ map.put(FelixConstants.BUNDLE_NAME, "System Bundle");
+ map.put(FelixConstants.BUNDLE_DESCRIPTION,
+ "This bundle is system specific; it implements various system services.");
+ map.put(FelixConstants.EXPORT_PACKAGE, exportString);
+ ((SystemBundleArchive) getInfo().getArchive()).setManifestHeader(map);
+ }
+
+ public Object[][] getAttributes()
+ {
+ return m_attributes;
+ }
+
+ public ResourceSource[] getResourceSources()
+ {
+ return m_resSources;
+ }
+
+ public LibrarySource[] getLibrarySources()
+ {
+ return m_libSources;
+ }
+
+ public synchronized void start() throws BundleException
+ {
+ // The system bundle is only started once and it
+ // is started by the framework.
+ if (getState() == Bundle.ACTIVE)
+ {
+ throw new BundleException("Cannot start the system bundle.");
+ }
+
+ getInfo().setState(Bundle.STARTING);
+
+ try {
+ getInfo().setContext(new BundleContextImpl(getFelix(), this));
+ getActivator().start(getInfo().getContext());
+ } catch (Throwable throwable) {
+throwable.printStackTrace();
+ throw new BundleException(
+ "Unable to start system bundle.", throwable);
+ }
+
+ // Do NOT set the system bundle state to active yet, this
+ // must be done after all other bundles have been restarted.
+ // This will be done after the framework is initialized.
+ }
+
+ public synchronized void stop() throws BundleException
+ {
+ if (System.getSecurityManager() != null)
+ {
+ AccessController.checkPermission(new AdminPermission());
+ }
+
+ // Spec says stop() on SystemBundle should return immediately and
+ // shutdown framework on another thread.
+ if (getFelix().getStatus() == Felix.RUNNING_STATUS)
+ {
+ // Initial call of stop, so kick off shutdown.
+ m_shutdownThread = new Thread("FelixShutdown") {
+ public void run()
+ {
+ try
+ {
+ getFelix().shutdown();
+ }
+ catch (Exception ex)
+ {
+ getFelix().getLogger().log(
+ LogWrapper.LOG_ERROR,
+ "SystemBundle: Error while shutting down.", ex);
+ }
+
+ // Only shutdown the JVM if the framework is running stand-alone.
+ String embedded = getFelix().getConfig()
+ .get(FelixConstants.EMBEDDED_EXECUTION_PROP);
+ boolean isEmbedded = (embedded == null)
+ ? false : embedded.equals("true");
+ if (!isEmbedded)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run()
+ {
+ System.exit(0);
+ return null;
+ }
+ });
+ }
+ else
+ {
+ System.exit(0);
+ }
+ }
+ }
+ };
+ getInfo().setState(Bundle.STOPPING);
+ m_shutdownThread.start();
+ }
+ else if ((getFelix().getStatus() == Felix.STOPPING_STATUS) &&
+ (Thread.currentThread() == m_shutdownThread))
+ {
+ // Callback from shutdown thread, so do our own stop.
+ try
+ {
+ getActivator().stop(getInfo().getContext());
+ }
+ catch (Throwable throwable)
+ {
+ throw new BundleException(
+ "Unable to stop system bundle.", throwable);
+ }
+ }
+ }
+
+ public synchronized void uninstall() throws BundleException
+ {
+ throw new BundleException("Cannot uninstall the system bundle.");
+ }
+
+ public synchronized void update() throws BundleException
+ {
+ update(null);
+ }
+
+ public synchronized void update(InputStream is) throws BundleException
+ {
+ if (System.getSecurityManager() != null)
+ {
+ AccessController.checkPermission(new AdminPermission());
+ }
+
+ // TODO: This is supposed to stop and then restart the framework.
+ throw new BundleException("System bundle update not implemented yet.");
+ }
+
+ protected BundleActivator getActivator()
+ throws Exception
+ {
+ if (m_activator == null)
+ {
+ m_activator = new SystemBundleActivator(getFelix(), m_activatorList);
+ }
+ return m_activator;
+ }
+}
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleActivator.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleActivator.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleActivator.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleActivator.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,61 @@
+/*
+ * 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.framework;
+
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+class SystemBundleActivator implements BundleActivator
+{
+ private Felix m_felix = null;
+ private List m_activatorList = null;
+ private BundleContext m_context = null;
+
+ SystemBundleActivator(Felix felix, List activatorList)
+ {
+ this.m_felix = felix;
+ this.m_activatorList = activatorList;
+ }
+
+ public void start(BundleContext context) throws Exception
+ {
+ this.m_context = context;
+
+ // Start all activators.
+ if (m_activatorList != null)
+ {
+ for (int i = 0; i < m_activatorList.size(); i++)
+ {
+ ((BundleActivator) m_activatorList.get(i)).start(context);
+ }
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception
+ {
+ if (m_activatorList != null)
+ {
+ // Stop all activators.
+ for (int i = 0; i < m_activatorList.size(); i++)
+ {
+ ((BundleActivator) m_activatorList.get(i)).stop(context);
+ }
+ }
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleArchive.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleArchive.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleArchive.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/SystemBundleArchive.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,109 @@
+/*
+ * 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.framework;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.osgi.framework.cache.BundleArchive;
+import org.apache.osgi.framework.util.FelixConstants;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+
+public class SystemBundleArchive implements BundleArchive
+{
+ private Map m_headerMap = null;
+
+ public long getId()
+ {
+ return 0;
+ }
+
+ public String getLocation()
+ throws Exception
+ {
+ return FelixConstants.SYSTEM_BUNDLE_LOCATION;
+ }
+
+ public int getPersistentState()
+ throws Exception
+ {
+ return Bundle.ACTIVE;
+ }
+
+ public void setPersistentState(int state)
+ throws Exception
+ {
+ }
+
+ public int getStartLevel()
+ throws Exception
+ {
+ return FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
+ }
+
+ public void setStartLevel(int level)
+ throws Exception
+ {
+ }
+
+ public File getDataFile(String fileName)
+ throws Exception
+ {
+ return null;
+ }
+
+ public BundleActivator getActivator(ClassLoader loader)
+ throws Exception
+ {
+ return null;
+ }
+
+ public void setActivator(Object obj)
+ throws Exception
+ {
+ }
+
+ public int getRevisionCount()
+ throws Exception
+ {
+ return 1;
+ }
+
+ public Map getManifestHeader(int revision)
+ throws Exception
+ {
+ return m_headerMap;
+ }
+
+ protected void setManifestHeader(Map headerMap)
+ {
+ m_headerMap = headerMap;
+ }
+
+ public String[] getClassPath(int revision)
+ throws Exception
+ {
+ return null;
+ }
+
+ public String findLibrary(int revision, String libName)
+ throws Exception
+ {
+ return null;
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleArchive.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleArchive.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleArchive.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleArchive.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,194 @@
+/*
+ * 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.framework.cache;
+
+import java.io.File;
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+
+/**
+ * <p>
+ * This interface represents an individual cached bundle in the
+ * bundle cache. Felix uses this interface to access all information
+ * about the associated bundle's cached information. Classes that implement
+ * this interface will be related to a specific implementation of the
+ * <tt>BundleCache</tt> interface.
+ * </p>
+ * @see org.apache.osgi.framework.BundleCache
+**/
+public interface BundleArchive
+{
+ /**
+ * <p>
+ * Returns the identifier of the bundle associated with this archive.
+ * </p>
+ * @return the identifier of the bundle associated with this archive.
+ **/
+ public long getId();
+
+ /**
+ * <p>
+ * Returns the location string of the bundle associated with this archive.
+ * </p>
+ * @return the location string of the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public String getLocation()
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the persistent state of the bundle associated with the archive;
+ * this value will be either <tt>Bundle.INSTALLED</tt> or <tt>Bundle.ACTIVE</tt>.
+ * </p>
+ * @return the persistent state of the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public int getPersistentState()
+ throws Exception;
+
+ /**
+ * <p>
+ * Sets the persistent state of the bundle associated with this archive;
+ * this value will be either <tt>Bundle.INSTALLED</tt> or <tt>Bundle.ACTIVE</tt>.
+ * </p>
+ * @param state the new bundle state to write to the archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public void setPersistentState(int state)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the start level of the bundle associated with this archive.
+ * </p>
+ * @return the start level of the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public int getStartLevel()
+ throws Exception;
+
+ /**
+ * <p>
+ * Sets the start level of the bundle associated with this archive.
+ * </p>
+ * @param level the new bundle start level to write to the archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public void setStartLevel(int level)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns an appropriate data file for the bundle associated with the
+ * archive using the supplied file name.
+ * </p>
+ * @return a <tt>File</tt> corresponding to the requested data file for
+ * the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public File getDataFile(String fileName)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the persistent bundle activator of the bundle associated with
+ * this archive; this is a non-standard OSGi method that is only called
+ * when Felix is running in non-strict OSGi mode.
+ * </p>
+ * @param loader the class loader to use when trying to instantiate
+ * the bundle activator.
+ * @return the persistent bundle activator of the bundle associated with
+ * this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public BundleActivator getActivator(ClassLoader loader)
+ throws Exception;
+
+ /**
+ * <p>
+ * Sets the persistent bundle activator of the bundle associated with
+ * this archive; this is a non-standard OSGi method that is only called
+ * when Felix is running in non-strict OSGi mode.
+ * </p>
+ * @param obj the new persistent bundle activator to write to the archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public void setActivator(Object obj)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the number of revisions of the bundle associated with the
+ * archive. When a bundle is updated, the previous version of the bundle
+ * is maintained along with the new revision until old revisions are
+ * purged. The revision count reflects how many revisions of the bundle
+ * are currently available in the cache.
+ * </p>
+ * @return the number of revisions of the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public int getRevisionCount()
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the main attributes of the JAR file manifest header of the
+ * specified revision of the bundle associated with this archive. The
+ * returned map should be case insensitive.
+ * </p>
+ * @param revision the specified revision.
+ * @return the case-insensitive JAR file manifest header of the specified
+ * revision of the bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public Map getManifestHeader(int revision)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns an array of <tt>String</tt>s that represent the class path of
+ * the specified revision of the bundle associated with this archive.
+ * Currently, these values are restricted to absolute paths in the file
+ * system, but this may be lifted in the future (perhaps they should be
+ * <tt>ResourceSource</tt>s from the Module Loader.
+ * </p>
+ * @param revision the specified revision.
+ * @return a <tt>String</tt> array of the absolute path names that
+ * comprise the class path of the specified revision of the
+ * bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public String[] getClassPath(int revision)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the absolute file path for the specified native library of the
+ * specified revision of the bundle associated with this archive.
+ * </p>
+ * @param revision the specified revision.
+ * @param libName the name of the library.
+ * @return a <tt>String</tt> that contains the absolute path name to
+ * the requested native library of the specified revision of the
+ * bundle associated with this archive.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public String findLibrary(int revision, String libName)
+ throws Exception;
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleCache.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleCache.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleCache.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/cache/BundleCache.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,148 @@
+/*
+ * 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.framework.cache;
+
+import java.io.InputStream;
+
+import org.apache.osgi.framework.LogWrapper;
+import org.apache.osgi.framework.util.PropertyResolver;
+
+/**
+ * <p>
+ * This interface represents the storage mechanism that Felix uses for
+ * caching bundles. It is possible for multiple implementations of
+ * this interface to exist for different storage technologies, such as the
+ * file system, memory, or a database. Felix includes a default implementation
+ * of this interface that uses the file system. Felix allows you to specify
+ * alternative implementations to use by specifying a class name via the
+ * <tt>felix.cache.class</tt> system property. Bundle cache implemenations
+ * should implement this interface and provide a default constructor.
+ * </p>
+ * @see org.apache.osgi.framework.BundleArchive
+**/
+public interface BundleCache
+{
+ /**
+ * <p>
+ * This method is called before using the BundleCache implementation
+ * to initialize it and to pass it a reference to its associated
+ * configuration property resolver and logger. The <tt>BundleCache</tt>
+ * implementation should not use <tt>System.getProperty()</tt> directly
+ * for configuration properties, it should use the property resolver
+ * instance passed into this method. The property resolver
+ * provides access to properties passed into the Felix instance's
+ * constructor. This approach allows multiple instances of Felix to
+ * exist in memory at the same time, but for
+ * them to be configured differently. For example, an application may
+ * want two instances of Felix, where each instance stores their cache
+ * in a different location in the file system. When using multiple
+ * instances of Felix in memory at the same time, system properties
+ * should be avoided and all properties should be passed in to Felix's
+ * constructor.
+ * </p>
+ * @param cfg the property resolver for obtaining configuration properties.
+ * @param logger the logger to use for reporting errors.
+ * @throws Exception if any error occurs.
+ **/
+ public void initialize(PropertyResolver cfg, LogWrapper logger)
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns all cached bundle archives.
+ * </p>
+ * @return an array of all cached bundle archives.
+ * @throws Exception if any error occurs.
+ **/
+ public BundleArchive[] getArchives()
+ throws Exception;
+
+ /**
+ * <p>
+ * Returns the bundle archive associated with the specified
+ * bundle indentifier.
+ * </p>
+ * @param id the identifier of the bundle archive to retrieve.
+ * @return the bundle archive assocaited with the specified bundle identifier.
+ * @throws Exception if any error occurs.
+ **/
+ public BundleArchive getArchive(long id)
+ throws Exception;
+
+ /**
+ * <p>
+ * Creates a new bundle archive for the specified bundle
+ * identifier using the supplied location string and input stream. The
+ * contents of the bundle JAR file should be read from the supplied
+ * input stream, which will not be <tt>null</tt>. The input stream is
+ * closed by the caller; the implementation is only responsible for
+ * closing streams it opens. If this method completes successfully, then
+ * it means that the initial bundle revision of the specified bundle was
+ * successfully cached.
+ * </p>
+ * @param id the identifier of the bundle associated with the new archive.
+ * @param location the location of the bundle associated with the new archive.
+ * @param is the input stream to the bundle's JAR file.
+ * @return the created bundle archive.
+ * @throws Exception if any error occurs.
+ **/
+ public BundleArchive create(long id, String location, InputStream is)
+ throws Exception;
+
+ /**
+ * <p>
+ * Saves an updated revision of the specified bundle to
+ * the bundle cache using the supplied input stream. The contents of the
+ * updated bundle JAR file should be read from the supplied input stream,
+ * which will not be <tt>null</tt>. The input stream is closed by the
+ * caller; the implementation is only responsible for closing streams
+ * it opens. Updating a bundle in the cache does not replace the current
+ * revision of the bundle, it makes a new revision available. If this
+ * method completes successfully, then it means that the number of
+ * revisions of the specified bundle has increased by one.
+ * </p>
+ * @param ba the bundle archive of the bundle to update.
+ * @param is the input stream to the bundle's updated JAR file.
+ * @throws Exception if any error occurs.
+ **/
+ public void update(BundleArchive ba, InputStream is)
+ throws Exception;
+
+ /**
+ * <p>
+ * Purges all old revisions of the specified bundle from
+ * the cache. If this method completes successfully, then it means that
+ * only the most current revision of the bundle should exist in the cache.
+ * </p>
+ * @param ba the bundle archive of the bundle to purge.
+ * @throws Exception if any error occurs.
+ **/
+ public void purge(BundleArchive ba)
+ throws Exception;
+
+ /**
+ * <p>
+ * Removes the specified bundle from the cache. If this method
+ * completes successfully, there should be no trace of the removed bundle
+ * in the cache.
+ * </p>
+ * @param ba the bundle archive of the bundle to remove.
+ * @throws Exception if any error occurs.
+ **/
+ public void remove(BundleArchive ba)
+ throws Exception;
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/cache/DefaultBundleArchive.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/cache/DefaultBundleArchive.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/cache/DefaultBundleArchive.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/cache/DefaultBundleArchive.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,1471 @@
+/*
+ * 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.framework.cache;
+
+import java.io.*;
+import java.security.*;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.apache.osgi.framework.LogWrapper;
+import org.apache.osgi.framework.util.*;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+
+/**
+ * <p>
+ * This class, combined with <tt>DefaultBundleCache</tt>, implements the
+ * default file system-based bundle cache for Felix.
+ * </p>
+ * @see org.apache.osgi.framework.util.DefaultBundleCache
+**/
+public class DefaultBundleArchive implements BundleArchive
+{
+ private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
+ private static final transient String BUNDLE_LOCATION_FILE = "bundle.location";
+ private static final transient String BUNDLE_STATE_FILE = "bundle.state";
+ private static final transient String BUNDLE_START_LEVEL_FILE = "bundle.startlevel";
+ private static final transient String REFRESH_COUNTER_FILE = "refresh.counter";
+ private static final transient String BUNDLE_ACTIVATOR_FILE = "bundle.activator";
+
+ private static final transient String REVISION_DIRECTORY = "version";
+ private static final transient String EMBEDDED_DIRECTORY = "embedded";
+ private static final transient String LIBRARY_DIRECTORY = "lib";
+ private static final transient String DATA_DIRECTORY = "data";
+
+ private static final transient String ACTIVE_STATE = "active";
+ private static final transient String INSTALLED_STATE = "installed";
+ private static final transient String UNINSTALLED_STATE = "uninstalled";
+
+ private LogWrapper m_logger = null;
+ private long m_id = -1;
+ private File m_dir = null;
+ private String m_location = null;
+ private int m_persistentState = -1;
+ private int m_startLevel = -1;
+ private Map m_currentHeader = null;
+
+ private long m_refreshCount = -1;
+ private int m_revisionCount = -1;
+
+ public DefaultBundleArchive(LogWrapper logger, File dir, long id, String location, InputStream is)
+ throws Exception
+ {
+ this(logger, dir, id);
+ m_location = location;
+
+ // Try to save and pre-process the bundle JAR.
+ try
+ {
+ initialize(is);
+ }
+ catch (Exception ex)
+ {
+ if (!deleteDirectoryTree(dir))
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "Unable to delete the archive directory: " + id);
+ }
+ throw ex;
+ }
+ }
+
+ public DefaultBundleArchive(LogWrapper logger, File dir, long id)
+ {
+ m_logger = logger;
+ m_dir = dir;
+ m_id = id;
+ if (m_id <= 0)
+ {
+ throw new IllegalArgumentException(
+ "Bundle ID cannot be less than or equal to zero.");
+ }
+ }
+
+ private void initialize(InputStream is)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.INITIALIZE_ACTION, this, is));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ initializeUnchecked(is);
+ }
+ }
+
+ private void initializeUnchecked(InputStream is)
+ throws Exception
+ {
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+
+ try
+ {
+ // Create archive directory.
+ if (!m_dir.mkdir())
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to create archive directory.");
+ throw new IOException("Unable to create archive directory.");
+ }
+
+ // Save location string.
+ File file = new File(m_dir, BUNDLE_LOCATION_FILE);
+ fw = new FileWriter(file);
+ bw = new BufferedWriter(fw);
+ bw.write(m_location, 0, m_location.length());
+
+ // Create version/revision directory for bundle JAR.
+ // Since this is only called when the bundle JAR is
+ // first saved, the update and revision will always
+ // be "0.0" for the directory name.
+ File revisionDir = new File(m_dir, REVISION_DIRECTORY + "0.0");
+ if (!revisionDir.mkdir())
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to create revision directory.");
+ throw new IOException("Unable to create revision directory.");
+ }
+
+ // Save the bundle jar file.
+ file = new File(revisionDir, BUNDLE_JAR_FILE);
+ copy(is, file);
+
+ // This will always be revision zero.
+ preprocessBundleJar(0, revisionDir);
+
+ }
+ finally
+ {
+ if (is != null) is.close();
+ if (bw != null) bw.close();
+ if (fw != null) fw.close();
+ }
+ }
+
+ public File getDirectory()
+ {
+ return m_dir;
+ }
+
+ public long getId()
+ {
+ return m_id;
+ }
+
+ public String getLocation()
+ throws Exception
+ {
+ if (m_location != null)
+ {
+ return m_location;
+ }
+ else if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return (String) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_LOCATION_ACTION, this));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getLocationUnchecked();
+ }
+ }
+
+ private String getLocationUnchecked()
+ throws Exception
+ {
+ // Get bundle location file.
+ File locFile = new File(m_dir, BUNDLE_LOCATION_FILE);
+
+ // Read bundle location.
+ FileReader fr = null;
+ BufferedReader br = null;
+ try
+ {
+ fr = new FileReader(locFile);
+ br = new BufferedReader(fr);
+ m_location = br.readLine();
+ return m_location;
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (fr != null) fr.close();
+ }
+ }
+
+ public int getPersistentState()
+ throws Exception
+ {
+ if (m_persistentState >= 0)
+ {
+ return m_persistentState;
+ }
+ else if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return ((Integer) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_PERSISTENT_STATE_ACTION, this))).intValue();
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getPersistentStateUnchecked();
+ }
+ }
+
+ private int getPersistentStateUnchecked()
+ throws Exception
+ {
+ // Get bundle state file.
+ File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
+
+ // If the state file doesn't exist, then
+ // assume the bundle was installed.
+ if (!stateFile.exists())
+ {
+ return Bundle.INSTALLED;
+ }
+
+ // Read the bundle state.
+ FileReader fr = null;
+ BufferedReader br= null;
+ try
+ {
+ fr = new FileReader(stateFile);
+ br = new BufferedReader(fr);
+ String s = br.readLine();
+ if (s.equals(ACTIVE_STATE))
+ {
+ m_persistentState = Bundle.ACTIVE;
+ }
+ else if (s.equals(UNINSTALLED_STATE))
+ {
+ m_persistentState = Bundle.UNINSTALLED;
+ }
+ else
+ {
+ m_persistentState = Bundle.INSTALLED;
+ }
+ return m_persistentState;
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (fr != null) fr.close();
+ }
+ }
+
+ public void setPersistentState(int state)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.SET_PERSISTENT_STATE_ACTION, this, state));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ setPersistentStateUnchecked(state);
+ }
+ }
+
+ private void setPersistentStateUnchecked(int state)
+ throws Exception
+ {
+ // Get bundle state file.
+ File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
+
+ // Write the bundle state.
+ FileWriter fw = null;
+ BufferedWriter bw= null;
+ try
+ {
+ fw = new FileWriter(stateFile);
+ bw = new BufferedWriter(fw);
+ String s = null;
+ switch (state)
+ {
+ case Bundle.ACTIVE:
+ s = ACTIVE_STATE;
+ break;
+ case Bundle.UNINSTALLED:
+ s = UNINSTALLED_STATE;
+ break;
+ default:
+ s = INSTALLED_STATE;
+ break;
+ }
+ bw.write(s, 0, s.length());
+ m_persistentState = state;
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to record state: " + ex);
+ throw ex;
+ }
+ finally
+ {
+ if (bw != null) bw.close();
+ if (fw != null) fw.close();
+ }
+ }
+
+ public int getStartLevel()
+ throws Exception
+ {
+ if (m_startLevel >= 0)
+ {
+ return m_startLevel;
+ }
+ else if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return ((Integer) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_START_LEVEL_ACTION, this))).intValue();
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getStartLevelUnchecked();
+ }
+ }
+
+ private int getStartLevelUnchecked()
+ throws Exception
+ {
+ // Get bundle start level file.
+ File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
+
+ // If the start level file doesn't exist, then
+ // return an error.
+ if (!levelFile.exists())
+ {
+ return -1;
+ }
+
+ // Read the bundle start level.
+ FileReader fr = null;
+ BufferedReader br= null;
+ try
+ {
+ fr = new FileReader(levelFile);
+ br = new BufferedReader(fr);
+ m_startLevel = Integer.parseInt(br.readLine());
+ return m_startLevel;
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (fr != null) fr.close();
+ }
+ }
+
+ public void setStartLevel(int level)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.SET_START_LEVEL_ACTION, this, level));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ setStartLevelUnchecked(level);
+ }
+ }
+
+ private void setStartLevelUnchecked(int level)
+ throws Exception
+ {
+ // Get bundle start level file.
+ File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
+
+ // Write the bundle start level.
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try
+ {
+ fw = new FileWriter(levelFile);
+ bw = new BufferedWriter(fw);
+ String s = Integer.toString(level);
+ bw.write(s, 0, s.length());
+ m_startLevel = level;
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to record start leel: " + ex);
+ throw ex;
+ }
+ finally
+ {
+ if (bw != null) bw.close();
+ if (fw != null) fw.close();
+ }
+ }
+
+ public File getDataFile(String fileName)
+ throws Exception
+ {
+ // Do some sanity checking.
+ if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
+ throw new IllegalArgumentException("The data file path must be relative, not absolute.");
+ else if (fileName.indexOf("..") >= 0)
+ throw new IllegalArgumentException("The data file path cannot contain a reference to the \"..\" directory.");
+
+ // Get bundle data directory.
+ File dataDir = new File(m_dir, DATA_DIRECTORY);
+
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.CREATE_DATA_DIR_ACTION, this, dataDir));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ createDataDirectoryUnchecked(dataDir);
+ }
+
+ // Return the data file.
+ return new File(dataDir, fileName);
+ }
+
+ private void createDataDirectoryUnchecked(File dir)
+ throws Exception
+ {
+ // Create data directory if necessary.
+ if (!dir.exists())
+ {
+ if (!dir.mkdir())
+ {
+ throw new IOException("Unable to create bundle data directory.");
+ }
+ }
+ }
+
+ public BundleActivator getActivator(ClassLoader loader)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return (BundleActivator) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_ACTIVATOR_ACTION, this, loader));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getActivatorUnchecked(loader);
+ }
+ }
+
+ private BundleActivator getActivatorUnchecked(ClassLoader loader)
+ throws Exception
+ {
+ // Get bundle activator file.
+ File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
+ // If the activator file doesn't exist, then
+ // assume there isn't one.
+ if (!activatorFile.exists())
+ return null;
+
+ // Deserialize the activator object.
+ InputStream is = null;
+ ObjectInputStreamX ois = null;
+ try
+ {
+ is = new FileInputStream(activatorFile);
+ ois = new ObjectInputStreamX(is, loader);
+ Object o = ois.readObject();
+ return (BundleActivator) o;
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Trying to deserialize - " + ex);
+ }
+ finally
+ {
+ if (ois != null) ois.close();
+ if (is != null) is.close();
+ }
+
+ return null;
+ }
+
+ public void setActivator(Object obj)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.SET_ACTIVATOR_ACTION, this, obj));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ setActivatorUnchecked(obj);
+ }
+ }
+
+ private void setActivatorUnchecked(Object obj)
+ throws Exception
+ {
+ if (!(obj instanceof Serializable))
+ {
+ return;
+ }
+
+ // Get bundle activator file.
+ File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
+
+ // Serialize the activator object.
+ OutputStream os = null;
+ ObjectOutputStream oos = null;
+ try
+ {
+ os = new FileOutputStream(activatorFile);
+ oos = new ObjectOutputStream(os);
+ oos.writeObject(obj);
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to serialize activator - " + ex);
+ throw ex;
+ }
+ finally
+ {
+ if (oos != null) oos.close();
+ if (os != null) os.close();
+ }
+ }
+
+ public int getRevisionCount()
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return ((Integer) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_REVISION_COUNT_ACTION, this))).intValue();
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getRevisionCountUnchecked();
+ }
+ }
+
+ public int getRevisionCountUnchecked()
+ {
+ // We should always have at least one revision
+ // directory, so try to count them if the value
+ // has not been initialized yet.
+ if (m_revisionCount <= 0)
+ {
+ m_revisionCount = 0;
+ File[] children = m_dir.listFiles();
+ for (int i = 0; (children != null) && (i < children.length); i++)
+ {
+ if (children[i].getName().startsWith(REVISION_DIRECTORY))
+ {
+ m_revisionCount++;
+ }
+ }
+ }
+ return m_revisionCount;
+ }
+
+ public Map getManifestHeader(int revision)
+ throws Exception
+ {
+ // If the request is for the current revision header,
+ // then return the cached copy if it is present.
+ if ((revision == (getRevisionCount() - 1)) && (m_currentHeader != null))
+ {
+ return m_currentHeader;
+ }
+
+ // Get the revision directory.
+ File revisionDir = new File(
+ m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
+
+ // Get the embedded resource.
+ JarFile jarFile = null;
+
+ try
+ {
+ // Create JarFile object using privileged block.
+ if (System.getSecurityManager() != null)
+ {
+ jarFile = (JarFile) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.OPEN_BUNDLE_JAR_ACTION, this, revisionDir));
+ }
+ else
+ {
+ jarFile = openBundleJarUnchecked(revisionDir);
+ }
+
+ // Error if no jar file.
+ if (jarFile == null)
+ {
+ throw new IOException("No JAR file found.");
+ }
+
+ // Get manifest.
+ Manifest mf = jarFile.getManifest();
+ // Create a case insensitive map of manifest attributes.
+ Map map = new CaseInsensitiveMap(mf.getMainAttributes());
+ // If the request is for the current revision's header,
+ // then cache it.
+ if (revision == (getRevisionCount() - 1))
+ {
+ m_currentHeader = map;
+ }
+ return map;
+
+ } catch (PrivilegedActionException ex) {
+ throw ((PrivilegedActionException) ex).getException();
+ } finally {
+ if (jarFile != null) jarFile.close();
+ }
+ }
+
+ private JarFile openBundleJarUnchecked(File revisionDir)
+ throws Exception
+ {
+ // Get bundle jar file.
+ File bundleJar = new File(revisionDir, BUNDLE_JAR_FILE);
+ // Get bundle jar file.
+ return new JarFile(bundleJar);
+ }
+
+ public String[] getClassPath(int revision)
+ throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ return (String []) AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.GET_CLASS_PATH_ACTION, this, revision));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ return getClassPathUnchecked(revision);
+ }
+ }
+
+ private String[] getClassPathUnchecked(int revision)
+ throws Exception
+ {
+ // Get the revision directory.
+ File revisionDir = new File(
+ m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
+
+ // Get the bundle's manifest header.
+ Map map = getManifestHeader(revision);
+ if (map == null)
+ {
+ map = new HashMap();
+ }
+
+ // Find class path meta-data.
+ String classPath = null;
+ Iterator iter = map.entrySet().iterator();
+ while ((classPath == null) && iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (entry.getKey().toString().toLowerCase().equals(
+ FelixConstants.BUNDLE_CLASSPATH.toLowerCase()))
+ {
+ classPath = entry.getValue().toString();
+ }
+ }
+
+ // Parse the class path into strings.
+ String[] classPathStrings = Util.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ // Now, check for "." in the class path.
+ boolean includeDot = false;
+ for (int i = 0; !includeDot && (i < classPathStrings.length); i++)
+ {
+ if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ includeDot = true;
+ }
+ }
+
+ // Include all JARs in the embedded jar directory, since they
+ // were extracted when the bundle was initially saved.
+ File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
+ String[] paths = null;
+ if (embedDir.exists())
+ {
+ // The size of the paths array is the number of
+ // embedded JAR files plus one, if we need to include
+ // ".", otherwise it is just the number of JAR files.
+ // If "." is included, then it will be added to the
+ // first place in the path array below.
+ File[] children = embedDir.listFiles();
+ int size = (children == null) ? 0 : children.length;
+ size = (includeDot) ? size + 1 : size;
+ paths = new String[size];
+ for (int i = 0; i < children.length; i++)
+ {
+ // If we are including "." then skip the first slot,
+ // because this is where we will put the bundle JAR file.
+ paths[(includeDot) ? i + 1 : i] = children[i].getPath();
+ }
+ }
+
+ // If there is nothing on the class path, then include
+ // "." by default, as per the spec.
+ if ((paths == null) || (paths.length == 0))
+ {
+ includeDot = true;
+ paths = new String[1];
+ }
+
+ // Put the bundle jar file first, if included.
+ if (includeDot)
+ {
+ paths[0] = revisionDir + File.separator + BUNDLE_JAR_FILE;
+ }
+
+ return paths;
+ }
+
+// TODO: This will need to consider security.
+ public String findLibrary(int revision, String libName)
+ throws Exception
+ {
+ return findLibraryUnchecked(revision, libName);
+ }
+
+ private String findLibraryUnchecked(int revision, String libName)
+ throws Exception
+ {
+ // Get the revision directory.
+ File revisionDir = new File(
+ m_dir.getAbsoluteFile(),
+ REVISION_DIRECTORY + getRefreshCount() + "." + revision);
+
+ // Get bundle lib directory.
+ File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
+ // Get lib file.
+ File libFile = new File(libDir, File.separatorChar + libName);
+ // Make sure that the library's parent directory exists;
+ // it may be in a sub-directory.
+ libDir = libFile.getParentFile();
+ if (!libDir.exists())
+ {
+ if (!libDir.mkdirs())
+ {
+ throw new IOException("Unable to create library directory.");
+ }
+ }
+ // Extract the library from the JAR file if it does not
+ // already exist.
+ if (!libFile.exists())
+ {
+ JarFile jarFile = null;
+ InputStream is = null;
+
+ try
+ {
+ jarFile = openBundleJarUnchecked(revisionDir);
+ ZipEntry ze = jarFile.getEntry(libName);
+ if (ze == null)
+ {
+ throw new IOException("No JAR entry: " + libName);
+ }
+ is = new BufferedInputStream(
+ jarFile.getInputStream(ze), DefaultBundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + libName);
+ }
+
+ // Create the file.
+ copy(is, libFile);
+
+ }
+ finally
+ {
+ if (jarFile != null) jarFile.close();
+ if (is != null) is.close();
+ }
+ }
+
+ return libFile.toString();
+ }
+
+ /**
+ * This utility method is used to retrieve the current refresh
+ * counter value for the bundle. This value is used when generating
+ * the bundle JAR directory name where native libraries are extracted.
+ * This is necessary because Sun's JVM requires a one-to-one mapping
+ * between native libraries and class loaders where the native library
+ * is uniquely identified by its absolute path in the file system. This
+ * constraint creates a problem when a bundle is refreshed, because it
+ * gets a new class loader. Using the refresh counter to generate the name
+ * of the bundle JAR directory resolves this problem because each time
+ * bundle is refresh, the native library will have a unique name.
+ * As a result of the unique name, the JVM will then reload the
+ * native library without a problem.
+ **/
+ private long getRefreshCount()
+ throws Exception
+ {
+ // If we have already read the update counter file,
+ // then just return the result.
+ if (m_refreshCount >= 0)
+ {
+ return m_refreshCount;
+ }
+
+ // Get update counter file.
+ File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
+
+ // If the update counter file doesn't exist, then
+ // assume the counter is at zero.
+ if (!counterFile.exists())
+ {
+ return 0;
+ }
+
+ // Read the bundle update counter.
+ FileReader fr = null;
+ BufferedReader br = null;
+ try
+ {
+ fr = new FileReader(counterFile);
+ br = new BufferedReader(fr);
+ long counter = Long.parseLong(br.readLine());
+ return counter;
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (fr != null) fr.close();
+ }
+ }
+
+ /**
+ * This utility method is used to retrieve the current refresh
+ * counter value for the bundle. This value is used when generating
+ * the bundle JAR directory name where native libraries are extracted.
+ * This is necessary because Sun's JVM requires a one-to-one mapping
+ * between native libraries and class loaders where the native library
+ * is uniquely identified by its absolute path in the file system. This
+ * constraint creates a problem when a bundle is refreshed, because it
+ * gets a new class loader. Using the refresh counter to generate the name
+ * of the bundle JAR directory resolves this problem because each time
+ * bundle is refresh, the native library will have a unique name.
+ * As a result of the unique name, the JVM will then reload the
+ * native library without a problem.
+ **/
+ private void setRefreshCount(long counter)
+ throws Exception
+ {
+ // Get update counter file.
+ File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
+
+ // Write the update counter.
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try
+ {
+ fw = new FileWriter(counterFile);
+ bw = new BufferedWriter(fw);
+ String s = Long.toString(counter);
+ bw.write(s, 0, s.length());
+ m_refreshCount = counter;
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "DefaultBundleArchive: Unable to write counter: " + ex);
+ throw ex;
+ }
+ finally
+ {
+ if (bw != null) bw.close();
+ if (fw != null) fw.close();
+ }
+ }
+
+ //
+ // File-oriented utility methods.
+ //
+
+ protected static boolean deleteDirectoryTree(File target)
+ {
+ if (!target.exists())
+ {
+ return true;
+ }
+
+ if (target.isDirectory())
+ {
+ File[] files = target.listFiles();
+ for (int i = 0; i < files.length; i++)
+ {
+ deleteDirectoryTree(files[i]);
+ }
+ }
+
+ return target.delete();
+ }
+
+ /**
+ * This method copies an input stream to the specified file.
+ * <p>
+ * Security: This method must be called from within a <tt>doPrivileged()</tt>
+ * block since it accesses the disk.
+ * @param is the input stream to copy.
+ * @param outputFile the file to which the input stream should be copied.
+ **/
+ private void copy(InputStream is, File outputFile)
+ throws IOException
+ {
+ OutputStream os = null;
+
+ try
+ {
+ os = new BufferedOutputStream(
+ new FileOutputStream(outputFile), DefaultBundleCache.BUFSIZE);
+ byte[] b = new byte[DefaultBundleCache.BUFSIZE];
+ int len = 0;
+ while ((len = is.read(b)) != -1)
+ os.write(b, 0, len);
+ }
+ finally
+ {
+ if (is != null) is.close();
+ if (os != null) os.close();
+ }
+ }
+
+ /**
+ * This method pre-processes a bundle JAR file making it ready
+ * for use. This entails extracting all embedded JAR files and
+ * all native libraries.
+ * @throws java.lang.Exception if any error occurs while processing JAR file.
+ **/
+ private void preprocessBundleJar(int revision, File revisionDir)
+ throws Exception
+ {
+ //
+ // Create special directories so that we can avoid checking
+ // for their existence all the time.
+ //
+
+ File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
+ if (!embedDir.exists())
+ {
+ if (!embedDir.mkdir())
+ {
+ throw new IOException("Could not create embedded JAR directory.");
+ }
+ }
+
+ File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
+ if (!libDir.exists())
+ {
+ if (!libDir.mkdir())
+ {
+ throw new IOException("Unable to create native library directory.");
+ }
+ }
+
+ //
+ // This block extracts all embedded JAR files.
+ //
+
+ try
+ {
+ // Get the bundle's manifest header.
+ Map map = getManifestHeader(revision);
+ if (map == null)
+ {
+ map = new HashMap();
+ }
+
+ // Find class path meta-data.
+ String classPath = null;
+ Iterator iter = map.entrySet().iterator();
+ while ((classPath == null) && iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (entry.getKey().toString().toLowerCase().equals(
+ FelixConstants.BUNDLE_CLASSPATH.toLowerCase()))
+ {
+ classPath = entry.getValue().toString();
+ }
+ }
+
+ // Parse the class path into strings.
+ String[] classPathStrings = Util.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ for (int i = 0; i < classPathStrings.length; i++)
+ {
+ if (!classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ extractEmbeddedJar(revisionDir, classPathStrings[i]);
+ }
+ }
+
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+
+ /**
+ * This method extracts an embedded JAR file from the bundle's
+ * JAR file.
+ * <p>
+ * Security: This method must be called from within a <tt>doPrivileged()</tt>
+ * block since it accesses the disk.
+ * @param id the identifier of the bundle that owns the embedded JAR file.
+ * @param jarPath the path to the embedded JAR file inside the bundle JAR file.
+ **/
+ private void extractEmbeddedJar(File revisionDir, String jarPath)
+ throws Exception
+ {
+ // Remove leading slash if present.
+ jarPath = (jarPath.charAt(0) == '/') ? jarPath.substring(1) : jarPath;
+ // Get only the JAR file name.
+ String jarName = (jarPath.lastIndexOf('/') >= 0)
+ ? jarPath.substring(jarPath.lastIndexOf('/') + 1) : jarPath;
+
+ // If JAR is already extracted, then don't
+ // re-extract it...
+ File embedFile = new File(
+ revisionDir, EMBEDDED_DIRECTORY + File.separatorChar + jarName);
+ if (!embedFile.exists())
+ {
+ JarFile jarFile = null;
+ InputStream is = null;
+
+ try
+ {
+ jarFile = openBundleJarUnchecked(revisionDir);
+ ZipEntry ze = jarFile.getEntry(jarPath);
+ if (ze == null)
+ {
+ throw new IOException("No JAR entry: " + jarPath);
+ }
+ is = new BufferedInputStream(jarFile.getInputStream(ze), DefaultBundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + jarPath);
+ }
+
+ // Create the file.
+ copy(is, embedFile);
+
+ }
+ finally
+ {
+ if (jarFile != null) jarFile.close();
+ if (is != null) is.close();
+ }
+ }
+ }
+
+ // INCREASES THE REVISION COUNT.
+ protected void update(InputStream is) throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.UPDATE_ACTION, this, is));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ updateUnchecked(is);
+ }
+ }
+
+ // INCREASES THE REVISION COUNT.
+ private void updateUnchecked(InputStream is) throws Exception
+ {
+ File revisionDir = null;
+
+ try
+ {
+ // Create the new revision directory.
+ int revision = getRevisionCountUnchecked();
+ revisionDir = new File(
+ m_dir, REVISION_DIRECTORY
+ + getRefreshCount() + "." + revision);
+ if (!revisionDir.mkdir())
+ {
+ throw new IOException("Unable to create revision directory.");
+ }
+
+ // Save the new revision bundle jar file.
+ File file = new File(revisionDir, BUNDLE_JAR_FILE);
+ copy(is, file);
+
+ preprocessBundleJar(revision, revisionDir);
+ }
+ catch (Exception ex)
+ {
+ if ((revisionDir != null) && revisionDir.exists())
+ {
+ try
+ {
+ deleteDirectoryTree(revisionDir);
+ }
+ catch (Exception ex2)
+ {
+ // There is very little we can do here.
+ m_logger.log(
+ LogWrapper.LOG_ERROR,
+ "Unable to remove partial revision directory.", ex2);
+ }
+ }
+ throw ex;
+ }
+
+ // If everything was successful, then update
+ // the revision count.
+ m_revisionCount++;
+ // Clear the cached revision header, since it is
+ // no longer the current revision.
+ m_currentHeader = null;
+ }
+
+ // DECREASES THE REVISION COUNT.
+ protected void purge() throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.PURGE_ACTION, this));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ purgeUnchecked();
+ }
+ }
+
+ // DECREASES THE REVISION COUNT.
+ private void purgeUnchecked() throws Exception
+ {
+ // Get the current update count.
+ long update = getRefreshCount();
+ // Get the current revision count.
+ int count = getRevisionCountUnchecked();
+
+ File revisionDir = null;
+ for (int i = 0; i < count - 1; i++)
+ {
+ revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + i);
+ if (revisionDir.exists())
+ {
+ deleteDirectoryTree(revisionDir);
+ }
+ }
+ // Increment the update count.
+ setRefreshCount(update + 1);
+
+ // Rename the current revision to be the current update.
+ File currentDir = new File(m_dir, REVISION_DIRECTORY + (update + 1) + ".0");
+ revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + (count - 1));
+ revisionDir.renameTo(currentDir);
+
+ // If everything is successful, then set the revision
+ // count to one.
+ m_revisionCount = 1;
+ // Although the cached current header should stay the same
+ // here, clear it for consistency.
+ m_currentHeader = null;
+ }
+
+ protected void remove() throws Exception
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(
+ new PrivilegedAction(
+ PrivilegedAction.REMOVE_ACTION, this));
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+ else
+ {
+ removeUnchecked();
+ }
+ }
+
+ private void removeUnchecked() throws Exception
+ {
+ deleteDirectoryTree(m_dir);
+ }
+
+ //
+ // Utility class for performing privileged actions.
+ //
+
+ private static class PrivilegedAction implements PrivilegedExceptionAction
+ {
+ private static final int INITIALIZE_ACTION = 0;
+ private static final int UPDATE_ACTION = 1;
+ private static final int PURGE_ACTION = 2;
+ private static final int REMOVE_ACTION = 3;
+ private static final int GET_REVISION_COUNT_ACTION = 4;
+ private static final int GET_LOCATION_ACTION = 5;
+ private static final int GET_PERSISTENT_STATE_ACTION = 6;
+ private static final int SET_PERSISTENT_STATE_ACTION = 7;
+ private static final int GET_START_LEVEL_ACTION = 8;
+ private static final int SET_START_LEVEL_ACTION = 9;
+ private static final int OPEN_BUNDLE_JAR_ACTION = 10;
+ private static final int CREATE_DATA_DIR_ACTION = 11;
+ private static final int GET_CLASS_PATH_ACTION = 12;
+ private static final int GET_ACTIVATOR_ACTION = 13;
+ private static final int SET_ACTIVATOR_ACTION = 14;
+
+ private int m_action = 0;
+ private DefaultBundleArchive m_archive = null;
+ private InputStream m_isArg = null;
+ private int m_intArg = 0;
+ private File m_fileArg = null;
+ private ClassLoader m_loaderArg = null;
+ private Object m_objArg = null;
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive)
+ {
+ m_action = action;
+ m_archive = archive;
+ }
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive, InputStream isArg)
+ {
+ m_action = action;
+ m_archive = archive;
+ m_isArg = isArg;
+ }
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive, int intArg)
+ {
+ m_action = action;
+ m_archive = archive;
+ m_intArg = intArg;
+ }
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive, File fileArg)
+ {
+ m_action = action;
+ m_archive = archive;
+ m_fileArg = fileArg;
+ }
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive, ClassLoader loaderArg)
+ {
+ m_action = action;
+ m_archive = archive;
+ m_loaderArg = loaderArg;
+ }
+
+ public PrivilegedAction(int action, DefaultBundleArchive archive, Object objArg)
+ {
+ m_action = action;
+ m_archive = archive;
+ m_objArg = objArg;
+ }
+
+ public Object run() throws Exception
+ {
+ switch (m_action)
+ {
+ case INITIALIZE_ACTION:
+ m_archive.initializeUnchecked(m_isArg);
+ return null;
+ case UPDATE_ACTION:
+ m_archive.updateUnchecked(m_isArg);
+ return null;
+ case PURGE_ACTION:
+ m_archive.purgeUnchecked();
+ return null;
+ case REMOVE_ACTION:
+ m_archive.removeUnchecked();
+ return null;
+ case GET_REVISION_COUNT_ACTION:
+ return new Integer(m_archive.getRevisionCountUnchecked());
+ case GET_LOCATION_ACTION:
+ return m_archive.getLocationUnchecked();
+ case GET_PERSISTENT_STATE_ACTION:
+ return new Integer(m_archive.getPersistentStateUnchecked());
+ case SET_PERSISTENT_STATE_ACTION:
+ m_archive.setPersistentStateUnchecked(m_intArg);
+ return null;
+ case GET_START_LEVEL_ACTION:
+ return new Integer(m_archive.getStartLevelUnchecked());
+ case SET_START_LEVEL_ACTION:
+ m_archive.setStartLevelUnchecked(m_intArg);
+ return null;
+ case OPEN_BUNDLE_JAR_ACTION:
+ return m_archive.openBundleJarUnchecked(m_fileArg);
+ case CREATE_DATA_DIR_ACTION:
+ m_archive.createDataDirectoryUnchecked(m_fileArg);
+ return null;
+ case GET_CLASS_PATH_ACTION:
+ return m_archive.getClassPathUnchecked(m_intArg);
+ case GET_ACTIVATOR_ACTION:
+ return m_archive.getActivatorUnchecked(m_loaderArg);
+ case SET_ACTIVATOR_ACTION:
+ m_archive.setActivatorUnchecked(m_objArg);
+ return null;
+ }
+
+ throw new IllegalArgumentException("Invalid action specified.");
+ }
+ }
+}
\ No newline at end of file