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 2014/05/13 08:40:03 UTC

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

Author: cziegeler
Date: Tue May 13 06:40:03 2014
New Revision: 1594142

URL: http://svn.apache.org/r1594142
Log:
Revert SLING-3529 : Move startup handling to a separate bundle

Added:
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/DefaultStartupHandler.java   (with props)
Modified:
    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/impl/StartupManager.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

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=1594142&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 Tue May 13 06:40:03 2014
@@ -0,0 +1,331 @@
+/*
+ * 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;
+
+    /** Use incremental start level handling. */
+    private final boolean useIncremental;
+
+    /**
+     * 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);
+
+        this.useIncremental = this.startupMode != StartupMode.RESTART && manager.isIncrementalStartupEnabled();
+
+        if ( !this.useIncremental ) {
+            final Bundle[] bundles = context.getBundles();
+            this.expectedBundlesCount = (bundles != null && bundles.length > 0 ? bundles.length : 10);
+
+            context.addBundleListener(this);
+        } else {
+            this.expectedBundlesCount = 10;
+        }
+
+        this.bundleContext.registerService(StartupHandler.class.getName(), this, null);
+        logger.log(Logger.LOG_INFO, "Started startup handler with target start level="
+               + String.valueOf(this.targetStartLevel) + ", and expected bundle count=" + String.valueOf(this.expectedBundlesCount));
+        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() {
+        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_DEBUG, "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.useIncremental ) {
+            // 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.useIncremental ) {
+            this.bundleContext.removeBundleListener(this);
+        }
+        this.bundleContext.removeFrameworkListener(this);
+    }
+
+    /**
+     * Notify startup progress
+     * @param ratio ratio
+     */
+    private void startupProgress(final float 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/Sling.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/Sling.java?rev=1594142&r1=1594141&r2=1594142&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 Tue May 13 06:40:03 2014
@@ -222,6 +222,8 @@ public class Sling {
                 init(tmpFramework);
             }
 
+            new DefaultStartupHandler(tmpFramework.getBundleContext(), logger, startupManager);
+
             // finally start
             tmpFramework.start();
 

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java?rev=1594142&r1=1594141&r2=1594142&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/impl/StartupManager.java Tue May 13 06:40:03 2014
@@ -29,6 +29,7 @@ import java.util.Map;
 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.shared.SharedConstants;
 import org.osgi.framework.Constants;
 
 /**
@@ -60,6 +61,10 @@ public class StartupManager {
 
     private final File confDir;
 
+    private final long targetStartLevel;
+
+    private final boolean incrementalStartupEnabled;
+
     StartupManager(final Map<String, String> properties,
                    final Logger logger) {
         this.logger = logger;
@@ -73,7 +78,16 @@ public class StartupManager {
         } else {
             this.mode = detectMode(properties.get(Constants.FRAMEWORK_STORAGE));
             this.logger.log(Logger.LOG_INFO, "Detected startup mode. Starting in mode " + this.mode);
-            properties.put(OVERRIDE_PROP, this.mode.toString());
+        }
+
+        this.targetStartLevel = Long.valueOf(properties.get(Constants.FRAMEWORK_BEGINNING_STARTLEVEL));
+
+        this.incrementalStartupEnabled = Boolean.valueOf(properties.get(SharedConstants.SLING_INSTALL_INCREMENTAL_START));
+
+        // if this is not a restart, reduce start level
+        if ( this.mode != StartupMode.RESTART && this.incrementalStartupEnabled ) {
+            final String startLevel = properties.get(SharedConstants.SLING_INSTALL_STARTLEVEL);
+            properties.put(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, startLevel != null ? startLevel : "10");
         }
     }
 
@@ -86,6 +100,21 @@ public class StartupManager {
     }
 
     /**
+     * Is the incremental startup enabled?
+     */
+    public boolean isIncrementalStartupEnabled() {
+        return this.incrementalStartupEnabled;
+    }
+
+    /**
+     * 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 String osgiStorageDir) {

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=1594142&r1=1594141&r2=1594142&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 Tue May 13 06:40:03 2014
@@ -125,9 +125,7 @@ public interface SharedConstants {
      * the initial framework start level is reached (value is "sling.framework.install.incremental").
      * The default value is false, disabling this feature.
      * @since 2.4.0
-     * @deprecated This property is not used anymore.
      */
-    @Deprecated
     public static final String SLING_INSTALL_INCREMENTAL_START = "sling.framework.install.incremental";
 
     /**
@@ -138,8 +136,6 @@ public interface SharedConstants {
      * This level is only used if {@link #SLING_INSTALL_INCREMENTAL_START} is
      * enabled. Default value is 10.
      * @since 2.4.0
-     * @deprecated This property is not used anymore.
      */
-    @Deprecated
     public static final String SLING_INSTALL_STARTLEVEL = "sling.framework.install.startlevel";
 }

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=1594142&r1=1594141&r2=1594142&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/resources/sling.properties (original)
+++ sling/trunk/launchpad/base/src/main/resources/sling.properties Tue May 13 06:40:03 2014
@@ -130,28 +130,13 @@ org.osgi.framework.storage = ${sling.lau
 # Default initial framework start level 
 org.osgi.framework.startlevel.beginning=30
 
-# 
-# By default the framework on startup tries to go straight to
-# the beginning start level (set above).
-# However, in the case of an install or update a slower startup
-# by increasing the level one by one and waiting for the OSGi
-# installer to finish might be more appropriate.
-# In this case, set org.osgi.framework.startlevel.beginning to
-# a lower start level and sling.framework.install.startlevel to
-# the final start level. The framework is then started with
-# the beginning start level and Sling's startup handler will
-# increase it to the sling.framework.startup.startlevel.
-# If sling.framework.startup.incremental is set to true for
-# an install and update this is done level by level.
-# If you use the below properties make sure to comment the above
-# definition for org.osgi.framework.startlevel out.
-# 
+# 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.install.incremental=true
-# org.osgi.framework.startlevel.beginning=10
-# sling.framework.install.startlevel=30
-# sling.framework.startup.incremental=true
-# sling.framework.startup.startlevel
-
+# sling.framework.install.startlevel=10
 
 #
 # Default start level for newly installed bundles not explicitly assigned