You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ol...@apache.org on 2015/11/27 11:23:40 UTC

svn commit: r1716822 - in /sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf: ./ src/main/java/org/apache/sling/launchpad/karaf/ src/main/java/org/apache/sling/startup/ src/main/java/org/apache/sling/startup/configurable/ src/main/re...

Author: olli
Date: Fri Nov 27 10:23:40 2015
New Revision: 1716822

URL: http://svn.apache.org/viewvc?rev=1716822&view=rev
Log:
SLING-5338 Provide a configurable Startup API implementation

refactor and extend org.apache.sling.launchpad.karaf

Added:
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandler.java
      - copied, changed from r1716821, sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/launchpad/karaf/KarafStartupHandler.java
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandlerConfiguration.java
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupService.java
Removed:
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/launchpad/karaf/KarafStartupHandler.java
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/resources/
Modified:
    sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/pom.xml

Modified: sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/pom.xml?rev=1716822&r1=1716821&r2=1716822&view=diff
==============================================================================
--- sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/pom.xml (original)
+++ sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/pom.xml Fri Nov 27 10:23:40 2015
@@ -24,7 +24,7 @@
   <parent>
     <groupId>org.apache.sling</groupId>
     <artifactId>sling</artifactId>
-    <version>25</version>
+    <version>26-SNAPSHOT</version>
     <relativePath/>
   </parent>
 
@@ -35,6 +35,10 @@
   <name>Apache Sling Launchpad Karaf</name>
   <description>Apache Sling Launchpad Karaf</description>
 
+  <properties>
+    <sling.java.version>8</sling.java.version>
+  </properties>
+
   <dependencies>
     <!-- Sling -->
     <dependency>
@@ -47,12 +51,25 @@
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
+      <version>6.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.cm</artifactId>
+      <version>1.5.0</version>
       <scope>provided</scope>
     </dependency>
-    <!-- Apache Felix -->
     <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.scr.annotations</artifactId>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.metatype.annotations</artifactId>
+      <version>1.3.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.util.tracker</artifactId>
+      <version>1.5.1</version>
       <scope>provided</scope>
     </dependency>
     <!-- logging -->
@@ -69,18 +86,21 @@
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <extensions>true</extensions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-scr-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Export-Package/>
+            <_metatypeannotations>*</_metatypeannotations>
+            <_dsannotations>*</_dsannotations>
+          </instructions>
+        </configuration>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.3</version>
         <configuration>
-          <source>1.6</source>
-          <target>1.6</target>
+          <source>1.8</source>
+          <target>1.8</target>
         </configuration>
       </plugin>
     </plugins>

Copied: sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandler.java (from r1716821, sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/launchpad/karaf/KarafStartupHandler.java)
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandler.java?p2=sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandler.java&p1=sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/launchpad/karaf/KarafStartupHandler.java&r1=1716821&r2=1716822&rev=1716822&view=diff
==============================================================================
--- sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/launchpad/karaf/KarafStartupHandler.java (original)
+++ sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandler.java Fri Nov 27 10:23:40 2015
@@ -16,117 +16,223 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.launchpad.karaf;
+package org.apache.sling.startup.configurable;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Modified;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyOption;
-import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.launchpad.api.StartupHandler;
+import org.apache.sling.launchpad.api.StartupListener;
 import org.apache.sling.launchpad.api.StartupMode;
+import org.apache.sling.launchpad.api.StartupService;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * TODO
- * figure out when we are finished @see #isFinished()
- */
 @Component(
-    name = "org.apache.sling.launchpad.karaf.KarafStartupHandler",
-    label = "%org.apache.sling.launchpad.karaf.label",
-    description = "%org.apache.sling.launchpad.karaf.description",
-    metatype = true,
-    immediate = true
+    immediate = true,
+    property = Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
 )
-@Service
-@Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation")
-public class KarafStartupHandler implements StartupHandler {
+@Designate(ocd = ConfigurableStartupHandlerConfiguration.class)
+public class ConfigurableStartupHandler implements StartupHandler {
+
+    private ConfigurationAdmin configurationAdmin;
+
+    private BundleContext bundleContext;
+
+    private ServiceListener serviceListener = serviceEvent -> evaluate();
+
+    private ServiceTracker<StartupListener, StartupListener> serviceTracker;
+
+    private ServiceRegistration startupServiceRegistration;
 
     private StartupMode startupMode;
 
-    private static final String STARTUP_MODE_INSTALL = "INSTALL";
+    private String[] requiredServices;
+
+    private boolean reconfigureStartupModeToRestartOnFinish;
+
+    private final AtomicBoolean finished = new AtomicBoolean(false);
 
-    private static final String STARTUP_MODE_RESTART = "RESTART";
+    private final Object lock = new Object();
 
-    private static final String STARTUP_MODE_UPDATE = "UPDATE";
+    private static final String SERVICES_FILTERS_TEMPLATE = "(|%s)";
 
-    /**
-     * TODO
-     * until fully figured out what happens on INSTALL we use RESTART even on initial startup as Sling is booting and
-     * running fine on Karaf with RESTART
-     */
-    private static final String DEFAULT_STARTUP_MODE = STARTUP_MODE_RESTART;
-
-    @Property(
-        value = DEFAULT_STARTUP_MODE,
-        options = {
-            @PropertyOption(name = STARTUP_MODE_INSTALL, value = "install"),
-            @PropertyOption(name = STARTUP_MODE_RESTART, value = "restart"),
-            @PropertyOption(name = STARTUP_MODE_UPDATE, value = "update")
-        })
-    private static final String STARTUP_MODE_PARAMETER_NAME = "org.apache.sling.launchpad.startupmode";
+    private final Logger logger = LoggerFactory.getLogger(ConfigurableStartupHandler.class);
 
-    private final Logger logger = LoggerFactory.getLogger(KarafStartupHandler.class);
+    public ConfigurableStartupHandler() {
+    }
 
-    public KarafStartupHandler() {
+    @Reference
+    public void setConfigurationAdmin(final ConfigurationAdmin configurationAdmin) {
+        this.configurationAdmin = configurationAdmin;
     }
 
     @Activate
-    private void activate(final BundleContext bundleContext, final Map<String, Object> properties) {
-        logger.info("activate('{}', '{}')", bundleContext, properties);
-        configure(properties);
+    private void activate(final BundleContext bundleContext, final ConfigurableStartupHandlerConfiguration configuration) {
+        logger.info("activating with Startup Mode '{}' and required services '{}'", configuration.startupMode(), configuration.requiredServices());
+        this.bundleContext = bundleContext;
+        configure(configuration);
+        serviceTracker = new ServiceTracker<>(bundleContext, StartupListener.class, new StartupListenerServiceTrackerCustomizer(bundleContext));
+        serviceTracker.open(true);
+        evaluate();
     }
 
     @Modified
-    private void modified(Map<String, Object> properties) {
-        logger.info("modified('{}')", properties);
-        configure(properties);
+    private void modified(final ConfigurableStartupHandlerConfiguration configuration) {
+        logger.info("modifying with Startup Mode '{}' and required services '{}'", configuration.startupMode(), configuration.requiredServices());
+        configure(configuration);
     }
 
     @Deactivate
-    private void deactivate(final BundleContext bundleContext) {
-        logger.info("deactivate('{}')", bundleContext);
-    }
-
-    private void configure(Map<String, Object> properties) {
-        logger.info("configure('{}')", properties);
-        if (properties == null) {
-            properties = new HashMap<String, Object>();
-        }
-        // startup mode
-        String startupMode = (String) properties.get(STARTUP_MODE_PARAMETER_NAME);
-        if (startupMode == null) {
-            startupMode = DEFAULT_STARTUP_MODE;
+    private void deactivate() {
+        logger.info("deactivating");
+        serviceTracker.close();
+        synchronized (lock) {
+            bundleContext.removeServiceListener(serviceListener);
+            startupServiceRegistration.unregister();
+            startupServiceRegistration = null;
+            bundleContext = null;
         }
-        this.startupMode = StartupMode.valueOf(startupMode);
-        logger.info("configured startup mode: {}", this.startupMode);
     }
 
     @Override
     public StartupMode getMode() {
-        logger.info("getMode(): {}", startupMode);
         return startupMode;
     }
 
-    // TODO
     @Override
     public boolean isFinished() {
-        logger.info("isFinished()");
-        return true;
+        return finished.get();
     }
 
-    // TODO
     @Override
     public void waitWithStartup(boolean flag) {
-        logger.info("waitWithStartup({})", flag);
+        logger.warn("Waiting with startup is not supported by this startup handler. Ignoring flag '{}'.", flag);
+    }
+
+    private void configure(final ConfigurableStartupHandlerConfiguration configuration) {
+        synchronized (lock) {
+            startupMode = configuration.startupMode();
+            requiredServices = configuration.requiredServices();
+            reconfigureStartupModeToRestartOnFinish = configuration.reconfigureStartupModeToRestartOnFinish();
+            bundleContext.removeServiceListener(serviceListener);
+            final StringBuilder filters = new StringBuilder();
+            for (final String clazz : requiredServices) {
+                filters.append(String.format("(%s=%s)", Constants.OBJECTCLASS, clazz));
+            }
+            final String filter = String.format(SERVICES_FILTERS_TEMPLATE, filters);
+            try {
+                bundleContext.addServiceListener(serviceListener, filter);
+            } catch (InvalidSyntaxException e) {
+                logger.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    private void evaluate() {
+        logger.info("evaluating");
+        synchronized (lock) {
+            if (!finished.get()) {
+                for (final String clazz : requiredServices) {
+                    if (bundleContext.getServiceReference(clazz) == null) {
+                        logger.info("Required service of type '{}' is missing.", clazz);
+                        return;
+                    } else {
+                        logger.info("Required service of type '{}' is found.", clazz);
+                    }
+                }
+                finished.set(true);
+                for (final StartupListener startupListener : serviceTracker.getServices(new StartupListener[0])) {
+                    try {
+                        logger.info("Notifying {} about finished startup ('{}').", startupListener, startupMode);
+                        startupListener.startupFinished(startupMode);
+                    } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                    }
+                }
+                registerStartupService();
+                if (reconfigureStartupModeToRestartOnFinish) {
+                    reconfigureStartupMode();
+                }
+            }
+        }
+    }
+
+    private void registerStartupService() {
+        logger.info("registering Startup Service with Startup Mode '{}'", startupMode);
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put(StartupMode.class.getName(), startupMode.name());
+        properties.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Startup Service");
+        properties.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+        final StartupService startupService = new ConfigurableStartupService(startupMode);
+        startupServiceRegistration = bundleContext.registerService(StartupService.class, startupService, properties);
+    }
+
+    private void reconfigureStartupMode() {
+        try {
+            final String pid = getClass().getName();
+            final Configuration configuration = configurationAdmin.getConfiguration(pid);
+            Dictionary<String, Object> properties = configuration.getProperties();
+            if (properties == null) {
+                properties = new Hashtable<>();
+            }
+            if (!StartupMode.RESTART.name().equals(properties.get("startupMode"))) {
+                properties.put("startupMode", StartupMode.RESTART.name());
+                configuration.update(properties);
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    private class StartupListenerServiceTrackerCustomizer implements ServiceTrackerCustomizer<StartupListener, StartupListener> {
+
+        private final BundleContext bundleContext;
+
+        public StartupListenerServiceTrackerCustomizer(final BundleContext bundleContext) {
+            this.bundleContext = bundleContext;
+        }
+
+        @Override
+        public StartupListener addingService(final ServiceReference<StartupListener> serviceReference) {
+            final StartupListener startupListener = bundleContext.getService(serviceReference);
+            if (startupListener != null) {
+                try {
+                    logger.info("Informing {} about startup ('{}', '{}').", new Object[]{startupListener, startupMode, finished});
+                    startupListener.inform(startupMode, finished.get());
+                } catch (Exception e) {
+                    logger.error(e.getMessage(), e);
+                }
+            }
+            return startupListener;
+        }
+
+        @Override
+        public void modifiedService(final ServiceReference<StartupListener> serviceReference, final StartupListener startupListener) {
+        }
+
+        @Override
+        public void removedService(final ServiceReference<StartupListener> serviceReference, final StartupListener startupListener) {
+            bundleContext.ungetService(serviceReference);
+        }
+
     }
 
 }

Added: sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandlerConfiguration.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandlerConfiguration.java?rev=1716822&view=auto
==============================================================================
--- sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandlerConfiguration.java (added)
+++ sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupHandlerConfiguration.java Fri Nov 27 10:23:40 2015
@@ -0,0 +1,64 @@
+/*
+ * 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.startup.configurable;
+
+import org.apache.sling.launchpad.api.StartupMode;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.Option;
+
+@ObjectClassDefinition(
+    name = "Apache Sling Configurable StartupHandler",
+    description = ""
+)
+@interface ConfigurableStartupHandlerConfiguration {
+
+    String STARTUP_MODE_INSTALL = "INSTALL";
+
+    String STARTUP_MODE_RESTART = "RESTART";
+
+    String STARTUP_MODE_UPDATE = "UPDATE";
+
+    @AttributeDefinition(
+        name = "Startup Mode",
+        description = "The Startup Mode to announce when Sling's startup has finished.",
+        options = {
+            @Option(label = STARTUP_MODE_INSTALL, value = STARTUP_MODE_INSTALL),
+            @Option(label = STARTUP_MODE_RESTART, value = STARTUP_MODE_RESTART),
+            @Option(label = STARTUP_MODE_UPDATE, value = STARTUP_MODE_UPDATE)
+        }
+    )
+    StartupMode startupMode() default StartupMode.INSTALL;
+
+    @AttributeDefinition(
+        name = "Required services",
+        description = "The services which are required to be present to announce Sling's startup as finished."
+    )
+    String[] requiredServices() default {
+        "org.apache.sling.engine.SlingRequestProcessor",
+        "org.apache.sling.api.resource.ResourceResolverFactory"
+    };
+
+    @AttributeDefinition(
+        name = "Reconfigure Startup Mode to RESTART",
+        description = "If true, the Startup Mode will be reconfigured to RESTART on finished startup for next startup."
+    )
+    boolean reconfigureStartupModeToRestartOnFinish() default true;
+
+}

Added: sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupService.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupService.java?rev=1716822&view=auto
==============================================================================
--- sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupService.java (added)
+++ sling/trunk/contrib/launchpad/karaf/org.apache.sling.launchpad.karaf/src/main/java/org/apache/sling/startup/configurable/ConfigurableStartupService.java Fri Nov 27 10:23:40 2015
@@ -0,0 +1,37 @@
+/*
+ * 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.startup.configurable;
+
+import org.apache.sling.launchpad.api.StartupMode;
+import org.apache.sling.launchpad.api.StartupService;
+
+public class ConfigurableStartupService implements StartupService {
+
+    private final StartupMode startupMode;
+
+    public ConfigurableStartupService(final StartupMode startupMode) {
+        this.startupMode = startupMode;
+    }
+
+    @Override
+    public StartupMode getStartupMode() {
+        return startupMode;
+    }
+
+}