Return-Path: X-Original-To: apmail-ace-commits-archive@www.apache.org Delivered-To: apmail-ace-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8F0B7DF2C for ; Thu, 5 Jul 2012 12:12:43 +0000 (UTC) Received: (qmail 3975 invoked by uid 500); 5 Jul 2012 12:12:43 -0000 Delivered-To: apmail-ace-commits-archive@ace.apache.org Received: (qmail 3910 invoked by uid 500); 5 Jul 2012 12:12:43 -0000 Mailing-List: contact commits-help@ace.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ace.apache.org Delivered-To: mailing list commits@ace.apache.org Received: (qmail 3866 invoked by uid 99); 5 Jul 2012 12:12:42 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jul 2012 12:12:42 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jul 2012 12:12:08 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id ACA152388B75 for ; Thu, 5 Jul 2012 12:10:36 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Thu, 05 Jul 2012 12:10:06 -0000 To: commits@ace.apache.org From: marrs@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120705121036.ACA152388B75@eris.apache.org> 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 @@ + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + 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 @@ + + + org.apache.ace.configurator.serveruseradmin + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + 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 @@ + + + + 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 @@ + + + + + + 4.0.0 + + + org.apache.ace + ace-pom + 0.8.1-SNAPSHOT + ../pom/pom.xml + + + 0.8.1-SNAPSHOT + org.apache.ace.configurator.serveruseradmin + bundle + + Apache ACE :: Configurator :: Server UserAdmin + + + + scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin + scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin + http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-serveruseradmin + + + + + * + + + org.apache.ace.configurator.serveruseradmin + + + org.apache.ace.configurator.serveruseradmin.Activator + + + + + + org.osgi + org.osgi.core + + + org.osgi + org.osgi.compendium + + + org.apache.felix + org.apache.felix.dependencymanager + + + + 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 @@ + + + + + + + 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 @@ + + + org.apache.ace.configurator.useradmin.task + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + 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 @@ + + + + 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 @@ + + + + + + 4.0.0 + + + org.apache.ace + ace-pom + 0.8.1-SNAPSHOT + ../pom/pom.xml + + + 0.8.1-SNAPSHOT + org.apache.ace.configurator.useradmin.task + bundle + + Apache ACE :: Configurator :: UserAdmin :: Task + + + + scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task + scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task + http://svn.apache.org/repos/asf/ace/trunk/ace-configurator-useradmin-task + + + + + org.apache.ace.range;version=${project.version}, + org.apache.ace.repository;version=${project.version}, + org.apache.ace.resourceprocessor.useradmin;version=${project.version}, + * + + + org.apache.ace.configurator.useradmin.task, + org.apache.ace.repository.ext, + org.apache.ace.repository.ext.impl + + + org.apache.ace.configurator.useradmin.task.Activator + + + + + + org.apache.ace + org.apache.ace.range.api + + + org.apache.ace + org.apache.ace.repository.api + + + org.apache.ace + org.apache.ace.repository.ext + + + org.apache.ace + org.apache.ace.resourceprocessor.useradmin + + + org.osgi + org.osgi.core + + + org.osgi + org.osgi.compendium + + + org.apache.felix + org.apache.felix.dependencymanager + + + + 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.
+ *
+ * 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. + *

+ * Due to the dependency on the configuration; the {@link #updated(Dictionary)} method is already called! + *

+ * + * @param comp this component, cannot be null. + */ + 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 null. + */ + 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 null. + */ + 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 null. + */ + 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 @@ + + + + + + + + + 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 @@ + + + org.apache.ace.configurator + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + 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 @@ + + + + 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 @@ + + + + + + 4.0.0 + + + org.apache.ace + ace-pom + 0.8.1-SNAPSHOT + ../pom/pom.xml + + + 0.8.1-SNAPSHOT + org.apache.ace.configurator + bundle + + Apache ACE :: Configurator + + + + scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-configurator + scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-configurator + http://svn.apache.org/repos/asf/ace/trunk/ace-configurator + + + + + * + + + org.apache.ace.configurator + + + org.apache.ace.configurator.Activator + + + + + + org.osgi + org.osgi.core + + + org.osgi + org.osgi.compendium + + + org.apache.felix + org.apache.felix.dependencymanager + + + org.apache.ace + org.apache.ace.util + + + commons-io + commons-io + test + + + + 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 ConfigurationAdmin. 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. + *

+ * 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 + *

+ * 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 false, 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; + } + + /** + *

+ * This method performs property variable substitution on the specified value. If the specified value contains the syntax + * ${<prop-name>}, where <prop-name> 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. + *

+ * + * @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. + *

This avoids the problem mentioned in ACE-155.

+ * + * @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); + } + } +}