You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2012/07/05 14:10:06 UTC
svn commit: r1357570 [24/34] - in /ace/sandbox/marrs: cnf/ cnf/ext/ cnf/lib/
cnf/releaserepo/ cnf/repo/ cnf/repo/.obrcache/
cnf/repo/.obrcache/http%3A%2F%2Fbundles.bndtools.org.s3.amazonaws.com%2Fcom.jcraft.jsch/
cnf/repo/.obrcache/http%3A%2F%2Fbundles...
Added: ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-reports.js
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-reports.js?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-reports.js (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-reports.js Thu Jul 5 12:09:30 2012
@@ -0,0 +1,130 @@
+$(document).ready(function() {
+ $('a.navigator-link').click(function() {
+ // Extract the panel for this link
+ var panel = getPanelName($(this));
+
+ // Mark this link as currently selected
+ $('.navigator-link').parent().removeClass('navigator-selected');
+ $(this).parent().addClass('navigator-selected');
+
+ showPanel(panel);
+ });
+
+ installMethodHandlers('failed');
+ installMethodHandlers('skipped');
+ installMethodHandlers('passed', true); // hide passed methods by default
+
+ $('a.method').click(function() {
+ showMethod($(this));
+ return false;
+ });
+
+ // Hide all the panels and display the first one (do this last
+ // to make sure the click() will invoke the listeners)
+ $('.panel').hide();
+ $('.navigator-link').first().click();
+
+ // Collapse/expand the suites
+ $('a.collapse-all-link').click(function() {
+ var contents = $('.navigator-suite-content');
+ if (contents.css('display') == 'none') {
+ contents.show();
+ } else {
+ contents.hide();
+ }
+ });
+
+ // Keep the navigator div always visible
+ var $scrollingDiv = $(".navigator-root");
+ $(window).scroll(function() {
+ $scrollingDiv.css('height', $(window).height() - 65);
+ $scrollingDiv.stop()
+ .animate({"marginTop": ($(window).scrollTop() + 60) + "px"} );
+ });
+});
+
+// The handlers that take care of showing/hiding the methods
+function installMethodHandlers(name, hide) {
+ function getContent(t) {
+ return $('.method-list-content.' + name + "." + t.attr('panel-name'));
+ }
+
+ function getHideLink(t, name) {
+ var s = 'a.hide-methods.' + name + "." + t.attr('panel-name');
+ return $(s);
+ }
+
+ function getShowLink(t, name) {
+ return $('a.show-methods.' + name + "." + t.attr('panel-name'));
+ }
+
+ function getMethodPanelClassSel(element, name) {
+ var panelName = getPanelName(element);
+ var sel = '.' + panelName + "-class-" + name;
+ return $(sel);
+ }
+
+ $('a.hide-methods.' + name).click(function() {
+ var w = getContent($(this));
+ w.hide();
+ getHideLink($(this), name).hide();
+ getShowLink($(this), name).show();
+ getMethodPanelClassSel($(this), name).hide();
+ });
+
+ $('a.show-methods.' + name).click(function() {
+ var w = getContent($(this));
+ w.show();
+ getHideLink($(this), name).show();
+ getShowLink($(this), name).hide();
+ showPanel(getPanelName($(this)));
+ getMethodPanelClassSel($(this), name).show();
+ });
+
+ if (hide) {
+ $('a.hide-methods.' + name).click();
+ } else {
+ $('a.show-methods.' + name).click();
+ }
+}
+
+function getHashForMethod(element) {
+ return element.attr('hash-for-method');
+}
+
+function getPanelName(element) {
+ return element.attr('panel-name');
+}
+
+function showPanel(panelName) {
+ $('.panel').hide();
+ var panel = $('.panel[panel-name="' + panelName + '"]');
+ panel.show();
+}
+
+function showMethod(element) {
+ var hashTag = getHashForMethod(element);
+ var panelName = getPanelName(element);
+ showPanel(panelName);
+ var current = document.location.href;
+ var base = current.substring(0, current.indexOf('#'))
+ document.location.href = base + '#' + hashTag;
+ var newPosition = $(document).scrollTop() - 65;
+ $(document).scrollTop(newPosition);
+}
+
+function drawTable() {
+ for (var i = 0; i < suiteTableInitFunctions.length; i++) {
+ window[suiteTableInitFunctions[i]]();
+ }
+
+ for (var k in window.suiteTableData) {
+ var v = window.suiteTableData[k];
+ var div = v.tableDiv;
+ var data = v.tableData
+ var table = new google.visualization.Table(document.getElementById(div));
+ table.draw(data, {
+ showRowNumber : false
+ });
+ }
+}
Added: ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-results.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-results.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-results.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng-results.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testng-results skipped="0" failed="0" total="2" passed="2">
+ <reporter-output>
+ </reporter-output>
+ <suite name="Default suite" duration-ms="429" started-at="2012-07-04T12:17:19Z" finished-at="2012-07-04T12:17:20Z">
+ <groups>
+ <group name="unit">
+ <method signature="RESTClientTest.testPathTransforms()[pri:0, instance:org.apache.ace.client.rest.RESTClientTest@44050988]" name="testPathTransforms" class="org.apache.ace.client.rest.RESTClientTest"/>
+ <method signature="RESTClientTest.testPropertyGetter()[pri:0, instance:org.apache.ace.client.rest.RESTClientTest@44050988]" name="testPropertyGetter" class="org.apache.ace.client.rest.RESTClientTest"/>
+ </group> <!-- unit -->
+ </groups>
+ <test name="Default test" duration-ms="429" started-at="2012-07-04T12:17:19Z" finished-at="2012-07-04T12:17:20Z">
+ <class name="org.apache.ace.client.rest.RESTClientTest">
+ <test-method status="PASS" signature="testPathTransforms()[pri:0, instance:org.apache.ace.client.rest.RESTClientTest@44050988]" name="testPathTransforms" duration-ms="414" started-at="2012-07-04T12:17:19Z" finished-at="2012-07-04T12:17:20Z">
+ </test-method> <!-- testPathTransforms -->
+ <test-method status="PASS" signature="testPropertyGetter()[pri:0, instance:org.apache.ace.client.rest.RESTClientTest@44050988]" name="testPropertyGetter" duration-ms="1" started-at="2012-07-04T12:17:20Z" finished-at="2012-07-04T12:17:20Z">
+ </test-method> <!-- testPropertyGetter -->
+ </class> <!-- org.apache.ace.client.rest.RESTClientTest -->
+ </test> <!-- Default test -->
+ </suite> <!-- Default suite -->
+</testng-results>
Added: ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng.css
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng.css?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng.css (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/test-output/testng.css Thu Jul 5 12:09:30 2012
@@ -0,0 +1,9 @@
+.invocation-failed, .test-failed { background-color: #DD0000; }
+.invocation-percent, .test-percent { background-color: #006600; }
+.invocation-passed, .test-passed { background-color: #00AA00; }
+.invocation-skipped, .test-skipped { background-color: #CCCC00; }
+
+.main-page {
+ font-size: x-large;
+}
+
Added: ace/sandbox/marrs/org.apache.ace.client.rest/test.sh
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/test.sh?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/test.sh (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/test.sh Thu Jul 5 12:09:30 2012
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Test script that sends out REST commands
+#
+
+# Check out a new workspace
+echo "*** Creating new workspace..."
+WORK=`curl -s -d dummy_data -w %{redirect_url} http://localhost:8080/client/work`
+echo "Workspace is ${WORK}"
+
+echo "*** Adding artifact, feature, distribution, target and all associations..."
+
+RND=$RANDOM
+BSN=org.apache.bundle${RND}
+VERSION=1.0.0
+NAME=${BSN}-${VERSION}
+ART=`curl -v -d "{attributes: { artifactName: '${NAME}' , mimetype: 'application/vnd.osgi.bundle', Bundle-Name: '${BSN}', Bundle-SymbolicName: '${BSN}', Bundle-Version: '${VERSION}', url: 'http://localhost:8080/obr/${NAME}.jar', artifactDescription: 'coolio', processorPid: '' }, tags: { generated: 'true' }}" -w %{redirect_url} ${WORK}/artifact`
+ARTID=`echo ${ART##*/}`
+echo "Artifact is ${ART} => ${ARTID}"
+
+FEAT=`curl -v -d "{ attributes: { name: 'feature-${RANDOM}', description: 'a feature' }, tags: {}}" -w %{redirect_url} ${WORK}/feature`
+FEATID=`echo ${FEAT##*/}`
+echo "Feature is ${FEAT} => ${FEATID}"
+
+DIST=`curl -v -d "{ attributes: { name: 'distribution-${RANDOM}', description: 'a distribution' }, tags: {}}" -w %{redirect_url} ${WORK}/distribution`
+DISTID=`echo ${DIST##*/}`
+echo "Distribution is ${DIST} => ${DISTID}"
+
+TARGET=`curl -v -d "{ attributes: { id: 'target-${RANDOM}', autoapprove: 'true' }, tags: {}}" -w %{redirect_url} ${WORK}/target`
+TARGETID=`echo ${TARGET##*/}`
+echo "Target is ${TARGET} => ${TARGETID}"
+
+ASSOC1=`curl -v -d "{ attributes: { left: '${ARTID}', leftCardinality: '1', right: '${FEATID}', rightCardinality: '1' }, tags: {}}" -w %{redirect_url} ${WORK}/artifact2feature`
+echo "Association is ${ASSOC1}"
+
+ASSOC2=`curl -v -d "{ attributes: { left: '${FEATID}', leftCardinality: '1', right: '${DISTID}', rightCardinality: '1' }, tags: {}}" -w %{redirect_url} ${WORK}/feature2distribution`
+echo "Association is ${ASSOC2}"
+
+ASSOC3=`curl -v -d "{ attributes: { left: '${DISTID}', leftCardinality: '1', right: '${TARGETID}', rightCardinality: '1' }, tags: {}}" -w %{redirect_url} ${WORK}/distribution2target`
+echo "Association is ${ASSOC3}"
+
+# Get a list of artifacts
+#curl ${WORK}/artifact
+
+# Commit the workspace
+echo "*** Committing workspace..."
+curl -v -d dummy_data ${WORK}
+
+echo "*** Done."
Added: ace/sandbox/marrs/org.apache.ace.client.rest/test/org/apache/ace/client/rest/RESTClientTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/test/org/apache/ace/client/rest/RESTClientTest.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/test/org/apache/ace/client/rest/RESTClientTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/test/org/apache/ace/client/rest/RESTClientTest.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,53 @@
+/*
+ * 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.ace.client.rest;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class RESTClientTest {
+ @Test(groups = { UNIT })
+ public void testPathTransforms() {
+ String path = "one/two/last%20path";
+ RESTClientServlet s = new RESTClientServlet();
+ HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(request.getPathInfo()).thenReturn(path);
+ String[] elements = s.getPathElements(request);
+ Assert.assertEquals(elements[0], "one");
+ Assert.assertEquals(elements[1], "two");
+ Assert.assertEquals(elements[2], "last path");
+ String result = s.buildPathFromElements(elements);
+ Assert.assertEquals(result, path);
+ }
+
+ @Test(groups = { UNIT })
+ public void testPropertyGetter() {
+ RESTClientServlet s = new RESTClientServlet();
+ Assert.assertEquals(s.getProperty(new Properties() {{ put("key", "value"); }}, "key", "notused"), "value");
+ Assert.assertEquals(s.getProperty(new Properties() {{ put("unusedkey", "value"); }}, "key", "default"), "default");
+ Assert.assertEquals(s.getProperty(null, "key", "default"), "default");
+ }
+}
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.classpath
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.classpath?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.classpath (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.classpath Thu Jul 5 12:09:30 2012
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.project
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.project?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.project (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/.project Thu Jul 5 12:09:30 2012
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.ace.configurator.serveruseradmin</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>bndtools.core.bndbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>bndtools.core.bndnature</nature>
+ </natures>
+</projectDescription>
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/bnd.bnd?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/bnd.bnd (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/bnd.bnd Thu Jul 5 12:09:30 2012
@@ -0,0 +1,5 @@
+-buildpath: osgi.core,\
+ osgi.cmpn,\
+ org.apache.felix.dependencymanager
+Private-Package: org.apache.ace.configurator.serveruseradmin
+Bundle-Activator: org.apache.ace.configurator.serveruseradmin.Activator
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/build.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/build.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/build.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/build.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="project" default="build">
+ <import file="../cnf/build.xml"/>
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/pom.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/pom.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/pom.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/pom.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>ace-pom</artifactId>
+ <version>0.8.1-SNAPSHOT</version>
+ <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+
+ <version>0.8.1-SNAPSHOT</version>
+ <artifactId>org.apache.ace.configurator.serveruseradmin</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Apache ACE :: Configurator :: Server UserAdmin</name>
+ <description />
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin</developerConnection>
+ <url>http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin</url>
+ </scm>
+
+ <properties>
+ <import.package>
+ *
+ </import.package>
+ <private.package>
+ org.apache.ace.configurator.serveruseradmin
+ </private.package>
+ <bundle.activator>
+ org.apache.ace.configurator.serveruseradmin.Activator
+ </bundle.activator>
+ </properties>
+
+ <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.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/src/org/apache/ace/configurator/serveruseradmin/Activator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/src/org/apache/ace/configurator/serveruseradmin/Activator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/src/org/apache/ace/configurator/serveruseradmin/Activator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.serveruseradmin/src/org/apache/ace/configurator/serveruseradmin/Activator.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,92 @@
+/*
+ * 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.ace.configurator.serveruseradmin;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * This bundle configures a single server user, which is to be used until we
+ * have a full-fledged user administration system.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private final static String TEST_USER = "serverUser";
+ private final static String TEST_PASSWORD = "serverPassword";
+
+ private volatile UserAdmin m_userAdmin; /* Injected by dependency manager */
+ private volatile LogService m_log; /* Injected by dependency manager */
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setImplementation(this)
+ .add(createServiceDependency().setService(UserAdmin.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+
+ public synchronized void start() {
+ // create users
+ createUser(TEST_USER, TEST_PASSWORD);
+ }
+
+ @SuppressWarnings("unchecked")
+ private User createUser(String username, String password) {
+ User user = (User) m_userAdmin.createRole(username, Role.USER);
+ if (user != null) {
+ Dictionary properties = user.getProperties();
+ if (properties != null) {
+ properties.put("username", username);
+ }
+ else {
+ m_log.log(LogService.LOG_ERROR, "Could not get properties for " + username);
+ }
+
+ Dictionary credentials = user.getCredentials();
+ if (credentials != null) {
+ credentials.put("password", password);
+ }
+ else {
+ m_log.log(LogService.LOG_ERROR, "Could not get credentials for " + username);
+ }
+ }
+ else {
+ try {
+ user = (User) m_userAdmin.getRole(username);
+ m_log.log(LogService.LOG_WARNING, "User " + username + " already existed.");
+ }
+ catch (ClassCastException e) {
+ m_log.log(LogService.LOG_WARNING, "Role " + username + " already existed (it's no user).");
+ }
+ }
+ return user;
+ }
+}
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.classpath
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.classpath?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.classpath (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.classpath Thu Jul 5 12:09:30 2012
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.project
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.project?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.project (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/.project Thu Jul 5 12:09:30 2012
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.ace.configurator.useradmin.task</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>bndtools.core.bndbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>bndtools.core.bndnature</nature>
+ </natures>
+</projectDescription>
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/bnd.bnd?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/bnd.bnd (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/bnd.bnd Thu Jul 5 12:09:30 2012
@@ -0,0 +1,11 @@
+-buildpath: osgi.core,\
+ osgi.cmpn,\
+ org.apache.felix.dependencymanager,\
+ org.apache.ace.range.api;version=latest,\
+ org.apache.ace.repository.api;version=latest,\
+ org.apache.ace.repository.ext;version=latest,\
+ org.apache.ace.resourceprocessor.useradmin;version=latest
+Private-Package: org.apache.ace.configurator.useradmin.task,\
+ org.apache.ace.repository.ext,\
+ org.apache.ace.repository.ext.impl
+Bundle-Activator: org.apache.ace.configurator.useradmin.task.Activator
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/build.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/build.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/build.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/build.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="project" default="build">
+ <import file="../cnf/build.xml"/>
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/pom.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/pom.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/pom.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/pom.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>ace-pom</artifactId>
+ <version>0.8.1-SNAPSHOT</version>
+ <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+
+ <version>0.8.1-SNAPSHOT</version>
+ <artifactId>org.apache.ace.configurator.useradmin.task</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Apache ACE :: Configurator :: UserAdmin :: Task</name>
+ <description />
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task</developerConnection>
+ <url>http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task</url>
+ </scm>
+
+ <properties>
+ <import.package>
+ org.apache.ace.range;version=${project.version},
+ org.apache.ace.repository;version=${project.version},
+ org.apache.ace.resourceprocessor.useradmin;version=${project.version},
+ *
+ </import.package>
+ <private.package>
+ org.apache.ace.configurator.useradmin.task,
+ org.apache.ace.repository.ext,
+ org.apache.ace.repository.ext.impl
+ </private.package>
+ <bundle.activator>
+ org.apache.ace.configurator.useradmin.task.Activator
+ </bundle.activator>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.range.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.repository.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.repository.ext</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.resourceprocessor.useradmin</artifactId>
+ </dependency>
+ <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.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/Activator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/Activator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/Activator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/Activator.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,53 @@
+/*
+ * 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.ace.configurator.useradmin.task;
+
+import java.util.Properties;
+
+import org.apache.ace.resourceprocessor.useradmin.UserAdminConfigurator;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Activator for the UserAdmin updater task.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put("taskName", UpdateUserAdminTask.PID);
+ props.put("description", "Synchronizes the UserAdmin with the server.");
+ manager.add(createComponent()
+ .setInterface(Runnable.class.getName(), props)
+ .setImplementation(UpdateUserAdminTask.class)
+ .add(createServiceDependency().setService(UserAdminConfigurator.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ .add(createConfigurationDependency().setPid(UpdateUserAdminTask.PID))
+ );
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do, the runnable will be pulled automatically.
+ }
+
+}
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator.useradmin.task/src/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,221 @@
+/*
+ * 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.ace.configurator.useradmin.task;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.ext.BackupRepository;
+import org.apache.ace.repository.ext.CachedRepository;
+import org.apache.ace.repository.ext.impl.CachedRepositoryImpl;
+import org.apache.ace.repository.ext.impl.FilebasedBackupRepository;
+import org.apache.ace.resourceprocessor.useradmin.UserAdminConfigurator;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * UpdateUserAdminTask processes the contents of a repository containing
+ * an XML description of the users that should be present in this system's
+ * user admin.<br>
+ * <br>
+ * From the first run on, this task will keep a local copy of the user repository,
+ * so login can happen when the server is offline.
+ */
+public class UpdateUserAdminTask implements Runnable, ManagedService {
+ /**
+ * The UpdateUserAdminTask is also used as its taskName for the scheduler.
+ */
+ public static final String PID = UpdateUserAdminTask.class.getName();
+
+ public static final String KEY_REPOSITORY_LOCATION = "repositoryLocation";
+ public static final String KEY_REPOSITORY_CUSTOMER = "repositoryCustomer";
+ public static final String KEY_REPOSITORY_NAME = "repositoryName";
+
+ private static final String FILE_ROOT = "userrepositories";
+ private static final String VERSION = "version";
+
+ // Will by injected by Dependency Manager...
+ private volatile UserAdminConfigurator m_configurator;
+ private volatile LogService m_log;
+ private volatile BundleContext m_context;
+
+ private CachedRepository m_repo;
+ private BackupRepository m_backup;
+ private String m_repoFilter;
+ private File m_properties;
+
+ /**
+ * Called by Dependency Manager upon initialization of this component.
+ * <p>
+ * Due to the dependency on the configuration; the {@link #updated(Dictionary)} method is already called!
+ * </p>
+ *
+ * @param comp this component, cannot be <code>null</code>.
+ */
+ public void init(Component comp) {
+ final DependencyManager dm = comp.getDependencyManager();
+ // Add the required dependency to the remote repository...
+ comp.add(dm.createServiceDependency()
+ .setService(Repository.class, m_repoFilter)
+ .setCallbacks("addRepo", "removeRepo")
+ .setInstanceBound(true)
+ .setRequired(true)
+ );
+ }
+
+ /**
+ * Checks whether there are updates to the remote repository, and if so, updates the users' backend with its contents.
+ *
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ try {
+ if (!m_repo.isCurrent()) {
+ m_configurator.setUsers(m_repo.checkout(true));
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask updates to a new version: " + m_repo.getMostRecentVersion());
+ saveVersion(m_properties, m_repo.getMostRecentVersion());
+ }
+ }
+ catch (IOException e) {
+ // If anything went wrong, this means the remote repository is not available;
+ // this also means the UserAdmin is left undisturbed.
+ m_log.log(LogService.LOG_WARNING, "Error running update UserAdmin task.", e);
+ }
+ }
+
+ /**
+ * Called by Dependency Manager upon starting of this component.
+ *
+ * @param comp this component, cannot be <code>null</code>.
+ */
+ public void start(Component comp) {
+ try {
+ // Try to read the server data
+ m_configurator.setUsers(m_repo.checkout(true));
+ }
+ catch (IOException e) {
+ try {
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask failed to load remote data; falling back to local data.");
+ // If reading remote fails, try to set whatever we have locally
+ m_configurator.setUsers(m_repo.getLocal(true));
+ }
+ catch (IOException e2) {
+ // No problem, now we just have an empty user admin...
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask failed to load local data.");
+ }
+ }
+ }
+
+ public void updated(Dictionary dict) throws ConfigurationException {
+ if (dict != null) {
+ String customer = (String) dict.get(KEY_REPOSITORY_CUSTOMER);
+ if (customer == null) {
+ throw new ConfigurationException(KEY_REPOSITORY_CUSTOMER, "Property missing.");
+ }
+ String name = (String) dict.get(KEY_REPOSITORY_NAME);
+ if (name == null) {
+ throw new ConfigurationException(KEY_REPOSITORY_NAME, "Property missing.");
+ }
+
+ String fileRoot = FILE_ROOT + File.separator + customer + File.separator + name + File.separator;
+
+ File local = getFile(fileRoot + "local");
+ File backup = getFile(fileRoot + "backup");
+ m_backup = new FilebasedBackupRepository(local, backup);
+
+ m_properties = getFile(fileRoot + "properties");
+
+ m_repoFilter = "(&(customer=" + customer + ")(name=" + name + "))";
+ }
+ }
+
+ /**
+ * Creates the cached repository when given a remote repository.
+ *
+ * @param remoteRepo the remote repository to add, cannot be <code>null</code>.
+ */
+ final void addRepo(Repository remoteRepo) {
+ m_repo = new CachedRepositoryImpl(remoteRepo, m_backup, loadVersion(m_properties));
+ }
+
+ /**
+ * Removes the cached repository when given a remote repository.
+ *
+ * @param remoteRepo the remote repository to remove, cannot be <code>null</code>.
+ */
+ final void removeRepo(Repository remoteRepo) {
+ m_repo = null;
+ }
+
+ private File getFile(String name) {
+ File result = m_context.getDataFile(name);
+ if (!result.exists()) {
+ result.getParentFile().mkdirs();
+ try {
+ if (!result.createNewFile()) {
+ m_log.log(LogService.LOG_ERROR, "Error creating new file " + name);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Error creating new file " + name, e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Loads the most recent version from the given properties file.
+ */
+ private long loadVersion(File propertiesfile) {
+ long result = CachedRepositoryImpl.UNCOMMITTED_VERSION;
+ try {
+ Properties props = new Properties();
+ props.loadFromXML(new FileInputStream(propertiesfile));
+ result = Long.parseLong((String) props.get(VERSION));
+ }
+ catch (IOException ioe) {
+ // We have no data; no problem.
+ }
+ return result;
+ }
+
+ /**
+ * Saves the most recent version to the given properties file.
+ */
+ private void saveVersion(File properties, Long version) {
+ Properties props = new Properties();
+ props.put(VERSION, version.toString());
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(properties);
+ props.storeToXML(fileOutputStream, null);
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "UpdateUserAdminTask failed to save local version number.");
+ }
+ }
+}
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator/.classpath
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/.classpath?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/.classpath (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/.classpath Thu Jul 5 12:09:30 2012
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" output="bin_test" path="test"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
Added: ace/sandbox/marrs/org.apache.ace.configurator/.project
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/.project?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/.project (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/.project Thu Jul 5 12:09:30 2012
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.ace.configurator</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>bndtools.core.bndbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>bndtools.core.bndnature</nature>
+ </natures>
+</projectDescription>
Added: ace/sandbox/marrs/org.apache.ace.configurator/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/bnd.bnd?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/bnd.bnd (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/bnd.bnd Thu Jul 5 12:09:30 2012
@@ -0,0 +1,7 @@
+-buildpath: osgi.core,\
+ osgi.cmpn,\
+ org.apache.felix.dependencymanager,\
+ org.apache.ace.util;version=latest,\
+ ../cnf/lib/commons-io-2.0.1.jar;version=file
+Bundle-Activator: org.apache.ace.configurator.Activator
+Private-Package: org.apache.ace.configurator
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator/build.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/build.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/build.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/build.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="project" default="build">
+ <import file="../cnf/build.xml"/>
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator/pom.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/pom.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/pom.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/pom.xml Thu Jul 5 12:09:30 2012
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>ace-pom</artifactId>
+ <version>0.8.1-SNAPSHOT</version>
+ <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+
+ <version>0.8.1-SNAPSHOT</version>
+ <artifactId>org.apache.ace.configurator</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Apache ACE :: Configurator</name>
+ <description />
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator</developerConnection>
+ <url>http://svn.apache.org/repos/asf/ace/trunk/ace-configurator</url>
+ </scm>
+
+ <properties>
+ <import.package>
+ *
+ </import.package>
+ <private.package>
+ org.apache.ace.configurator
+ </private.package>
+ <bundle.activator>
+ org.apache.ace.configurator.Activator
+ </bundle.activator>
+ </properties>
+
+ <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.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
Added: ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Activator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Activator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Activator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Activator.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,60 @@
+/*
+ * 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.ace.configurator;
+
+import java.io.File;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setImplementation(new Configurator(new File(
+ getProperty(context.getProperty(Activator.class.getPackage().getName() + ".CONFIG_DIR"), "conf")),
+ getProperty(context.getProperty(Activator.class.getPackage().getName() + ".POLL_INTERVAL"), 2000),
+ getProperty(context.getProperty(Activator.class.getPackage().getName() + ".RECONFIG"), true)))
+ .add(createServiceDependency()
+ .setService(ConfigurationAdmin.class)
+ .setRequired(true))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+
+ public String getProperty(String prop, String def) {
+ return (prop == null) ? def : prop;
+ }
+
+ public long getProperty(String prop, long def) {
+ return (prop == null) ? def : Long.parseLong(prop);
+ }
+
+ public boolean getProperty(String prop, boolean def) {
+ return (prop == null) ? def : Boolean.getBoolean(prop);
+ }
+}
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,411 @@
+/*
+ * 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.ace.configurator;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
+
+/**
+ * Configures bundles managed by the <code>ConfigurationAdmin</code>. This Configurator uses text files as configuration
+ * files containing properties. When a configuration file is added, the properties are being read and added. If the config file is
+ * removed, the properties are removed as well.
+ * <p>
+ * The configuration files should be stored in the configuration directory (often the 'conf' directory) of the OSGi framework and
+ * should have the format: <pid>.cfg
+ * <p>
+ * Note: this Configurator is based upon the principle in the FileInstall bundle Peter Kriens wrote. (see
+ * http://www.aqute.biz/Code/FileInstall for more information)
+ */
+public class Configurator implements Runnable {
+
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+ private static final FileFilter FILENAME_FILTER = new FileFilter() {
+ public boolean accept(File file) {
+ return !file.isHidden() && (file.getName().endsWith(".cfg") || file.isDirectory());
+ }
+ };
+ private static final String FACTORY_INSTANCE_KEY = "factory.instance.pid";
+
+ private volatile LogService m_log; /* injected by dependency manager */
+ private volatile ConfigurationAdmin m_configAdmin; /* injected by dependency manager */
+ private volatile BundleContext m_context; /* injected by dependency manager */
+
+ private final File m_configDir;
+ private final long m_pollInterval;
+ private final Map m_checksums = new HashMap(); // absolutepath -> xor(length, date)
+ private final Map m_foundFactories = new HashMap(); // absolutedirpath -> (absolutepath -> xor(length, date))
+ private Thread m_configThread;
+ private final boolean m_reconfig;
+
+ /**
+ * Instantiates a new configurator.
+ * @param dir The directory to watch.
+ * @param pollInterval The poll iterval in ms.
+ * @param reconfig Whether or not to use reconfiguration: if <code>false</code>, existing configuration
+ * values will not be overwritten, only new values (for a given pid) will be added.
+ */
+ public Configurator(File dir, long pollInterval, boolean reconfig) {
+ if ((dir == null) || !dir.isDirectory() || (pollInterval < 0)) {
+ throw new IllegalArgumentException("Bad arguments; either not an existing directory or an invalid interval.");
+ }
+ m_configDir = dir;
+ m_pollInterval = pollInterval;
+ m_reconfig = reconfig;
+ }
+
+ /**
+ * Starts the Configuration timer.
+ */
+ synchronized void start() {
+ if (m_configThread == null) {
+ m_configThread = new Thread(this, "Apache ACE Configurator");
+ }
+ m_configThread.setDaemon(true);
+ m_configThread.start();
+ }
+
+ /**
+ * Stops the Configuration timer.
+ *
+ * @throws InterruptedException
+ */
+ synchronized void stop() throws InterruptedException {
+ // Join in stop to prevent race condition, careful with bundle location setting to null
+ m_configThread.interrupt();
+ m_configThread.join();
+ m_configThread = null;
+ m_checksums.clear();
+ }
+
+ /**
+ * Starts the actual Timer task, and calls the configurator to make sure the configurations are performed. Checking whether
+ * a new configuration is present, will be done with an interval that can be defined via a system property.
+ */
+ public void run() {
+ try {
+ while (!Thread.interrupted()) {
+ doConfigs();
+ Thread.sleep(m_pollInterval);
+ }
+ }
+ catch (InterruptedException ex) {
+ // We are requested to stop.
+ }
+ }
+
+ /**
+ * Enables the actual configuring of OSGi ManagedServices. It makes sure all new configurations are added, changed
+ * configurations are updated, and old configurations are removed. Configurations are updated when the timestamp or
+ * the size of the new configuration has changed.
+ */
+ private void doConfigs() {
+ Set pids = new HashSet(m_checksums.keySet());
+
+ File[] files = m_configDir.listFiles(FILENAME_FILTER);
+ for (int i = 0; (files != null) && (i < files.length); i++) {
+ File file = files[i];
+ String pid = parsePid(file);
+
+ if (file.isDirectory()) {
+ doFactoryConfigs(pid, file.listFiles(FILENAME_FILTER));
+ }
+ else {
+ Long newChecksum = new Long(file.lastModified() ^ file.length());
+ Long oldChecksum = (Long) m_checksums.get(pid); // may be null, intended
+ if (!newChecksum.equals(oldChecksum)) {
+ m_checksums.put(pid, newChecksum);
+ processConfigFile(file, null);
+ }
+ pids.remove(pid);
+ }
+ }
+ for (Iterator e = pids.iterator(); e.hasNext();) {
+ String pid = (String) e.next();
+ deleteConfig(pid, null);
+ m_checksums.remove(pid);
+ }
+ }
+
+ private void doFactoryConfigs(String factoryPid, File[] newInstances) {
+ if (!m_foundFactories.containsKey(factoryPid)) {
+ m_foundFactories.put(factoryPid, new HashMap());
+ }
+ Map instances = (Map) m_foundFactories.get(factoryPid);
+ Set instancesPids = new HashSet(instances.keySet());
+
+ for (int j = 0; j < newInstances.length; j++) {
+ File instanceConfigFile = newInstances[j];
+ String instancePid = parsePid(instanceConfigFile);
+
+ Long newChecksum = new Long(instanceConfigFile.lastModified() ^ instanceConfigFile.length());
+ Long oldChecksum = (Long) instances.get(instancePid);
+ if (!newChecksum.equals(oldChecksum)) {
+ instances.put(instancePid, newChecksum);
+ processConfigFile(instanceConfigFile, factoryPid);
+ }
+ instancesPids.remove(instancePid);
+ }
+
+ for (Iterator e = instancesPids.iterator(); e.hasNext(); ) {
+ String instancePid = (String) e.next();
+ deleteConfig(instancePid, factoryPid);
+ instances.remove(instancePid);
+ }
+ }
+
+ /**
+ * Sets the Configuration and calls update() to do the actual configuration on the ManagedService. If and only if the configuration
+ * did not exist before or has changed. A configuration has changed if the length or the lastModified date has changed.
+ */
+ private void processConfigFile(File configFile, String factoryPid) {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(configFile);
+ Properties properties = new Properties();
+ properties.load(in);
+ String pid = parsePid(configFile);
+ properties = substVars(properties);
+ configure(pid, factoryPid, properties);
+ }
+ catch (IOException ex) {
+ m_log.log(LogService.LOG_ERROR, "Unable to read configuration from file: " + configFile.getAbsolutePath(), ex);
+ }
+ finally {
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (Exception ex) {
+ // Not much we can do
+ }
+ }
+ }
+ }
+
+ private void configure(String pid, String factoryPid, Properties properties) {
+ try {
+ Configuration config = getConfiguration(pid, factoryPid);
+ Dictionary oldProps = config.getProperties();
+ if (!m_reconfig) {
+ if (oldProps != null) {
+ Enumeration keys = oldProps.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (properties.containsKey(key)) {
+ properties.put(key, oldProps.get(key));
+ m_log.log(LogService.LOG_DEBUG, "Using previously configured value for bundle=" + pid + " key=" + key);
+ }
+ }
+ }
+ }
+ if (factoryPid != null) {
+ properties.put(FACTORY_INSTANCE_KEY, factoryPid + "_" + pid);
+ }
+ config.update(properties);
+ m_log.log(LogService.LOG_DEBUG, "Updated configuration for pid '" + pid + "' (" + properties +")");
+ }
+ catch (IOException ex) {
+ m_log.log(LogService.LOG_ERROR, "Unable to update configuration for pid '" + pid + "'", ex);
+ }
+ }
+
+ private Configuration getConfiguration(String pid, String factoryPid) throws IOException {
+ if (factoryPid != null) {
+ Configuration[] configs = null;
+ try {
+ configs = m_configAdmin.listConfigurations("(" + FACTORY_INSTANCE_KEY + "=" + factoryPid + "_" + pid + ")");
+ }
+ catch (InvalidSyntaxException e) {
+ m_log.log(LogService.LOG_ERROR, "Exception during lookup of configuration of managed service factory instance '" + pid + "'", e);
+ }
+ if ((configs == null) || (configs.length == 0)) {
+ return m_configAdmin.createFactoryConfiguration(factoryPid, null);
+ }
+ else {
+ return configs[0];
+ }
+ }
+ else {
+ return m_configAdmin.getConfiguration(pid, null);
+ }
+ }
+
+ /**
+ * Removes a configuration from ConfigAdmin.
+ */
+ protected void deleteConfig(String pid, String factoryPid) {
+ try {
+ Configuration config = getConfiguration(pid, factoryPid);
+ config.delete();
+ m_log.log(LogService.LOG_DEBUG, "Removed configuration for pid '" + pid + "'");
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_ERROR, "Unable to remove configuration for pid '" + pid + "'", e);
+ }
+ }
+
+ /**
+ * Remove the config extension (.cfg) and return the resulting String.
+ */
+ protected String parsePid(File file) {
+ String name = file.getName();
+ if (file.isDirectory()) {
+ // factory pid
+ return name;
+ }
+ else {
+ return name.substring(0, name.length() - 4);
+ }
+ }
+
+
+ /**
+ * Performs variable substitution for a complete set of properties
+ *
+ * @see #substVars(String, String, java.util.Map, java.util.Properties)
+ * @param properties Set of properties to apply substitution on.
+ * @return Same set of properties with all variables substituted.
+ */
+ private Properties substVars(Properties properties) {
+ for (Enumeration propertyKeys = properties.propertyNames(); propertyKeys.hasMoreElements(); ) {
+ String name = (String) propertyKeys.nextElement();
+ String value = properties.getProperty(name);
+ properties.setProperty(name, substVars(value, name, null, properties));
+ }
+ return properties;
+ }
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt> refers to either a configuration property or a
+ * system property, then the corresponding property value is substituted for the variable placeholder. Multiple variable
+ * placeholders may exist in the specified value as well as nested variable placeholders, which are substituted from inner
+ * most to outer most. Configuration properties override system properties.
+ * </p>
+ *
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the property placeholder syntax or a recursive variable
+ * reference.
+ */
+ private String substVars(String val, String currentKey, Map cycleMap, Properties configProps) throws IllegalArgumentException {
+ // If there is currently no cycle map, then create
+ // one for detecting cycles for this invocation.
+ if (cycleMap == null) {
+ cycleMap = new HashMap();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0) {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim)) {
+ break;
+ }
+ else if (idx < stopDelim) {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) && (stopDelim < 0)) {
+ return val;
+ }
+ // At this point, we found a stop delimiter without a start,
+ // so throw an exception.
+ else if (((startDelim < 0) || (startDelim > stopDelim)) && (stopDelim >= 0)) {
+ throw new IllegalArgumentException("stop delimiter with no start delimiter: " + val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null) {
+ throw new IllegalArgumentException("recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (configProps != null) ? configProps.getProperty(variable, null) : null;
+ if (substValue == null) {
+ // Ignore unknown property values.
+ substValue = m_context.getProperty(variable);
+ if (substValue == null) {
+ substValue = "";
+ }
+ }
+
+ // Remove the found variable from the cycle map, since
+ // it may appear more than once in the value and we don't
+ // want such situations to appear as a recursive reference.
+ cycleMap.remove(variable);
+
+ // Append the leading characters, the substituted value of
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim) + substValue + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps);
+
+ // Return the value.
+ return val;
+ }
+}
\ No newline at end of file
Added: ace/sandbox/marrs/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java Thu Jul 5 12:09:30 2012
@@ -0,0 +1,372 @@
+/*
+ * 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.ace.configurator;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.ace.test.utils.FileUtils;
+import org.apache.ace.test.utils.TestUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ConfiguratorTest {
+
+ private Configurator m_configurator;
+ private File m_configDir;
+ private ConfigurationAdmin m_configAdmin;
+
+ @BeforeMethod(alwaysRun = true)
+ protected void setUp() throws Exception {
+ setUp(false);
+ }
+
+ /**
+ * Sets up the environment for testing.
+ * @param reconfig Indicates whether or not the configurator should use reconfiguration.
+ */
+ protected void setUp(boolean reconfig) throws Exception {
+ m_configAdmin = new MockConfigAdmin();
+
+ m_configDir = FileUtils.createTempFile(null);
+ m_configDir.mkdir();
+ m_configurator = new Configurator(m_configDir, 400, reconfig);
+
+ TestUtils.configureObject(m_configurator, ConfigurationAdmin.class, m_configAdmin);
+ TestUtils.configureObject(m_configurator, LogService.class);
+ TestUtils.configureObject(m_configurator, BundleContext.class, TestUtils.createMockObjectAdapter(BundleContext.class, new Object() {
+ @SuppressWarnings("unused")
+ public String getProperty(String key) {
+ return "contextProp";
+ }
+ }));
+ m_configurator.start();
+ }
+
+ /**
+ * save the properties into a configuration file the configurator can read.
+ * The file is first created and then moved to make sure the configuration doesn't read an empty file
+ */
+ private void saveConfiguration(String servicePid, Properties configuration) {
+ saveConfiguration(servicePid, null, configuration);
+ }
+
+ /**
+ * save the properties into a configuration file stored in a directory reflecting the factory pid
+ */
+ private void saveConfiguration(String servicePid, String factoryPid, Properties configuration) {
+ OutputStream fileOutputStream = null;
+ File outFile = null;
+ try {
+ outFile = FileUtils.createTempFile(null);
+ fileOutputStream = new FileOutputStream(outFile);
+ configuration.store(fileOutputStream, null);
+ } catch (IOException ioe) {
+ // the test will fail, ignore this.
+ } finally {
+ if (fileOutputStream != null) {
+ try {
+ fileOutputStream.close();
+ }
+ catch (IOException e) {
+ // nothing we can do
+ }
+ }
+ }
+ if (outFile != null) {
+ if (factoryPid == null) {
+ File dest = new File(m_configDir, servicePid + ".cfg");
+ if (dest.exists()) {
+ dest.delete();
+ }
+ renameFile(outFile, dest);
+ }
+ else {
+ File file = new File(m_configDir, factoryPid);
+ file.mkdirs();
+ File dest = new File(file, servicePid + ".cfg");
+ if (dest.exists()) {
+ dest.delete();
+ }
+ renameFile(outFile, dest);
+ }
+ }
+ }
+
+ // remove a created configuration file
+ private void removeConfiguration(String servicePid) {
+ removeConfiguration(servicePid, null);
+ }
+
+ private void removeConfiguration(String servicePid, String factoryPid) {
+ if (factoryPid != null) {
+ new File(m_configDir, factoryPid + File.separator + servicePid + ".cfg").delete();
+ } else {
+ new File(m_configDir, servicePid + ".cfg").delete();
+ }
+ }
+
+ // set some standard properties for testing
+ private Properties createProperties() {
+ Properties props = new Properties();
+ props.put("test", "value1");
+ props.put("test2", "value2");
+ return props;
+ }
+
+ // add a configuration
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testAddConfiguration() {
+ Properties initialConfiguration = createProperties();
+ saveConfiguration("test-add", initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(createProperties()) : "Configuration content is unexpected";
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testAddFactoryConfiguration() {
+ Properties props = createProperties();
+ saveConfiguration("test-add", "testFactory", props);
+
+ Dictionary configuration = getAndWaitForConfiguration(props);
+ assert configuration != null : "No configuration received from configurator";
+ assert "testFactory_test-add".equals(configuration.remove("factory.instance.pid")) : "Incorrect factory instance pid was added to the configuration";
+ assert configuration.equals(createProperties()) : "Configuration content is unexpected";
+ }
+
+ // remove a configuration
+ @Test(groups = { UNIT })
+ public void testRemoveFactoryConfiguration() {
+ Properties props = createProperties();
+ saveConfiguration("test-remove", "testFactory", props);
+ getAndWaitForConfiguration(props);
+
+ removeConfiguration("test-remove", "testFactory");
+
+ // after some processing time, we should get a message that the configuration is now removed.
+ long startTimeMillis = System.currentTimeMillis();
+ boolean isDeleted = false;
+ try {
+ while (!isDeleted && (System.currentTimeMillis() < startTimeMillis + 2000)) {
+ isDeleted = ((MockConfiguration) m_configAdmin.getConfiguration("")).isDeleted();
+ if (!isDeleted) {
+ Thread.sleep(100);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // not much we can do
+ }
+ catch (IOException e) {
+ // cannot come from our mock config admin
+ }
+ assert isDeleted : "The configuration is not removed as expected";
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testPropertySubstitution( ) {
+ Properties initialConfiguration = createProperties();
+ initialConfiguration.put("var", "value");
+ initialConfiguration.put("subst", "${var}");
+ saveConfiguration("test-subst", initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.get("subst").equals(configuration.get("var")) : "Substitution failed";
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testPropertySubstitutionFromContext() {
+ Properties initialConfiguration = createProperties();
+ initialConfiguration.put("subst", "${var}");
+ saveConfiguration("test-subst", initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.get("subst") != null : "Substitution failed";
+ }
+
+ // update a configuration, only adding a key (this is allowed in all cases)
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testChangeConfigurationUsingNewKey() {
+ Properties initialConfiguration = createProperties();
+ saveConfiguration("test-change", initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(initialConfiguration) : "Configuration content not expected. Was expecting " + initialConfiguration.size() + " but got " + configuration.size();
+
+ initialConfiguration.put("anotherKey","anotherValue");
+ saveConfiguration("test-change", initialConfiguration);
+
+ // now the configuration should be updated
+ configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(initialConfiguration) : "Configuration content not expected. Was expecting " + initialConfiguration.size() + " but got " + configuration.size();
+ }
+
+ // update a configuration, changing an already existing key, not using reconfiguration
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testChangeConfigurationUsingSameKeyNoReconfigure() {
+ Properties configurationValues = createProperties();
+ Properties initialConfigurationValues = new Properties();
+ initialConfigurationValues.putAll(configurationValues);
+ saveConfiguration("test-change", configurationValues);
+
+ Dictionary configuration = getAndWaitForConfiguration(configurationValues);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(configurationValues) : "Configuration content not expected. Was expecting " + configurationValues.size() + " but got " + configuration.size();
+
+ configurationValues.put("test","value42");
+ saveConfiguration("test-change", configurationValues);
+
+ // The update should have been ignored, and the old values should still be present.
+ configuration = getAndWaitForConfiguration(configurationValues);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(initialConfigurationValues) : "Configuration content not expected. Was expecting " + configurationValues.size() + " but got " + configuration.size();
+ }
+
+ // update a configuration, changing an already existing key, using reconfiguration
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testChangeConfigurationUsingSameKeyWithReconfigure() throws Exception {
+ setUp(true); // Instruct the configurator to reconfigure
+ Properties configurationValues = createProperties();
+ saveConfiguration("test-change", configurationValues);
+
+ Dictionary configuration = getAndWaitForConfiguration(configurationValues);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(configurationValues) : "Configuration content not expected. Was expecting " + configurationValues.size() + " but got " + configuration.size();
+
+ configurationValues.put("test","value42");
+ saveConfiguration("test-change", configurationValues);
+
+ // now the configuration should be updated
+ configuration = getAndWaitForConfiguration(configurationValues);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(configurationValues) : "Configuration content not expected. Was expecting " + configurationValues.size() + " but got " + configuration.size();
+ }
+
+ // remove a configuration
+ @SuppressWarnings("unchecked")
+ @Test(groups = { UNIT })
+ public void testRemoveConfiguration() {
+ Properties initialConfiguration = createProperties();
+ saveConfiguration("test-remove", initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfiguration(initialConfiguration);
+ assert configuration != null : "No configuration received from configurator";
+ assert configuration.equals(createProperties()) : "Configuration content is unexpected";
+
+ // ok, the configuration is done.
+ // now try to remove it.
+ removeConfiguration("test-remove");
+
+ // after some processing time, we should get a message that the configuration is now removed.
+ long startTimeMillis = System.currentTimeMillis();
+ boolean isDeleted = false;
+ try {
+ while (!isDeleted && (System.currentTimeMillis() < startTimeMillis + 2000)) {
+ isDeleted = ((MockConfiguration) m_configAdmin.getConfiguration("")).isDeleted();
+ if (!isDeleted) {
+ Thread.sleep(100);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // not much we can do
+ }
+ catch (IOException e) {
+ // cannot come from our mock config admin
+ }
+ assert isDeleted : "The configuration is not removed as expected";
+ }
+
+ /**
+ * Get the configuration and if it not available yet wait for it.
+ * If there is still no configuration after the wait time,
+ * null is returned.
+ */
+ @SuppressWarnings("unchecked")
+ public Dictionary getAndWaitForConfiguration(Dictionary expectedConfiguration) {
+ long startTimeMillis = System.currentTimeMillis();
+ // make sure we iterate at least once
+ Dictionary configuration = null;
+ try {
+ boolean success = false;
+ while (!success && (System.currentTimeMillis() < startTimeMillis + 2000)) {
+ configuration = m_configAdmin.getConfiguration("").getProperties();
+ if (configuration != null) {
+ synchronized(configuration) {
+ if (expectedConfiguration.equals(configuration)) {
+ success = true;
+ }
+ }
+ }
+ if (!success) {
+ Thread.sleep(100);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // not much we can do
+ }
+ catch (IOException e) {
+ // cannot come from our mock config admin
+ }
+ return configuration;
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() throws Exception {
+ m_configurator.stop();
+ FileUtils.removeDirectoryWithContent(m_configDir);
+ }
+
+ /**
+ * Renames a given source file to a new destination file, using Commons-IO.
+ * <p>This avoids the problem mentioned in ACE-155.</p>
+ *
+ * @param source the file to rename;
+ * @param dest the file to rename to.
+ */
+ private void renameFile(File source, File dest) {
+ try {
+ org.apache.commons.io.FileUtils.moveFile(source, dest);
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Failed to rename file!", e);
+ }
+ }
+}