You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2012/01/19 17:16:56 UTC

svn commit: r1233447 - in /sling/trunk/launchpad: api/ api/src/main/java/org/apache/sling/launchpad/api/ base/ base/src/main/java/org/apache/sling/launchpad/base/impl/ base/src/main/java/org/apache/sling/launchpad/base/shared/ base/src/main/resources/ ...

Author: cziegeler
Date: Thu Jan 19 16:16:55 2012
New Revision: 1233447

URL: http://svn.apache.org/viewvc?rev=1233447&view=rev
Log:
SLING-2376 : New Startup Features

Added:
    sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java   (with props)
    sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java   (with props)
    sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java   (with props)
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java   (with props)
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java   (contents, props changed)
      - copied, changed from r1232820, sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupHandler.java
    sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java   (with props)
Removed:
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupHandler.java
Modified:
    sling/trunk/launchpad/api/pom.xml
    sling/trunk/launchpad/base/pom.xml
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/BootstrapInstaller.java
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DirectoryUtil.java
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/shared/SharedConstants.java
    sling/trunk/launchpad/base/src/main/resources/sling.properties
    sling/trunk/launchpad/builder/src/main/bundles/list.xml
    sling/trunk/launchpad/installer/pom.xml
    sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/ServicesListener.java

Modified: sling/trunk/launchpad/api/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/api/pom.xml?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/api/pom.xml (original)
+++ sling/trunk/launchpad/api/pom.xml Thu Jan 19 16:16:55 2012
@@ -43,7 +43,7 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>org.apache.sling.launchpad.api;version=1.0.0</Export-Package>
+                        <Export-Package>org.apache.sling.launchpad.api;version=1.1.0</Export-Package>
                     </instructions>
                 </configuration>
             </plugin>

Added: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java?rev=1233447&view=auto
==============================================================================
--- sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java (added)
+++ sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java Thu Jan 19 16:16:55 2012
@@ -0,0 +1,36 @@
+/*
+ * 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.sling.launchpad.api;
+
+
+
+/**
+ * The <code>StartupHandler</code> tries to detect the startup mode:
+ * It distinguishes between an initial startup (INSTALL), an update (UPDATE)
+ * and a restart without a change (RESTART).
+ * @since 1.1.0
+ */
+public interface StartupHandler {
+
+    StartupMode getMode();
+
+    boolean isFinished();
+
+    void waitWithStartup(final boolean flag);
+}

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java?rev=1233447&view=auto
==============================================================================
--- sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java (added)
+++ sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java Thu Jan 19 16:16:55 2012
@@ -0,0 +1,42 @@
+/*
+ * 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.sling.launchpad.api;
+
+
+/**
+ * A startup listener receives events about the startup.
+ *
+ * @since 1.1.0
+ */
+public interface StartupListener {
+
+    void inform(StartupMode mode, boolean finished);
+
+    /**
+     * Notify finished startup.
+     * @param The startup mode
+     */
+    void startupFinished(StartupMode mode);
+
+    /**
+     * Notify startup progress
+     * @param ratio The current ratio
+     */
+    void startupProgress(final float ratio);
+}

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java?rev=1233447&view=auto
==============================================================================
--- sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java (added)
+++ sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java Thu Jan 19 16:16:55 2012
@@ -0,0 +1,32 @@
+/*
+ * 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.sling.launchpad.api;
+
+/**
+ * The <code>StartupMode</code>
+ * It distinguishes between an initial startup (INSTALL), an update (UPDATE)
+ * and a restart without a change (RESTART).
+ * @since 1.1.0
+ */
+public enum StartupMode {
+
+        INSTALL,
+        UPDATE,
+        RESTART
+}

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/api/src/main/java/org/apache/sling/launchpad/api/StartupMode.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/launchpad/base/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/pom.xml?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/pom.xml (original)
+++ sling/trunk/launchpad/base/pom.xml Thu Jan 19 16:16:55 2012
@@ -270,7 +270,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.launchpad.api</artifactId>
-            <version>1.0.0</version>
+            <version>1.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/BootstrapInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/BootstrapInstaller.java?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/BootstrapInstaller.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/BootstrapInstaller.java Thu Jan 19 16:16:55 2012
@@ -36,6 +36,7 @@ import java.util.jar.Manifest;
 
 import org.apache.felix.framework.Logger;
 import org.apache.sling.launchpad.api.LaunchpadContentProvider;
+import org.apache.sling.launchpad.api.StartupMode;
 import org.apache.sling.launchpad.base.impl.bootstrapcommands.BootstrapCommandFile;
 import org.apache.sling.launchpad.base.shared.SharedConstants;
 import org.osgi.framework.Bundle;
@@ -72,11 +73,6 @@ class BootstrapInstaller {
     private static final String PATH_RESOURCES = "resources/";
 
     /**
-     * The path of startup bundles in the sling home
-     */
-    private static final String PATH_STARTUP = "startup/";
-
-    /**
      * The location of the core Bundles (value is "resources/corebundles").
      * These bundles are installed at startlevel
      * {@link #STARTLEVEL_CORE_BUNDLES}.
@@ -135,7 +131,14 @@ class BootstrapInstaller {
     /** The bundle context. */
     private final BundleContext bundleContext;
 
-    BootstrapInstaller(final BundleContext bundleContext, Logger logger, LaunchpadContentProvider resourceProvider) {
+    /** The startup mode. */
+    private final StartupMode startupMode;
+
+    BootstrapInstaller(final BundleContext bundleContext,
+            final Logger logger,
+            final LaunchpadContentProvider resourceProvider,
+            final StartupMode startupMode) {
+        this.startupMode = startupMode;
         this.logger = logger;
         this.resourceProvider = resourceProvider;
         this.bundleContext = bundleContext;
@@ -166,8 +169,6 @@ class BootstrapInstaller {
         }
         final File slingStartupDir = getSlingStartupDir(launchpadHome);
 
-        final StartupHandler startupHandler = new StartupHandler(this.bundleContext, this.logger, slingStartupDir);
-
         // execute bootstrap commands, if needed
         final BootstrapCommandFile cmd = new BootstrapCommandFile(logger,
             new File(launchpadHome, BOOTSTRAP_CMD_FILENAME));
@@ -180,7 +181,7 @@ class BootstrapInstaller {
         if (Boolean.valueOf(fpblString)) {
             shouldInstall = true;
         } else {
-            shouldInstall = startupHandler.getMode() != StartupHandler.StartupMode.RESTART;
+            shouldInstall = this.startupMode != StartupMode.RESTART;
         }
 
         if (shouldInstall) {
@@ -235,9 +236,6 @@ class BootstrapInstaller {
 
             // start all the newly installed bundles (existing bundles are not started if they are stopped)
             startBundles(installed);
-
-            // mark everything installed
-            startupHandler.finished();
         }
 
         // due to the upgrade of a framework extension bundle, the framework
@@ -262,7 +260,7 @@ class BootstrapInstaller {
      */
     private File getSlingStartupDir(String slingHome) {
         final File slingHomeDir = new File(slingHome);
-        final File slingHomeStartupDir = getOrCreateDirectory(slingHomeDir, PATH_STARTUP);
+        final File slingHomeStartupDir = getOrCreateDirectory(slingHomeDir, DirectoryUtil.PATH_STARTUP);
         return slingHomeStartupDir;
     }
 
@@ -280,7 +278,7 @@ class BootstrapInstaller {
                     || ! parentDir.canRead()
                     || ! parentDir.canWrite() ) {
                 throw new IllegalStateException("Fatal error in bootstrap: Cannot find accessible existing "
-                        +SharedConstants.SLING_HOME+PATH_STARTUP+" directory: " + slingHomeStartupDir);
+                        +SharedConstants.SLING_HOME+DirectoryUtil.PATH_STARTUP+" directory: " + slingHomeStartupDir);
             }
         } else if (! slingHomeStartupDir.mkdirs() ) {
             throw new IllegalStateException("Sling Home " + slingHomeStartupDir + " cannot be created as a directory");

Added: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java?rev=1233447&view=auto
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java (added)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java Thu Jan 19 16:16:55 2012
@@ -0,0 +1,327 @@
+/*
+ * 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.sling.launchpad.base.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.felix.framework.Logger;
+import org.apache.sling.launchpad.api.StartupHandler;
+import org.apache.sling.launchpad.api.StartupListener;
+import org.apache.sling.launchpad.api.StartupMode;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * The installation listener is listening for
+ * - framework events
+ * - events from other services
+ *
+ * It notifies listeners about the finish of the startup and
+ * about an approximate progress.
+ *
+ * @see StartupHandler
+ * @see StartupListener
+ * @since 2.4.0
+ */
+public class DefaultStartupHandler
+    implements StartupHandler, BundleListener, FrameworkListener, Runnable {
+
+    /** Logger. */
+    private final Logger logger;
+
+    /** Marker for finished. */
+    private final AtomicBoolean finished = new AtomicBoolean(false);
+
+    /** Marker if startup should wait */
+    private final AtomicInteger startupShouldWait = new AtomicInteger(0);
+
+    /** The queue for increasing the start level. */
+    private final BlockingQueue<Boolean> queue = new LinkedBlockingQueue<Boolean>();
+
+    /** The start level service. */
+    private final StartLevel startLevelService;
+
+    /** The target start level. */
+    private final long targetStartLevel;
+
+    /** The startup mode. */
+    private final StartupMode startupMode;
+
+    /** Service tracker for startup listeners. */
+    private final ServiceTracker listenerTracker;
+
+    /** Expected bundle counts. */
+    private final int expectedBundlesCount;
+
+    /** Active bundle set. */
+    private final Set<String> activeBundles = new HashSet<String>();
+
+    /** Bundle Context. */
+    private final BundleContext bundleContext;
+
+    /**
+     * Constructor.
+     * @param context Bundle context
+     * @param logger  Logger
+     * @param manager The startup manager
+     */
+    public DefaultStartupHandler(final BundleContext context, final Logger logger, final StartupManager manager) {
+        this.logger = logger;
+        this.bundleContext = context;
+        this.startupMode = manager.getMode();
+        this.targetStartLevel = manager.getTargetStartLevel();
+
+        this.listenerTracker = new ServiceTracker(context, StartupListener.class.getName(),
+                new ServiceTrackerCustomizer() {
+
+                    public void removedService(final ServiceReference reference, final Object service) {
+                        context.ungetService(reference);
+                    }
+
+                    public void modifiedService(final ServiceReference reference, final Object service) {
+                        // nothing to do
+                    }
+
+                    public Object addingService(final ServiceReference reference) {
+                        final StartupListener listener = (StartupListener) context.getService(reference);
+                        if ( listener != null ) {
+                            listener.inform(startupMode, finished.get());
+                        }
+                        return listener;
+                    }
+                });
+        this.listenerTracker.open();
+        this.startLevelService = (StartLevel)context.getService(context.getServiceReference(StartLevel.class.getName()));
+        context.addFrameworkListener(this);
+
+        if ( this.startupMode == StartupMode.RESTART ) {
+            final Bundle[] bundles = context.getBundles();
+            this.expectedBundlesCount = (bundles != null ? bundles.length : 0);
+
+            context.addBundleListener(this);
+        } else {
+            this.expectedBundlesCount = 0;
+        }
+
+        final Thread t = new Thread(this);
+        t.start();
+    }
+
+    /**
+     * @see org.apache.sling.launchpad.api.StartupHandler#getMode()
+     */
+    public StartupMode getMode() {
+        return this.startupMode;
+    }
+
+    /**
+     * @see org.apache.sling.launchpad.api.StartupHandler#isFinished()
+     */
+    public boolean isFinished() {
+        return this.finished.get();
+    }
+
+    /**
+     * @see java.lang.Runnable#run()
+     */
+    public void run() {
+        this.bundleContext.registerService(StartupHandler.class.getName(), this, null);
+        logger.log(Logger.LOG_INFO, "Started startup listener with target start level="
+               + String.valueOf(this.targetStartLevel) + ", and expected bundle count=" + String.valueOf(this.expectedBundlesCount));
+        while ( !this.finished.get() ) {
+            Boolean doInc = null;
+            try {
+                doInc = this.queue.take();
+            } catch (final InterruptedException e) {
+                // ignore
+            }
+            if ( doInc != null && doInc ) {
+                // if the installer is idle we first wait
+                // we have to do this to give the installer or plugins for the installer,
+                // time to start after the start level has changed
+                if ( this.startupShouldWait.get() == 0 ) {
+                    this.sleep(2000L);
+                }
+                // now we wait until the installer is idle
+                while ( this.startupShouldWait.get() != 0 ) {
+                    this.sleep(50L);
+                }
+                this.incStartLevel();
+            }
+        }
+    }
+
+    /**
+     * Increment the current start level by one
+     */
+    private void incStartLevel() {
+        final int newLevel = this.startLevelService.getStartLevel() + 1;
+        logger.log(Logger.LOG_DEBUG, "Increasing start level to " + String.valueOf(newLevel));
+        this.startLevelService.setStartLevel(newLevel);
+    }
+
+    /**
+     * @see org.apache.sling.launchpad.api.StartupHandler#waitWithStartup(boolean)
+     */
+    public void waitWithStartup(final boolean flag) {
+        logger.log(Logger.LOG_INFO, "Wait with startup " + flag);
+        if ( flag ) {
+            this.startupShouldWait.incrementAndGet();
+        } else {
+            this.startupShouldWait.decrementAndGet();
+        }
+    }
+
+    /**
+     * Sleep a little bit
+     * @param time Sleeping time
+     */
+    private void sleep(final long time) {
+        try {
+            Thread.sleep(time);
+        } catch ( final InterruptedException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Put a task in the queue
+     * @param info Add new boolean information to queue
+     */
+    private void enqueue(final boolean info) {
+        try {
+            this.queue.put(info);
+        } catch (final InterruptedException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.framework.FrameworkEvent)
+     */
+    public void frameworkEvent(final FrameworkEvent event) {
+        if ( finished.get() ) {
+            return;
+        }
+        logger.log(Logger.LOG_DEBUG, "Received framework event " + event);
+
+        if ( this.startupMode == StartupMode.RESTART ) {
+            // restart
+            if ( event.getType() == FrameworkEvent.STARTED ) {
+                this.startupFinished();
+            }
+
+        } else {
+            // first startup or update
+            if ( event.getType() == FrameworkEvent.STARTED ) {
+                this.enqueue(true);
+
+            } else if ( event.getType() == FrameworkEvent.STARTLEVEL_CHANGED ) {
+                if ( this.startLevelService.getStartLevel() >= this.targetStartLevel ) {
+                    this.startupFinished();
+                } else {
+                    this.enqueue(true);
+                    final int startLevel = this.startLevelService.getStartLevel();
+                    logger.log(Logger.LOG_INFO, "Startup progress " + String.valueOf(startLevel) + '/' + String.valueOf(targetStartLevel));
+                    final float ratio = (float) startLevel / (float) targetStartLevel;
+                    this.startupProgress(ratio);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notify finished startup
+     */
+    private void startupFinished() {
+        logger.log(Logger.LOG_INFO, "Startup finished.");
+        this.finished.set(true);
+
+        final Object[] listeners = this.listenerTracker.getServices();
+        if ( listeners != null ) {
+            for(final Object l : listeners) {
+                if ( l instanceof StartupListener ) {
+                    ((StartupListener) l).startupFinished(this.startupMode);
+                }
+            }
+        }
+        // stop the queue
+        this.enqueue(false);
+
+        // clear bundle set
+        this.activeBundles.clear();
+
+        // unregister listeners
+        if ( this.startupMode == StartupMode.RESTART ) {
+            this.bundleContext.removeBundleListener(this);
+        }
+        this.bundleContext.removeFrameworkListener(this);
+    }
+
+    /**
+     * Notify startup progress
+     * @param ratio ratio
+     */
+    private void startupProgress(final float ratio) {
+        logger.log(Logger.LOG_INFO, "Startup progress " + String.valueOf(ratio));
+        final Object[] listeners = this.listenerTracker.getServices();
+        if ( listeners != null ) {
+            for(final Object l : listeners) {
+                if ( l instanceof StartupListener ) {
+                    ((StartupListener) l).startupProgress(ratio);
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
+     */
+    public void bundleChanged(final BundleEvent event) {
+        if (!finished.get()) {
+            logger.log(Logger.LOG_DEBUG, "Received bundle event " + event);
+
+            if (event.getType() == BundleEvent.RESOLVED || event.getType() == BundleEvent.STARTED) {
+                // Add (if not existing) bundle to active bundles and refresh progress bar
+                activeBundles.add(event.getBundle().getSymbolicName());
+
+                logger.log(Logger.LOG_INFO, "Startup progress " + String.valueOf(activeBundles.size()) + '/' + String.valueOf(expectedBundlesCount));
+                final float ratio = (float) activeBundles.size() / (float) expectedBundlesCount;
+                this.startupProgress(ratio);
+            } else if (event.getType() == BundleEvent.STOPPED) {
+                // Only remove bundle from active bundles,
+                // but do not refresh progress bar, to prevent progress bar from going back
+                activeBundles.remove(event.getBundle().getSymbolicName());
+            }
+        }
+    }
+}

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DirectoryUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DirectoryUtil.java?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DirectoryUtil.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DirectoryUtil.java Thu Jan 19 16:16:55 2012
@@ -18,6 +18,9 @@ package org.apache.sling.launchpad.base.
 
 import java.io.File;
 import java.io.FileFilter;
+import java.util.Map;
+
+import org.apache.sling.launchpad.base.shared.SharedConstants;
 
 public class DirectoryUtil {
 
@@ -26,13 +29,23 @@ public class DirectoryUtil {
      */
     private static final String[] BUNDLE_EXTENSIONS = { ".jar", ".war" };
 
+    /**
+     * The path of startup bundles in the sling home
+     */
+    public static final String PATH_STARTUP = "startup";
+
+    /**
+     * The path of startup bundles in the sling home
+     */
+    public static final String PATH_CONF = "conf";
+
     //---------- FileFilter implementations to scan startup folders
 
     /**
      * Simple directory filter
      */
     public static final FileFilter DIRECTORY_FILTER = new FileFilter() {
-        public boolean accept(File f) {
+        public boolean accept(final File f) {
             return f.isDirectory();
         }
     };
@@ -41,7 +54,7 @@ public class DirectoryUtil {
      * Simple bundle file filter
      */
     public static final FileFilter BUNDLE_FILE_FILTER = new FileFilter() {
-        public boolean accept(File f) {
+        public boolean accept(final File f) {
             return f.isFile() && isBundle(f.getName());
         }
     };
@@ -52,7 +65,7 @@ public class DirectoryUtil {
      * @param path the path to the file
      * @return true if the path could be a bundle
      */
-    public static boolean isBundle(String path) {
+    public static boolean isBundle(final String path) {
         for (String extension : BUNDLE_EXTENSIONS) {
             if (path.endsWith(extension)) {
                 return true;
@@ -60,4 +73,26 @@ public class DirectoryUtil {
         }
         return false;
     }
+
+    public static File getHomeDir(final Map<String, String> properties) {
+        String home = properties.get(SharedConstants.SLING_LAUNCHPAD);
+        if (home == null) {
+            home = properties.get(SharedConstants.SLING_HOME);
+        }
+        return new File(home);
+    }
+
+    /**
+     * Return the config dir.
+     */
+    public static File getConfigDir(final Map<String, String> properties) {
+        return new File(getHomeDir(properties), PATH_CONF);
+    }
+
+    /**
+     * Return the startup dir.
+     */
+    public static File getStartupDir(final Map<String, String> properties) {
+        return new File(getHomeDir(properties), PATH_STARTUP);
+    }
 }

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java Thu Jan 19 16:16:55 2012
@@ -220,25 +220,33 @@ public class Sling {
 
         // create the framework and start it
         try {
+            // initiate startup handler
+            final StartupManager startupManager = new StartupManager(props, logger);
+
             Framework tmpFramework = createFramework(notifiable, logger, props);
 
             init(tmpFramework);
 
-            if (new BootstrapInstaller(tmpFramework.getBundleContext(), logger,
-                resourceProvider).install()) {
+            final boolean restart = new BootstrapInstaller(tmpFramework.getBundleContext(), logger,
+                    resourceProvider, startupManager.getMode()).install();
+            startupManager.markInstalled();
+
+            if (restart) {
                 stop(tmpFramework);
                 tmpFramework = createFramework(notifiable, logger, props);
                 init(tmpFramework);
             }
 
+            new DefaultStartupHandler(tmpFramework.getBundleContext(), logger, startupManager);
+
             // finally start
             tmpFramework.start();
 
             // only assign field if start succeeds
             this.framework = tmpFramework;
-        } catch (BundleException be) {
+        } catch (final BundleException be) {
             throw be;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             // thrown by SlingFelix constructor
             throw new BundleException("Uncaught Instantiation Issue: " + e, e);
         }

Copied: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java (from r1232820, sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupHandler.java)
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java?p2=sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java&p1=sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupHandler.java&r1=1232820&r2=1233447&rev=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupHandler.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java Thu Jan 19 16:16:55 2012
@@ -19,15 +19,18 @@
 package org.apache.sling.launchpad.base.impl;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.Map;
 
 import org.apache.felix.framework.Logger;
 import org.apache.sling.launchpad.api.LaunchpadContentProvider;
-import org.osgi.framework.BundleContext;
+import org.apache.sling.launchpad.api.StartupMode;
+import org.apache.sling.launchpad.base.shared.SharedConstants;
+import org.osgi.framework.Constants;
 
 /**
  * The <code>StartupHandler</code> tries to detect the startup mode:
@@ -35,16 +38,10 @@ import org.osgi.framework.BundleContext;
  * and a restart without a change (RESTART).
  * @since 2.4.0
  */
-class StartupHandler {
-
-    public enum StartupMode {
-        INSTALL,
-        UPDATE,
-        RESTART
-    }
+public class StartupManager {
 
     /** The data file which works as a marker to detect the first startup. */
-    private static final String DATA_FILE = "bootstrapinstaller.ser";
+    private static final String DATA_FILE = "launchpad-timestamp.txt";
 
     /**
      * The {@link Logger} use for logging messages during installation and
@@ -52,53 +49,69 @@ class StartupHandler {
      */
     private final Logger logger;
 
-    /** The bundle context. */
-    private final BundleContext bundleContext;
-
     private final StartupMode mode;
 
     private final File startupDir;
 
-    private final long selfStamp;
+    private final File confDir;
 
-    StartupHandler(final BundleContext bundleContext,
-            final Logger logger,
-            final File slingStartupDir)
-    throws IOException {
+    private final long targetStartLevel;
+
+    StartupManager(final Map<String, String> properties,
+            final Logger logger) {
         this.logger = logger;
-        this.bundleContext = bundleContext;
-        this.startupDir = slingStartupDir;
-        this.selfStamp = this.getSelfTimestamp();
+        this.startupDir = DirectoryUtil.getStartupDir(properties);
+        this.confDir = DirectoryUtil.getConfigDir(properties);
         this.mode = detectMode();
         this.logger.log(Logger.LOG_INFO, "Starting in mode " + this.mode);
+
+        this.targetStartLevel = Long.valueOf(properties.get(Constants.FRAMEWORK_BEGINNING_STARTLEVEL));
+
+        // if this is not a restart, reduce start level
+        if ( this.mode != StartupMode.RESTART ) {
+            final String startLevel = properties.get(SharedConstants.SLING_INSTALL_STARTLEVEL);
+            properties.put(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, startLevel);
+        }
     }
 
+    /**
+     * Return the startup mode
+     * @return The startup mode
+     */
     public StartupMode getMode() {
         return this.mode;
     }
 
-    public void finished() {
-        this.markInstalled();
+    /**
+     * Return the target start level.
+     * @return Target start level
+     */
+    public long getTargetStartLevel() {
+        return this.targetStartLevel;
     }
 
+    /**
+     * Detect the startup mode by comparing time stamps
+     */
     private StartupMode detectMode() {
-        final File dataFile = this.bundleContext.getDataFile(DATA_FILE);
-        if (dataFile != null && dataFile.exists()) {
+        final File dataFile = new File(this.confDir, DATA_FILE);
+        if (dataFile.exists()) {
 
-            FileInputStream fis = null;
+            FileReader fis = null;
             try {
-                if (this.selfStamp > 0) {
+                final long selfStamp = this.getSelfTimestamp();
+                if (selfStamp > 0) {
 
-                    fis = new FileInputStream(dataFile);
-                    byte[] bytes = new byte[20];
-                    int len = fis.read(bytes);
-                    String value = new String(bytes, 0, len);
+                    fis = new FileReader(dataFile);
+                    final char[] txt = new char[128];
+                    final int len = fis.read(txt);
+                    final String value = new String(txt, 0, len);
 
-                    long storedStamp = Long.parseLong(value);
+                    final long storedStamp = Long.parseLong(value);
 
                     logger.log(Logger.LOG_INFO, String.format("Stored timestamp: %s", storedStamp));
 
-                    return (storedStamp >= this.selfStamp ? StartupMode.RESTART : StartupMode.UPDATE);
+                    return (storedStamp >= selfStamp ? StartupMode.RESTART : StartupMode.UPDATE);
                 }
 
             } catch (final NumberFormatException nfe) {
@@ -120,8 +133,10 @@ class StartupHandler {
         return StartupMode.INSTALL;
     }
 
-    private long getTimeStampOfClass(final Class<?> clazz)
-    throws IOException {
+    /**
+     * Get the timestamp of a class through its url classloader (if possible)
+     */
+    private long getTimeStampOfClass(final Class<?> clazz) {
         long selfStamp = -1;
         final ClassLoader loader = clazz.getClassLoader();
         if (loader instanceof URLClassLoader) {
@@ -130,7 +145,9 @@ class StartupHandler {
             if (urls.length > 0) {
                 final URL url = urls[0];
                 logger.log(Logger.LOG_INFO, String.format("Using timestamp from %s.", url));
-                selfStamp = urls[0].openConnection().getLastModified();
+                try {
+                    selfStamp = urls[0].openConnection().getLastModified();
+                } catch (final IOException ignore) {}
             }
         }
         return selfStamp;
@@ -152,7 +169,7 @@ class StartupHandler {
      * @throws IOException If an error occurrs reading accessing the last
      *             modification time stampe.
      */
-    private long getSelfTimestamp() throws IOException {
+    private long getSelfTimestamp() {
 
         // the timestamp of the launcher jar and the bootstrap jar
         long selfStamp = this.getTimeStampOfClass(this.getClass());
@@ -163,14 +180,18 @@ class StartupHandler {
 
         // check whether any bundle is younger than the launcher jar
         final File[] directories = this.startupDir.listFiles(DirectoryUtil.DIRECTORY_FILTER);
-        for (final File levelDir : directories) {
+        if ( directories != null ) {
+            for (final File levelDir : directories) {
 
-            // iterate through all files in the startlevel dir
-            final File[] jarFiles = levelDir.listFiles(DirectoryUtil.BUNDLE_FILE_FILTER);
-            for (final File bundleJar : jarFiles) {
-                if (bundleJar.lastModified() > selfStamp) {
-                    logger.log(Logger.LOG_INFO, String.format("Using timestamp from %s.", bundleJar));
-                    selfStamp = bundleJar.lastModified();
+                // iterate through all files in the startlevel dir
+                final File[] jarFiles = levelDir.listFiles(DirectoryUtil.BUNDLE_FILE_FILTER);
+                if ( jarFiles != null ) {
+                    for (final File bundleJar : jarFiles) {
+                        if (bundleJar.lastModified() > selfStamp) {
+                            logger.log(Logger.LOG_INFO, String.format("Using timestamp from %s.", bundleJar));
+                            selfStamp = bundleJar.lastModified();
+                        }
+                    }
                 }
             }
         }
@@ -182,12 +203,16 @@ class StartupHandler {
         return selfStamp;
     }
 
-    private void markInstalled() {
-        final File dataFile = this.bundleContext.getDataFile(DATA_FILE);
+    /**
+     * Set the finished installation marker.
+     */
+    public void markInstalled() {
+        final File dataFile = new File(this.confDir, DATA_FILE);
         try {
-            final FileOutputStream fos = new FileOutputStream(dataFile);
+            this.confDir.mkdirs();
+            final FileWriter fos = new FileWriter(dataFile);
             try {
-                fos.write(String.valueOf(this.selfStamp).getBytes());
+                fos.write(String.valueOf(this.getSelfTimestamp()));
             } finally {
                 try { fos.close(); } catch (final IOException ignore) {}
             }

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/shared/SharedConstants.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/shared/SharedConstants.java?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/shared/SharedConstants.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/shared/SharedConstants.java Thu Jan 19 16:16:55 2012
@@ -115,4 +115,13 @@ public interface SharedConstants {
      * @since 2.4.0
      */
     public static final String SLING_LAUNCHPAD = "sling.launchpad";
+
+    /**
+     * The name of the configuration property defining the startlevel
+     * for installs and updates. The framework starts with this start level
+     * and the startup manager increases the start level one by one until
+     * the initial framework start level is reached (value is "sling.framework.installstartlevel").
+     * @since 2.4.0
+     */
+    public static final String SLING_INSTALL_STARTLEVEL = "sling.framework.installstartlevel";
 }

Modified: sling/trunk/launchpad/base/src/main/resources/sling.properties
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/resources/sling.properties?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/resources/sling.properties (original)
+++ sling/trunk/launchpad/base/src/main/resources/sling.properties Thu Jan 19 16:16:55 2012
@@ -127,9 +127,15 @@ org.osgi.framework.storage = ${sling.lau
 
  
 #
-# Default initial Framework start level 
+# Default initial framework start level 
 org.osgi.framework.startlevel.beginning=30
 
+# Install and update initial framework start level
+# This should be lower than the default initial framework start level
+# During an install or update, the framework starts with this level
+# and the startup manager increases the start level by one
+# until the initial framework start level is reached
+sling.framework.installstartlevel=10
 
 #
 # Default start level for newly installed bundles not explicitly assigned
@@ -158,7 +164,7 @@ org.osgi.framework.executionenvironment=
 org.osgi.framework.system.packages= \
  ${osgi-core-packages}, \
  ${osgi-compendium-services}, \
- org.apache.sling.launchpad.api;version=1.0.0, \
+ org.apache.sling.launchpad.api;version=1.1.0, \
  ${jre-${java.specification.version}} \
  ${org.apache.sling.launcher.system.packages}
 

Modified: sling/trunk/launchpad/builder/src/main/bundles/list.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/builder/src/main/bundles/list.xml?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/builder/src/main/bundles/list.xml (original)
+++ sling/trunk/launchpad/builder/src/main/bundles/list.xml Thu Jan 19 16:16:55 2012
@@ -223,7 +223,7 @@
         <bundle>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.launchpad.installer</artifactId>
-            <version>1.0.6</version>
+            <version>1.0.7-SNAPSHOT</version>
         </bundle>
         <bundle>
             <groupId>org.apache.sling</groupId>

Modified: sling/trunk/launchpad/installer/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/installer/pom.xml?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/installer/pom.xml (original)
+++ sling/trunk/launchpad/installer/pom.xml Thu Jan 19 16:16:55 2012
@@ -55,13 +55,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.launchpad.api</artifactId>
-            <version>1.0.0</version>
+            <version>1.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.installer.core</artifactId>
-            <version>3.2.2</version>
+            <version>3.3.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

Added: sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java?rev=1233447&view=auto
==============================================================================
--- sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java (added)
+++ sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java Thu Jan 19 16:16:55 2012
@@ -0,0 +1,47 @@
+/*
+ * 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.sling.launchpad.installer.impl;
+
+import org.apache.sling.installer.api.event.InstallationEvent;
+import org.apache.sling.installer.api.event.InstallationListener;
+import org.apache.sling.launchpad.api.StartupHandler;
+
+public class LaunchpadListener implements InstallationListener {
+
+    private final StartupHandler startupHandler;
+
+    private volatile boolean started = false;
+
+    public LaunchpadListener(final StartupHandler startupHandler) {
+        this.startupHandler = startupHandler;
+    }
+
+    /**
+     * @see org.apache.sling.installer.api.event.InstallationListener#onEvent(org.apache.sling.installer.api.event.InstallationEvent)
+     */
+    public void onEvent(final InstallationEvent event) {
+        if ( event.getType() == InstallationEvent.TYPE.STARTED ) {
+            this.startupHandler.waitWithStartup(true);
+            started = true;
+        } else if ( event.getType() == InstallationEvent.TYPE.SUSPENDED ) {
+            if ( started ) {
+                this.startupHandler.waitWithStartup(false);
+            }
+        }
+    }
+
+}
\ No newline at end of file

Propchange: sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/LaunchpadListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/ServicesListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/ServicesListener.java?rev=1233447&r1=1233446&r2=1233447&view=diff
==============================================================================
--- sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/ServicesListener.java (original)
+++ sling/trunk/launchpad/installer/src/main/java/org/apache/sling/launchpad/installer/impl/ServicesListener.java Thu Jan 19 16:16:55 2012
@@ -19,13 +19,16 @@
 package org.apache.sling.launchpad.installer.impl;
 
 import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.installer.api.event.InstallationListener;
 import org.apache.sling.launchpad.api.LaunchpadContentProvider;
+import org.apache.sling.launchpad.api.StartupHandler;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 
 /**
  * The <code>ServicesListener</code> listens for the required services
@@ -43,12 +46,21 @@ public class ServicesListener {
     /** The listener for the provider. */
     private final Listener providerListener;
 
+    /** The listener for the startup handler. */
+    private final Listener startupListener;
+
+    private ServiceRegistration launchpadListenerReg;
+
+    private volatile boolean installed = false;
+
     public ServicesListener(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
         this.installerListener = new Listener(OsgiInstaller.class.getName());
         this.providerListener = new Listener(LaunchpadContentProvider.class.getName());
+        this.startupListener = new Listener(StartupHandler.class.getName());
         this.installerListener.start();
         this.providerListener.start();
+        this.startupListener.start();
     }
 
     public synchronized void notifyChange() {
@@ -57,7 +69,22 @@ public class ServicesListener {
         final LaunchpadContentProvider lcp = (LaunchpadContentProvider)this.providerListener.getService();
 
         if ( installer != null && lcp != null ) {
-            LaunchpadConfigInstaller.install(installer, lcp);
+            if ( !installed ) {
+                installed = true;
+                LaunchpadConfigInstaller.install(installer, lcp);
+            }
+        }
+        final StartupHandler handler = (StartupHandler)this.startupListener.getService();
+        if ( handler != null ) {
+            if ( launchpadListenerReg == null ) {
+                final LaunchpadListener launchpadListener = new LaunchpadListener(handler);
+                launchpadListenerReg = this.bundleContext.registerService(InstallationListener.class.getName(), launchpadListener, null);
+            }
+        } else {
+            if ( launchpadListenerReg != null ) {
+                launchpadListenerReg.unregister();
+                launchpadListenerReg = null;
+            }
         }
     }
 
@@ -67,6 +94,7 @@ public class ServicesListener {
     public void deactivate() {
         this.installerListener.deactivate();
         this.providerListener.deactivate();
+        this.startupListener.deactivate();
     }
 
     protected final class Listener implements ServiceListener {