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 2009/06/27 17:53:26 UTC

svn commit: r788992 [8/25] - in /incubator/ace/trunk: gateway/ gateway/src/ gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/ gateway/src/net/luminis/liq/bootstrap/ gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/ne...

Added: incubator/ace/trunk/liq/target-dev-server-upnp.xml
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/liq/target-dev-server-upnp.xml?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/liq/target-dev-server-upnp.xml (added)
+++ incubator/ace/trunk/liq/target-dev-server-upnp.xml Sat Jun 27 15:53:04 2009
@@ -0,0 +1,137 @@
+<project name="dev-server-upnp" default="warning">
+    <property name="deploy-dir" value="deploy/target/${ant.project.name}" />
+	<property name="app-name" value="L-iQ PS server with UPnP support" />
+
+    <target name="warning">
+        <echo message="Please run top level build." />
+    </target>
+    
+    <target name="deploy">
+        <mkdir dir="${deploy-dir}" />
+        <mkdir dir="${deploy-dir}/store" />
+        <copy todir="${deploy-dir}">
+            <fileset dir="lib/felix/1.4.1">
+                <include name="**" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/bundle">
+            <fileset dir="deploy/bundle">
+            	<include name="net.luminis.liq.consolelogger-1.0.0.jar" />
+            	<include name="net.luminis.liq.configurator-1.0.0.jar" />
+            	<include name="net.luminis.liq.configurator.useradmin.task-1.0.0.jar" />
+            	<include name="net.luminis.liq.resourceprocessor.useradmin-1.0.0.jar" />
+            	<include name="net.luminis.liq.http.listener-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.servlet-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.provider.repositorybased-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.streamgenerator-1.0.0.jar" />
+            	<include name="net.luminis.liq.discovery.upnp-1.0.0.jar" />
+            	<include name="net.luminis.liq.location.upnp-1.0.0.jar" />
+            	<include name="net.luminis.liq.server.log.store-1.0.0.jar" />
+            	<include name="net.luminis.liq.server.log-1.0.0.jar" />
+            	<include name="net.luminis.liq.scheduler-1.0.0.jar" />
+            	<include name="net.luminis.liq.repository-1.0.0.jar" />
+            	<include name="net.luminis.liq.repository.servlet-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.stateful-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.helper.base-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.helper.bundle-1.0.0.jar" />
+                <include name="net.luminis.liq.activationinfo.factory.server-1.0.0.jar" />
+            	<include name="net.luminis.liq.license.factory.server-1.0.0.jar" />
+                <include name="net.luminis.liq.server.activation-1.0.0.jar" />
+                <include name="net.luminis.liq.server.activation.store-1.0.0.jar" />
+                <include name="net.luminis.liq.server.activation.matcher-1.0.0.jar" />
+                <include name="net.luminis.liq.server.activation.servlet-1.0.0.jar" />
+            </fileset>
+            <fileset dir="ext">
+            	<include name="org.osgi.compendium.jar" />
+                <include name="osgi.mobile.jar" />
+                <include name="javax.servlet.jar" />
+                <include name="org.apache.felix.dependencymanager.jar" />
+            </fileset>
+            <fileset dir="lib">
+                <include name="net.luminis.liq.xstream-1.3.jar" />
+                <include name="org.apache.felix.configadmin.jar" />
+                <include name="org.apache.felix.eventadmin.jar" />
+                <include name="log_all-2.0.0.jar" />
+                <include name="http.jetty.jar" />
+            	<include name="useradmin_all-2.0.0.jar" />
+            	<include name="org.apache.felix.prefs-0.1.0-SNAPSHOT.jar" />
+                <include name="org.apache.felix.upnp.basedriver-0.8.0.jar" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/conf">
+            <fileset dir="conf/${ant.project.name}">
+                <include name="**/*.cfg" />
+            </fileset>
+        </copy>
+        <copy file="${deploy-dir}/conf/dev-config.properties" overwrite="true" tofile="${deploy-dir}/conf/config.properties" />
+        <delete file="${deploy-dir}/conf/test-config.properties" />
+        <property name="bundles-noshell" value="
+        	file:bundle/net.luminis.liq.consolelogger-1.0.0.jar
+        	file:bundle/log_all-2.0.0.jar
+            file:bundle/osgi.mobile.jar 
+            file:bundle/org.osgi.compendium.jar 
+            file:bundle/http.jetty.jar
+            file:bundle/javax.servlet.jar
+            file:bundle/useradmin_all-2.0.0.jar
+            file:bundle/net.luminis.liq.xstream-1.3.jar
+    	    file:bundle/org.apache.felix.eventadmin.jar
+            file:bundle/org.apache.felix.shell-1.0.1.jar 
+            file:bundle/org.apache.felix.shell.tui-1.0.1.jar 
+            file:bundle/org.apache.felix.configadmin.jar 
+            file:bundle/org.apache.felix.dependencymanager.jar 
+            file:bundle/org.apache.felix.prefs-0.1.0-SNAPSHOT.jar
+            file:bundle/org.apache.felix.upnp.basedriver-0.8.0.jar
+            file:bundle/net.luminis.liq.configurator-1.0.0.jar
+            file:bundle/net.luminis.liq.http.listener-1.0.0.jar
+            file:bundle/net.luminis.liq.location.upnp-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.servlet-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.provider.repositorybased-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.streamgenerator-1.0.0.jar
+            file:bundle/net.luminis.liq.discovery.upnp-1.0.0.jar
+            file:bundle/net.luminis.liq.server.log.store-1.0.0.jar
+            file:bundle/net.luminis.liq.server.log-1.0.0.jar
+            file:bundle/net.luminis.liq.scheduler-1.0.0.jar
+            file:bundle/net.luminis.liq.repository-1.0.0.jar
+            file:bundle/net.luminis.liq.repository.servlet-1.0.0.jar
+    	    file:bundle/net.luminis.liq.client.repository.stateful-1.0.0.jar
+    	    file:bundle/net.luminis.liq.client.repository-1.0.0.jar
+            file:bundle/net.luminis.liq.client.repository.helper.base-1.0.0.jar
+            file:bundle/net.luminis.liq.client.repository.helper.bundle-1.0.0.jar
+            file:bundle/net.luminis.liq.configurator.useradmin.task-1.0.0.jar
+        	file:bundle/net.luminis.liq.resourceprocessor.useradmin-1.0.0.jar
+        	file:bundle/net.luminis.liq.license.factory.server-1.0.0.jar
+        	file:bundle/net.luminis.liq.activationinfo.factory.server-1.0.0.jar
+            file:bundle/net.luminis.liq.server.activation-1.0.0.jar
+            file:bundle/net.luminis.liq.server.activation.store-1.0.0.jar
+            file:bundle/net.luminis.liq.server.activation.matcher-1.0.0.jar
+            file:bundle/net.luminis.liq.server.activation.servlet-1.0.0.jar" />
+        <property name="bundles-shell" value="${bundles-noshell}
+            file:bundle/org.apache.felix.shell-1.0.0.jar 
+            file:bundle/org.apache.felix.shell.tui-1.0.0.jar" />
+        <replace file="${deploy-dir}/conf/config.properties" token="@bundles@" value="${bundles-shell}" />
+        <move file="${deploy-dir}/conf/dev-config.properties" tofile="${deploy-dir}/conf/config-noshell.properties" />
+        <replace file="${deploy-dir}/conf/config-noshell.properties" token="@bundles@" value="${bundles-noshell}" />
+        <replace file="${deploy-dir}/conf/config.properties" token="@properties@" value="org.osgi.service.http.port=8080" />
+        <replace file="${deploy-dir}/conf/config-noshell.properties" token="@properties@" value="org.osgi.service.http.port=8080" />
+
+    	<property name="runCommand" value="java -Xms256m -Xmx256m -jar bin/felix.jar"/>
+    	<property name="runNoshellCommand" value="java -Xms256m -Xmx256m -Dfelix.config.properties=file:conf/config-noshell.properties -jar bin/felix.jar > output-felix.txt 2>&amp;1"/>
+    	<property name="debugCommand" value="java -Xms256m -Xmx256m -jar -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y -Dcom.sun.management.jmxremote.port=6666 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false bin/felix.jar"/>
+    	<echo file="${deploy-dir}/run.sh">#!/bin/sh
+${runCommand}
+        </echo>
+		<echo file="${deploy-dir}/run.bat">${runCommand}
+        </echo>
+    	<echo file="${deploy-dir}/run-noshell.sh">#!/bin/sh
+${runNoshellCommand}
+        </echo>
+		<echo file="${deploy-dir}/run-noshell.bat">${runNoshellCommand}
+        </echo>
+        <echo file="${deploy-dir}/debug.sh">#!/bin/sh
+${debugCommand}
+        </echo>
+        <echo file="${deploy-dir}/debug.bat">${debugCommand}
+        </echo>
+    </target>
+</project>

Added: incubator/ace/trunk/liq/target-dev-server.xml
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/liq/target-dev-server.xml?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/liq/target-dev-server.xml (added)
+++ incubator/ace/trunk/liq/target-dev-server.xml Sat Jun 27 15:53:04 2009
@@ -0,0 +1,172 @@
+<project name="dev-server" default="warning">
+    <property name="deploy-dir" value="deploy/target/${ant.project.name}" />
+	<property name="app-name" value="L-iQ PS server" />
+
+    <target name="warning">
+        <echo message="Please run top level build." />
+    </target>
+    
+    <target name="deploy">
+        <mkdir dir="${deploy-dir}" />
+        <mkdir dir="${deploy-dir}/store" />
+        <copy todir="${deploy-dir}">
+            <fileset dir="lib/felix/1.4.1">
+                <include name="**" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/bundle">
+            <fileset dir="deploy/bundle">
+            	<include name="net.luminis.liq.consolelogger-1.0.0.jar" />
+            	<include name="net.luminis.liq.configurator-1.0.0.jar" />
+            	<include name="net.luminis.liq.configurator.useradmin.task-1.0.0.jar" />
+            	<include name="net.luminis.liq.resourceprocessor.useradmin-1.0.0.jar" />
+            	<include name="net.luminis.liq.http.listener-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.servlet-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.provider.repositorybased-1.0.0.jar" />
+            	<include name="net.luminis.liq.deployment.streamgenerator-1.0.0.jar" />
+            	<include name="net.luminis.liq.discovery.property-1.0.0.jar" />
+            	<include name="net.luminis.liq.server.log.store-1.0.0.jar" />
+            	<include name="net.luminis.liq.server.log-1.0.0.jar" />
+            	<include name="net.luminis.liq.scheduler-1.0.0.jar" />
+            	<include name="net.luminis.liq.repository-1.0.0.jar" />
+            	<include name="net.luminis.liq.repository.servlet-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.stateful-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.helper.base-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.helper.bundle-1.0.0.jar" />
+                <include name="net.luminis.liq.activation.store-1.0.0.jar" />
+                <include name="net.luminis.liq.activationinfo.store-1.0.0.jar" />
+                <include name="net.luminis.liq.activation.matcher-1.0.0.jar" />
+                <include name="net.luminis.liq.activation.servlet-1.0.0.jar" />
+                <include name="net.luminis.liq.activation.service-1.0.0.jar" />
+                <include name="net.luminis.liq.client.repository.helper.license-1.0.0.jar" />
+                <!-- for testing -->
+                <!--
+                <include name="net.luminis.liq.http.echo.servlet-1.0.0.jar" />
+                -->
+            </fileset>
+            <fileset dir="ext">
+            	<include name="org.osgi.compendium.jar" />
+                <include name="osgi.mobile.jar" />
+                <include name="javax.servlet.jar" />
+                <include name="org.apache.felix.dependencymanager.jar" />
+                <include name="org.apache.felix.dependencymanager.shell.jar" />
+            </fileset>
+            <fileset dir="lib">
+                <include name="net.luminis.liq.xstream-1.3.jar" />
+                <include name="org.apache.felix.configadmin.jar" />
+                <include name="org.apache.felix.eventadmin.jar" />
+                <include name="log_all-2.0.0.jar" />
+                <include name="http.jetty.jar" />
+            	<include name="useradmin_all-2.0.0.jar" />
+            	<include name="org.apache.felix.prefs.jar" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/conf">
+            <fileset dir="conf/${ant.project.name}">
+                <include name="**/*.cfg" />
+            </fileset>
+        </copy>
+        <copy file="${deploy-dir}/conf/dev-config.properties" overwrite="true" tofile="${deploy-dir}/conf/config.properties" />
+        <delete file="${deploy-dir}/conf/test-config.properties" />
+        <property name="bundles-noshell" value="
+        	file:bundle/net.luminis.liq.consolelogger-1.0.0.jar
+        	file:bundle/log_all-2.0.0.jar
+            file:bundle/osgi.mobile.jar 
+            file:bundle/org.osgi.compendium.jar 
+            file:bundle/http.jetty.jar
+            file:bundle/javax.servlet.jar
+        	file:bundle/useradmin_all-2.0.0.jar
+            file:bundle/net.luminis.liq.xstream-1.3.jar
+    	    file:bundle/org.apache.felix.eventadmin.jar
+        	file:bundle/org.apache.felix.configadmin.jar 
+            file:bundle/org.apache.felix.dependencymanager.jar 
+            file:bundle/org.apache.felix.prefs.jar
+            file:bundle/net.luminis.liq.configurator-1.0.0.jar
+            file:bundle/net.luminis.liq.http.listener-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.servlet-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.provider.repositorybased-1.0.0.jar
+            file:bundle/net.luminis.liq.deployment.streamgenerator-1.0.0.jar
+        	file:bundle/net.luminis.liq.discovery.property-1.0.0.jar
+            file:bundle/net.luminis.liq.server.log.store-1.0.0.jar
+            file:bundle/net.luminis.liq.server.log-1.0.0.jar
+            file:bundle/net.luminis.liq.scheduler-1.0.0.jar
+            file:bundle/net.luminis.liq.repository-1.0.0.jar
+            file:bundle/net.luminis.liq.repository.servlet-1.0.0.jar
+    	    file:bundle/net.luminis.liq.client.repository.stateful-1.0.0.jar
+    	    file:bundle/net.luminis.liq.client.repository-1.0.0.jar
+            file:bundle/net.luminis.liq.client.repository.helper.base-1.0.0.jar
+            file:bundle/net.luminis.liq.client.repository.helper.bundle-1.0.0.jar
+            file:bundle/net.luminis.liq.configurator.useradmin.task-1.0.0.jar
+        	file:bundle/net.luminis.liq.resourceprocessor.useradmin-1.0.0.jar
+            file:bundle/net.luminis.liq.activation.store-1.0.0.jar
+            file:bundle/net.luminis.liq.activationinfo.store-1.0.0.jar
+            file:bundle/net.luminis.liq.activation.matcher-1.0.0.jar
+            file:bundle/net.luminis.liq.activation.servlet-1.0.0.jar
+            file:bundle/net.luminis.liq.activation.service-1.0.0.jar
+            file:bundle/net.luminis.liq.client.repository.helper.license-1.0.0.jar
+            " />
+        <property name="bundles-shell" value="${bundles-noshell}
+            file:bundle/org.apache.felix.shell-1.0.2.jar 
+            file:bundle/org.apache.felix.shell.tui-1.0.2.jar
+            file:bundle/org.apache.felix.dependencymanager.shell.jar 
+            " />
+        <replace file="${deploy-dir}/conf/config.properties" token="@bundles@" value="${bundles-shell}" />
+        <move file="${deploy-dir}/conf/dev-config.properties" tofile="${deploy-dir}/conf/config-noshell.properties" />
+        <replace file="${deploy-dir}/conf/config-noshell.properties" token="@bundles@" value="${bundles-noshell}" />
+        <replace file="${deploy-dir}/conf/config.properties" token="@properties@">
+            <replacevalue><![CDATA[
+org.apache.felix.http.nio=true
+org.apache.felix.http.enable=true
+org.osgi.service.http.port=8080
+org.apache.felix.https.enable=false
+org.osgi.service.http.port.secure=8443
+org.apache.felix.http.debug=false
+org.apache.felix.https.keystore=/tmp/node1Keystore
+org.apache.felix.https.keystore.password=secret
+org.apache.felix.https.keystore.key.password=secret
+org.apache.felix.https.truststore=/tmp/truststore
+org.apache.felix.https.truststore.password=secret
+org.apache.felix.https.clientcertificate=needs
+                ]]></replacevalue>
+        </replace>
+        <replace file="${deploy-dir}/conf/config-noshell.properties" token="@properties@">
+            <replacevalue><![CDATA[
+org.apache.felix.http.nio=true
+org.apache.felix.http.enable=true
+org.osgi.service.http.port=8080
+org.apache.felix.https.enable=false
+org.osgi.service.http.port.secure=8443
+org.apache.felix.http.debug=false
+org.apache.felix.https.keystore=/tmp/node1Keystore
+org.apache.felix.https.keystore.password=secret
+org.apache.felix.https.keystore.key.password=secret
+org.apache.felix.https.truststore=/tmp/truststore
+org.apache.felix.https.truststore.password=secret
+org.apache.felix.https.clientcertificate=needs
+            ]]></replacevalue>
+        </replace>
+
+        <property name="runCommand" value="java -Xms256m -Xmx256m -jar bin/felix.jar"/>
+    	<property name="runNoshellCommand" value="java -Xms256m -Xmx256m -Dfelix.config.properties=file:conf/config-noshell.properties -jar bin/felix.jar > output-felix.txt 2>&amp;1"/>
+    	<property name="debugCommand" value="java -Xms256m -Xmx256m -jar -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y -Dcom.sun.management.jmxremote.port=6666 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false bin/felix.jar"/>
+    	<echo file="${deploy-dir}/run.sh">#!/bin/sh
+${runCommand}
+        </echo>
+		<echo file="${deploy-dir}/run.bat">${runCommand}
+        </echo>
+    	<echo file="${deploy-dir}/run-noshell.sh">#!/bin/sh
+${runNoshellCommand}
+        </echo>
+		<echo file="${deploy-dir}/run-noshell.bat">${runNoshellCommand}
+        </echo>
+        <echo file="${deploy-dir}/debug.sh">#!/bin/sh
+${debugCommand}
+        </echo>
+        <echo file="${deploy-dir}/debug.bat">${debugCommand}
+        </echo>
+        <echo file="${deploy-dir}/run_secure.sh">#!/bin/sh
+${runSecureCommand}
+        </echo>
+    </target>
+</project>

Added: incubator/ace/trunk/liq/target-dev-tools.xml
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/liq/target-dev-tools.xml?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/liq/target-dev-tools.xml (added)
+++ incubator/ace/trunk/liq/target-dev-tools.xml Sat Jun 27 15:53:04 2009
@@ -0,0 +1,19 @@
+<project name="dev-tools" default="warning">
+    <property name="deploy-dir" value="deploy/target/${ant.project.name}" />
+
+    <target name="warning">
+        <echo message="Please run top level build." />
+    </target>
+    
+    <target name="deploy">
+        <mkdir dir="${deploy-dir}" />
+        <copy todir="${deploy-dir}">
+            <fileset dir="deploy/tools">
+                <include name="**" />
+            </fileset>
+            <fileset dir="../test/ext/commons">
+                <include name="*.jar" />
+            </fileset>
+        </copy>
+    </target>
+</project>
\ No newline at end of file

Added: incubator/ace/trunk/liq/target-test.xml
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/liq/target-test.xml?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/liq/target-test.xml (added)
+++ incubator/ace/trunk/liq/target-test.xml Sat Jun 27 15:53:04 2009
@@ -0,0 +1,53 @@
+<project name="test" default="warning">
+    <property name="deploy-dir" value="deploy/target/${ant.project.name}" />
+
+    <target name="warning">
+        <echo message="Please run top level build." />
+    </target>
+    
+    <target name="deploy">
+        <mkdir dir="${deploy-dir}" />
+        <copy todir="${deploy-dir}">
+            <fileset dir="lib/felix/1.4.1">
+                <include name="**" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/bundle">
+            <fileset dir="deploy/test">
+                <include name="*.jar" />
+            </fileset>
+            <fileset dir="deploy/bundle">
+                <include name="*.jar" />
+            </fileset>
+            <fileset dir="ext">
+                <include name="*.jar" />
+            </fileset>
+            <fileset dir="lib">
+                <include name="http.jetty.jar" />
+                <include name="log_all-2.0.0.jar" />
+                <include name="org.apache.felix.eventadmin.jar" />
+                <include name="org.apache.felix.configadmin.jar" />
+				<include name="org.apache.felix.prefs.jar" />
+            	<include name="useradmin_all-2.0.0.jar" />
+            	<include name="org.apache.felix.deploymentadmin-0.9.0-SNAPSHOT.jar" />
+            	<!-- Temporarily added until we can use xstream in some other way -->
+				<include name="net.luminis.liq.xstream-1.3.jar" />
+            </fileset>
+        </copy>
+        <copy todir="${deploy-dir}/conf">
+            <fileset dir="conf/${ant.project.name}">
+                <include name="**/*.cfg" />
+            </fileset>
+        </copy>
+        <move file="${deploy-dir}/conf/test-config.properties" tofile="${deploy-dir}/conf/config.properties" />
+
+        <!-- instrument the classes for code coverage -->
+        <mkdir dir="${deploy-dir}/instrumented" />
+        <echo file="${deploy-dir}/run.sh">#!/bin/sh
+java -jar bin/felix.jar
+        </echo>
+        <echo file="${deploy-dir}/debug.sh">#!/bin/sh
+java -jar -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y -Dcom.sun.management.jmxremote.port=6666 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false bin/felix.jar
+        </echo>
+    </target>
+</project>
\ No newline at end of file

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/automation/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/automation/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/automation/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/automation/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,41 @@
+package net.luminis.liq.client.automation;
+
+
+import net.luminis.liq.client.repository.RepositoryAdmin;
+import net.luminis.liq.client.repository.repository.DeploymentVersionRepository;
+import net.luminis.liq.client.repository.repository.GatewayRepository;
+import net.luminis.liq.client.repository.stateful.StatefulGatewayRepository;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.UserAdmin;
+
+
+/**
+ * Bundle activator for the gateway operator automation.
+ */
+public class Activator extends DependencyActivatorBase {
+
+    /**
+     * Initialize and set dependencies
+     */
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        manager.add(createService()
+            .setImplementation(AutoGatewayOperator.class)
+            .add(createConfigurationDependency().setPid(AutoGatewayOperator.PID))
+            .add(createServiceDependency().setRequired(true).setService(UserAdmin.class))
+            .add(createServiceDependency().setRequired(true).setService(GatewayRepository.class))
+            .add(createServiceDependency().setRequired(true).setService(StatefulGatewayRepository.class))
+            .add(createServiceDependency().setRequired(true).setService(DeploymentVersionRepository.class))
+            .add(createServiceDependency().setRequired(true).setService(RepositoryAdmin.class))
+            .add(createServiceDependency().setRequired(false).setService(LogService.class))
+        );
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/automation/AutoGatewayOperator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/automation/AutoGatewayOperator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/automation/AutoGatewayOperator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/automation/AutoGatewayOperator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,242 @@
+package net.luminis.liq.client.automation;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import net.luminis.liq.client.repository.RepositoryAdmin;
+import net.luminis.liq.client.repository.RepositoryAdminLoginContext;
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.stateful.StatefulGatewayObject;
+import net.luminis.liq.client.repository.stateful.StatefulGatewayRepository;
+import net.luminis.liq.scheduler.constants.SchedulerConstants;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Automatic gateway operator, when configured will automatically register, approve, auto-approve
+ * and commit gateways to the repository. An LDAP filter can be used to filter for the correct gateways
+ *
+ */
+public class AutoGatewayOperator implements ManagedService {
+
+    public static final String PID = "net.luminis.liq.client.automation";
+    public static final String SCHEDULER_NAME = "net.luminis.liq.client.processauditlog";
+
+    private volatile StatefulGatewayRepository m_statefulGatewayRepos;
+    private volatile RepositoryAdmin m_reposAdmin;
+    private volatile UserAdmin m_userAdmin;
+    private volatile BundleContext m_bundleContext;
+    private volatile LogService m_log;
+    @SuppressWarnings("unchecked")
+    private volatile Dictionary m_settings;
+
+    private static String username = "serverUser";
+
+    // used for processing the auditlog (tell the repository about that)
+    private final AuditLogProcessTask m_task = new AuditLogProcessTask();
+    private Object m_serviceReg = null;
+
+    public void start() {
+        // get user
+        User user = m_userAdmin.getUser("username",username);
+
+        // login at Repository admin
+        try {
+            URL url =  new URL(getConfigValue(ConfigItem.HOSTNAME) + getConfigValue(ConfigItem.ENDPOINT));
+            String customerName = getConfigValue(ConfigItem.CUSTOMER_NAME);
+
+            RepositoryAdminLoginContext loginContext = m_reposAdmin.createLoginContext(user);
+            loginContext.addShopRepository(url, customerName, getConfigValue(ConfigItem.STORE_REPOSITORY), false)
+            .addGatewayRepository(url, customerName, getConfigValue(ConfigItem.GATEWAY_REPOSITORY), true)
+            .addDeploymentRepository(url, customerName, getConfigValue(ConfigItem.DEPLOYMENT_REPOSITORY), true);
+            m_reposAdmin.login(loginContext);
+
+            // start refresh task
+            Properties props = new Properties();
+            props.put(SchedulerConstants.SCHEDULER_NAME_KEY, SCHEDULER_NAME);
+            m_serviceReg = m_bundleContext.registerService(Runnable.class.getName(), m_task, props);
+        }
+        catch (IOException ioe) {
+            m_log.log(LogService.LOG_ERROR, "Unable to login at repository admin.", ioe);
+        }
+    }
+
+    public void stop() {
+        // service present, pull it
+        if (m_serviceReg != null) {
+            ((ServiceRegistration) m_serviceReg).unregister();
+        }
+
+        m_serviceReg = null;
+
+        //logout
+        try {
+            m_reposAdmin.logout(true);
+        }
+        catch (IOException ioe) {
+            // not a lot we can
+            System.err.println(ioe.getMessage());
+        }
+    }
+
+    /**
+     * Runnable that will synchronize audit log data with the server and tell the repository about the changes if applicable.
+     */
+    private final class AuditLogProcessTask implements Runnable {
+
+        private final Object m_lock = new Object();
+
+        public void process() {
+            // perform synchronous model actions
+            synchronized(m_lock) {
+                m_statefulGatewayRepos.refresh();
+                boolean changed = false;
+                try {
+                    checkoutModel();
+                    changed |=registerGateways();
+                    changed |=approveGateways();
+                    changed |=setAutoApprove();
+                }
+                catch (IOException ioe) {
+                    m_log.log(LogService.LOG_WARNING, "Checkout of model failed.", ioe);
+                }
+                catch (InvalidSyntaxException ise) {
+                    m_log.log(LogService.LOG_WARNING, "Illegal register gateway filter.", ise);
+                }
+
+                // Commit any changes
+                try {
+                    if (changed) {
+                        m_reposAdmin.commit();
+                    }
+                }
+                catch (IOException ioe) {
+                    m_log.log(LogService.LOG_WARNING, "Commit of model failed", ioe);
+                }
+            }
+        }
+
+        public void run() {
+                process();
+        }
+    }
+
+    private void checkoutModel() throws IOException {
+        // Do a checkout
+        if (!m_reposAdmin.isCurrent()) {
+               m_reposAdmin.checkout();
+        }
+    }
+
+    private boolean registerGateways() throws InvalidSyntaxException {
+        boolean changed = false;
+        String filter = "(&" + getConfigValue(ConfigItem.REGISTER_GW_FILTER) +
+        "(" + StatefulGatewayObject.KEY_REGISTRATION_STATE + "=" + StatefulGatewayObject.RegistrationState.Unregistered + "))";
+        List<StatefulGatewayObject> sgos =  m_statefulGatewayRepos.get(m_bundleContext.createFilter(filter));
+        for (StatefulGatewayObject sgo : sgos) {
+            sgo.register();
+            changed = true;
+        }
+        return changed;
+    }
+
+    private boolean setAutoApprove() throws InvalidSyntaxException {
+        boolean changed = false;
+        String filter = "(&" + getConfigValue(ConfigItem.AUTO_APPROVE_GW_FILTER) +
+        "(" + StatefulGatewayObject.KEY_REGISTRATION_STATE + "=" + StatefulGatewayObject.RegistrationState.Registered + ")" +
+        "(!(" + GatewayObject.KEY_AUTO_APPROVE + "=true)))";
+
+        List<StatefulGatewayObject> sgos =  m_statefulGatewayRepos.get(m_bundleContext.createFilter(filter));
+        for (StatefulGatewayObject sgo : sgos) {
+                sgo.setAutoApprove(true);
+                changed = true;
+            }
+        return changed;
+    }
+
+    private boolean approveGateways() throws InvalidSyntaxException {
+        boolean changed = false;
+        String filter = "(&" + getConfigValue(ConfigItem.APPROVE_GW_FILTER) +
+        "(" + StatefulGatewayObject.KEY_STORE_STATE + "=" + StatefulGatewayObject.StoreState.Unapproved + "))";
+
+        List<StatefulGatewayObject> sgos =  m_statefulGatewayRepos.get(m_bundleContext.createFilter(filter));
+            for (StatefulGatewayObject sgo : sgos) {
+                sgo.approve();
+                changed = true;
+            }
+        return changed;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updated(Dictionary settings) throws ConfigurationException {
+        if (settings != null) {
+            for (ConfigItem item : ConfigItem.values()) {
+                String value = (String) settings.get(item.toString());
+                if ((value == null) || value.equals("")) {
+                    throw new ConfigurationException(item.toString(), item.getErrorText());
+                }
+            }
+            // store configuration
+            m_settings = settings;
+        }
+    }
+
+    /**
+     * @param item The configuration item (enum)
+     * @return The value stored in the configuration dictionary.
+     */
+    private String getConfigValue(ConfigItem item) {
+        return (String) m_settings.get(item.toString());
+    }
+
+    /**
+     *  Helper class used for gateway automation client configuration.
+     *  ENUM (itemname, errormessage, filter true/false)
+     *
+     */
+    private enum ConfigItem {
+        REGISTER_GW_FILTER ("registerGatewayFilter","Register gateway filter missing", true),
+        APPROVE_GW_FILTER ("approveGatewayFilter","Approve gateway filter missing", true),
+        AUTO_APPROVE_GW_FILTER ("autoApproveGatewayFilter", "Auto approve config value missing", true),
+        COMMIT_REPO ("commitRepositories","Commit value missing.", false),
+        GATEWAY_REPOSITORY ("gatewayRepository","GatewayRepository id missing.", false),
+        DEPLOYMENT_REPOSITORY ("deploymentRepository","DeploymentRepository id missing.", false),
+        STORE_REPOSITORY ("storeRepository","Store Repository id missing.", false),
+        CUSTOMER_NAME ("customerName", "Customer name missing", false),
+        HOSTNAME ("hostName", "Hostname missing.", false),
+        ENDPOINT ("endpoint", "Endpoint missing in config.", false);
+
+        private final String m_name;
+        private final String m_errorText;
+        private final boolean m_isFilter;
+
+        private ConfigItem(String name, String errorText, boolean isFilter) {
+            m_name = name;
+            m_errorText = errorText;
+            m_isFilter = isFilter;
+        }
+
+        @Override
+        public String toString() {
+            return m_name;
+        }
+
+        public String getErrorText() {
+            return m_errorText;
+        }
+
+        public boolean isFilter() {
+            return m_isFilter;
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Associatable.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Associatable.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Associatable.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Associatable.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,41 @@
+package net.luminis.liq.client.repository;
+
+import java.util.List;
+
+/**
+ * The Associatable interface is to be implemented by any object that wants to be
+ * used in an Association.
+ */
+public interface Associatable {
+    /**
+     * Adds the association to this object, which has the given class. The 'other side' of
+     * the association will now show up when filtering for <code>clazz</code>, which is the
+     * class of the other end of the association.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable> void add(Association association, Class<T> clazz);
+    /**
+     * Removes the association from this object, with the given class. The 'other side' of
+     * the association will no longer show up when filtering for <code>clazz</code>.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable> void remove(Association association, Class<T> clazz);
+    /**
+     * Gets all Associatable objects of the <code>clazz</code> with which this object
+     * is associated. If <code>clazz</code> is not in use, this function will return
+     * an empty list.
+     */
+    public <T extends Associatable> List<T> getAssociations(Class<T> clazz);
+    /**
+     * Checks whether this object is related with <code>obj</code>, which is to be of class
+     * <code>clazz</code>. Will also return <code>false</code> when the class does not match.
+     */
+    public <T extends Associatable> boolean isAssociated(Object obj, Class<T> clazz);
+    /**
+     * Returns the associations that exist between this object and the other, of the
+     * given <code>clazz</code>, in a typed list of associations <code>associationType</code>.
+     * Will return an empty list if no associations exist.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable, A extends Association> List<A> getAssociationsWith(Associatable other, Class<T> clazz, Class<A> associationType);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Association.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Association.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Association.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/Association.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,61 @@
+package net.luminis.liq.client.repository;
+
+import java.util.List;
+
+/**
+ * Represents a basic association between two Associatable objects, whose types
+ * are given by the generic parameters.
+ */
+public interface Association<L extends Associatable, R extends Associatable> extends RepositoryObject {
+    /**
+     * A filter string indicating the left endpoint.
+     */
+    public final static String LEFT_ENDPOINT = "leftEndpoint";
+    /**
+     * A filter string indicating the right endpoint.
+     */
+    public final static String RIGHT_ENDPOINT = "rightEndpoint";
+    /**
+     * A string indicating the cardinality for the left endpoint.
+     */
+    public final static String LEFT_CARDINALITY = "leftCardinality";
+    /**
+     * A string indicating the cardinality for the right endpoint.
+     */
+    public final static String RIGHT_CARDINALITY = "rightCardinality";
+
+    /**
+     * Used for <code>Event</code> properties: A <code>List&lt;Associatable&gt;</code> indicating the
+     * objects making up this endpoint before the event happened.
+     */
+    public final static String EVENT_OLD = "old";
+    /**
+     * Used for <code>Event</code> properties: A <code>List&lt;Associatable&gt;</code> indicating the
+     * objects making up this endpoint before the event happened.
+     */
+    public final static String EVENT_NEW = "new";
+
+    /**
+     * Gets the Associatable object on the 'other side' of <code>from</code>. If
+     * <code>from</code> does not point to either end of this association, this
+     * function will return <code>null</code>.
+     */
+    public List<Associatable> getTo(Associatable from);
+    /**
+     * Returns the 'left' side of this association.
+     */
+    public List<L> getLeft();
+    /**
+     * Returns the 'left' side of this association.
+     */
+    public List<R> getRight();
+    /**
+     * Removes this association from both endpoints.
+     */
+    public void remove();
+    /**
+     * Indicates whether both sides of this association are available. In an m-to-n assocation,
+     * this function will indicate whether at least one object is available on either side.
+     */
+    public boolean isSatisfied();
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/AssociationRepository.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/AssociationRepository.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/AssociationRepository.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/AssociationRepository.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,37 @@
+package net.luminis.liq.client.repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a basic repository of associations. The associations are to be of type
+ * <code>T</code>, associating types <code>L</code> and <code>R</code>.
+ */
+public interface AssociationRepository<L extends Associatable, R extends Associatable, T extends Association<L, R>> extends ObjectRepository<T>{
+    /**
+     * Creates a static association between two filters for objects, stores it,
+     * and returns the association object. This association will link all objects
+     * that apply to the filters in an m-to-n fashion.
+     */
+    public T create(String left, String right);
+    /**
+     * Creates a static association between two objects, stores it,
+     * and returns the association object.
+     */
+    public T create(L left, R right);
+    /**
+     * Creates an association between the given objects, with the <code>Props</code> containing
+     * additional directives for the endpoints, stores it, and returns the association.
+     */
+    public T create(L left, Map<String, String> leftProps, R right, Map<String, String> rightProps);
+    /**
+     * Creates a static association between two lists of objects, stores it,
+     * and returns the association object.
+     */
+    public T create(List<L> left, List<R> right);
+    /**
+     * Removes the given association, and deletes the association from the left- and right
+     * side of the association.
+     */
+    public void remove(T entity);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/ObjectRepository.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/ObjectRepository.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/ObjectRepository.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/ObjectRepository.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,34 @@
+package net.luminis.liq.client.repository;
+
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Filter;
+
+/**
+ * A basic object repository, storing implementers of RepositoryObject.
+ */
+public interface ObjectRepository<T extends RepositoryObject> {
+    /**
+     * Removes the given entity from this repository. Will silently fail
+     * when the entity does not exist in this repository.
+     */
+    public void remove(T entity);
+    /**
+     * Gets a list of all entities in this repository.
+     */
+    public List<T> get();
+    /**
+     * Returns a list of all entities in this repository that satisfy
+     * the conditions set in <code>filter</code>. If none match, an
+     * empty list will be returned.
+     */
+    public List<T> get(Filter filter);
+
+    /**
+     * Creates a new inhabitant based on the given attributes. The object
+     * will be stored in this repository's store, and will be returned.
+     * @throws IllegalArgumentException Will be thrown when the attributes cannot be accepted.
+     */
+    public T create(Map<String, String> attributes, Map<String, String> tags) throws IllegalArgumentException;
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdmin.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdmin.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdmin.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdmin.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,143 @@
+package net.luminis.liq.client.repository;
+
+import java.io.IOException;
+import java.net.URL;
+
+import net.luminis.liq.client.repository.RepositoryObject.WorkingState;
+
+import org.osgi.service.useradmin.User;
+
+public interface RepositoryAdmin {
+
+    public static final String PUBLIC_TOPIC_ROOT = RepositoryAdmin.class.getPackage().getName().replace('.', '/') + "/public/";
+    public static final String PRIVATE_TOPIC_ROOT = RepositoryAdmin.class.getPackage().getName().replace('.', '/') + "/private/";
+
+    public static final String TOPIC_ENTITY_ROOT = RepositoryAdmin.class.getSimpleName() + "/";
+
+    public static final String TOPIC_REFRESH_SUFFIX = "REFRESH";
+    public static final String TOPIC_LOGIN_SUFFIX = "LOGIN";
+    public static final String TOPIC_LOGOUT_SUFFIX = "LOGOUT";
+    public static final String TOPIC_STATUSCHANGED_SUFFIX = "STATUSCHANGED";
+    public static final String TOPIC_FLUSHED_SUFFIX = "FLUSHED";
+    public static final String TOPIC_ALL_SUFFIX = "*";
+    
+    /**
+     * Indicates a serious change to the structure of the repository, which is too complicated to use
+     * the Object's own Changed topic.
+     */
+    public static final String TOPIC_REFRESH = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REFRESH_SUFFIX;
+    /**
+     * Indicates a successful login; the model will now be filled, as signaled by the earlier TOPIC_REFRESH.
+     */
+    public static final String TOPIC_LOGIN = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_LOGIN_SUFFIX;
+    /**
+     * Indicates a successful logout; the model will now be empty, as signaled by the earlier TOPIC_REFRESH.
+     */
+    public static final String TOPIC_LOGOUT = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_LOGOUT_SUFFIX;
+    /**
+     * Signals that isCurrent or isModified have (potentially) changed.
+     */
+    public static final String TOPIC_STATUSCHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_STATUSCHANGED_SUFFIX;
+    /**
+     * Signals that a flush() has been done.
+     */
+    public static final String TOPIC_FLUSHED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_FLUSHED_SUFFIX;
+
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    /**
+     * Checks out the most recent version of the repositories from <code>login</code>.
+     * @throws IOException Thrown when we are not logged in, or when there is a problem
+     * communicating with either the local repository or the remote one.
+     */
+    public void checkout() throws IOException;
+
+    /**
+     * Commits the most what we have in memory to the repositories from <code>login</code>.
+     * @throws IOException Thrown when we are not logged in, or when there is a problem
+     * communicating with either the local repository or the remote one.
+     */
+    public void commit() throws IOException;
+
+    /**
+     * Reverts what we have in memory to the most recent one that was checked our or
+     * committed.
+     * @throws IOException Thrown when we are not logged in, or when there is a problem
+     * communicating with either the local repository or the remote one.
+     */
+    public void revert() throws IOException;
+
+    /**
+     * Indicates that the version on which the changes are made is the most recent on the server.
+     * This indication only applies to repositories for which write access has been received.
+     * @throws IOException Thrown when there is a problem communicating with the backup repository,
+     * which keeps track of the local copies of the repository.
+     */
+    public boolean isCurrent() throws IOException;
+
+    /**
+     * Indicates whether the data we have (in memory and persisted) has changed in respect to
+     * what is on the server.
+     * @throws IOException Thrown when there is a problem communicating with the backup repository,
+     * which keeps track of the local copies of the repository.
+     */
+    public boolean isModified() throws IOException;
+
+    /**
+     * Writes what we have in memory to a backup repository, so it can be persisted between runs
+     * of the client, without committing it to the remote repository.
+     * @throws IOException Thrown when there is a problem communicating with the backup repository,
+     * which keeps track of the local copies of the repository.
+     */
+    public void flush() throws IOException;
+
+    /**
+     * Writes what we have in memory to a backup repository, and prepares the repository for use by
+     * a new user.
+     * @param force Indicates that, even when an exception is thrown, we still want to log the
+     * user out.
+     * @throws IOException Thrown when there is a problem communicating with the backup repository,
+     * which keeps track of the local copies of the repository. If this exception is thrown,
+     * the user is still logged in, unless <code>force = true</code>.
+     * @throws IllegalStateException Thrown if no user is logged in.
+     */
+    public void logout(boolean force) throws IOException, IllegalStateException;
+
+    /**
+     * Creates a new login context.
+     * @param user The user to use for this context.
+     * @return A new RepositoryAdminLoginContext,
+     */
+    public RepositoryAdminLoginContext createLoginContext(User user);
+    
+    /**
+     * Logs in using the given RepositoryAdminLoginContext; use createLoginContext for an initial
+     * RepositoryAdminLoginContext.
+     * @param context The context to use for this login.
+     * @throws IOException Thrown when there is a problem communicating with the backup repository,
+     * which keeps track of the local copies of the repository.
+     * @throws IllegalArgumentException If <code>context</code> was not one that was 
+     * created by <code>createLoginContext</code<.
+     * @throws IllegalStateException If there already is a user logged in.
+     */
+    public void login(RepositoryAdminLoginContext context) throws IOException;
+
+    /**
+     * Return the working state of the given object. If the object is not part of any
+     * repository managed by this admin, <code>New</code> will be returned.
+     * @param object A repository object.
+     * @return The current working state of this object.
+     */
+    public WorkingState getWorkingState(RepositoryObject object);
+
+    /**
+     * Gets the number of objects of a given class with a given state. Note that
+     * this class applies to all objects of that class, and all its descendents.
+     * @param clazz The class of objects to be counted.
+     * @param state A working state.
+     * @return The number of objects which are (or descend from) the given class,
+     * and have the given working state.
+     */
+    public int getNumberWithWorkingState(Class<? extends RepositoryObject> clazz, WorkingState state);
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdminLoginContext.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdminLoginContext.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdminLoginContext.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryAdminLoginContext.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,63 @@
+package net.luminis.liq.client.repository;
+
+import java.net.URL;
+
+/**
+ * RepositoryAdminLoginContext represents a context for logins to the repository admin. It is used to specify
+ * which repositories are to be used in a given session with the RepositoryAdmin.
+ */
+public interface RepositoryAdminLoginContext {
+    
+    /**
+     * Adds a set of repositories and their location to this login context.
+     * @param repositoryLocation The location of the repository where this set's data resides.
+     * @param repositoryCustomer The customer name for the location of the repository where this set's data resides.
+     * @param repositoryName The repository name for the location of the repository where this set's data resides.
+     * @param writeAccess Whether or not we need write access to this location. If <code>false</code>, readonly access
+     * will be used.
+     * @param objectRepositories The interfaces classes of the repositories to be used for this set.
+     * @return this object, to allow chaining.
+     */
+    @SuppressWarnings("unchecked")
+    public RepositoryAdminLoginContext addRepositories(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories);
+    
+    /**
+     * Adds a shop repository to this login context. 
+     * @param repositoryLocation The location of the repository where this set's data resides.
+     * @param repositoryCustomer The customer name for the location of the repository where this set's data resides.
+     * @param repositoryName The repository name for the location of the repository where this set's data resides.
+     * @param writeAccess Whether or not we need write access to this location. If <code>false</code>, readonly access
+     * will be used.
+     * @return this object, to allow chaining.
+     */
+    public RepositoryAdminLoginContext addShopRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess);
+
+    /**
+     * When uploads are needed, this is the base OBR that will be used.
+     * @param base The URL of the OBR to be used.
+     * @return this object, to allow chaining.
+     */
+    public RepositoryAdminLoginContext setObrBase(URL base);
+    
+    /**
+     * Adds a gateway repository to this login context. 
+     * @param repositoryLocation The location of the repository where this set's data resides.
+     * @param repositoryCustomer The customer name for the location of the repository where this set's data resides.
+     * @param repositoryName The repository name for the location of the repository where this set's data resides.
+     * @param writeAccess Whether or not we need write access to this location. If <code>false</code>, readonly access
+     * will be used.
+     * @return this object, to allow chaining.
+     */
+    public RepositoryAdminLoginContext addGatewayRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess);
+
+    /**
+     * Adds a deployment repository to this login context. 
+     * @param repositoryLocation The location of the repository where this set's data resides.
+     * @param repositoryCustomer The customer name for the location of the repository where this set's data resides.
+     * @param repositoryName The repository name for the location of the repository where this set's data resides.
+     * @param writeAccess Whether or not we need write access to this location. If <code>false</code>, readonly access
+     * will be used.
+     * @return this object, to allow chaining.
+     */
+    public RepositoryAdminLoginContext addDeploymentRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryObject.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryObject.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryObject.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryObject.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,103 @@
+package net.luminis.liq.client.repository;
+
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+ * A basic repository object, intended to be stored in a ObjectRepository of its given type.
+ * A RepositoryObject is associatable.
+ */
+public interface RepositoryObject extends Associatable {
+    /**
+     * This key is used to store the RepositoryObject an event comes from, in the Event object.
+     */
+    public final static String EVENT_ENTITY = "entity";
+
+    public static final String PUBLIC_TOPIC_ROOT = RepositoryObject.class.getPackage().getName().replace('.', '/') + "/public/";
+    public static final String PRIVATE_TOPIC_ROOT = RepositoryObject.class.getPackage().getName().replace('.', '/') + "/private/";
+
+    public static final String TOPIC_ADDED_SUFFIX = "ADDED";
+    public static final String TOPIC_REMOVED_SUFFIX = "REMOVED";
+    public static final String TOPIC_CHANGED_SUFFIX = "CHANGED";
+    public static final String TOPIC_ALL_SUFFIX = "*";
+
+    /**
+     * Adds a named attribute to this object's attributes. If the name already exists,
+     * it will be overwritten, and the old value is returned; will return <code>null</code>
+     * when the value is new.
+     */
+    public String addAttribute(String key, String value);
+    /**
+     * Gets a named attribute. Returns <code>null<code> when the attribute named by
+     * <code>key</code> does not exist.
+     */
+    public String getAttribute(String key);
+    /**
+     * Returns an enumeration of all attribute keys.
+     */
+    public Enumeration<String> getAttributeKeys();
+    /**
+     * Adds a named tag to this object's attributes. If the name already exists,
+     * it will be overwritten, and the old value is returned; will return <code>null</code>
+     * when the value is new.
+     */
+    public String addTag(String key, String value);
+    /**
+     * Gets a named tag. Returns <code>null<code> when the attribute named by
+     * <code>key</code> does not exist.
+     */
+    public String getTag(String key);
+    /**
+     * Returns an enumeration of all tags in this object, coming from both the
+     * tags and the attributes.
+     */
+    public Enumeration<String> getTagKeys();
+    /**
+     * Returns a <code>Dictionary</code> representing this object. It will contain all keys,
+     * from <code>getTagKeys</code>, and all values that correspond to them. If a key is present
+     * in both the attributes and the tags, the corresponding value will be an array of two
+     * <code>String</code> objects; otherwise a single <code>String</code> is returned.
+     */
+    public Dictionary<String, Object> getDictionary();
+    /**
+     * Indicates that this object should no longer be used.
+     */
+    public boolean isDeleted();
+    /**
+     * Creates a filter string for use in associations, optionally with some
+     * additional properties. The basic implementation will use all <code>getDefiningKeys</code>.
+     * @param properties Properties indicating specifics of the filter to be created.
+     * @return A string representation of a filter, for use in <code>Association</code>s.
+     */
+    public String getAssociationFilter(Map<String, String> properties);
+
+    /**
+     * Determines the cardinality of this endpoint of an association, given
+     * the passed properties.
+     * @param properties Properties indicating specifics of this endpoint.
+     * @return The necessary cardinality.
+     */
+    public int getCardinality(Map<String, String> properties);
+
+    /**
+     * Returns a <code>Comparator</code> for this type of object. Descendent
+     * classes are expected to return a comparator if they can be meaningfully compared,
+     * and otherwise (if no order is natural), return <code>null</code>.
+     * @return A <code>Comparator</code> for this type of object
+     */
+    @SuppressWarnings("unchecked")
+    public Comparator getComparator();
+
+    /**
+     * Returns a string which uniquely defines this object. The content is
+     * not intended to 'mean' anything.
+     * @return A uniquely defining string.
+     */
+    public String getDefinition();
+
+    public enum WorkingState {
+        New, Changed, Unchanged, Removed;
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryUtil.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryUtil.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryUtil.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/RepositoryUtil.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,15 @@
+package net.luminis.liq.client.repository;
+
+public class RepositoryUtil {
+	/**
+	 * Before passing user input into an LDAP filter, some precautions need to be taken
+	 * (see section 3.2.6 of the OSGi core specification). This function escapes
+	 * illegal characters, and returns the resulting string.
+	 */
+    public static String escapeFilterValue(String value) {
+        return value.replaceAll("\\\\", "\\\\\\\\")
+                    .replaceAll("\\(", "\\\\(")
+                    .replaceAll("\\)", "\\\\)")
+                    .replaceAll("\\*", "\\\\*");
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactHelper.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactHelper.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactHelper.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactHelper.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,75 @@
+package net.luminis.liq.client.repository.helper;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.ArtifactObject;
+
+/**
+ * Interface to an artifact helper. For each type of artifact, there should be a helper
+ * service implementing this interface. The service should be registered with the mimetype
+ * in the service's properties, so it can be identified. The <code>KEY_MIMETYPE</code> in 
+ * this class can be used for this purpose.
+ */
+public interface ArtifactHelper {
+    public static final String KEY_MIMETYPE = "mimetype";
+    
+    /**
+     * Checks whether this helper can 'do anything' with this artifact object.
+     * @param object An artifact object.
+     * @return <code>true</code> when this helper can use the object, <code>false</code> otherwise.
+     */
+    public boolean canUse(ArtifactObject object);
+    
+    /**
+     * Returns the artifact preprocessor that is associated with the type of artifact this
+     * helper helps. Return null when no useful processor is available.
+     * @return An artifact preprocessor, or <code>null</code> if no useful preprocessor can be created.
+     */
+    public ArtifactPreprocessor getPreprocessor();
+
+    /**
+     * Creates a filter string for use in associations, optionally with some
+     * additional properties. The basic implementation will use all <code>getDefiningKeys</code>.
+     * @param properties Properties indicating specifics of the filter to be created.
+     * @return A string representation of a filter, for use in <code>Association</code>s.
+     */
+    public <TYPE extends ArtifactObject> String getAssociationFilter(TYPE obj, Map<String, String> properties);
+
+    /**
+     * Determines the cardinality of this endpoint of an association, given
+     * the passed properties.
+     * @param properties Properties indicating specifics of this endpoint.
+     * @return The necessary cardinality.
+     */
+    public <TYPE extends ArtifactObject> int getCardinality(TYPE obj, Map<String, String> properties);
+
+    /**
+     * Returns a <code>Comparator</code> for this type of object. Descendent
+     * classes are expected to return a comparator if they can be meaningfully compared,
+     * and otherwise (if no order is natural), return <code>null</code>.
+     * @return A <code>Comparator</code> for this type of object
+     */
+    public Comparator<ArtifactObject> getComparator();
+
+    /**
+     * Checks the correctness of the given attributes for this type of object. If they
+     * are correct, the map will be returned, potentially with some changes, and if not,
+     * an {@link IllegalArgumentException} will be raised. Optionally, this
+     * function can do some validation of input parameters, such as normalizing numbers.
+     */
+    public Map<String, String> checkAttributes(Map<String, String> attributes);
+
+    /**
+     * Gets an array of keys in the attributes that are considered defining for this type
+     * of object; the combination of values of these keys should result in a unique
+     * identification of the object.
+     */
+    public String[] getDefiningKeys();
+    
+    /**
+     * Gets an array of all attributes that have to be present when creating an object
+     * of this type.
+     */
+    public String[] getMandatoryAttributes();
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactPreprocessor.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactPreprocessor.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactPreprocessor.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactPreprocessor.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,37 @@
+package net.luminis.liq.client.repository.helper;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ *  An ArtifactPreprocessor processes an artifact before it is deployed.
+ */
+public interface ArtifactPreprocessor {
+
+    /**
+     * Preprocesses a single artifact, uploads it to the obr, and returns the new URL as a string.
+     * @param url A string representing a URL to the original artifact.
+     * @param props A PropertyResolver which can be used to fill in 'holes' in the template.
+     * @param gatewayID The gatewayID of the gateway for which this artifact is being processed.
+     * @param version The deployment version for which this artifact is being processed.
+     * @param obrBase A base OBR to upload the new artifact to.
+     * @return A URL to the new object (or the old one, if no replacing was necessary), as a string.
+     * @throws IOException Thrown if reading the original artifact goes wrong, or storing the processed one.
+     */
+    public String preprocess(String url, PropertyResolver props, String gatewayID, String version, URL obrBase) throws IOException;
+
+    /**
+     * Indicates whether the template should be processed again, given the properties, and the version to which it
+     * should be compared. 
+     * @param url A string representing a URL to the original artifact.
+     * @param props A PropertyResolver which can be used to fill in 'holes' in the template.
+     * @param gatewayID The gatewayID of the gateway for which this artifact is being processed.
+     * @param version The deployment version for which this artifact is being processed.
+     * @param fromVersion The deployment version to which the current one should be compared.
+     * @return <code>false</code> if the version of the processed artifact identified by <code>fromVersion</code>
+     * is identical to what would be created using the new <code>props</code>; <code>true</code> otherwise.
+     * @throws IOException 
+     */
+    public boolean needsNewVersion(String url, PropertyResolver props, String gatewayID, String fromVersion);
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactRecognizer.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactRecognizer.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactRecognizer.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/ArtifactRecognizer.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,40 @@
+package net.luminis.liq.client.repository.helper;
+
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * Service interface for services that can recognize the type of an artifact, given a URL
+ * to that artifact.
+ */
+public interface ArtifactRecognizer {
+    /**
+     * Tries to determine the type of the artifact. If this recognizer cannot determine the type, it
+     * should return <code>null</code>.
+     * @param artifact A URL to a 'physical' artifact.
+     * @return The mimetype of the artifact, or <code>null</code> if the artifact is not recognized.
+     */
+    public String recognize(URL artifact);
+
+    /**
+     * Gets the relevant metadata for this artifact.
+     * @param artifact A URL to a 'physical' artifact.
+     * @return A map of strings, representing the relevant metadata specific for this artifact. The
+     * keys are best defined in the corresponding <code>ArtifactHelper</code> interface for this type of artifact.
+     * This function should also set the <code>ArtifactObject.KEY_PROCESSOR_PID</code> attribute.<br>
+     * Optionally, <code>ArtifactObject.KEY_ARTIFACT_NAME</code> and <code>ArtifactObject.KEY_ARTIFACT_DESCRIPTION</code>
+     * can be set. 
+     * @throws IllegalArgumentException when the metadata cannot be retrieved from the <code>artifact</code>.
+     */
+    public Map<String, String> extractMetaData(URL artifact) throws IllegalArgumentException;
+    
+    /**
+     * Indicates whether this recognizer can handle (i.e., extract metadata) from an artifact of
+     * a given mime type.
+     * @param mimetype The mimetype of an artifact.
+     * @return <code>true</code> when this type should be able to be handled by this recognizer;
+     * <code>false</code> otherwise.
+     */
+    public boolean canHandle(String mimetype);
+    
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/PropertyResolver.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/PropertyResolver.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/PropertyResolver.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/PropertyResolver.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,15 @@
+package net.luminis.liq.client.repository.helper;
+
+/**
+ * Interface for resolving properties about the template's
+ * environment which are to be used by an ArtifactPreprocessor.
+ */
+public interface PropertyResolver {
+    /**
+     * Gets a property, based on the given key. If the key cannot be found, <code>null</code>
+     * can be used.
+     * @param key A key to some property. Cannot be null.
+     * @return The property identified by <code>key</code> if it can be found, <code>null</code> otherwise.
+     */
+    public String get(String key);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/ArtifactPreprocessorBase.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/ArtifactPreprocessorBase.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/ArtifactPreprocessorBase.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/ArtifactPreprocessorBase.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,162 @@
+package net.luminis.liq.client.repository.helper.base;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import net.luminis.liq.client.repository.helper.ArtifactPreprocessor;
+import net.luminis.liq.client.repository.helper.PropertyResolver;
+
+/**
+ * This class can be used as a base class for artifact preprocessors. It comes with its
+ * own upload() method, which will be used by all artifact preprocessors anyway.
+ */
+public abstract class ArtifactPreprocessorBase implements ArtifactPreprocessor {
+
+    private static final int BUFFER_SIZE = 4 * 1024;
+
+    /**
+     * Uploads an artifact to an OBR.
+     * @param input A inputstream from which the artifact can be read.
+     * @param name The name of the artifact. If the name is not unique, an IOException will be thrown.
+     * @param obrBase The base URL of the obr to which this artifact should be written.
+     * @return A URL to the uploaded artifact; this is identical to calling <code>determineNewUrl(name, obrBase)</code>
+     * @throws IOException If there was an error reading from <code>input</code>, or if there was a problem communicating
+     * with the OBR.
+     */
+    protected URL upload(InputStream input, String name, URL obrBase) throws IOException {
+        if (obrBase == null) {
+            throw new IOException("There is no storage available for this artifact.");
+        }
+        if ((name == null) || (input == null)) {
+            throw new IllegalArgumentException("None of the parameters can be null.");
+        }
+
+        OutputStream output = null;
+        URL url = null;
+        try {
+            url = determineNewUrl(name, obrBase);
+            URLConnection connection = url.openConnection();
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            output = connection.getOutputStream();
+            byte[] buffer = new byte[BUFFER_SIZE];
+            for (int count = input.read(buffer); count != -1; count = input.read(buffer)) {
+                output.write(buffer, 0, count);
+            }
+            output.close();
+            if (connection instanceof HttpURLConnection) {
+                int responseCode = ((HttpURLConnection) connection).getResponseCode();
+                switch (responseCode) {
+                    case HttpURLConnection.HTTP_OK :
+                        break;
+                    case HttpURLConnection.HTTP_CONFLICT:
+                        throw new IOException("Artifact already exists in storage.");
+                    case HttpURLConnection.HTTP_INTERNAL_ERROR:
+                        throw new IOException("The storage server returned an internal server error.");
+                    default:
+                        throw new IOException("The storage server returned code " + responseCode + " writing to " + url.toString());
+                }
+            }
+        }
+        catch (IOException ioe) {
+            throw new IOException("Error uploading " + name + ": " + ioe.getMessage());
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+            if (output != null) {
+                try {
+                    output.close();
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     * Gets a stream to write an artifact to, which will be uploaded to the OBR.
+     * @param name The name of the artifact.
+     * @param obrBase The base URL of the obr to which this artifact should be written.
+     * @return An outputstream, to which the artifact can be written.
+     * @throws IOException If there is a problem setting up the outputstream.
+     */
+    protected OutputStream upload(final String name, final URL obrBase) throws IOException {
+        /*
+         * This function works by starting a thread which reads from the outputstream which is passed out,
+         * and writing it to another stream, which is read by a thread that does the Upload.
+         */
+        PipedOutputStream externalOutput = new PipedOutputStream();
+        final PipedInputStream externalInput = new PipedInputStream(externalOutput);
+
+        final PipedOutputStream internalOutput = new PipedOutputStream();
+        final PipedInputStream internalInput = new PipedInputStream(internalOutput);
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    byte[] buffer = new byte[BUFFER_SIZE];
+                    for (int count = externalInput.read(buffer); count != -1; count = externalInput.read(buffer)) {
+                        internalOutput.write(buffer, 0, count);
+                    }
+                }
+                catch (IOException e) {
+                    // We cannot signal this to the user, but he will notice (in the original thread)
+                    // that the pipe has been broken.
+                }
+                finally {
+                    try {
+                        internalOutput.close();
+                    }
+                    catch (IOException e) {
+                        // Not much to be done.
+                    }
+                    try {
+                        externalInput.close();
+                    }
+                    catch (IOException e) {
+                        // Not much to be done.
+                    }
+                }
+            }
+        }, "upload-Outputstream(" + name + ")").start();
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    upload(internalInput, name, obrBase);
+                }
+                catch (IOException e) {
+                    // We cannot signal this to the user, but he will notice (in the original thread)
+                    // that the pipe has been broken.
+                }
+            }
+        }, "upload-Inputstream(" + name + ")").start();
+
+        return externalOutput;
+    }
+
+    protected URL determineNewUrl(String name, URL obrBase) throws MalformedURLException {
+        return new URL(obrBase, name);
+    }
+
+    public abstract String preprocess(String url, PropertyResolver props, String gatewayID, String version, URL obrBase) throws IOException;
+
+    public abstract boolean needsNewVersion(String url, PropertyResolver props, String gatewayID, String fromVersion);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/VelocityArtifactPreprocessor.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/VelocityArtifactPreprocessor.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/VelocityArtifactPreprocessor.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/base/VelocityArtifactPreprocessor.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,230 @@
+package net.luminis.liq.client.repository.helper.base;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.helper.PropertyResolver;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+/**
+ * This class can be used as a 'default' artifact preprocessor, using the Velocity template engine to preprocess
+ * the artifact.
+ */
+public class VelocityArtifactPreprocessor extends ArtifactPreprocessorBase {
+
+    private static final int BUFFER_SIZE = 1024;
+    private Map<String, byte[]> m_cachedArtifacts = new HashMap<String, byte[]>();
+    private Map<String, String> m_cachedHashes = new HashMap<String, String>();
+
+    private static Object m_initLock = new Object();
+    private static boolean m_velocityInitialized = false;
+
+    private void init() throws IOException {
+        if (m_velocityInitialized) {
+            return;
+        }
+        else {
+            synchronized (m_initLock) {
+                if (!m_velocityInitialized) {
+                    try {
+                        Velocity.init();
+                        m_velocityInitialized = true;
+                    }
+                    catch (Exception e) {
+                        // Something went seriously bad initializing velocity.
+                        throw new IOException("Error initializing Velocity: " + e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public String preprocess(String url, PropertyResolver props, String gatewayID, String version, URL obrBase) throws IOException {
+        init();
+        // first, get the original data.
+        byte[] input = null;
+        try {
+            input = getArtifactAsBytes(url);
+        }
+        catch (IOException ioe) {
+            throw new IOException("Error retrieving the original artifact for preprocessing: " + ioe.getMessage());
+        }
+
+        // process the template
+        byte[] result = process(input, props);
+
+        if (Arrays.equals(result, input)) {
+            return url;
+        }
+        else {
+            try {
+                String name = getFilename(url, gatewayID, version);
+                OutputStream output = upload(name, obrBase);
+                output.write(result);
+                output.close();
+                setHashForVersion(url, gatewayID, version, hash(result));
+                return determineNewUrl(name, obrBase).toString();
+            }
+            catch (IOException ioe) {
+                throw new IOException("Error storing the processed: " + ioe.getMessage());
+            }
+        }
+    }
+
+    private String getFilename(String url, String gatewayID, String version) throws MalformedURLException {
+        return new File(new URL(url).getFile()).getName() + "-" + gatewayID + "-" + version;
+    }
+
+    private String getFullUrl(String url, String gatewayID, String version) throws MalformedURLException {
+        return url + "-" + gatewayID + "-" + version;
+    }
+
+    private String getHashForVersion(String url, String gateway, String version) {
+        String key = new StringBuilder().append('[')
+        .append(url)
+        .append("][")
+        .append(gateway)
+        .append("][")
+        .append(version)
+        .append(']').toString();
+
+        if (m_cachedHashes.containsKey(key)) {
+            return m_cachedHashes.get(key);
+        }
+        else {
+            byte[] processedTemplate;
+            try {
+                processedTemplate = getBytesFromUrl(getFullUrl(url, gateway, version));
+            }
+            catch (IOException e) {
+                // we cannot retrieve the artifact, so we cannot say anything about it.
+                return null;
+            }
+            String result = hash(processedTemplate);
+
+            m_cachedHashes.put(key, result);
+            return result;
+        }
+    }
+
+    private void setHashForVersion(String url, String gateway, String version, String hash) {
+        String key = new StringBuilder().append('[')
+        .append(url)
+        .append("][")
+        .append(gateway)
+        .append("][")
+        .append(version)
+        .append(']').toString();
+
+        m_cachedHashes.put(key, hash);
+    }
+
+    private byte[] process(byte[] input, PropertyResolver props) throws IOException {
+        try {
+            VelocityContext context = new VelocityContext();
+            context.put("context", props);
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Writer writer = new OutputStreamWriter(baos);
+            Velocity.evaluate(context, writer, "", new InputStreamReader(new ByteArrayInputStream(input)));
+            writer.flush();
+            return baos.toByteArray();
+        }
+        catch (IOException ioe) {
+            throw new IOException("Error processing the artifact: " + ioe.getMessage());
+        }
+    }
+
+    /**
+     * Helper method, which reads all information from a stream, and returns that as a
+     * byte array. The byte array is not to be changed.
+     */
+    private byte[] getArtifactAsBytes(String url) throws IOException {
+        if (m_cachedArtifacts.containsKey(url)) {
+            return m_cachedArtifacts.get(url);
+        }
+        else {
+            return getBytesFromUrl(url);
+        }
+    }
+
+    private byte[] getBytesFromUrl(String url) throws IOException, MalformedURLException {
+        ByteArrayOutputStream found = new ByteArrayOutputStream();
+        InputStream in = new URL(url).openStream();
+
+        byte[] buf = new byte[BUFFER_SIZE];
+        for (int count = in.read(buf); count != -1; count = in.read(buf)) {
+            found.write(buf, 0, count);
+        }
+        in.close();
+        byte[] result = found.toByteArray();
+        m_cachedArtifacts.put(url, result);
+        return result;
+    }
+
+    @Override
+    public boolean needsNewVersion(String url, PropertyResolver props, String gatewayID, String fromVersion) {
+        try {
+            init();
+        }
+        catch (IOException e) {
+            // problem initializing velocity... we cannot say anything.
+            return true;
+        }
+        // get the tempate
+        byte[] input = null;
+        byte[] result = null;
+        try {
+            input = getArtifactAsBytes(url);
+            result = process(input, props);
+        }
+        catch (IOException ioe) {
+            // we cannot retrieve the original artifact, or process it; we can't say anyting now.
+            return true;
+        }
+
+        // process the template
+
+        // first check: did we need any processing at all?
+        if (Arrays.equals(result, input)) {
+            return false;
+        }
+
+        // hash the processed template
+        String newHash = hash(result);
+
+        // find the hash for the previous version
+        String oldHash = getHashForVersion(url, gatewayID, fromVersion);
+
+        // Note: we do not cache any previously created processed templates, since the call that asks us to approve a new version
+        // may cross a pending needsNewVersion call.
+        return !newHash.equals(oldHash);
+    }
+
+    private String hash(byte[] input) {
+        try {
+            return new String(MessageDigest.getInstance("MD5").digest(input));
+        }
+        catch (NoSuchAlgorithmException e) {
+            // Will not happen: MD5 is a standard algorithm.
+        }
+        return null;
+    }
+
+}