You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:43:15 UTC
[sling-org-apache-sling-installer-provider-file] 01/19: Add new
file installer module
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.installer.provider.file-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-file.git
commit e1c446cb3d9e514de63d72945d010864c261a2ff
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Aug 13 11:36:08 2010 +0000
Add new file installer module
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/installer/fileinstall@985160 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 85 +++++++
.../sling/installer/file/impl/Activator.java | 84 +++++++
.../installer/file/impl/FileChangesListener.java | 33 +++
.../sling/installer/file/impl/FileMonitor.java | 268 +++++++++++++++++++++
.../sling/installer/file/impl/Installer.java | 120 +++++++++
.../installer/file/impl/ScanConfiguration.java | 29 +++
.../installer/file/impl/ServicesListener.java | 180 ++++++++++++++
7 files changed, 799 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..31c6ab8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>9</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.install.fileinstall</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>Apache Sling File Installer</name>
+ <description>
+ Installs OSGi bundles and configurations from the file system.
+ </description>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/installer/fileinstall</connection>
+ <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/installer/fileinstall</developerConnection>
+ <url>http://svn.apache.org/viewvc/sling/trunk/installer/fileinstall/</url>
+ </scm>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>
+ org.apache.sling.installer.file.impl.Activator
+ </Bundle-Activator>
+ <Private-Package>
+ org.apache.sling.installer.file.impl.*
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.osgi.installer</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/installer/file/impl/Activator.java b/src/main/java/org/apache/sling/installer/file/impl/Activator.java
new file mode 100644
index 0000000..9702cbe
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/Activator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.installer.file.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * The <code>Activator</code>
+ */
+public class Activator implements BundleActivator {
+
+ public static final String KEY_DIR = "sling.fileinstall.dir";
+ public static final String KEY_DELAY = "sling.fileinstall.interval";
+
+ /** The services listener will activate the installer. */
+ private ServicesListener servicesListener;
+
+ /**
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(final BundleContext context) throws InvalidSyntaxException {
+ // read initial scan configurations
+ final List<ScanConfiguration> configs = new ArrayList<ScanConfiguration>();
+ final Object dir = this.getProp(context, KEY_DIR);
+ if ( dir != null ) {
+ Long delay = null;
+ final Object interval = this.getProp(context, KEY_DELAY);
+ if ( interval != null ) {
+ if ( interval instanceof Number ) {
+ delay = ((Number)interval).longValue();
+ } else {
+ delay = Long.valueOf(interval.toString());
+ }
+ }
+ final StringTokenizer st = new StringTokenizer(dir.toString(), ",");
+ while ( st.hasMoreTokens() ) {
+ final ScanConfiguration sc = new ScanConfiguration();
+ sc.directory = st.nextToken();
+ sc.scanInterval = delay;
+
+ configs.add(sc);
+ }
+ }
+ this.servicesListener = new ServicesListener(context, configs);
+ }
+
+ /**
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(final BundleContext context) {
+ this.servicesListener.deactivate();
+ this.servicesListener = null;
+ }
+
+ private Object getProp(final BundleContext bundleContext, final String key) {
+ Object o = bundleContext.getProperty(key);
+ if (o == null) {
+ o = System.getProperty(key.toUpperCase().replace('.', '_'));
+ }
+ return o;
+ }
+}
diff --git a/src/main/java/org/apache/sling/installer/file/impl/FileChangesListener.java b/src/main/java/org/apache/sling/installer/file/impl/FileChangesListener.java
new file mode 100644
index 0000000..b9d7485
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/FileChangesListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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.installer.file.impl;
+
+import java.io.File;
+import java.util.List;
+
+public interface FileChangesListener {
+
+ void initialSet(List<File> files);
+
+ void removed(File file);
+
+ void added(File file);
+
+ void changed(File file);
+}
diff --git a/src/main/java/org/apache/sling/installer/file/impl/FileMonitor.java b/src/main/java/org/apache/sling/installer/file/impl/FileMonitor.java
new file mode 100644
index 0000000..a80a0ba
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/FileMonitor.java
@@ -0,0 +1,268 @@
+/*
+ * 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.installer.file.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is a monitor for the file system
+ * that periodically checks for changes.
+ */
+public class FileMonitor extends TimerTask {
+
+ /** The logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Timer timer = new Timer();
+ private boolean stop = false;
+ private boolean stopped = true;
+
+ private final Monitorable root;
+
+ private final FileChangesListener listener;
+
+ /**
+ * Creates a new instance of this class.
+ * @param interval The interval between executions of the task, in milliseconds.
+ */
+ public FileMonitor(final File rootDir, final Long interval, final FileChangesListener listener) {
+ this.listener = listener;
+ this.root = new Monitorable(rootDir);
+ createStatus(this.root);
+ final List<File> files = new ArrayList<File>();
+ collect(this.root.file, files);
+ this.listener.initialSet(files);
+ logger.debug("Starting file monitor for {} with an interval of {}ms", this.root.file, interval);
+ timer.schedule(this, 0, (interval != null ? interval : 5000));
+ }
+
+ private void collect(final File file, final List<File> files) {
+ if ( file.exists() ) {
+ if ( file.isDirectory() ) {
+ for(final File child : file.listFiles() ) {
+ collect(child, files);
+ }
+ } else {
+ files.add(file);
+ }
+ }
+ }
+
+ private void collectDeleted(final Monitorable m, final List<File> files) {
+ if ( m.status instanceof DirStatus ) {
+ for(final Monitorable child : ((DirStatus)m.status).children ) {
+ collectDeleted(child, files);
+ }
+ } else {
+ files.add(m.file);
+ }
+ }
+
+ /**
+ * Stop periodically executing this task. If the task is currently executing it
+ * will never be run again after the current execution, otherwise it will simply
+ * never run (again).
+ */
+ void stop() {
+ synchronized (timer) {
+ if (!stop) {
+ stop = true;
+ cancel();
+ timer.cancel();
+ }
+
+ boolean interrupted = false;
+ while (!stopped) {
+ try {
+ timer.wait();
+ }
+ catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ logger.debug("Stopped file monitor for {}", this.root.file);
+ }
+
+ /**
+ * @see java.util.TimerTask#run()
+ */
+ public void run() {
+ synchronized (timer) {
+ stopped = false;
+ if (stop) {
+ stopped = true;
+ timer.notifyAll();
+ return;
+ }
+ }
+ synchronized ( this ) {
+ try {
+ this.check(this.root);
+ } catch (Exception e) {
+ // ignore this
+ }
+ }
+ synchronized (timer) {
+ stopped = true;
+ timer.notifyAll();
+ }
+ }
+
+ /**
+ * Check the monitorable
+ * @param monitorable The monitorable to check
+ * @param localEA The event admin
+ */
+ private void check(final Monitorable monitorable) {
+ logger.debug("Checking {}", monitorable.file);
+ // if the file is non existing, check if it has been readded
+ if ( monitorable.status instanceof NonExistingStatus ) {
+ if ( monitorable.file.exists() ) {
+ // new file and reset status
+ createStatus(monitorable);
+ final List<File> files = new ArrayList<File>();
+ collect(monitorable.file, files);
+ for(final File file : files ) {
+ this.listener.added(file);
+ }
+ }
+ } else {
+ // check if the file has been removed
+ if ( !monitorable.file.exists() ) {
+ // removed file and update status
+ final List<File> files = new ArrayList<File>();
+ collectDeleted(monitorable, files);
+ for(final File file : files ) {
+ this.listener.removed(file);
+ }
+ monitorable.status = NonExistingStatus.SINGLETON;
+ } else {
+ // check for changes
+ final FileStatus fs = (FileStatus)monitorable.status;
+ boolean changed = false;
+ if ( fs.lastModified < monitorable.file.lastModified() ) {
+ fs.lastModified = monitorable.file.lastModified();
+ // changed
+ if ( monitorable.file.isFile() ) {
+ this.listener.changed(monitorable.file);
+ }
+ changed = true;
+ }
+ if ( fs instanceof DirStatus ) {
+ // directory
+ final DirStatus ds = (DirStatus)fs;
+ for(int i=0; i<ds.children.length; i++) {
+ check(ds.children[i]);
+ }
+ // if the dir changed we have to update
+ if ( changed ) {
+ // and now update
+ final File[] files = monitorable.file.listFiles();
+ if (files != null) {
+ final Monitorable[] children = new Monitorable[files.length];
+ for (int i = 0; i < files.length; i++) {
+ // search in old list
+ for (int m = 0; m < ds.children.length; m++) {
+ if (ds.children[m].file.equals(files[i])) {
+ children[i] = ds.children[m];
+ break;
+ }
+ }
+ if (children[i] == null) {
+ children[i] = new Monitorable(files[i]);
+ children[i].status = NonExistingStatus.SINGLETON;
+ check(children[i]);
+ }
+ }
+ ds.children = children;
+ } else {
+ ds.children = new Monitorable[0];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a status object for the monitorable
+ */
+ private static void createStatus(final Monitorable monitorable) {
+ if ( !monitorable.file.exists() ) {
+ monitorable.status = NonExistingStatus.SINGLETON;
+ } else if ( monitorable.file.isFile() ) {
+ monitorable.status = new FileStatus(monitorable.file);
+ } else {
+ monitorable.status = new DirStatus(monitorable.file);
+ }
+ }
+
+ /** The monitorable to hold the resource path, the file and the status. */
+ private static final class Monitorable {
+ public final File file;
+ public Object status;
+
+ public Monitorable(final File file) {
+ this.file = file;
+ }
+ }
+
+ /** Status for files. */
+ private static class FileStatus {
+ public long lastModified;
+ public FileStatus(final File file) {
+ this.lastModified = file.lastModified();
+ }
+ }
+
+ /** Status for directories. */
+ private static final class DirStatus extends FileStatus {
+ public Monitorable[] children;
+
+ public DirStatus(final File dir) {
+ super(dir);
+ final File[] files = dir.listFiles();
+ if (files != null) {
+ this.children = new Monitorable[files.length];
+ for (int i = 0; i < files.length; i++) {
+ this.children[i] = new Monitorable(files[i]);
+ FileMonitor.createStatus(this.children[i]);
+ }
+ } else {
+ this.children = new Monitorable[0];
+ }
+ }
+ }
+
+ /** Status for non existing files. */
+ private static final class NonExistingStatus {
+ public static NonExistingStatus SINGLETON = new NonExistingStatus();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/installer/file/impl/Installer.java b/src/main/java/org/apache/sling/installer/file/impl/Installer.java
new file mode 100644
index 0000000..7029957
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/Installer.java
@@ -0,0 +1,120 @@
+/*
+ * 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.installer.file.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.sling.osgi.installer.InstallableResource;
+import org.apache.sling.osgi.installer.OsgiInstaller;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>Installer</code> is the service calling the
+ * OSGi installer
+ */
+public class Installer implements FileChangesListener {
+
+ private static final String SCHEME_PREFIX = "fileinstall";
+
+ /** The OSGi installer service. */
+ private final OsgiInstaller installer;
+
+ /** The scheme to use. */
+ private final String scheme;
+
+ public Installer(final OsgiInstaller installer,
+ final String id) {
+ this.scheme = SCHEME_PREFIX + id;
+ this.installer = installer;
+ }
+
+ /**
+ * @see org.apache.sling.installer.file.impl.FileChangesListener#added(java.io.File)
+ */
+ public void added(final File file) {
+ LoggerFactory.getLogger(this.getClass()).info("Added file {}", file);
+ final InstallableResource resource = this.createResource(file);
+ this.installer.addResource(this.scheme, resource);
+ }
+
+ /**
+ * @see org.apache.sling.installer.file.impl.FileChangesListener#changed(java.io.File)
+ */
+ public void changed(final File file) {
+ LoggerFactory.getLogger(this.getClass()).info("Changed file {}", file);
+ final InstallableResource resource = this.createResource(file);
+ this.installer.addResource(this.scheme, resource);
+ }
+
+ /**
+ * @see org.apache.sling.installer.file.impl.FileChangesListener#initialSet(java.util.List)
+ */
+ public void initialSet(final List<File> files) {
+ LoggerFactory.getLogger(this.getClass()).info("Initial set for {}", this.scheme);
+ final List<InstallableResource> resources = new ArrayList<InstallableResource>();
+ for(final File f : files) {
+ LoggerFactory.getLogger(this.getClass()).info("File {}", f);
+ final InstallableResource resource = this.createResource(f);
+ if ( resource != null ) {
+ resources.add(resource);
+ }
+ }
+ this.installer.registerResources(this.scheme, resources);
+ }
+
+ private InstallableResource createResource(final File file) {
+ try {
+ final InputStream is = new FileInputStream(file);
+ final String digest = String.valueOf(file.lastModified());
+ // if this is a bundle check for start level directory!
+ Dictionary<String, Object> dict = null;
+ if ( file.getName().endsWith(".jar") || file.getName().endsWith(".war") ) {
+ final String parentName = file.getParentFile().getName();
+ try {
+ final int startLevel = Integer.valueOf(parentName);
+ if ( startLevel > 0 ) {
+ dict = new Hashtable<String, Object>();
+ dict.put(InstallableResource.BUNDLE_START_LEVEL, startLevel);
+ }
+ } catch (NumberFormatException nfe) {
+ // ignore this
+ }
+ }
+ return new InstallableResource(file.getAbsolutePath(), is, dict, digest,
+ null, null);
+ } catch (IOException io) {
+ // ignore this for now (TODO)
+ }
+ return null;
+ }
+ /**
+ * @see org.apache.sling.installer.file.impl.FileChangesListener#removed(java.io.File)
+ */
+ public void removed(final File file) {
+ LoggerFactory.getLogger(this.getClass()).info("Removed file {}", file);
+ this.installer.removeResource(this.scheme, file.getAbsolutePath());
+ }
+}
diff --git a/src/main/java/org/apache/sling/installer/file/impl/ScanConfiguration.java b/src/main/java/org/apache/sling/installer/file/impl/ScanConfiguration.java
new file mode 100644
index 0000000..54d8a1a
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/ScanConfiguration.java
@@ -0,0 +1,29 @@
+/*
+ * 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.installer.file.impl;
+
+/**
+ * A scan configuration for the file install.
+ */
+public class ScanConfiguration {
+
+ public String directory;
+
+ public Long scanInterval;
+}
diff --git a/src/main/java/org/apache/sling/installer/file/impl/ServicesListener.java b/src/main/java/org/apache/sling/installer/file/impl/ServicesListener.java
new file mode 100644
index 0000000..72dd79c
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/file/impl/ServicesListener.java
@@ -0,0 +1,180 @@
+/*
+ * 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.installer.file.impl;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sling.osgi.installer.OsgiInstaller;
+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;
+
+/**
+ * The <code>ServicesListener</code> listens for the required services
+ * and starts/stops the scanners based on the availability of the
+ * services.
+ */
+public class ServicesListener {
+
+ /** The name of the installer service. */
+ private static final String INSTALLER_SERVICE_NAME = OsgiInstaller.class.getName();
+
+ /** The bundle context. */
+ private final BundleContext bundleContext;
+
+ /** The listener for the installer. */
+ private final Listener installerListener;
+
+ /** All active scan configurations. */
+ private final List<ScanConfiguration> scanConfigurations = new ArrayList<ScanConfiguration>();
+
+ /** All monitors. */
+ private final List<FileMonitor> monitors = new ArrayList<FileMonitor>();
+
+ private boolean running = false;
+
+ public ServicesListener(final BundleContext bundleContext,
+ final List<ScanConfiguration> configs)
+ throws InvalidSyntaxException{
+ if ( configs != null ) {
+ scanConfigurations.addAll(configs);
+ }
+ this.bundleContext = bundleContext;
+ this.installerListener = new Listener(INSTALLER_SERVICE_NAME);
+ }
+
+ public synchronized void notifyChange() {
+ final boolean shouldRun = !this.scanConfigurations.isEmpty();
+ if ( (shouldRun && !running) || (!shouldRun && running) ) {
+ final OsgiInstaller installer = (OsgiInstaller)this.installerListener.getService();
+
+ if ( installer != null&& !running ) {
+ this.startScanner(installer);
+ } else if ( running && installer == null ) {
+ this.stopScanner();
+ }
+ }
+ }
+
+ /**
+ * Deactivate this listener.
+ */
+ public void deactivate() {
+ this.installerListener.deactivate();
+ this.stopScanner();
+ }
+
+ private void startScanner(final OsgiInstaller installer) {
+ if ( !running ) {
+ for(final ScanConfiguration config : this.scanConfigurations) {
+ this.monitors.add(new FileMonitor(new File(config.directory),
+ config.scanInterval, new Installer(installer, hash(config.directory))));
+ }
+ running = true;
+ }
+ }
+
+ private void stopScanner() {
+ if ( running ) {
+ for(final FileMonitor monitor : this.monitors) {
+ monitor.stop();
+ }
+ this.monitors.clear();
+ running = false;
+ }
+ }
+
+ protected final class Listener implements ServiceListener {
+
+ private final String serviceName;
+
+ private ServiceReference reference;
+ private Object service;
+
+ public Listener(final String serviceName) throws InvalidSyntaxException {
+ this.serviceName = serviceName;
+ bundleContext.addServiceListener(this, "("
+ + Constants.OBJECTCLASS + "=" + serviceName + ")");
+ this.getService();
+ }
+
+ public void deactivate() {
+ bundleContext.removeServiceListener(this);
+ }
+
+ public synchronized Object getService() {
+ return this.service;
+ }
+ private synchronized void retainService() {
+ if ( this.reference == null ) {
+ this.reference = bundleContext.getServiceReference(this.serviceName);
+ if ( this.reference != null ) {
+ this.service = bundleContext.getService(this.reference);
+ if ( this.service == null ) {
+ this.reference = null;
+ } else {
+ notifyChange();
+ }
+ }
+ }
+ }
+
+ private synchronized void releaseService() {
+ if ( this.reference != null ) {
+ this.service = null;
+ bundleContext.ungetService(this.reference);
+ this.reference = null;
+ notifyChange();
+ }
+ }
+
+ /**
+ * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
+ */
+ public void serviceChanged(ServiceEvent event) {
+ if (event.getType() == ServiceEvent.REGISTERED && this.service == null ) {
+ this.retainService();
+ } else if ( event.getType() == ServiceEvent.UNREGISTERING && this.service != null ) {
+ this.releaseService();
+ }
+ }
+ }
+
+ /**
+ * Hash the string
+ */
+ private static String hash(String value) {
+ try {
+ final MessageDigest d = MessageDigest.getInstance("MD5");
+ d.update(value.getBytes("UTF-8"));
+ final BigInteger bigInt = new BigInteger(1, d.digest());
+ return new String(bigInt.toString(16));
+ } catch (Exception ignore) {
+ // if anything goes wrong we just return the value
+ return value;
+ }
+ }
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.