You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2008/08/22 08:03:30 UTC
svn commit: r687956 - in
/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles:
AbstractNodeProcessor.java BundleNodeProcessor.java BundlesFolder.java
JcrBundlesManager.java
Author: cziegeler
Date: Thu Aug 21 23:03:29 2008
New Revision: 687956
URL: http://svn.apache.org/viewvc?rev=687956&view=rev
Log:
SLING-587 - Apply patch from Tobias Bocanegra adding a queue to install bundles
Modified:
incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java
incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java
incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java
Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
*/
package org.apache.sling.jcr.jcrbundles;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
-
import java.io.InputStream;
import java.util.Calendar;
import java.util.regex.Pattern;
@@ -29,98 +27,108 @@
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** NodeProcessor that accepts nodes based on a regexp on
- * their name, but does nothing with them */
+/**
+ * NodeProcessor that accepts nodes based on a regexp on
+ * their name, but does nothing with them
+ */
abstract class AbstractNodeProcessor implements NodeProcessor {
-
- /** The relative path of the data and last modified date of an nt:file node */
- public static final String JCR_CONTENT = "jcr:content";
+
+ /**
+ * The relative path of the data and last modified date of an nt:file node
+ */
+ public static final String JCR_CONTENT = "jcr:content";
public static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
public static final String JCR_LAST_MODIFIED = "jcr:lastModified";
public static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
protected final Logger log = LoggerFactory.getLogger(this.getClass());
- private final Pattern pattern;
-
- AbstractNodeProcessor(String regexp) {
- pattern = Pattern.compile(regexp);
- }
+ private final Pattern pattern;
- public boolean accepts(Node n) throws RepositoryException {
- boolean result = pattern.matcher(n.getName()).matches();
- if(result) {
- log.debug("Node {} accepted by {}", n.getPath(), getClass().getName());
- }
- return result;
- }
-
- static InputStream getInputStream(Node fileNode) throws RepositoryException
- {
- Property prop = fileNode.getProperty(JCR_CONTENT_DATA);
- return prop.getStream();
- }
-
- /** Return the Node to use to store status of given fileNode.
- * Session is not saved if status node is created */
- protected Node getStatusNode(Node fileNode, boolean createIfNotFound) throws RepositoryException {
- final String path = STATUS_BASE_PATH + fileNode.getPath();
- final Session s = fileNode.getSession();
- Node result = null;
-
- if(s.itemExists(path)) {
- result = (Node)s.getItem(path);
- } else if(createIfNotFound) {
- result = deepCreate(s, path);
- }
-
- if(result != null) {
- log.debug("Status node for {} is at {}", fileNode.getPath(), result.getPath());
- } else {
- log.debug("No status node found for {}", fileNode.getPath());
- }
-
- return result;
- }
-
- /** Find the path of the node in the main tree that corresponds to the given status node path */
- protected String getMainNodePath(String statusNodePath) {
- return statusNodePath.substring(STATUS_BASE_PATH.length());
- }
-
- /** Find the node in the main tree that corresponds to the given status node */
- protected Node getMainNode(Node statusNode) throws RepositoryException {
- final String path = getMainNodePath(statusNode.getPath());
- final Session s = statusNode.getSession();
- Node result = null;
-
- if(s.itemExists(path)) {
- result = (Node)s.getItem(path);
- }
-
- return result;
- }
-
- protected Calendar getLastModified(Node fileNode) throws RepositoryException {
- if(fileNode.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
- return fileNode.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate();
- }
- return null;
- }
-
- protected Node deepCreate(Session s, String path) throws RepositoryException {
- if(path.startsWith("/")) {
- path = path.substring(1);
- }
- final String [] names = path.split("/");
- String currentPath = "";
-
- for(int i=0; i < names.length; i++) {
- currentPath += "/" + names[i];
- if(!s.itemExists(currentPath)) {
- s.getRootNode().addNode(currentPath.substring(1));
+ AbstractNodeProcessor(String regexp) {
+ pattern = Pattern.compile(regexp);
+ }
+
+ public boolean accepts(Node n) throws RepositoryException {
+ boolean result = pattern.matcher(n.getName()).matches();
+ if (result) {
+ log.debug("Node {} accepted by {}", n.getPath(), getClass().getName());
+ }
+ return result;
+ }
+
+ static InputStream getInputStream(Node fileNode) throws RepositoryException {
+ Property prop = fileNode.getProperty(JCR_CONTENT_DATA);
+ return prop.getStream();
+ }
+
+ /**
+ * Return the Node to use to store status of given fileNode.
+ * Session is not saved if status node is created
+ */
+ protected Node getStatusNode(Node fileNode, boolean createIfNotFound) throws RepositoryException {
+ final String path = STATUS_BASE_PATH + fileNode.getPath();
+ final Session s = fileNode.getSession();
+ Node result = null;
+
+ if (s.itemExists(path)) {
+ result = (Node) s.getItem(path);
+ } else if (createIfNotFound) {
+ result = deepCreate(s, path);
+ }
+
+ if (result != null) {
+ log.debug("Status node for {} is at {}", fileNode.getPath(), result.getPath());
+ } else {
+ log.debug("No status node found for {}", fileNode.getPath());
+ }
+
+ return result;
+ }
+
+ /**
+ * Find the path of the node in the main tree that corresponds to the given status node path
+ */
+ protected String getMainNodePath(String statusNodePath) {
+ return statusNodePath.substring(STATUS_BASE_PATH.length());
+ }
+
+ /**
+ * Find the node in the main tree that corresponds to the given status node
+ */
+ protected Node getMainNode(Node statusNode) throws RepositoryException {
+ final String path = getMainNodePath(statusNode.getPath());
+ final Session s = statusNode.getSession();
+ Node result = null;
+
+ if (s.itemExists(path)) {
+ result = (Node) s.getItem(path);
+ }
+
+ return result;
+ }
+
+ protected Calendar getLastModified(Node fileNode) throws RepositoryException {
+ if (fileNode.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
+ return fileNode.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate();
+ }
+ return null;
+ }
+
+ protected Node deepCreate(Session s, String path) throws RepositoryException {
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ final String[] names = path.split("/");
+ String currentPath = "";
+
+ for (int i = 0; i < names.length; i++) {
+ currentPath += "/" + names[i];
+ if (!s.itemExists(currentPath)) {
+ s.getRootNode().addNode(currentPath.substring(1));
}
}
Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
*/
package org.apache.sling.jcr.jcrbundles;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.JCRBUNDLES_LOCATION_PREFIX;
-
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
@@ -28,121 +26,140 @@
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.JCRBUNDLES_LOCATION_PREFIX;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.packageadmin.PackageAdmin;
-/** Process nodes that look like bundles, based on their node name */
+/**
+ * Process nodes that look like bundles, based on their node name
+ */
class BundleNodeProcessor extends AbstractNodeProcessor {
private final ComponentContext context;
- private final PackageAdmin padmin;
-
- public BundleNodeProcessor(ComponentContext ctx, PackageAdmin pa) {
- super("[a-zA-Z0-9].*\\.jar$");
- context = ctx;
- padmin = pa;
- }
-
- public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
-
- // Do we have file data?
- InputStream is = null;
- try {
- is = getInputStream(n);
- } catch(Exception e) {
- log.info("Unable to get InputStream on Node {}, will be ignored ({})", n.getPath(), e);
- return;
- }
+ private final PackageAdmin padmin;
+ private final JcrBundlesManager mgr;
+
+ public BundleNodeProcessor(JcrBundlesManager mgr, ComponentContext ctx, PackageAdmin pa) {
+ super("[a-zA-Z0-9].*\\.jar$");
+ this.mgr = mgr;
+ context = ctx;
+ padmin = pa;
+ }
+
+ public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
- // We have data - install, update or do nothing with bundle
- // TODO handle deletes (store a list of bundles that were installed)
+ // Do we have file data?
+ InputStream is = null;
+ try {
+ is = getInputStream(n);
+ } catch (Exception e) {
+ log.info("Unable to get InputStream on Node {}, will be ignored ({})", n.getPath(), e);
+ return;
+ }
+
+ // We have data - install, update or do nothing with bundle
+ // TODO handle deletes (store a list of bundles that were installed)
final String location = getBundleLocation(n.getPath());
- try {
- final Node status = getStatusNode(n, true);
- final Bundle oldBundle = getBundleByLocation(location);
- final Calendar lastModified = getLastModified(n);
- Bundle newBundle = null;
- boolean changed = false;
-
- if(oldBundle == null) {
- // Bundle not installed yet, install it
- changed = true;
- newBundle = getBundleContext().installBundle(location, is);
- if (!isFragment(newBundle)) {
- newBundle.start();
- log.info("Bundle {} successfully installed and started", location);
- } else {
- log.info("Fragment bundle {} successfully installed", location);
- }
- status.setProperty("status", "installed");
-
- } else {
- // Bundle already installed, did it change?
- Calendar savedLastModified = null;
- if(status.hasProperty(JCR_LAST_MODIFIED)) {
- savedLastModified = status.getProperty(JCR_LAST_MODIFIED).getDate();
- }
-
- changed =
- savedLastModified == null
- || lastModified == null
- || !(lastModified.equals(savedLastModified))
- ;
-
- if(changed) {
- oldBundle.update(is);
- log.info("Bundle {} successfully updated", location);
- status.setProperty("status", "updated");
- } else {
- log.debug("Bundle {} unchanged, no update needed", location);
- }
- }
-
- if(changed) {
- flags.put("refresh.packages", Boolean.TRUE);
- if(lastModified != null) {
- status.setProperty(JCR_LAST_MODIFIED, lastModified);
- }
- n.getSession().save();
- }
-
- } catch(Exception e) {
- log.warn("Exception while processing bundle {}", e);
-
- } finally {
- try {
- is.close();
- } catch(IOException ioe) {
- log.error("IOException on closing InputStream", ioe);
- }
- }
- }
+ try {
+ final Node status = getStatusNode(n, true);
+ final Bundle oldBundle = getBundleByLocation(location);
+ final Calendar lastModified = getLastModified(n);
+ Bundle newBundle = null;
+ boolean changed = false;
+
+ if (oldBundle == null) {
+ // Bundle not installed yet, install it
+ changed = true;
+ newBundle = getBundleContext().installBundle(location, is);
+ if (!isFragment(newBundle)) {
+ if ((newBundle.getState() & Bundle.RESOLVED) > 0) {
+ newBundle.start();
+ log.info("Bundle {} successfully installed and started", location);
+ } else {
+ mgr.addPendingBundle(location, newBundle);
+ log.info("Bundle {} not resolved. delay start", location);
+ }
+ } else {
+ log.info("Fragment bundle {} successfully installed", location);
+ }
+ status.setProperty("status", "installed");
+ flags.put("refresh.packages", Boolean.TRUE);
+
+ } else {
+ // Bundle already installed, did it change?
+ Calendar savedLastModified = null;
+ if (status.hasProperty(JCR_LAST_MODIFIED)) {
+ savedLastModified = status.getProperty(JCR_LAST_MODIFIED).getDate();
+ }
+
+ changed = savedLastModified == null
+ || lastModified == null
+ || !(lastModified.equals(savedLastModified));
+
+ if (changed) {
+ try {
+ oldBundle.update(is);
+ log.info("Bundle {} successfully updated", location);
+ status.setProperty("status", "updated");
+ } catch (BundleException e) {
+ log.info("Bundle {} could not be updated. adding to pending list", location, e);
+ mgr.addPendingBundle(location, oldBundle);
+ }
+ } else {
+ log.debug("Bundle {} unchanged, no update needed", location);
+ }
+ }
+
+ if (changed) {
+ flags.put("refresh.packages", Boolean.TRUE);
+ if (lastModified != null) {
+ status.setProperty(JCR_LAST_MODIFIED, lastModified);
+ }
+ n.getSession().save();
+ }
+
+ } catch (Exception e) {
+ log.warn("Exception while processing bundle {}", e);
+
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ log.error("IOException on closing InputStream", ioe);
+ }
+ }
+ }
- /** Check if the given statusNode still has an equivalent in the main tree, and if not
- * uninstall the corresponding bundle, if found. Do the same thing for statusNodes' children */
+ /**
+ * Check if the given statusNode still has an equivalent in the main tree, and if not
+ * uninstall the corresponding bundle, if found. Do the same thing for statusNodes' children
+ */
public void checkDeletions(Node statusNode, Map<String, Boolean> flags) throws Exception {
- final Node mainNode = getMainNode(statusNode);
- if(mainNode == null) {
- final String mainPath = getMainNodePath(statusNode.getPath());
- final String location = getBundleLocation(mainPath);
- final Bundle b = getBundleByLocation(location);
- if(b == null) {
- log.info("Node {} has been deleted, but corresponding bundle {} not found - deleting status node only",
- mainPath, location);
- } else {
- try {
- b.uninstall();
- flags.put("refresh.packages", Boolean.TRUE);
- log.info("Node {} has been deleted, bundle {} uninstalled", mainPath, location);
- } catch(Exception e) {
- log.error("Exception while trying to uninstall bundle " + location, e);
- }
- }
-
- statusNode.remove();
- statusNode.getSession().save();
- }
+ final Node mainNode = getMainNode(statusNode);
+ if (mainNode == null) {
+ final String mainPath = getMainNodePath(statusNode.getPath());
+ final String location = getBundleLocation(mainPath);
+ final Bundle b = getBundleByLocation(location);
+ // make sure it's removed from the pending list
+ mgr.removePendingBundle(location);
+ if (b == null) {
+ log.info("Node {} has been deleted, but corresponding bundle {} not found - deleting status node only",
+ mainPath, location);
+ } else {
+ try {
+ b.uninstall();
+ flags.put("refresh.packages", Boolean.TRUE);
+ log.info("Node {} has been deleted, bundle {} uninstalled", mainPath, location);
+ } catch (Exception e) {
+ log.error("Exception while trying to uninstall bundle " + location, e);
+ }
+ }
+
+ statusNode.remove();
+ statusNode.getSession().save();
+ }
}
private boolean isFragment(Bundle bundle) {
@@ -155,15 +172,15 @@
protected Bundle getBundleByLocation(String location) {
Bundle bundles[] = getBundleContext().getBundles();
- for(Bundle b : bundles) {
- if(location.equals(b.getLocation())) {
- return b;
- }
+ for (Bundle b : bundles) {
+ if (location.equals(b.getLocation())) {
+ return b;
+ }
}
return null;
}
-
+
protected String getBundleLocation(String bundleNodePath) {
- return JCRBUNDLES_LOCATION_PREFIX + bundleNodePath;
+ return JCRBUNDLES_LOCATION_PREFIX + bundleNodePath;
}
}
\ No newline at end of file
Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
*/
package org.apache.sling.jcr.jcrbundles;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -34,58 +32,68 @@
import javax.jcr.observation.EventListener;
import org.apache.sling.jcr.api.SlingRepository;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Manages a folder that contains bundles and configurations: listens to
- * changes in the folder, and rescan it when needed, passing nodes to
- * NodeProcessors that want to process them.
+/**
+ * Manages a folder that contains bundles and configurations: listens to
+ * changes in the folder, and rescan it when needed, passing nodes to
+ * NodeProcessors that want to process them.
*/
class BundlesFolder implements EventListener {
protected static final Logger log = LoggerFactory.getLogger(BundlesFolder.class);
-
+
private final String path;
private final Session session;
private long nextScan;
-
- /** After receiving JCR events, we wait for this many msec before
- * re-scanning the folder, as events often come in bursts.
+
+ /**
+ * After receiving JCR events, we wait for this many msec before
+ * re-scanning the folder, as events often come in bursts.
*/
public static final long SCAN_DELAY_MSEC = 1000;
-
- /** List of processors for our bundles and configs */
+
+ /**
+ * List of processors for our bundles and configs
+ */
private final List<NodeProcessor> processors;
- /** Create a BundlesFolder on the given Repository, at the
- * given path
+ /**
+ * Create a BundlesFolder on the given Repository, at the
+ * given path
*/
BundlesFolder(SlingRepository r, String path, List<NodeProcessor> processors) throws RepositoryException {
this.path = path;
this.processors = processors;
session = r.loginAdministrative(r.getDefaultWorkspace());
-
+
// observe any changes in our folder, recursively
- final int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED
- | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
+ final int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED
+ | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
final boolean isDeep = true;
final boolean noLocal = true;
- session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, path,
+ session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, path,
isDeep, null, null, noLocal);
-
+
// trigger the initial scan without waiting
setScanTimer(0);
-
+
log.info("{} created for path {}", getClass().getSimpleName(), path);
}
-
- /** MUST be called when done using this object */
+
+ /**
+ * MUST be called when done using this object
+ */
void cleanup() throws RepositoryException {
session.getWorkspace().getObservationManager().removeEventListener(this);
session.logout();
}
-
- /** Any event causes us to rescan our folder, once there are no more
- * events during SCAN_DELAY_MSEC
+
+ /**
+ * Any event causes us to rescan our folder, once there are no more
+ * events during SCAN_DELAY_MSEC
*/
public void onEvent(EventIterator it) {
setScanTimer(SCAN_DELAY_MSEC);
@@ -93,11 +101,11 @@
@Override
public boolean equals(Object obj) {
- if( !(obj instanceof BundlesFolder) ) {
+ if (!(obj instanceof BundlesFolder)) {
return false;
}
-
- final BundlesFolder other = (BundlesFolder)obj;
+
+ final BundlesFolder other = (BundlesFolder) obj;
return path.equals(other.path);
}
@@ -106,16 +114,18 @@
return path.hashCode();
}
- /** Find all bundles folders under rootPath */
- static Set<BundlesFolder> findBundlesFolders(SlingRepository r, String rootPath,
- List<NodeProcessor> processors)
- throws RepositoryException {
+ /**
+ * Find all bundles folders under rootPath
+ */
+ static Set<BundlesFolder> findBundlesFolders(SlingRepository r, String rootPath,
+ List<NodeProcessor> processors)
+ throws RepositoryException {
final Set<BundlesFolder> result = new HashSet<BundlesFolder>();
Session s = null;
-
+
try {
s = r.loginAdministrative(r.getDefaultWorkspace());
- if(!s.getRootNode().hasNode(relPath(rootPath))) {
+ if (!s.getRootNode().hasNode(relPath(rootPath))) {
log.info("Bundles root node {} not found, ignored", rootPath);
} else {
log.debug("Bundles root node {} found, looking for bundle folders inside it", rootPath);
@@ -123,110 +133,121 @@
findBundlesFolders(r, n, result, processors);
}
} finally {
- if(s != null) {
+ if (s != null) {
s.logout();
}
}
-
+
return result;
}
- /** Add n to setToUpdate if it is a bundle folder, and recurse into its children
- * to do the same.
- */
- static void findBundlesFolders(SlingRepository r, Node n,
- Set<BundlesFolder> setToUpdate, List<NodeProcessor> processors)
- throws RepositoryException {
- if(n.getName().equals(BUNDLES_NODENAME)) {
+ /**
+ * Add n to setToUpdate if it is a bundle folder, and recurse into its children
+ * to do the same.
+ */
+ static void findBundlesFolders(SlingRepository r, Node n,
+ Set<BundlesFolder> setToUpdate, List<NodeProcessor> processors)
+ throws RepositoryException {
+ if (n.getName().equals(BUNDLES_NODENAME)) {
setToUpdate.add(new BundlesFolder(r, n.getPath(), processors));
}
final NodeIterator it = n.getNodes();
- while(it.hasNext()) {
+ while (it.hasNext()) {
findBundlesFolders(r, it.nextNode(), setToUpdate, processors);
}
-
+
}
-
- /** Set or reset our scanning timer: scanIfNeeded() will do nothing
- * unless this timer has expired */
+
+ /**
+ * Set or reset our scanning timer: scanIfNeeded() will do nothing
+ * unless this timer has expired
+ */
protected void setScanTimer(long delayMsec) {
nextScan = System.currentTimeMillis() + delayMsec;
}
-
- /** If our timer allows it, recursively call processNode
- * on our Node and its children */
+
+ /**
+ * If our timer allows it, recursively call processNode
+ * on our Node and its children
+ */
void scanIfNeeded(Map<String, Boolean> flags) throws Exception {
- if(nextScan != -1 && System.currentTimeMillis() > nextScan) {
+ if (nextScan != -1 && System.currentTimeMillis() > nextScan) {
nextScan = -1;
log.debug("Timer expired, scanning {}", path);
checkDeletions(flags);
checkUpdatesAndDeletes(flags);
}
}
-
- /** Let our processors handle all nodes under the main tree */
+
+ /**
+ * Let our processors handle all nodes under the main tree
+ */
void checkUpdatesAndDeletes(Map<String, Boolean> flags) throws Exception {
- if(session.getRootNode().hasNode(relPath(path))) {
- final Node n = session.getRootNode().getNode(relPath(path));
- processNode(n, flags, false);
- } else {
- log.info("Bundles folder {} does not exist anymore", path);
- }
- }
-
- /** Check for nodes in the status tree that have disappeared from their main
- * locations, and let the processors handle these deletes.
+ if (session.getRootNode().hasNode(relPath(path))) {
+ final Node n = session.getRootNode().getNode(relPath(path));
+ processNode(n, flags, false);
+ } else {
+ log.info("Bundles folder {} does not exist anymore", path);
+ }
+ }
+
+ /**
+ * Check for nodes in the status tree that have disappeared from their main
+ * locations, and let the processors handle these deletes.
*/
void checkDeletions(Map<String, Boolean> flags) throws Exception {
- final String statusPath = STATUS_BASE_PATH + path;
- if(session.getRootNode().hasNode(relPath(statusPath))) {
+ final String statusPath = STATUS_BASE_PATH + path;
+ if (session.getRootNode().hasNode(relPath(statusPath))) {
final Node n = session.getRootNode().getNode(relPath(statusPath));
processNode(n, flags, true);
} else {
- log.info("Status folder {} does not exist, checkDeletions does nothing", statusPath);
+ log.info("Status folder {} does not exist, checkDeletions does nothing", statusPath);
}
}
-
- /** Let the first NodeProcessor that accepts n process it (for normal processing
- * or deletions), and recurse into n's children to do the same
+
+ /**
+ * Let the first NodeProcessor that accepts n process it (for normal processing
+ * or deletions), and recurse into n's children to do the same
*/
protected void processNode(Node n, Map<String, Boolean> flags, boolean checkDeletions) throws Exception {
-
+
boolean accepted = false;
final String path = n.getPath();
final Session s = n.getSession();
-
- for(NodeProcessor p : processors) {
- if(p.accepts(n)) {
+
+ for (NodeProcessor p : processors) {
+ if (p.accepts(n)) {
accepted = true;
- if(checkDeletions) {
- p.checkDeletions(n, flags);
+ if (checkDeletions) {
+ p.checkDeletions(n, flags);
} else {
- p.process(n, flags);
+ p.process(n, flags);
}
break;
}
}
-
- if(!accepted) {
+
+ if (!accepted) {
log.debug("No NodeProcessor found for node {}, ignored", n.getPath());
}
-
+
// n might have been deleted above, if it's a status done
- if(s.itemExists(path)) {
- final NodeIterator it = n.getNodes();
- while(it.hasNext()) {
- processNode(it.nextNode(), flags, checkDeletions);
- }
+ if (s.itemExists(path)) {
+ final NodeIterator it = n.getNodes();
+ while (it.hasNext()) {
+ processNode(it.nextNode(), flags, checkDeletions);
+ }
}
}
-
- /** Return the relative path for supplied path */
+
+ /**
+ * Return the relative path for supplied path
+ */
static String relPath(String path) {
- if(path.startsWith("/")) {
+ if (path.startsWith("/")) {
return path.substring(1);
}
return path;
}
-
+
}
\ No newline at end of file
Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java Thu Aug 21 23:03:29 2008
@@ -18,15 +18,13 @@
*/
package org.apache.sling.jcr.jcrbundles;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Iterator;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
@@ -34,66 +32,80 @@
import javax.jcr.observation.Event;
import org.apache.sling.jcr.api.SlingRepository;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** JcrBundlesManager service, manages OSGi bundles and
- * configurations stored in the JCR Repository.
- *
- * @scr.service
- *
- * @scr.component
- * immediate="true"
- * metatype="no"
- *
- * @scr.property
- * name="service.description"
- * value="Sling JCR Bundles Manager Service"
+/**
+ * JcrBundlesManager service, manages OSGi bundles and
+ * configurations stored in the JCR Repository.
*
- * @scr.property
- * name="service.vendor"
- * value="The Apache Software Foundation"
+ * @scr.service
+ * @scr.component immediate="true"
+ * metatype="no"
+ * @scr.property name="service.description"
+ * value="Sling JCR Bundles Manager Service"
+ * @scr.property name="service.vendor"
+ * value="The Apache Software Foundation"
*/
public class JcrBundlesManager implements Runnable {
- /** Ordered list of root paths to observe for bundles and configs
- * TODO should be configurable
+ /**
+ * Ordered list of root paths to observe for bundles and configs
+ * TODO should be configurable
+ */
+ private String[] bundleRoots = {"/libs", "/apps"};
+
+ /**
+ * List of processors for our bundles and configs
*/
- private String [] bundleRoots = { "/libs", "/apps" };
-
- /** List of processors for our bundles and configs */
private List<NodeProcessor> processors;
-
- /** Set of BundleFolders that we manage */
+
+ /**
+ * Set of BundleFolders that we manage
+ */
private Set<BundlesFolder> folders;
-
- /** @scr.reference */
+
+ /**
+ * @scr.reference
+ */
private SlingRepository repository;
-
- /** @scr.reference */
+
+ /**
+ * @scr.reference
+ */
private PackageAdmin padmin;
-
- /** @scr.reference */
+
+ /**
+ * @scr.reference
+ */
private ConfigurationAdmin cadmin;
- /** Listeners for new BundleFolders under our bundleRoots */
+ /**
+ * Listeners for new BundleFolders under our bundleRoots
+ */
private List<BundlesFolderCreationListener> listeners = new LinkedList<BundlesFolderCreationListener>();
-
+
private Session session;
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private boolean running;
-
- /** When activated, collect the list of bundle folders to scan
- * and register as a listener for future updates.
+
+ private final Map<String, Bundle> pendingBundles = new HashMap<String, Bundle>();
+
+ /**
+ * When activated, collect the list of bundle folders to scan
+ * and register as a listener for future updates.
*/
protected void activate(ComponentContext context) throws RepositoryException {
// setup our processors
processors = new LinkedList<NodeProcessor>();
- processors.add(new BundleNodeProcessor(context, padmin));
+ processors.add(new BundleNodeProcessor(this, context, padmin));
processors.add(new ConfigNodeProcessor(cadmin));
processors.add(new NodeProcessor() {
public boolean accepts(Node n) throws RepositoryException {
@@ -103,148 +115,211 @@
public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
log.debug("Node {} ignored in process() call, no NodeProcessor accepts it", n.getPath());
}
-
+
public void checkDeletions(Node statusNode, Map<String, Boolean> flags) throws Exception {
log.debug("Node {} ignored in checkDeletions() call, no NodeProcessor accepts it", statusNode.getPath());
}
-
+
});
-
+
// find "bundles" folders and add them to processing queue
folders = new HashSet<BundlesFolder>();
- for(String rootPath : bundleRoots) {
+ for (String rootPath : bundleRoots) {
folders.addAll(BundlesFolder.findBundlesFolders(repository, rootPath, processors));
}
-
+
// Listen for any new "bundles" folders created after activation
session = repository.loginAdministrative(repository.getDefaultWorkspace());
final int eventTypes = Event.NODE_ADDED;
final boolean isDeep = true;
final boolean noLocal = true;
- for(String path : this.bundleRoots) {
+ for (String path : this.bundleRoots) {
final BundlesFolderCreationListener bfc = new BundlesFolderCreationListener();
listeners.add(bfc);
- session.getWorkspace().getObservationManager().addEventListener(bfc, eventTypes, path,
+ session.getWorkspace().getObservationManager().addEventListener(bfc, eventTypes, path,
isDeep, null, null, noLocal);
}
-
+
// start queue processing
final Thread t = new Thread(this, getClass().getSimpleName() + "_" + System.currentTimeMillis());
t.setDaemon(true);
running = true;
t.start();
}
-
- /** Cleanup */
+
+ /**
+ * Cleanup
+ */
protected void deactivate(ComponentContext oldContext) {
- running = false;
-
- for(BundlesFolder bf : folders) {
+ running = false;
+
+ for (BundlesFolder bf : folders) {
try {
bf.cleanup();
- } catch(RepositoryException e) {
+ } catch (RepositoryException e) {
log.warn("RepositoryException in deactivate/cleanup", e);
}
}
-
+
folders.clear();
-
- if(session != null) {
+
+ if (session != null) {
try {
- for(BundlesFolderCreationListener bfc : listeners) {
+ for (BundlesFolderCreationListener bfc : listeners) {
session.getWorkspace().getObservationManager().removeEventListener(bfc);
}
- } catch(RepositoryException re) {
+ } catch (RepositoryException re) {
log.warn("RepositoryException in deactivate()/removeEventListener", re);
}
-
+
listeners.clear();
-
+
session.logout();
session = null;
}
}
+
+ protected void addPendingBundle(String path, Bundle bundle) {
+ synchronized (pendingBundles) {
+ pendingBundles.put(path, bundle);
+ }
+ }
- /** Scan paths once their timer expires */
+ protected void removePendingBundle(String path) {
+ synchronized (pendingBundles) {
+ if (pendingBundles.remove(path) != null) {
+ log.info("Removed bundle {} from pending list.", path);
+ }
+ }
+ }
+
+ /**
+ * Scan paths once their timer expires
+ */
public void run() {
log.info("{} thread {} starts", getClass().getSimpleName(), Thread.currentThread().getName());
Session s = null;
-
+
// First check for deletions, using the status folder root path
BundlesFolder statusFolder = null;
try {
- statusFolder = new BundlesFolder(repository, STATUS_BASE_PATH, processors);
- final Map<String, Boolean> flags = new HashMap<String, Boolean>();
- statusFolder.checkDeletions(flags);
+ statusFolder = new BundlesFolder(repository, STATUS_BASE_PATH, processors);
+ final Map<String, Boolean> flags = new HashMap<String, Boolean>();
+ statusFolder.checkDeletions(flags);
refreshPackagesIfNeeded(flags);
- } catch(Exception e) {
- log.error("Exception during initial scanning of " + STATUS_BASE_PATH, e);
+ } catch (Exception e) {
+ log.error("Exception during initial scanning of " + STATUS_BASE_PATH, e);
} finally {
- if(statusFolder != null) {
- try {
- statusFolder.cleanup();
- } catch(Exception e) {
- log.error("Exception during BundlesFolder cleanup of " + STATUS_BASE_PATH, e);
- }
- }
+ if (statusFolder != null) {
+ try {
+ statusFolder.cleanup();
+ } catch (Exception e) {
+ log.error("Exception during BundlesFolder cleanup of " + STATUS_BASE_PATH, e);
+ }
+ }
}
-
+
// We could use the scheduler service but that makes things harder to test
- while(running) {
+ while (running) {
try {
- s = repository.loginAdministrative(repository.getDefaultWorkspace());
- runOneCycle(s);
- } catch(IllegalArgumentException ie) {
+ s = repository.loginAdministrative(repository.getDefaultWorkspace());
+ runOneCycle(s);
+ } catch (IllegalArgumentException ie) {
log.warn("IllegalArgumentException in " + getClass().getSimpleName(), ie);
- } catch(RepositoryException re) {
+ } catch (RepositoryException re) {
log.warn("RepositoryException in " + getClass().getSimpleName(), re);
- } catch(Throwable t) {
- log.error("Unhandled Throwable in runOneCycle() - "
- + getClass().getSimpleName() + " thread will be stopped", t);
+ } catch (Throwable t) {
+ log.error("Unhandled Throwable in runOneCycle() - "
+ + getClass().getSimpleName() + " thread will be stopped", t);
} finally {
- if(s!= null) {
+ if (s != null) {
s.logout();
s = null;
}
try {
Thread.sleep(1000L);
- } catch(InterruptedException ignore) {
+ } catch (InterruptedException ignore) {
// ignore
}
}
}
-
+
log.info("{} thread {} ends", getClass().getSimpleName(), Thread.currentThread().getName());
}
-
- /** Run one cycle of processing our scanTimes queue */
+
+ /**
+ * Run one cycle of processing our scanTimes queue
+ */
void runOneCycle(Session s) throws Exception {
-
+
// Add new bundle folders that onEvent created
- for(BundlesFolderCreationListener bfc : listeners) {
- for(String path : bfc.getAndClearPaths()) {
+ for (BundlesFolderCreationListener bfc : listeners) {
+ for (String path : bfc.getAndClearPaths()) {
log.info("New \"" + BUNDLES_NODENAME + "\" node was detected at {}, creating BundlesFolder to watch it", path);
folders.add(new BundlesFolder(repository, path, processors));
}
}
-
+
// Let ouf BundlesFolders do their work
- final Map<String, Boolean> flags = new HashMap<String, Boolean>();
-
- for(BundlesFolder bf : folders) {
- if(!running) {
- break;
- }
+ final Map<String, Boolean> flags = new HashMap<String, Boolean>();
+
+ for (BundlesFolder bf : folders) {
+ if (!running) {
+ break;
+ }
bf.scanIfNeeded(flags);
}
+
+ // try to resolve pending bundles
+ synchronized (pendingBundles) {
+ boolean tryStart = true;
+ while (tryStart) {
+ tryStart = false;
+ Iterator<String> iter = pendingBundles.keySet().iterator();
+ while (iter.hasNext()) {
+ String location = iter.next();
+ Bundle bundle = pendingBundles.get(location);
+ log.info("Checking bundle {} which status is " + bundle.getState(), location);
+ if ((bundle.getState() & Bundle.ACTIVE) > 0) {
+ log.info("Bundle {} is active.", location);
+ flags.put("refresh.packages", Boolean.TRUE);
+ flags.put("resolve.bundles", Boolean.TRUE);
+ iter.remove();
+ } else if ((bundle.getState() & Bundle.STARTING) > 0) {
+ log.info("Bundle {} is starting.", location);
+ } else if ((bundle.getState() & Bundle.STOPPING) > 0) {
+ log.info("Bundle {} is stopping.", location);
+ } else if ((bundle.getState() & Bundle.UNINSTALLED) > 0) {
+ log.info("Bundle {} is uninstalled.", location);
+ iter.remove();
+ } else if ((bundle.getState() & Bundle.RESOLVED) > 0) {
+ log.info("Bundle {} is resolved, starting it.", location);
+ flags.put("resolve.bundles", Boolean.TRUE);
+ bundle.start();
+ tryStart = true;
+ } else if ((bundle.getState() & Bundle.INSTALLED) > 0) {
+ log.info("Bundle {} is installed but not resolved.", location);
+ flags.put("resolve.bundles", Boolean.TRUE);
+ }
+ }
+ }
+ if (!pendingBundles.isEmpty()) {
+ log.info("Could not start all pending bundles. Still {} remaining.", pendingBundles.size());
+ }
+ }
+
refreshPackagesIfNeeded(flags);
}
-
+
void refreshPackagesIfNeeded(Map<String, Boolean> flags) {
- if(Boolean.TRUE.equals(flags.get("refresh.packages"))) {
- log.info("Refreshing packages");
- padmin.refreshPackages(null);
- }
+ if (Boolean.TRUE.equals(flags.get("resolve.bundles"))) {
+ log.info("Resolving bundles");
+ padmin.resolveBundles(null);
+ }
+ if (Boolean.TRUE.equals(flags.get("refresh.packages"))) {
+ log.info("Refreshing packages");
+ padmin.refreshPackages(null);
+ }
}
-
- }
\ No newline at end of file
+
+}
\ No newline at end of file