You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ma...@apache.org on 2011/08/12 17:44:36 UTC

svn commit: r1157163 - in /aries/trunk/application: application-itests/src/test/java/org/apache/aries/application/runtime/itests/ application-itests/src/test/java/org/apache/aries/isolated/sample/ application-itests/src/test/java/org/apache/aries/isola...

Author: mahrwald
Date: Fri Aug 12 15:44:36 2011
New Revision: 1157163

URL: http://svn.apache.org/viewvc?rev=1157163&view=rev
Log:
ARIES-726: Don't expose Equinox NPE

Added:
    aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/sample/SharedImpl.java
    aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/
    aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/Shared.java
Modified:
    aries/trunk/application/application-itests/src/test/java/org/apache/aries/application/runtime/itests/IsolatedRuntimeTest.java
    aries/trunk/application/application-runtime-framework/src/main/java/org/apache/aries/application/runtime/framework/BundleFrameworkImpl.java

Modified: aries/trunk/application/application-itests/src/test/java/org/apache/aries/application/runtime/itests/IsolatedRuntimeTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/application/application-itests/src/test/java/org/apache/aries/application/runtime/itests/IsolatedRuntimeTest.java?rev=1157163&r1=1157162&r2=1157163&view=diff
==============================================================================
--- aries/trunk/application/application-itests/src/test/java/org/apache/aries/application/runtime/itests/IsolatedRuntimeTest.java (original)
+++ aries/trunk/application/application-itests/src/test/java/org/apache/aries/application/runtime/itests/IsolatedRuntimeTest.java Fri Aug 12 15:44:36 2011
@@ -21,6 +21,7 @@ package org.apache.aries.application.run
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.ops4j.pax.exam.CoreOptions.equinox;
 import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.repository;
 
@@ -28,6 +29,7 @@ import static org.apache.aries.itest.Ext
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.aries.application.management.AriesApplication;
 import org.apache.aries.application.management.AriesApplicationContext;
@@ -49,6 +51,10 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.service.framework.CompositeBundle;
 
 @RunWith(JUnit4TestRunner.class)
 public class IsolatedRuntimeTest extends AbstractIntegrationTest {
@@ -67,14 +73,26 @@ public class IsolatedRuntimeTest extends
       .jar("sample.jar")
         .manifest().symbolicName("org.apache.aries.isolated.sample")
           .attribute("Bundle-Version", "1.0.0")
-          .attribute("Import-Package", "org.osgi.service.blueprint")
+          .attribute("Import-Package", "org.osgi.service.blueprint, org.apache.aries.isolated.shared")
+          // needed for testFrameworkResolvedBeforeInnerBundlesStart()
+          .attribute("Bundle-ActivationPolicy", "lazy")
           .end()
         .binary("org/apache/aries/isolated/sample/HelloWorld.class", 
             IsolatedRuntimeTest.class.getClassLoader().getResourceAsStream("org/apache/aries/isolated/sample/HelloWorld.class"))
         .binary("org/apache/aries/isolated/sample/HelloWorldImpl.class", 
             IsolatedRuntimeTest.class.getClassLoader().getResourceAsStream("org/apache/aries/isolated/sample/HelloWorldImpl.class"))
+        .binary("org/apache/aries/isolated/sample/SharedImpl.class", 
+            IsolatedRuntimeTest.class.getClassLoader().getResourceAsStream("org/apache/aries/isolated/sample/SharedImpl.class"))
         .binary("OSGI-INF/blueprint/sample-blueprint.xml", 
             IsolatedRuntimeTest.class.getClassLoader().getResourceAsStream("isolated/sample-blueprint.xml"))
+        .end()
+      .jar("shared.jar")
+        .manifest().symbolicName("org.apache.aries.isolated.shared")
+          .attribute("Bundle-Version", "1.0.0")
+          .attribute("Export-Package", "org.apache.aries.isolated.shared")
+        .end()
+        .binary("org/apache/aries/isolated/shared/Shared.class",
+            IsolatedRuntimeTest.class.getClassLoader().getResourceAsStream("org/apache/aries/isolated/shared/Shared.class"))
         .end();
       
     FileOutputStream fout = new FileOutputStream("test.eba");
@@ -227,7 +245,53 @@ public class IsolatedRuntimeTest extends
     manager.uninstall(ctx);
   }  
   
-
+  @Test
+  public void testFrameworkResolvedBeforeInnerBundlesStart() throws Exception {
+      /*
+       * Lazy bundles have in the past triggered recursive bundle trackers to handle them before
+       * the composite bundle framework was even resolved. In such a case the below loadClass
+       * operation on a class that depends on a class imported from the outside of the composite 
+       * will fail with an NPE.
+       */
+      
+      final AtomicBoolean loadedClass = new AtomicBoolean(false);
+      
+      context().addBundleListener(new SynchronousBundleListener() {
+        public void bundleChanged(BundleEvent event) {
+            Bundle b = event.getBundle();
+            if (event.getType() == BundleEvent.STARTING || event.getType() == BundleEvent.LAZY_ACTIVATION) {
+                if (b.getEntry("org/apache/aries/isolated/sample/SharedImpl.class") != null) {
+                    try {
+                        Class<?> cl = b.loadClass("org.apache.aries.isolated.sample.SharedImpl");
+                        cl.newInstance();
+                        loadedClass.set(true);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        throw new RuntimeException(e);
+                    }
+                }
+            } else if (event.getType() == BundleEvent.INSTALLED && b instanceof CompositeBundle) {
+                ((CompositeBundle) b).getCompositeFramework().getBundleContext().addBundleListener(this);
+            }
+        }
+    });
+      
+    AriesApplicationManager manager = context().getService(AriesApplicationManager.class);
+    AriesApplication app = manager.createApplication(FileSystem.getFSRoot(new File("test2.eba")));
+    AriesApplicationContext ctx = manager.install(app);      
+    
+    try {
+        ctx.start();
+        
+        app = ctx.getApplication();
+        assertEquals(1, app.getDeploymentMetadata().getApplicationDeploymentContents().size());
+        assertEquals(1, app.getDeploymentMetadata().getApplicationProvisionBundles().size());
+        assertTrue(loadedClass.get());
+    } finally {
+        manager.uninstall(ctx);
+    }
+  }
+  
   private void assertHelloWorldService(String appName) throws Exception
   {
     assertHelloWorldService(appName, "hello world");

Added: aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/sample/SharedImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/sample/SharedImpl.java?rev=1157163&view=auto
==============================================================================
--- aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/sample/SharedImpl.java (added)
+++ aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/sample/SharedImpl.java Fri Aug 12 15:44:36 2011
@@ -0,0 +1,25 @@
+/*  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.aries.isolated.sample;
+
+import org.apache.aries.isolated.shared.Shared;
+
+public class SharedImpl implements Shared {
+
+    @Override
+    public void something() {}
+
+}

Added: aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/Shared.java
URL: http://svn.apache.org/viewvc/aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/Shared.java?rev=1157163&view=auto
==============================================================================
--- aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/Shared.java (added)
+++ aries/trunk/application/application-itests/src/test/java/org/apache/aries/isolated/shared/Shared.java Fri Aug 12 15:44:36 2011
@@ -0,0 +1,20 @@
+/*  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.aries.isolated.shared;
+
+public interface Shared {
+    public void something();
+}

Modified: aries/trunk/application/application-runtime-framework/src/main/java/org/apache/aries/application/runtime/framework/BundleFrameworkImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/application/application-runtime-framework/src/main/java/org/apache/aries/application/runtime/framework/BundleFrameworkImpl.java?rev=1157163&r1=1157162&r2=1157163&view=diff
==============================================================================
--- aries/trunk/application/application-runtime-framework/src/main/java/org/apache/aries/application/runtime/framework/BundleFrameworkImpl.java (original)
+++ aries/trunk/application/application-runtime-framework/src/main/java/org/apache/aries/application/runtime/framework/BundleFrameworkImpl.java Fri Aug 12 15:44:36 2011
@@ -24,18 +24,23 @@ import static org.apache.aries.applicati
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.aries.application.management.AriesApplication;
 import org.apache.aries.application.management.spi.framework.BundleFramework;
 import org.apache.aries.application.management.spi.repository.BundleRepository.BundleSuggestion;
-import org.eclipse.osgi.framework.internal.core.InternalSystemBundle;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.launch.Framework;
 import org.osgi.service.framework.CompositeBundle;
-import org.osgi.service.framework.SurrogateBundle;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
 import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,6 +54,8 @@ public class BundleFrameworkImpl impleme
   Framework _framework;
 
   ServiceTracker _packageAdminTracker;
+  
+  private final AtomicBoolean startLevelIncreased = new AtomicBoolean(false);
 
   BundleFrameworkImpl(CompositeBundle cb)
   {
@@ -68,6 +75,11 @@ public class BundleFrameworkImpl impleme
           PackageAdmin.class.getName(), null);
       _packageAdminTracker.open();
     }
+    
+    // make sure inner bundles are now startable
+    if (startLevelIncreased.compareAndSet(false, true)) {
+        increaseStartLevel(_compositeBundle.getCompositeFramework().getBundleContext());
+    }
   }
   
   @Override
@@ -80,9 +92,81 @@ public class BundleFrameworkImpl impleme
       _packageAdminTracker = new ServiceTracker(_compositeBundle.getCompositeFramework().getBundleContext(),
           PackageAdmin.class.getName(), null);
       _packageAdminTracker.open();
+      
+      setupStartLevelToPreventAutostart(_compositeBundle.getCompositeFramework().getBundleContext());
+    }
+  }
+  
+  /**
+   * Name says it all if we don't make some adjustments bundles will be autostarted, which in the
+   * grand scheme of things causes extenders to act on the inner bundles before the outer composite is even
+   * resolved ...
+   */
+  private void setupStartLevelToPreventAutostart(BundleContext frameworkBundleContext)
+  {
+    ServiceReference ref = frameworkBundleContext.getServiceReference(StartLevel.class.getName());
+    if (ref != null) {
+      StartLevel sl = (StartLevel) frameworkBundleContext.getService(ref);
+      if (sl != null) {
+        // make sure new bundles are *not* automatically started (because that causes havoc)
+        sl.setInitialBundleStartLevel(sl.getStartLevel()+1);
+        frameworkBundleContext.ungetService(ref);
+      }
     }
   }
 
+  private void increaseStartLevel(BundleContext context) {
+      /*
+       * Algorithm for doing this
+       * 
+       * 1. Set up a framework listener that will tell us when the start level has been set.
+       * 
+       * 2. Change the start level. This is asynchronous so by the time the method returned the event 
+       *    could have been sent. This is why we set up the listener in step 1.
+       * 
+       * 3. Wait until the start level has been set appropriately. At this stage all the bundles are startable
+       *    and some have been started (most notably lazy activated bundles it appears). Other bundles are still
+       *    in resolved state.
+       */    
+      
+      ServiceReference ref = context.getServiceReference(StartLevel.class.getName());
+      if (ref != null) {
+        StartLevel sl = (StartLevel) context.getService(ref);
+        if (sl != null) {
+
+          final Semaphore waitForStartLevelChangedEventToOccur = new Semaphore(0);
+          
+          // step 1
+          FrameworkListener listener = new FrameworkListener() {
+            public void frameworkEvent(FrameworkEvent event)
+            {
+              if (event.getType() == FrameworkEvent.STARTLEVEL_CHANGED) {
+                waitForStartLevelChangedEventToOccur.release();
+              }
+            }
+          };
+          
+          context.addFrameworkListener(listener);
+          
+          // step 2
+          sl.setStartLevel(sl.getStartLevel()+1);
+          
+          // step 3
+          try {
+            if (!!!waitForStartLevelChangedEventToOccur.tryAcquire(60, TimeUnit.SECONDS)) {
+              LOGGER.debug("Starting CBA child bundles took longer than 60 seconds");
+            }
+          } catch (InterruptedException e) {
+            // restore the interrupted status
+            Thread.currentThread().interrupt();
+          }
+          
+          context.removeFrameworkListener(listener);
+        }
+        context.ungetService(ref);
+      }
+  }
+  
   public void close() throws BundleException
   {
     // close out packageadmin service tracker