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:44:25 UTC
[sling-org-apache-sling-installer-provider-file] 11/14:
[maven-release-plugin] copy for tag
org.apache.sling.installer.provider.file-1.1.0
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.1.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-file.git
commit c917afce158c6e9c299e8f53793cecf805a7e058
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Apr 17 09:51:33 2015 +0000
[maven-release-plugin] copy for tag org.apache.sling.installer.provider.file-1.1.0
git-svn-id: https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.installer.provider.file-1.1.0@1674248 13f79535-47bb-0310-9956-ffa450edef68
---
file/pom.xml | 109 ++++++++
.../installer/provider/file/impl/Activator.java | 87 +++++++
.../provider/file/impl/FileChangesListener.java | 31 +++
.../provider/file/impl/FileInstaller.java | 234 +++++++++++++++++
.../installer/provider/file/impl/FileMonitor.java | 287 +++++++++++++++++++++
.../installer/provider/file/impl/Installer.java | 196 ++++++++++++++
.../provider/file/impl/ScanConfiguration.java | 29 +++
.../provider/file/impl/ServicesListener.java | 200 ++++++++++++++
8 files changed, 1173 insertions(+)
diff --git a/file/pom.xml b/file/pom.xml
new file mode 100644
index 0000000..4c3d6b9
--- /dev/null
+++ b/file/pom.xml
@@ -0,0 +1,109 @@
+<?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>22</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.installer.provider.file</artifactId>
+ <version>1.1.0</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/tags/org.apache.sling.installer.provider.file-1.1.0</connection>
+ <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.installer.provider.file-1.1.0</developerConnection>
+ <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.installer.provider.file-1.1.0</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.provider.file.impl.Activator
+ </Bundle-Activator>
+ <!--
+ We need at least 3.1.2 as this allows reading comments from a config
+ -->
+ <Import-Package>
+ org.apache.sling.installer.api;version="[3.1.2,4)",
+ *
+ </Import-Package>
+ <Private-Package>
+ org.apache.sling.installer.provider.file.impl.*
+ </Private-Package>
+ <Embed-Dependency>
+ org.apache.felix.configadmin;inline="org/apache/felix/cm/file/ConfigurationHandler.*"
+ </Embed-Dependency>
+ </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.installer.core</artifactId>
+ <version>3.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.settings</artifactId>
+ <version>1.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- We use a class from the config admin implementation to read config files -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ <version>1.2.8</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/Activator.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/Activator.java
new file mode 100644
index 0000000..2b06592
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/Activator.java
@@ -0,0 +1,87 @@
+/*
+ * 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.provider.file.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * 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";
+ public static final String KEY_WRITEBACK = "sling.fileinstall.writeback";
+
+ /** 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) {
+ // read initial scan configurations
+ final List<ScanConfiguration> configs = new ArrayList<ScanConfiguration>();
+ final Object dir = getProp(context, KEY_DIR);
+ if ( dir != null ) {
+ Long delay = null;
+ final Object interval = 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;
+ }
+
+ public static Object getProp(final BundleContext bundleContext, final String key) {
+ Object o = bundleContext.getProperty(key);
+ if (o == null) {
+ o = System.getProperty(key);
+ if ( o == null ) {
+ o = System.getProperty(key.toUpperCase().replace('.', '_'));
+ }
+ }
+ return o;
+ }
+}
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileChangesListener.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileChangesListener.java
new file mode 100644
index 0000000..2fd34d5
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileChangesListener.java
@@ -0,0 +1,31 @@
+/*
+ * 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.provider.file.impl;
+
+import java.io.File;
+import java.util.List;
+
+public interface FileChangesListener {
+
+ void initialSet(List<File> files);
+
+ void updated(List<File> added, List<File> changed, List<File> removed);
+
+ String getScheme();
+}
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileInstaller.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileInstaller.java
new file mode 100644
index 0000000..a646f61
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileInstaller.java
@@ -0,0 +1,234 @@
+/*
+ * 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.provider.file.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.cm.file.ConfigurationHandler;
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.installer.api.UpdateHandler;
+import org.apache.sling.installer.api.UpdateResult;
+import org.apache.sling.settings.SlingSettingsService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>FileInstaller</code> manages the file installers and
+ * handles updates.
+ *
+ */
+public class FileInstaller
+ implements UpdateHandler {
+
+ /** The scheme we use to register our resources. */
+ public static final String SCHEME_PREFIX = "fileinstall";
+
+ /** Logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** All active scan configurations. */
+ private final List<ScanConfiguration> scanConfigurations = new ArrayList<ScanConfiguration>();
+
+ /** All monitors. */
+ private final List<FileMonitor> monitors = new ArrayList<FileMonitor>();
+
+ private final boolean writeBack;
+
+ public FileInstaller(final List<ScanConfiguration> configs, final boolean writeBack) {
+ this.writeBack = writeBack;
+ if ( configs != null ) {
+ scanConfigurations.addAll(configs);
+ }
+ }
+
+ public boolean hasConfigurations() {
+ return !this.scanConfigurations.isEmpty();
+ }
+
+ public void start(final OsgiInstaller installer, final SlingSettingsService settings) {
+ for(final ScanConfiguration config : this.scanConfigurations) {
+ String key = config.directory;
+ if ( key.startsWith(settings.getSlingHomePath() + File.separator) ) {
+ key = "${sling.home}" + key.substring(settings.getSlingHomePath().length());
+ }
+ logger.debug("Starting monitor for {}", config.directory);
+ this.monitors.add(new FileMonitor(new File(config.directory),
+ config.scanInterval, new Installer(installer, settings, config.directory, hash(key))));
+ }
+ }
+
+ public void stop() {
+ for(final FileMonitor monitor : this.monitors) {
+ monitor.stop();
+ }
+ this.monitors.clear();
+
+ }
+
+ public String[] getSchemes() {
+ final String[] schemes = new String[this.monitors.size()];
+ int index = 0;
+
+ for(final FileMonitor m : this.monitors) {
+ schemes[index] = m.getListener().getScheme();
+ index++;
+ }
+
+ return schemes;
+ }
+
+ /**
+ * @see org.apache.sling.installer.api.UpdateHandler#handleRemoval(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public UpdateResult handleRemoval(final String resourceType,
+ final String id,
+ final String url) {
+ if ( !this.writeBack ) {
+ return null;
+ }
+ final int pos = url.indexOf(':');
+ final String path = url.substring(pos + 1);
+ // remove
+ logger.debug("Removal of {}", path);
+ final File file = new File(path);
+ if ( file.exists() ) {
+ file.delete();
+ }
+ return new UpdateResult(url);
+ }
+
+ /**
+ * @see org.apache.sling.installer.api.UpdateHandler#handleUpdate(java.lang.String, java.lang.String, java.lang.String, java.util.Dictionary, Map)
+ */
+ public UpdateResult handleUpdate(final String resourceType,
+ final String id,
+ final String url,
+ final Dictionary<String, Object> dict,
+ final Map<String, Object> attributes) {
+ return this.handleUpdate(resourceType, id, url, null, dict, attributes);
+ }
+
+ /**
+ * @see org.apache.sling.installer.api.UpdateHandler#handleUpdate(java.lang.String, java.lang.String, java.lang.String, java.io.InputStream, Map)
+ */
+ public UpdateResult handleUpdate(final String resourceType,
+ final String id,
+ final String url,
+ final InputStream is,
+ final Map<String, Object> attributes) {
+ return this.handleUpdate(resourceType, id, url, is, null, attributes);
+ }
+
+ /**
+ * Internal implementation of update handling
+ */
+ private UpdateResult handleUpdate(final String resourceType,
+ final String id,
+ final String url,
+ final InputStream is,
+ final Dictionary<String, Object> dict,
+ final Map<String, Object> attributes) {
+ if ( !this.writeBack ) {
+ return null;
+ }
+
+ // we only handle add/update of configs for now
+ if ( !resourceType.equals(InstallableResource.TYPE_CONFIG) ) {
+ return null;
+ }
+
+ try {
+ final String path;
+ final String prefix;
+ if ( url != null ) {
+ // update
+ final int pos = url.indexOf(':');
+ final String oldPath = url.substring(pos + 1);
+ prefix = url.substring(0, pos);
+ // ensure extension 'config'
+ if ( !oldPath.endsWith(".config") ) {
+ final File file = new File(oldPath);
+ if ( file.exists() ) {
+ file.delete();
+ }
+ final int lastDot = oldPath.lastIndexOf('.');
+ final int lastSlash = oldPath.lastIndexOf('/');
+ if ( lastDot <= lastSlash ) {
+ path = oldPath + ".config";
+ } else {
+ path = oldPath.substring(0, lastDot) + ".config";
+ }
+ } else {
+ path = oldPath;
+ }
+ logger.debug("Update of {} at {}", resourceType, path);
+ } else {
+ // add
+ final FileMonitor first = this.monitors.get(0);
+ path = first.getRoot().getAbsolutePath() + '/' + id + ".config";
+ prefix = first.getListener().getScheme();
+ logger.debug("Add of {} at {}", resourceType, path);
+ }
+
+ final File file = new File(path);
+ file.getParentFile().mkdirs();
+ final FileOutputStream fos = new FileOutputStream(file);
+ try {
+ fos.write("# Configuration created by Apache Sling File Installer\n".getBytes("UTF-8"));
+ ConfigurationHandler.write(fos, dict);
+ } finally {
+ try {
+ fos.close();
+ } catch (final IOException ignore) {}
+ }
+
+ final UpdateResult result = new UpdateResult(prefix + ':' + path);
+ result.setResourceIsMoved(true);
+ return result;
+ } catch (final IOException e) {
+ logger.error("Unable to add/update resource " + resourceType + ':' + id, e);
+ return null;
+ }
+ }
+
+ /**
+ * Hash the string
+ */
+ private static String hash(final 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 (final Exception ignore) {
+ // if anything goes wrong we just return the value
+ return value;
+ }
+ }
+}
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileMonitor.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileMonitor.java
new file mode 100644
index 0000000..085d07e
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/FileMonitor.java
@@ -0,0 +1,287 @@
+/*
+ * 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.provider.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));
+ }
+
+ public File getRoot() {
+ return this.root.file;
+ }
+
+ public FileChangesListener getListener() {
+ return this.listener;
+ }
+
+ private void collect(final File file, final List<File> files) {
+ if ( file.exists() ) {
+ if ( file.isDirectory() ) {
+ final File[] children = file.listFiles();
+ if ( children != null ) {
+ for(final File child : children ) {
+ 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);
+ }
+ }
+
+ private final static class Collector {
+ public final List<File> added = new ArrayList<File>();
+ public final List<File> removed = new ArrayList<File>();
+ public final List<File> changed = new ArrayList<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 {
+ final Collector c = new Collector();
+ this.check(this.root, c);
+ this.listener.updated(c.added, c.changed, c.removed);
+ } 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, final Collector collector) {
+ 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 ) {
+ collector.added.add(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 ) {
+ collector.removed.add(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() ) {
+ collector.changed.add(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], collector);
+ }
+ // 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], collector);
+ }
+ }
+ 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/file/src/main/java/org/apache/sling/installer/provider/file/impl/Installer.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/Installer.java
new file mode 100644
index 0000000..1b05e78
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/Installer.java
@@ -0,0 +1,196 @@
+/*
+ * 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.provider.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 java.util.Set;
+
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.settings.SlingSettingsService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>Installer</code> is the service calling the
+ * OSGi installer
+ *
+ */
+public class Installer
+ implements FileChangesListener {
+
+ /** Logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** The OSGi installer service. */
+ private final OsgiInstaller installer;
+
+ /** The settings service. */
+ private final Set<String> activeRunModes;
+
+ /** The scheme to use. */
+ private final String scheme;
+
+ /** Prefix. */
+ private final String prefix;
+
+ public Installer(final OsgiInstaller installer,
+ final SlingSettingsService settings,
+ final String root,
+ final String id) {
+ this.scheme = FileInstaller.SCHEME_PREFIX + id;
+ this.installer = installer;
+ this.activeRunModes = settings.getRunModes();
+ this.prefix = new File(root).getAbsolutePath() + File.separator;
+ }
+
+ /**
+ * @see org.apache.sling.installer.provider.file.impl.FileChangesListener#getScheme()
+ */
+ public String getScheme() {
+ return this.scheme;
+ }
+
+ /**
+ * @see org.apache.sling.installer.provider.file.impl.FileChangesListener#initialSet(java.util.List)
+ */
+ public void initialSet(final List<File> files) {
+ logger.debug("Initial set for {}", this.scheme);
+ final List<InstallableResource> resources = new ArrayList<InstallableResource>();
+ for(final File f : files) {
+ logger.debug("Initial file {}", f);
+ final InstallableResource resource = this.createResource(f);
+ if ( resource != null ) {
+ resources.add(resource);
+ }
+ }
+ this.installer.registerResources(this.scheme, resources.toArray(new InstallableResource[resources.size()]));
+ }
+
+ /**
+ * @see org.apache.sling.installer.provider.file.impl.FileChangesListener#updated(java.util.List, java.util.List, java.util.List)
+ */
+ public void updated(List<File> added, List<File> changed, List<File> removed) {
+ final List<InstallableResource> updated;
+ if ( (added != null && added.size() > 0) || (changed != null && changed.size() > 0) ) {
+ updated = new ArrayList<InstallableResource>();
+ if ( added != null ) {
+ for(final File f : added) {
+ logger.debug("Added file {}", f);
+ final InstallableResource resource = this.createResource(f);
+ if ( resource != null ) {
+ updated.add(resource);
+ }
+ }
+ }
+ if ( changed != null ) {
+ for(final File f : changed) {
+ logger.debug("Changed file {}", f);
+ final InstallableResource resource = this.createResource(f);
+ if ( resource != null ) {
+ updated.add(resource);
+ }
+ }
+ }
+ } else {
+ updated = null;
+ }
+ final String[] removedUrls;
+ if ( removed != null && removed.size() > 0 ) {
+ removedUrls = new String[removed.size()];
+ int index = 0;
+ for(final File f : removed) {
+ removedUrls[index] = f.getAbsolutePath();
+ logger.debug("Removed file {}", removedUrls[index]);
+ index++;
+ }
+ } else {
+ removedUrls = null;
+ }
+ if ( updated != null || removedUrls != null ) {
+ this.installer.updateResources(this.scheme,
+ updated == null ? null : updated.toArray(new InstallableResource[updated.size()]), removedUrls);
+ }
+ }
+
+ private InstallableResource createResource(final File file) {
+ try {
+ // check for run modes
+ final String name = file.getAbsolutePath().substring(this.prefix.length()).replace(File.separatorChar, '/');
+ boolean isActive = true;
+ Integer prio = null;
+ final int pos = name.indexOf('/');
+ if ( pos != -1 && name.startsWith("install.") ) {
+ final String runModes = name.substring(8, pos);
+ final int activeModes = this.isActive(runModes);
+ if ( activeModes > 0 ) {
+ prio = InstallableResource.DEFAULT_PRIORITY + activeModes;
+ } else {
+ isActive = false;
+ }
+ }
+ if ( isActive ) {
+ final InputStream is = new FileInputStream(file);
+ final String digest = String.valueOf(file.lastModified());
+ // if this is a bundle check for start level directory!
+ final Dictionary<String, Object> dict = new Hashtable<String, Object>();
+ 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.put(InstallableResource.BUNDLE_START_LEVEL, startLevel);
+ }
+ } catch (NumberFormatException nfe) {
+ // ignore this
+ }
+ }
+ dict.put(InstallableResource.RESOURCE_URI_HINT, file.toURI().toString());
+ return new InstallableResource(file.getAbsolutePath(), is, dict, digest,
+ null, prio);
+ } else {
+ logger.info("Ignoring inactive resource at {}", file);
+ }
+
+ } catch (IOException io) {
+ logger.error("Unable to read file " + file, io);
+ }
+ return null;
+ }
+
+ private int isActive(final String runModesString) {
+ final String[] runModes = runModesString.split("\\.");
+ boolean active = true;
+ for(final String mode : runModes) {
+ if ( !activeRunModes.contains(mode) ) {
+ active = false;
+ break;
+ }
+ }
+
+ return active ? runModes.length : 0;
+ }
+}
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/ScanConfiguration.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/ScanConfiguration.java
new file mode 100644
index 0000000..0e82304
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/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.provider.file.impl;
+
+/**
+ * A scan configuration for the file install.
+ */
+public class ScanConfiguration {
+
+ public String directory;
+
+ public Long scanInterval;
+}
diff --git a/file/src/main/java/org/apache/sling/installer/provider/file/impl/ServicesListener.java b/file/src/main/java/org/apache/sling/installer/provider/file/impl/ServicesListener.java
new file mode 100644
index 0000000..90d016a
--- /dev/null
+++ b/file/src/main/java/org/apache/sling/installer/provider/file/impl/ServicesListener.java
@@ -0,0 +1,200 @@
+/*
+ * 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.provider.file.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.installer.api.UpdateHandler;
+import org.apache.sling.settings.SlingSettingsService;
+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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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 logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** The name of the installer service. */
+ private static final String INSTALLER_SERVICE_NAME = OsgiInstaller.class.getName();
+
+ /** The name of the settings service. */
+ private static final String SETTINGS_SERVICE_NAME = SlingSettingsService.class.getName();
+
+ /** The bundle context. */
+ private final BundleContext bundleContext;
+
+ /** The listener for the installer. */
+ private final Listener installerListener;
+
+ /** The listener for the settings service. */
+ private final Listener settingsListener;
+
+ /** The file installer. */
+ private final FileInstaller installer;
+
+ /** Service registration. */
+ private ServiceRegistration registration;
+
+ private boolean running = false;
+
+ public ServicesListener(final BundleContext bundleContext,
+ final List<ScanConfiguration> configs) {
+ this.bundleContext = bundleContext;
+ boolean writeBack = true;
+ final Object writeBackObj = Activator.getProp(this.bundleContext, Activator.KEY_WRITEBACK);
+ if ( writeBackObj != null && "false".equalsIgnoreCase(writeBackObj.toString())) {
+ writeBack = false;
+ }
+ this.installer = new FileInstaller(configs, writeBack);
+ this.installerListener = new Listener(INSTALLER_SERVICE_NAME);
+ this.settingsListener = new Listener(SETTINGS_SERVICE_NAME);
+ this.installerListener.start();
+ this.settingsListener.start();
+ }
+
+ public synchronized void notifyChange() {
+ final boolean shouldRun = this.installer.hasConfigurations();
+ if ( (shouldRun && !running) || (!shouldRun && running) ) {
+ final OsgiInstaller installer = (OsgiInstaller)this.installerListener.getService();
+ final SlingSettingsService settings = (SlingSettingsService)this.settingsListener.getService();
+ if ( installer != null && settings != null && !running ) {
+ logger.debug("Starting scanner");
+ this.startScanner(installer, settings);
+ } else if ( running && (installer == null || settings == null) ) {
+ logger.debug("Stopping scanner");
+ this.stopScanner();
+ }
+ }
+ }
+
+ /**
+ * Deactivate this listener.
+ */
+ public void deactivate() {
+ this.installerListener.deactivate();
+ this.stopScanner();
+ }
+
+ /** Vendor of all registered services. */
+ public static final String VENDOR = "The Apache Software Foundation";
+
+ private void startScanner(final OsgiInstaller installer, final SlingSettingsService settings) {
+ if ( !running ) {
+ this.installer.start(installer, settings);
+ final Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling File Installer Controller Service");
+ props.put(Constants.SERVICE_VENDOR, VENDOR);
+ props.put(UpdateHandler.PROPERTY_SCHEMES, this.installer.getSchemes());
+
+ this.registration = this.bundleContext.registerService(UpdateHandler.class.getName(),
+ this.installer, props);
+ running = true;
+ }
+ }
+
+ private void stopScanner() {
+ if ( running ) {
+ if ( this.registration != null ) {
+ this.registration.unregister();
+ this.registration = null;
+ }
+ this.installer.stop();
+ running = false;
+ }
+ }
+
+ protected final class Listener implements ServiceListener {
+
+ private final String serviceName;
+
+ private ServiceReference reference;
+ private Object service;
+
+ public Listener(final String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public void start() {
+ this.retainService();
+ try {
+ bundleContext.addServiceListener(this, "("
+ + Constants.OBJECTCLASS + "=" + serviceName + ")");
+ } catch (final InvalidSyntaxException ise) {
+ // this should really never happen
+ throw new RuntimeException("Unexpected exception occured.", ise);
+ }
+ }
+
+ 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();
+ }
+ }
+ }
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.