From: Mathieu Baudier Date: Fri, 13 Feb 2015 09:25:26 +0000 (+0000) Subject: Merge JCR, OSGi and Ant support into SLC Core X-Git-Tag: argeo-slc-2.1.7~113 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=805e3301575e708491482a2a00dd37bc2b41b851;p=gpl%2Fargeo-slc.git Merge JCR, OSGi and Ant support into SLC Core git-svn-id: https://svn.argeo.org/slc/trunk@7837 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.slc.core/bin/log4j.properties b/org.argeo.slc.core/bin/log4j.properties deleted file mode 100644 index 0133bab88..000000000 --- a/org.argeo.slc.core/bin/log4j.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Set root logger level to DEBUG and its only appender to A1. -log4j.rootLogger=WARN, console - -## Levels -# Slc -log4j.logger.org.argeo=DEBUG - -# Castor -log4j.logger.org.exolab.castor=WARN - -# Spring -log4j.logger.org.springframework=WARN - - -## Appenders -# A1 is set to be a ConsoleAppender. -log4j.appender.console=org.apache.log4j.ConsoleAppender - -# A1 uses PatternLayout. -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/DefaultResourceSetTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/DefaultResourceSetTest.class deleted file mode 100644 index 296f6ef99..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/DefaultResourceSetTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/data/file1.txt b/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/data/file1.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/data/subdir/file2.txt b/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/data/subdir/file2.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/relativeResourceSet.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/relativeResourceSet.xml deleted file mode 100644 index 074c205a5..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/deploy/relativeResourceSet.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/AbstractExecutionFlowTestCase.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/AbstractExecutionFlowTestCase.class deleted file mode 100644 index 8fdcba1e1..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/AbstractExecutionFlowTestCase.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/BasicExecutionFlowTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/BasicExecutionFlowTest.class deleted file mode 100644 index 6a1b8176a..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/BasicExecutionFlowTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/DefaultAgentCliTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/DefaultAgentCliTest.class deleted file mode 100644 index a1e3b70fa..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/DefaultAgentCliTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ExceptionIfInitCalledTwice.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ExceptionIfInitCalledTwice.class deleted file mode 100644 index e5553236c..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ExceptionIfInitCalledTwice.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesSpringTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesSpringTest.class deleted file mode 100644 index 59737ac63..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesSpringTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesTest.class deleted file mode 100644 index 6371da2f7..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/FileExecutionResourcesTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ParameterRefTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ParameterRefTest.class deleted file mode 100644 index f0913e54b..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/ParameterRefTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/applicationContext.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/applicationContext.xml deleted file mode 100644 index d83c2c125..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/applicationContext.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-001.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-001.xml deleted file mode 100644 index a1c59c9b1..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-001.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-002.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-002.xml deleted file mode 100644 index 57f0c8a89..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-002.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-003.error.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-003.error.xml deleted file mode 100644 index 6de881047..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-003.error.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-004.error.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-004.error.xml deleted file mode 100644 index 2638ed6ee..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic-004.error.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic.xml deleted file mode 100644 index 8d6af0ef1..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/canonic.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/executionResources.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/executionResources.xml deleted file mode 100644 index 654f8b420..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/executionResources.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/imports.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/imports.xml deleted file mode 100644 index 7ddb4ea80..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/imports.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMap.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMap.xml deleted file mode 100644 index 8cf72e354..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMap.xml +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - myValue - _myValue_ - - - - - @{testKey} - _@{testKey}_ - - - - - - - - - myValue - _myValue_ - - myValue - - - - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - - - - - - myValue - _myValue_ - - - - - @{testKey} - _@{testKey}_ - - - - - - - - - myValue - _myValue_ - - myValue - - - - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - myValue - _myValue_ - - - myValue - _myValue_ - - myValue - - - myValue - - - - - - - - - - - - - - - - - - - - - @{testKey} - _@{testKey}_ - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - @{testKey} - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMapMultipleFlow.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMapMultipleFlow.xml deleted file mode 100644 index b8626f851..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/listSetMapMultipleFlow.xml +++ /dev/null @@ -1,326 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - myValue - _myValue_ - - - - - @{testKey} - _@{testKey}_ - - - - - - - - - myValue - _myValue_ - - myValue - - - - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - - - - - - myValue - _myValue_ - - - - - @{testKey} - _@{testKey}_ - - - - - - - - - myValue - _myValue_ - - myValue - - - - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - myValue - _myValue_ - - - myValue - _myValue_ - - myValue - - - myValue - - - - - - - - - - - - - - - - - - - - - @{testKey} - _@{testKey}_ - - - @{testKey} - _@{testKey}_ - - @{testKey} - - - @{testKey} - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/minimal.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/minimal.xml deleted file mode 100644 index 5b166970b..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/minimal.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/multipleFlow.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/multipleFlow.xml deleted file mode 100644 index 58a43e418..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/multipleFlow.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/parameterRef.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/parameterRef.xml deleted file mode 100644 index 98cc14d3d..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/parameterRef.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.exec.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.exec.xml deleted file mode 100644 index a36b4a167..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.exec.xml +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.xml deleted file mode 100644 index 893768be3..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/placeHolders.cascading.xml +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/specOverriding.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/specOverriding.xml deleted file mode 100644 index de1dc8504..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/specOverriding.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/SystemCallTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/SystemCallTest.class deleted file mode 100644 index fe381e492..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/SystemCallTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/systemCall.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/systemCall.xml deleted file mode 100644 index 8d3565040..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/tasks/systemCall.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - echo - Hello - World - - - - - - - - - - - - - - - - - - - - - - - - - dir - - - - - - - ls - - - - - ls - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/test.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/test.xml deleted file mode 100644 index d8bec3df0..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/test.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From basic @{testedComponentId} - - - testData1='@{testData1}' - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/FlowNamespaceTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/FlowNamespaceTest.class deleted file mode 100644 index 3e0cd7cb5..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/FlowNamespaceTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/advanced.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/advanced.xml deleted file mode 100644 index 49b6f7e1d..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/advanced.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - spec description - - - - - - - flow description - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would fail if param 2 is not changed at execution - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-001.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-001.xml deleted file mode 100644 index aeef3a3af..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-001.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Canonic 001 - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-002.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-002.xml deleted file mode 100644 index ac5f085eb..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns-002.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns.xml deleted file mode 100644 index facb27761..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/canonic-ns.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/containers.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/containers.xml deleted file mode 100644 index 61bfa0703..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/containers.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - val1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - val1 - val2 - - - - - val1 - val2 - - - - - - - - - - - - - - - - - - use default value for parameter "list1" - - - val1 - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/tests.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/tests.xml deleted file mode 100644 index ee58a1869..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/execution/xml/tests.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/AbstractInternalSpringTestCase.class b/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/AbstractInternalSpringTestCase.class deleted file mode 100644 index 0cad464a7..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/AbstractInternalSpringTestCase.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/ContextTest.class b/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/ContextTest.class deleted file mode 100644 index 4146eca03..000000000 Binary files a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/ContextTest.class and /dev/null differ diff --git a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/applicationContext.xml b/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/applicationContext.xml deleted file mode 100644 index 4949c4eb1..000000000 --- a/org.argeo.slc.core/bin/org/argeo/slc/core/test/context/applicationContext.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.slc.core/bnd.bnd b/org.argeo.slc.core/bnd.bnd index 40b504bd2..3c198f97f 100644 --- a/org.argeo.slc.core/bnd.bnd +++ b/org.argeo.slc.core/bnd.bnd @@ -1,8 +1,4 @@ -Import-Package: org.w3c.dom;version="0.0.0",\ -org.dbunit;resolution:="optional",\ -org.dbunit.database;resolution:="optional",\ -org.dbunit.dataset;resolution:="optional",\ -org.dbunit.dataset.xml;resolution:="optional",\ -org.dbunit.operation;resolution:="optional",\ +Import-Package: javax.jcr.nodetype,\ +org.apache.tools.ant.*;resolution:="optional",\ junit.framework;resolution:="optional",\ * diff --git a/org.argeo.slc.core/src/org/argeo/slc/ant/AntFlowGenerator.java b/org.argeo.slc.core/src/org/argeo/slc/ant/AntFlowGenerator.java new file mode 100644 index 000000000..9dea43e49 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/ant/AntFlowGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.ant; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.argeo.slc.core.execution.AbstractExecutionFlowGenerator; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.core.io.Resource; + +public class AntFlowGenerator extends AbstractExecutionFlowGenerator { + private List antFiles = new ArrayList(); + + protected Map createExecutionFlowDefinitions( + ConfigurableListableBeanFactory beanFactory) { + Map definitions = new HashMap(); + + for (Resource antFile : antFiles) { + AntRun antRun = new AntRun(); + antRun.setBuildFile(antFile); + + List executables = new ArrayList(); + executables.add(antRun); + definitions.put("ant." + antFile.getFilename(), + createDefaultFlowDefinition(executables)); + } + return definitions; + } + + public void setAntFiles(List antFiles) { + this.antFiles = antFiles; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/ant/AntRun.java b/org.argeo.slc.core/src/org/argeo/slc/ant/AntRun.java new file mode 100644 index 000000000..1e2dcb940 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/ant/AntRun.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.ant; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.tools.ant.BuildEvent; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; +import org.apache.tools.ant.helper.ProjectHelper2; +import org.argeo.slc.SlcException; +import org.springframework.core.io.Resource; + +public class AntRun implements Runnable { + private final static Log log = LogFactory.getLog(AntRun.class); + + private Resource buildFile; + private File baseDir; + + private List targets = new ArrayList(); + private Map properties = new HashMap(); + + public void run() { + Project project = new Project(); + + try { + String path = buildFile.getURL().getPath(); + project.setUserProperty("ant.file", path); + project.setBaseDir(extractBaseDir(path)); + + project.init(); + ProjectHelper projectHelper = new ProjectHelper2(); + project.addReference(ProjectHelper.PROJECTHELPER_REFERENCE, + projectHelper); + projectHelper.parse(project, buildFile.getURL()); + } catch (Exception e) { + throw new SlcException("Could not parse " + buildFile, e); + } + + if (properties != null) { + for (Map.Entry entry : properties.entrySet()) { + project.setUserProperty(entry.getKey().toString(), entry + .getValue().toString()); + } + } + + project.fireBuildStarted(); + Throwable exception = null; + try { + project.addBuildListener(new LoggingListener()); + if (targets.size() == 0) { + project.executeTarget(project.getDefaultTarget()); + } else { + project.executeTargets(new Vector(targets)); + } + } catch (Throwable e) { + exception = e; + throw new SlcException("Could not run Ant script " + buildFile, e); + } finally { + project.fireBuildFinished(exception); + } + } + + private File extractBaseDir(String path) { + if(this.baseDir!=null) + return this.baseDir; + + String baseDir = null; + if (path.length() > 1) { + int indx = path.lastIndexOf('/', path.length() - 1); + if (indx == -1 || indx == 0) { + baseDir = "/"; + } else { + baseDir = path.substring(0, indx) + "/"; + } + } else { + baseDir = "/"; + } + File file = new File(baseDir); + if (file.exists()) { + return file; + } else { + return new File(System.getProperty("user.dir")); + } + } + + public void setBuildFile(Resource buildFile) { + this.buildFile = buildFile; + } + + public void setTargets(List targets) { + this.targets = targets; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setBaseDir(File baseDir) { + this.baseDir = baseDir; + } + + protected static class LoggingListener implements BuildListener { + + public void buildFinished(BuildEvent event) { + if (log.isDebugEnabled()) + log.debug("Ant build finished: " + event); + } + + public void buildStarted(BuildEvent event) { + if (log.isDebugEnabled()) + log.debug("Ant build started: " + event); + } + + public void messageLogged(BuildEvent event) { + if (event.getPriority() == Project.MSG_DEBUG) { + if (log.isTraceEnabled()) + log.trace(event.getMessage()); + } else if (event.getPriority() == Project.MSG_VERBOSE) { + if (log.isDebugEnabled()) + log.debug(event.getMessage()); + } else if (event.getPriority() == Project.MSG_INFO) { + log.info(event.getMessage()); + + } else if (event.getPriority() == Project.MSG_WARN) { + log.warn(event.getMessage()); + + } else if (event.getPriority() == Project.MSG_ERR) { + log.error(event.getMessage()); + } else { + log.error(event.getMessage()); + } + } + + public void targetFinished(BuildEvent event) { + if (log.isTraceEnabled()) + log.debug("Target finished: " + event.getTarget()); + } + + public void targetStarted(BuildEvent event) { + if (log.isTraceEnabled()) + log.debug("Target started: " + event.getTarget()); + } + + public void taskFinished(BuildEvent event) { + } + + public void taskStarted(BuildEvent event) { + } + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrMetadataWriter.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrMetadataWriter.java new file mode 100644 index 000000000..19db9cbf7 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrMetadataWriter.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; + +/** + * Writes arbitrary metadata into a child node of a given node (or the node + * itself if metadata node name is set to null) + */ +public class JcrMetadataWriter implements Runnable { + private final static Log log = LogFactory.getLog(JcrMetadataWriter.class); + + private Node baseNode; + private String metadataNodeName = SlcNames.SLC_METADATA; + + private Map metadata = new HashMap(); + + public void run() { + try { + Node metadataNode; + if (metadataNodeName != null) + metadataNode = baseNode.hasNode(metadataNodeName) ? baseNode + .getNode(metadataNodeName) : baseNode + .addNode(metadataNodeName); + else + metadataNode = baseNode; + + for (String key : metadata.keySet()) + metadataNode.setProperty(key, metadata.get(key)); + + baseNode.getSession().save(); + + if (log.isDebugEnabled()) + log.debug("Wrote " + metadata.size() + " metadata entries to " + + metadataNode); + } catch (RepositoryException e) { + throw new SlcException("Cannot write metadata to " + baseNode, e); + } finally { + JcrUtils.discardUnderlyingSessionQuietly(baseNode); + } + + } + + public void setBaseNode(Node baseNode) { + this.baseNode = baseNode; + } + + public void setMetadataNodeName(String metadataNodeName) { + this.metadataNodeName = metadataNodeName; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrTestResult.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrTestResult.java new file mode 100644 index 000000000..e3394e05c --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/JcrTestResult.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.jcr.Credentials; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.Repository; +import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.attachment.Attachment; +import org.argeo.slc.core.attachment.AttachmentsEnabled; +import org.argeo.slc.test.TestResult; +import org.argeo.slc.test.TestResultPart; +import org.argeo.slc.test.TestRun; +import org.argeo.slc.test.TestStatus; + +/** + * {@link TestResult} wrapping a JCR node of type + * {@link SlcTypes#SLC_TEST_RESULT}. + */ +public class JcrTestResult implements TestResult, SlcNames, AttachmentsEnabled { + private final static Log log = LogFactory.getLog(JcrTestResult.class); + + /** Should only be set for an already existing result. */ + private String uuid; + private Repository repository; + private Session session; + /** + * For testing purposes, best practice is to not set them explicitely but + * via other mechanisms such as JAAS or SPring Security. + */ + private Credentials credentials = null; + private String resultType = SlcTypes.SLC_TEST_RESULT; + + /** cached for performance purposes */ + private String nodeIdentifier = null; + + private Map attributes = new HashMap(); + + public void init() { + try { + session = repository.login(credentials); + if (uuid == null) { + // create new result + uuid = UUID.randomUUID().toString(); + String path = SlcJcrUtils.createResultPath(session, uuid); + Node resultNode = JcrUtils.mkdirs(session, path, resultType); + resultNode.setProperty(SLC_UUID, uuid); + for (String attr : attributes.keySet()) { + String property = attr; + // compatibility with legacy applications + if ("testCase".equals(attr)) + property = SLC_TEST_CASE; + else if ("testCaseType".equals(attr)) + property = SLC_TEST_CASE_TYPE; + resultNode.setProperty(property, attributes.get(attr)); + } + session.save(); + if (log.isDebugEnabled()) + log.debug("Created test result " + uuid); + } + } catch (Exception e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot initialize JCR result", e); + } + } + + public void destroy() { + JcrUtils.logoutQuietly(session); + if (log.isTraceEnabled()) + log.trace("Logged out session for result " + uuid); + } + + public Node getNode() { + try { + Node resultNode; + if (nodeIdentifier != null) { + return session.getNodeByIdentifier(nodeIdentifier); + } else { + QueryManager qm = session.getWorkspace().getQueryManager(); + Query q = qm.createQuery("select * from [" + + SlcTypes.SLC_TEST_RESULT + "] where [slc:uuid]='" + + uuid + "'", Query.JCR_SQL2); + resultNode = JcrUtils.querySingleNode(q); + if (resultNode != null) + nodeIdentifier = resultNode.getIdentifier(); + } + return resultNode; + } catch (Exception e) { + throw new SlcException("Cannot get result node", e); + } + } + + public void notifyTestRun(TestRun testRun) { + // TODO store meta data about the test running + // if (log.isDebugEnabled()) + // log.debug("Running test " + // + testRun.getTestDefinition().getClass().getName() + "..."); + } + + public void addResultPart(TestResultPart testResultPart) { + Node node = getNode(); + + try { + // error : revert all unsaved changes on the resultNode to be sure + // it is in a consistant state + if (testResultPart.getExceptionMessage() != null) + JcrUtils.discardQuietly(node.getSession()); + node.getSession().save(); + + // add the new result part, retrieving status information + Node resultPartNode = node.addNode(SlcNames.SLC_RESULT_PART, + SlcTypes.SLC_CHECK); + resultPartNode.setProperty(SLC_SUCCESS, testResultPart.getStatus() + .equals(TestStatus.PASSED)); + if (testResultPart.getMessage() != null) + resultPartNode.setProperty(SLC_MESSAGE, + testResultPart.getMessage()); + if (testResultPart.getStatus().equals(TestStatus.ERROR)) { + resultPartNode.setProperty(SLC_ERROR_MESSAGE, + (testResultPart.getExceptionMessage() == null) ? "" + : testResultPart.getExceptionMessage()); + } + + // helper update aggregate status node + Node mainStatus; + if (!node.hasNode(SLC_AGGREGATED_STATUS)) { + + mainStatus = node.addNode(SLC_AGGREGATED_STATUS, + SlcTypes.SLC_CHECK); + mainStatus.setProperty(SLC_SUCCESS, + resultPartNode.getProperty(SLC_SUCCESS).getBoolean()); + if (resultPartNode.hasProperty(SLC_MESSAGE)) + mainStatus.setProperty(SLC_MESSAGE, resultPartNode + .getProperty(SLC_MESSAGE).getString()); + if (resultPartNode.hasProperty(SLC_ERROR_MESSAGE)) + mainStatus.setProperty(SLC_ERROR_MESSAGE, resultPartNode + .getProperty(SLC_ERROR_MESSAGE).getString()); + } else { + mainStatus = node.getNode(SLC_AGGREGATED_STATUS); + if (mainStatus.hasProperty(SLC_ERROR_MESSAGE)) { + // main status already in error we do nothing + } else if (resultPartNode.hasProperty(SLC_ERROR_MESSAGE)) { + // main status was not in error and new result part is in + // error; we update main status + mainStatus.setProperty(SLC_SUCCESS, false); + mainStatus.setProperty(SLC_ERROR_MESSAGE, resultPartNode + .getProperty(SLC_ERROR_MESSAGE).getString()); + if (resultPartNode.hasProperty(SLC_MESSAGE)) + mainStatus.setProperty(SLC_MESSAGE, resultPartNode + .getProperty(SLC_MESSAGE).getString()); + else + // remove old message to remain consistent + mainStatus.setProperty(SLC_MESSAGE, ""); + } else if (!mainStatus.getProperty(SLC_SUCCESS).getBoolean()) { + // main status was already failed and new result part is not + // in error, we do nothing + } else if (!resultPartNode.getProperty(SLC_SUCCESS) + .getBoolean()) { + // new resultPart that is failed + mainStatus.setProperty(SLC_SUCCESS, false); + if (resultPartNode.hasProperty(SLC_MESSAGE)) + mainStatus.setProperty(SLC_MESSAGE, resultPartNode + .getProperty(SLC_MESSAGE).getString()); + else + // remove old message to remain consistent + mainStatus.setProperty(SLC_MESSAGE, ""); + } else if (resultPartNode.hasProperty(SLC_MESSAGE) + && (!mainStatus.hasProperty(SLC_MESSAGE) || ("" + .equals(mainStatus.getProperty(SLC_MESSAGE) + .getString().trim())))) { + mainStatus.setProperty(SLC_MESSAGE, resultPartNode + .getProperty(SLC_MESSAGE).getString()); + } + } + JcrUtils.updateLastModified(node); + node.getSession().save(); + } catch (Exception e) { + JcrUtils.discardUnderlyingSessionQuietly(node); + throw new SlcException("Cannot add ResultPart to node " + node, e); + } + } + + public String getUuid() { + Node node = getNode(); + try { + return node.getProperty(SLC_UUID).getString(); + } catch (Exception e) { + throw new SlcException("Cannot get UUID from " + node, e); + } + } + + /** JCR session is NOT logged out */ + public void close() { + Node node = getNode(); + try { + if (node.hasNode(SLC_COMPLETED)) + return; + node.setProperty(SLC_COMPLETED, new GregorianCalendar()); + JcrUtils.updateLastModified(node); + node.getSession().save(); + } catch (Exception e) { + JcrUtils.discardUnderlyingSessionQuietly(node); + throw new SlcException("Cannot get close date from " + node, e); + } + } + + public Date getCloseDate() { + Node node = getNode(); + try { + if (!node.hasNode(SLC_COMPLETED)) + return null; + return node.getProperty(SLC_COMPLETED).getDate().getTime(); + } catch (Exception e) { + throw new SlcException("Cannot get close date from " + node, e); + } + } + + public Map getAttributes() { + Node node = getNode(); + try { + Map map = new HashMap(); + PropertyIterator pit = node.getProperties(); + while (pit.hasNext()) { + Property p = pit.nextProperty(); + if (!p.isMultiple()) + map.put(p.getName(), p.getValue().getString()); + } + return map; + } catch (Exception e) { + throw new SlcException("Cannot get close date from " + node, e); + } + } + + public void addAttachment(Attachment attachment) { + // TODO implement it + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setResultType(String resultType) { + this.resultType = resultType; + } + + public void setAttributes(Map attributes) { + if (uuid != null) + throw new SlcException( + "Attributes cannot be set on an already initialized test result." + + " Update the related JCR node directly instead."); + this.attributes = attributes; + } + + public void setCredentials(Credentials credentials) { + this.credentials = credentials; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrConstants.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrConstants.java new file mode 100644 index 000000000..179a081f4 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrConstants.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +/** JCR related constants used across SLC */ +public interface SlcJcrConstants { + public final static String PROPERTY_PATH = "argeo.slc.jcr.path"; + + public final static String SLC_BASE_PATH = "/" + SlcNames.SLC_SYSTEM; + public final static String AGENTS_BASE_PATH = SLC_BASE_PATH + "/" + + SlcNames.SLC_AGENTS; + public final static String VM_AGENT_FACTORY_PATH = AGENTS_BASE_PATH + "/" + + SlcNames.SLC_VM; +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrResultUtils.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrResultUtils.java new file mode 100644 index 000000000..729c2d74b --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrResultUtils.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.UserJcrUtils; +import org.argeo.slc.SlcException; + +/** + * Utilities around the SLC JCR Result model. Note that it relies on fixed base + * paths (convention over configuration) for optimization purposes. + */ +public class SlcJcrResultUtils { + + /** + * Returns the path to the current slc:result node + */ + public static String getSlcResultsBasePath(Session session) { + try { + Node userHome = UserJcrUtils.getUserHome(session); + if (userHome == null) + throw new SlcException("No user home available for " + + session.getUserID()); + return userHome.getPath() + '/' + SlcNames.SLC_SYSTEM + '/' + + SlcNames.SLC_RESULTS; + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while getting Slc Results Base Path.", re); + } + } + + /** + * Returns the base node to store SlcResults. If it does not exists, it is + * created. If a node already exists at the given path with the wrong type, + * it throws an exception. + * + * @param session + * @return + */ + public static Node getSlcResultsParentNode(Session session) { + try { + String absPath = getSlcResultsBasePath(session); + if (session.nodeExists(absPath)) { + Node currNode = session.getNode(absPath); + if (currNode.isNodeType(NodeType.NT_UNSTRUCTURED)) + return currNode; + else + throw new SlcException( + "A node already exists at this path : " + absPath + + " that has the wrong type. "); + } else { + Node slcResParNode = JcrUtils.mkdirs(session, absPath); + slcResParNode.setPrimaryType(NodeType.NT_UNSTRUCTURED); + session.save(); + return slcResParNode; + } + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while creating slcResult root parent node.", + re); + } + } + + /** + * Returns the path to the current Result UI specific node, depending the + * current user + */ + public static String getMyResultsBasePath(Session session) { + try { + Node userHome = UserJcrUtils.getUserHome(session); + if (userHome == null) + throw new SlcException("No user home available for " + + session.getUserID()); + return userHome.getPath() + '/' + SlcNames.SLC_SYSTEM + '/' + + SlcNames.SLC_MY_RESULTS; + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while getting Slc Results Base Path.", re); + } + } + + /** + * Creates a new node with type SlcTypes.SLC_MY_RESULT_ROOT_FOLDER at the + * given absolute path. If a node already exists at the given path, returns + * that node if it has the correct type and throws an exception otherwise. + * + * @param session + * @return + */ + public static Node getMyResultParentNode(Session session) { + try { + String absPath = getMyResultsBasePath(session); + if (session.nodeExists(absPath)) { + Node currNode = session.getNode(absPath); + if (currNode.isNodeType(SlcTypes.SLC_MY_RESULT_ROOT_FOLDER)) + return currNode; + else + throw new SlcException( + "A node already exists at this path : " + absPath + + " that has the wrong type. "); + } else { + Node myResParNode = JcrUtils.mkdirs(session, absPath); + myResParNode.setPrimaryType(SlcTypes.SLC_MY_RESULT_ROOT_FOLDER); + session.save(); + return myResParNode; + } + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while creating user MyResult base node.", + re); + } + } + + /** + * Creates a new node with type SlcTypes.SLC_RESULT_FOLDER at the given + * absolute path. If a node already exists at the given path, returns that + * node if it has the correct type and throws an exception otherwise. + * + * @param session + * @param absPath + * @return + */ + public static synchronized Node createResultFolderNode(Session session, + String absPath) { + try { + if (session.nodeExists(absPath)) { + // Sanity check + Node currNode = session.getNode(absPath); + if (currNode.isNodeType(SlcTypes.SLC_RESULT_FOLDER)) + return currNode; + else + throw new SlcException( + "A node already exists at this path : " + absPath + + " that has the wrong type. "); + } + Node rfNode = JcrUtils.mkdirs(session, absPath); + rfNode.setPrimaryType(SlcTypes.SLC_RESULT_FOLDER); + Node statusNode = rfNode.addNode(SlcNames.SLC_AGGREGATED_STATUS, + SlcTypes.SLC_CHECK); + statusNode.setProperty(SlcNames.SLC_SUCCESS, true); + session.save(); + return rfNode; + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while creating Result Folder node.", re); + } + } +} \ No newline at end of file diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrUtils.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrUtils.java new file mode 100644 index 000000000..5c00bcffb --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcJcrUtils.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.UserJcrUtils; +import org.argeo.slc.BasicNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.PrimitiveAccessor; +import org.argeo.slc.core.execution.PrimitiveUtils; +import org.argeo.slc.deploy.ModuleDescriptor; +import org.argeo.slc.test.TestStatus; + +/** + * Utilities around the SLC JCR model. Note that it relies on fixed base paths + * (convention over configuration) for optimization purposes. + */ +public class SlcJcrUtils implements SlcNames { + public final static Integer AGENT_FACTORY_DEPTH = 3; + + /** Extracts the path of a flow relative to its execution module */ + public static String flowRelativePath(String fullFlowPath) { + String[] tokens = fullFlowPath.split("/"); + StringBuffer buf = new StringBuffer(fullFlowPath.length()); + for (int i = AGENT_FACTORY_DEPTH + 3; i < tokens.length; i++) { + buf.append('/').append(tokens[i]); + } + return buf.toString(); + } + + /** Extracts the path to the related execution module */ + public static String modulePath(String fullFlowPath) { + String[] tokens = fullFlowPath.split("/"); + StringBuffer buf = new StringBuffer(fullFlowPath.length()); + for (int i = 0; i < AGENT_FACTORY_DEPTH + 3; i++) { + if (!tokens[i].equals("")) + buf.append('/').append(tokens[i]); + } + return buf.toString(); + } + + /** Extracts the module name from a flow path */ + public static String moduleName(String fullFlowPath) { + String[] tokens = fullFlowPath.split("/"); + String moduleName = tokens[AGENT_FACTORY_DEPTH + 2]; + moduleName = moduleName.substring(0, moduleName.indexOf('_')); + return moduleName; + } + + /** Extracts the module name and version from a flow path */ + public static NameVersion moduleNameVersion(String fullFlowPath) { + String[] tokens = fullFlowPath.split("/"); + String module = tokens[AGENT_FACTORY_DEPTH + 2]; + String moduleName = module.substring(0, module.indexOf('_')); + String moduleVersion = module.substring(module.indexOf('_') + 1); + return new BasicNameVersion(moduleName, moduleVersion); + } + + /** Module node name based on module name and version */ + public static String getModuleNodeName(ModuleDescriptor moduleDescriptor) { + return moduleDescriptor.getName() + "_" + moduleDescriptor.getVersion(); + } + + /** Extracts the agent factory of a flow */ + public static String flowAgentFactoryPath(String fullFlowPath) { + String[] tokens = fullFlowPath.split("/"); + StringBuffer buf = new StringBuffer(fullFlowPath.length()); + // first token is always empty + for (int i = 1; i < AGENT_FACTORY_DEPTH + 1; i++) { + buf.append('/').append(tokens[i]); + } + return buf.toString(); + } + + /** Create a new execution process path based on the current time */ + public static String createExecutionProcessPath(Session session, String uuid) { + Calendar now = new GregorianCalendar(); + return getSlcProcessesBasePath(session) + '/' + + JcrUtils.dateAsPath(now, true) + uuid; + } + + /** Get the base for the user processi. */ + public static String getSlcProcessesBasePath(Session session) { + try { + Node userHome = UserJcrUtils.getUserHome(session); + if (userHome == null) + throw new SlcException("No user home available for " + + session.getUserID()); + return userHome.getPath() + '/' + SlcNames.SLC_SYSTEM + '/' + + SlcNames.SLC_PROCESSES; + } catch (RepositoryException re) { + throw new SlcException( + "Unexpected error while getting Slc Results Base Path.", re); + } + } + + /** + * Create a new execution result path in the user home based on the current + * time + */ + public static String createResultPath(Session session, String uuid) + throws RepositoryException { + Calendar now = new GregorianCalendar(); + StringBuffer absPath = new StringBuffer( + SlcJcrResultUtils.getSlcResultsBasePath(session) + '/'); + // Remove hours and add title property to the result process path on + // request of O. Capillon + // return getSlcProcessesBasePath(session) + '/' + // + JcrUtils.dateAsPath(now, true) + uuid; + String relPath = JcrUtils.dateAsPath(now, false); + List names = JcrUtils.tokenize(relPath); + for (String name : names) { + absPath.append(name + "/"); + Node node = JcrUtils.mkdirs(session, absPath.toString()); + try { + node.addMixin(NodeType.MIX_TITLE); + node.setProperty(Property.JCR_TITLE, name.substring(1)); + } catch (RepositoryException e) { + throw new SlcException( + "unable to create execution process path", e); + } + } + return absPath.toString() + uuid; + } + + /** + * Set the value of the primitive accessor as a JCR property. Does nothing + * if the value is null. + */ + public static void setPrimitiveAsProperty(Node node, String propertyName, + PrimitiveAccessor primitiveAccessor) { + String type = primitiveAccessor.getType(); + Object value = primitiveAccessor.getValue(); + setPrimitiveAsProperty(node, propertyName, type, value); + } + + /** Map a primitive value to JCR property value. */ + public static void setPrimitiveAsProperty(Node node, String propertyName, + String type, Object value) { + if (value == null) + return; + if (value instanceof CharSequence) + value = PrimitiveUtils.convert(type, + ((CharSequence) value).toString()); + if (value instanceof char[]) + value = new String((char[]) value); + + try { + if (type.equals(PrimitiveAccessor.TYPE_STRING)) + node.setProperty(propertyName, value.toString()); + else if (type.equals(PrimitiveAccessor.TYPE_PASSWORD)) + node.setProperty(propertyName, value.toString()); + else if (type.equals(PrimitiveAccessor.TYPE_INTEGER)) + node.setProperty(propertyName, (long) ((Integer) value)); + else if (type.equals(PrimitiveAccessor.TYPE_LONG)) + node.setProperty(propertyName, ((Long) value)); + else if (type.equals(PrimitiveAccessor.TYPE_FLOAT)) + node.setProperty(propertyName, (double) ((Float) value)); + else if (type.equals(PrimitiveAccessor.TYPE_DOUBLE)) + node.setProperty(propertyName, ((Double) value)); + else if (type.equals(PrimitiveAccessor.TYPE_BOOLEAN)) + node.setProperty(propertyName, ((Boolean) value)); + else + throw new SlcException("Unsupported type " + type); + } catch (RepositoryException e) { + throw new SlcException("Cannot set primitive of " + type + + " as property " + propertyName + " on " + node, e); + } + } + + /** Aggregates the {@link TestStatus} of this sub-tree. */ + public static Integer aggregateTestStatus(Node node) { + try { + Integer status = TestStatus.PASSED; + if (node.isNodeType(SlcTypes.SLC_CHECK)) + if (node.getProperty(SLC_SUCCESS).getBoolean()) + status = TestStatus.PASSED; + else if (node.hasProperty(SLC_ERROR_MESSAGE)) + status = TestStatus.ERROR; + else + status = TestStatus.FAILED; + + NodeIterator it = node.getNodes(); + while (it.hasNext()) { + Node curr = it.nextNode(); + + // Manually skip aggregated status + if (!SlcNames.SLC_AGGREGATED_STATUS.equals(curr.getName())) { + Integer childStatus = aggregateTestStatus(curr); + if (childStatus > status) + status = childStatus; + } + } + return status; + } catch (Exception e) { + throw new SlcException("Could not aggregate test status from " + + node, e); + } + } + + /** + * Aggregates the {@link TestStatus} of this sub-tree. + * + * @return the same {@link StringBuffer}, for convenience (typically calling + * toString() on it) + */ + public static StringBuffer aggregateTestMessages(Node node, + StringBuffer messages) { + try { + if (node.isNodeType(SlcTypes.SLC_CHECK)) { + if (node.hasProperty(SLC_MESSAGE)) { + if (messages.length() > 0) + messages.append('\n'); + messages.append(node.getProperty(SLC_MESSAGE).getString()); + } + if (node.hasProperty(SLC_ERROR_MESSAGE)) { + if (messages.length() > 0) + messages.append('\n'); + messages.append(node.getProperty(SLC_ERROR_MESSAGE) + .getString()); + } + } + NodeIterator it = node.getNodes(); + while (it.hasNext()) { + Node child = it.nextNode(); + // Manually skip aggregated status + if (!SlcNames.SLC_AGGREGATED_STATUS.equals(child.getName())) { + aggregateTestMessages(child, messages); + } + } + return messages; + } catch (Exception e) { + throw new SlcException("Could not aggregate test messages from " + + node, e); + } + } + + /** Prevents instantiation */ + private SlcJcrUtils() { + } +} \ No newline at end of file diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcNames.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcNames.java new file mode 100644 index 000000000..15213fc0a --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcNames.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +/** JCR names used by SLC */ +public interface SlcNames { + public final static String SLC_ = "slc:"; + + /* + * GENERAL + */ + public final static String SLC_UUID = "slc:uuid"; + public final static String SLC_STATUS = "slc:status"; + // generic name for result parts of a given test result (slc:testResult) + // note that corresponding nodes can be retrieved using + // myTestResultNode.getNodes(SLC_RESULT_PART+"*") method + public final static String SLC_RESULT_PART = "slc:resultPart"; + // Fixed name of the child node of a slc:testResult of type + // slc:check that aggregate status of all result parts of a given test + // result + public final static String SLC_AGGREGATED_STATUS = "slc:aggregatedStatus"; + + public final static String SLC_TYPE = "slc:type"; + public final static String SLC_NAME = "slc:name"; + public final static String SLC_VERSION = "slc:version"; + public final static String SLC_CATEGORY = "slc:category"; + public final static String SLC_VALUE = "slc:value"; + public final static String SLC_ADDRESS = "slc:address"; + public final static String SLC_METADATA = "slc:metadata"; + + public final static String SLC_TIMESTAMP = "slc:timestamp"; + public final static String SLC_STARTED = "slc:started"; + public final static String SLC_COMPLETED = "slc:completed"; + + public final static String SLC_VM = "slc:vm"; + /* + * SLC RUNTIME + */ + // execution + public final static String SLC_SPEC = "slc:spec"; + public final static String SLC_EXECUTION_SPECS = "slc:executionSpecs"; + public final static String SLC_FLOW = "slc:flow"; + public final static String SLC_LOG = "slc:log"; + public final static String SLC_AGENTS = "slc:agents"; + + // spec attribute + public final static String SLC_IS_IMMUTABLE = "slc:isImmutable"; + public final static String SLC_IS_CONSTANT = "slc:isConstant"; + public final static String SLC_IS_HIDDEN = "slc:isHidden"; + + // base directories + public final static String SLC_SYSTEM = "slc:system"; + public final static String SLC_RESULTS = "slc:results"; + public final static String SLC_MY_RESULTS = "slc:myResults"; + public final static String SLC_PROCESSES = "slc:processes"; + + // result + public final static String SLC_SUCCESS = "slc:success"; + public final static String SLC_MESSAGE = "slc:message"; + public final static String SLC_TAG = "slc:tag"; + public final static String SLC_ERROR_MESSAGE = "slc:errorMessage"; + public final static String SLC_TEST_CASE = "slc:testCase"; + public final static String SLC_TEST_CASE_TYPE = "slc:testCaseType"; + + // diff result + public final static String SLC_SUMMARY = "slc:summary"; + public final static String SLC_ISSUES = "slc:issues"; + + /* + * SLC REPO + */ + // shared + public final static String SLC_URL = "slc:url"; + public final static String SLC_OPTIONAL = "slc:optional"; + public final static String SLC_AS_STRING = "slc:asString"; + + // origin + public final static String SLC_ORIGIN = "slc:origin"; + public final static String SLC_PROXY = "slc:proxy"; + + // slc:artifact + public final static String SLC_ARTIFACT_ID = "slc:artifactId"; + public final static String SLC_GROUP_ID = "slc:groupId"; + public final static String SLC_GROUP_BASE_ID = "slc:groupBaseId"; + public final static String SLC_ARTIFACT_VERSION = "slc:artifactVersion"; + public final static String SLC_ARTIFACT_EXTENSION = "slc:artifactExtension"; + public final static String SLC_ARTIFACT_CLASSIFIER = "slc:artifactClassifier"; + + // slc:jarArtifact + public final static String SLC_MANIFEST = "slc:manifest"; + + // shared OSGi + public final static String SLC_SYMBOLIC_NAME = "slc:symbolic-name"; + public final static String SLC_BUNDLE_VERSION = "slc:bundle-version"; + + // slc:osgiBaseVersion + public final static String SLC_MAJOR = "slc:major"; + public final static String SLC_MINOR = "slc:minor"; + public final static String SLC_MICRO = "slc:micro"; + // slc:osgiVersion + public final static String SLC_QUALIFIER = "slc:qualifier"; + + // slc:exportedPackage + public final static String SLC_USES = "slc:uses"; + + // slc:modularDistribution + public final static String SLC_MODULES = "slc:modules"; + + // RPM + // slc:rpm + public final static String SLC_RPM_VERSION = "slc:rpmVersion"; + public final static String SLC_RPM_RELEASE = "slc:rpmRelease"; + public final static String SLC_RPM_ARCH = "slc:rpmArch"; + public final static String SLC_RPM_ARCHIVE_SIZE = "slc:rpmArchiveSize"; +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcTypes.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcTypes.java new file mode 100644 index 000000000..5cb6a1361 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/SlcTypes.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr; + +/** JCR node types used by SLC */ +public interface SlcTypes { + + public final static String SLC_NAME_VERSION = "slc:nameVersion"; + public final static String SLC_CATEGORIZED_NAME_VERSION = "slc:categorizedNameVersion"; + + public final static String SLC_AGENT_FACTORY = "slc:agentFactory"; + public final static String SLC_AGENT = "slc:agent"; + public final static String SLC_MODULE = "slc:module"; + public final static String SLC_EXECUTION_MODULE = "slc:executionModule"; + public final static String SLC_EXECUTION_SPEC = "slc:executionSpec"; + public final static String SLC_EXECUTION_FLOW = "slc:executionFlow"; + public final static String SLC_PROCESS = "slc:process"; + public final static String SLC_REALIZED_FLOW = "slc:realizedFlow"; + + public final static String SLC_EXECUTION_SPEC_ATTRIBUTE = "slc:executionSpecAttribute"; + public final static String SLC_PRIMITIVE_SPEC_ATTRIBUTE = "slc:primitiveSpecAttribute"; + public final static String SLC_REF_SPEC_ATTRIBUTE = "slc:refSpecAttribute"; + + public final static String SLC_TEST_RESULT = "slc:testResult"; + public final static String SLC_CHECK = "slc:check"; + public final static String SLC_PROPERTY = "slc:property"; + public final static String SLC_DIFF_RESULT = "slc:diffResult"; + + // Node types used for user defined and managed result UI tree + public final static String SLC_MY_RESULT_ROOT_FOLDER = "slc:myResultRootFolder"; + public final static String SLC_RESULT_FOLDER = "slc:resultFolder"; + + // Log levels + public final static String SLC_LOG_ENTRY = "slc:logEntry"; + public final static String SLC_LOG_TRACE = "slc:logTrace"; + public final static String SLC_LOG_DEBUG = "slc:logDebug"; + public final static String SLC_LOG_INFO = "slc:logInfo"; + public final static String SLC_LOG_WARNING = "slc:logWarning"; + public final static String SLC_LOG_ERROR = "slc:logError"; + + /* + * REPO + */ + public final static String SLC_ARTIFACT = "slc:artifact"; + public final static String SLC_ARTIFACT_VERSION_BASE = "slc:artifactVersion"; + public final static String SLC_ARTIFACT_BASE = "slc:artifactBase"; + public final static String SLC_GROUP_BASE = "slc:groupBase"; + public final static String SLC_JAR_FILE = "slc:jarFile"; + public final static String SLC_BUNDLE_ARTIFACT = "slc:bundleArtifact"; + public final static String SLC_OSGI_VERSION = "slc:osgiVersion"; + public final static String SLC_JAVA_PACKAGE = "slc:javaPackage"; + public final static String SLC_EXPORTED_PACKAGE = "slc:exportedPackage"; + public final static String SLC_IMPORTED_PACKAGE = "slc:importedPackage"; + public final static String SLC_DYNAMIC_IMPORTED_PACKAGE = "slc:dynamicImportedPackage"; + public final static String SLC_REQUIRED_BUNDLE = "slc:requiredBundle"; + public final static String SLC_FRAGMENT_HOST = "slc:fragmentHost"; + + // Distribution management + // public final static String SLC_CATEGORY = "slc:category"; + public final static String SLC_MODULAR_DISTRIBUTION_BASE = "slc:modularDistributionBase"; + public final static String SLC_MODULAR_DISTRIBUTION = "slc:modularDistribution"; + public final static String SLC_MODULE_COORDINATES = "slc:moduleCoordinates"; + + // origin + public final static String SLC_KNOWN_ORIGIN = "slc:knownOrigin"; + public final static String SLC_PROXIED = "slc:proxied"; + + // rpm + public final static String SLC_RPM = "slc:rpm"; +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAgent.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAgent.java new file mode 100644 index 000000000..1c00b47fd --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAgent.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr.execution; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.DefaultAgent; +import org.argeo.slc.core.execution.ProcessThread; +import org.argeo.slc.execution.ExecutionModulesManager; +import org.argeo.slc.execution.ExecutionProcess; +import org.argeo.slc.jcr.SlcJcrConstants; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; + +/** SLC VM agent synchronizing with a JCR repository. */ +public class JcrAgent extends DefaultAgent implements SlcNames { + // final static String ROLE_REMOTE = "ROLE_REMOTE"; + final static String NODE_REPO_URI = "argeo.node.repo.uri"; + + private Repository repository; + + private String agentNodeName = "default"; + + /* + * LIFECYCLE + */ + protected String initAgentUuid() { + Session session = null; + try { + session = repository.login(); + + String agentFactoryPath = getAgentFactoryPath(); + Node vmAgentFactoryNode = JcrUtils.mkdirsSafe(session, + agentFactoryPath, SlcTypes.SLC_AGENT_FACTORY); + if (!vmAgentFactoryNode.hasNode(agentNodeName)) { + String uuid = UUID.randomUUID().toString(); + Node agentNode = vmAgentFactoryNode.addNode(agentNodeName, + SlcTypes.SLC_AGENT); + agentNode.setProperty(SLC_UUID, uuid); + } + session.save(); + return vmAgentFactoryNode.getNode(agentNodeName) + .getProperty(SLC_UUID).getString(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot find JCR agent UUID", e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + @Override + public void destroy() { + super.destroy(); + } + + /* + * SLC AGENT + */ + @Override + protected ProcessThread createProcessThread( + ThreadGroup processesThreadGroup, + ExecutionModulesManager modulesManager, ExecutionProcess process) { + if (process instanceof JcrExecutionProcess) + return new JcrProcessThread(processesThreadGroup, modulesManager, + (JcrExecutionProcess) process); + else + return super.createProcessThread(processesThreadGroup, + modulesManager, process); + } + + /* + * UTILITIES + */ + public String getNodePath() { + return getAgentFactoryPath() + '/' + getAgentNodeName(); + } + + public String getAgentFactoryPath() { + try { + Boolean isRemote = System.getProperty(NODE_REPO_URI) != null; + String agentFactoryPath; + if (isRemote) { + InetAddress localhost = InetAddress.getLocalHost(); + agentFactoryPath = SlcJcrConstants.AGENTS_BASE_PATH + "/" + + localhost.getCanonicalHostName(); + + if (agentFactoryPath + .equals(SlcJcrConstants.VM_AGENT_FACTORY_PATH)) + throw new SlcException("Unsupported hostname " + + localhost.getCanonicalHostName()); + } else {// local + agentFactoryPath = SlcJcrConstants.VM_AGENT_FACTORY_PATH; + } + return agentFactoryPath; + } catch (UnknownHostException e) { + throw new SlcException("Cannot find agent factory base path", e); + } + } + + /* + * BEAN + */ + public String getAgentNodeName() { + return agentNodeName; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setAgentNodeName(String agentNodeName) { + this.agentNodeName = agentNodeName; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAttachmentUploader.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAttachmentUploader.java new file mode 100644 index 000000000..105d549cd --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrAttachmentUploader.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr.execution; + +import javax.jcr.Session; + +import org.argeo.slc.core.attachment.Attachment; +import org.argeo.slc.core.attachment.AttachmentUploader; +import org.springframework.core.io.Resource; + +/** JCR based attachment uploader */ +public class JcrAttachmentUploader implements AttachmentUploader { + private Session session; + + public void upload(Attachment attachment, Resource resource) { + session.toString(); + // not yet implemented, need to review the interface + } + + public void setSession(Session session) { + this.session = session; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java new file mode 100644 index 000000000..ea734a8bb --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr.execution; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.PrimitiveSpecAttribute; +import org.argeo.slc.core.execution.PrimitiveValue; +import org.argeo.slc.core.execution.RefSpecAttribute; +import org.argeo.slc.core.execution.RefValueChoice; +import org.argeo.slc.deploy.ModuleDescriptor; +import org.argeo.slc.execution.ExecutionFlowDescriptor; +import org.argeo.slc.execution.ExecutionModuleDescriptor; +import org.argeo.slc.execution.ExecutionModulesListener; +import org.argeo.slc.execution.ExecutionModulesManager; +import org.argeo.slc.execution.ExecutionSpec; +import org.argeo.slc.execution.ExecutionSpecAttribute; +import org.argeo.slc.jcr.SlcJcrUtils; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; + +/** + * Synchronizes the local execution runtime with a JCR repository. For the time + * being the state is completely reset from one start to another. + */ +public class JcrExecutionModulesListener implements ExecutionModulesListener, + SlcNames { + private final static String SLC_EXECUTION_MODULES_PROPERTY = "slc.executionModules"; + + private final static Log log = LogFactory + .getLog(JcrExecutionModulesListener.class); + private JcrAgent agent; + + private ExecutionModulesManager modulesManager; + + private Repository repository; + /** + * We don't use a thread bound session because many different threads will + * call this critical component and we don't want to login each time. We + * therefore rather protect access to this session via synchronized. + */ + private Session session; + + /* + * LIFECYCLE + */ + public void init() { + try { + session = repository.login(); + clearAgent(); + if (modulesManager != null) { + Node agentNode = session.getNode(agent.getNodePath()); + + List moduleDescriptors = modulesManager + .listModules(); + + // scan SLC-ExecutionModule metadata + for (ModuleDescriptor md : moduleDescriptors) { + if (md.getMetadata().containsKey( + ExecutionModuleDescriptor.SLC_EXECUTION_MODULE)) { + String moduleNodeName = SlcJcrUtils + .getModuleNodeName(md); + Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode + .getNode(moduleNodeName) : agentNode + .addNode(moduleNodeName); + moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, md.getName()); + moduleNode.setProperty(SLC_VERSION, md.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, + md.getTitle()); + moduleNode.setProperty(Property.JCR_DESCRIPTION, + md.getDescription()); + moduleNode.setProperty(SLC_STARTED, md.getStarted()); + } + } + + // scan execution modules property + String executionModules = System + .getProperty(SLC_EXECUTION_MODULES_PROPERTY); + if (executionModules != null) { + for (String executionModule : executionModules.split(",")) { + allModules: for (ModuleDescriptor md : moduleDescriptors) { + String moduleNodeName = SlcJcrUtils + .getModuleNodeName(md); + if (md.getName().equals(executionModule)) { + Node moduleNode = agentNode + .hasNode(moduleNodeName) ? agentNode + .getNode(moduleNodeName) : agentNode + .addNode(moduleNodeName); + moduleNode + .addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, md.getName()); + moduleNode.setProperty(SLC_VERSION, + md.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, + md.getTitle()); + moduleNode.setProperty( + Property.JCR_DESCRIPTION, + md.getDescription()); + moduleNode.setProperty(SLC_STARTED, + md.getStarted()); + break allModules; + } + } + } + + // save if needed + if (session.hasPendingChanges()) + session.save(); + } + } + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + JcrUtils.logoutQuietly(session); + throw new SlcException("Cannot initialize modules", e); + } + } + + public void destroy() { + clearAgent(); + JcrUtils.logoutQuietly(session); + } + + protected synchronized void clearAgent() { + try { + Node agentNode = session.getNode(agent.getNodePath()); + for (NodeIterator nit = agentNode.getNodes(); nit.hasNext();) + nit.nextNode().remove(); + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot clear agent " + agent, e); + } + } + + /* + * EXECUTION MODULES LISTENER + */ + + public synchronized void executionModuleAdded( + ModuleDescriptor moduleDescriptor) { + syncExecutionModule(moduleDescriptor); + } + + protected void syncExecutionModule(ModuleDescriptor moduleDescriptor) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + String moduleNodeName = SlcJcrUtils + .getModuleNodeName(moduleDescriptor); + Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode + .getNode(moduleNodeName) : agentNode + .addNode(moduleNodeName); + moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, moduleDescriptor.getName()); + moduleNode.setProperty(SLC_VERSION, moduleDescriptor.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, + moduleDescriptor.getTitle()); + moduleNode.setProperty(Property.JCR_DESCRIPTION, + moduleDescriptor.getDescription()); + moduleNode.setProperty(SLC_STARTED, moduleDescriptor.getStarted()); + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot sync module " + moduleDescriptor, e); + } + } + + public synchronized void executionModuleRemoved( + ModuleDescriptor moduleDescriptor) { + try { + String moduleName = SlcJcrUtils.getModuleNodeName(moduleDescriptor); + Node agentNode = session.getNode(agent.getNodePath()); + if (agentNode.hasNode(moduleName)) { + Node moduleNode = agentNode.getNode(moduleName); + for (NodeIterator nit = moduleNode.getNodes(); nit.hasNext();) { + nit.nextNode().remove(); + } + moduleNode.setProperty(SLC_STARTED, false); + } + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot remove module " + moduleDescriptor, + e); + } + } + + public synchronized void executionFlowAdded(ModuleDescriptor module, + ExecutionFlowDescriptor efd) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + Node moduleNode = agentNode.getNode(SlcJcrUtils + .getModuleNodeName(module)); + String relativePath = getExecutionFlowRelativePath(efd); + @SuppressWarnings("unused") + Node flowNode = null; + if (!moduleNode.hasNode(relativePath)) { + flowNode = createExecutionFlowNode(moduleNode, relativePath, + efd); + session.save(); + } else { + flowNode = moduleNode.getNode(relativePath); + } + + if (log.isTraceEnabled()) + log.trace("Flow " + efd + " added to JCR"); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot add flow " + efd + " from module " + + module, e); + } + + } + + protected Node createExecutionFlowNode(Node moduleNode, + String relativePath, ExecutionFlowDescriptor efd) + throws RepositoryException { + Node flowNode = null; + List pathTokens = Arrays.asList(relativePath.split("/")); + + Iterator names = pathTokens.iterator(); + // create intermediary paths + Node currNode = moduleNode; + while (names.hasNext()) { + String name = names.next(); + if (currNode.hasNode(name)) + currNode = currNode.getNode(name); + else { + if (names.hasNext()) + currNode = currNode.addNode(name); + else + flowNode = currNode.addNode(name, + SlcTypes.SLC_EXECUTION_FLOW); + } + } + + // name, description + flowNode.setProperty(SLC_NAME, efd.getName()); + String endName = pathTokens.get(pathTokens.size() - 1); + flowNode.setProperty(Property.JCR_TITLE, endName); + if (efd.getDescription() != null + && !efd.getDescription().trim().equals("")) { + flowNode.setProperty(Property.JCR_DESCRIPTION, efd.getDescription()); + } else { + flowNode.setProperty(Property.JCR_DESCRIPTION, endName); + } + + // execution spec + ExecutionSpec executionSpec = efd.getExecutionSpec(); + String esName = executionSpec.getName(); + if (esName == null || esName.equals(ExecutionSpec.INTERNAL_NAME) + || esName.contains("#")/* automatically generated bean name */) { + // internal spec node + mapExecutionSpec(flowNode, executionSpec); + } else { + // reference spec node + Node executionSpecsNode = moduleNode.hasNode(SLC_EXECUTION_SPECS) ? moduleNode + .getNode(SLC_EXECUTION_SPECS) : moduleNode + .addNode(SLC_EXECUTION_SPECS); + Node executionSpecNode = executionSpecsNode.addNode(esName, + SlcTypes.SLC_EXECUTION_SPEC); + executionSpecNode.setProperty(SLC_NAME, esName); + executionSpecNode.setProperty(Property.JCR_TITLE, esName); + if (executionSpec.getDescription() != null + && !executionSpec.getDescription().trim().equals("")) + executionSpecNode.setProperty(Property.JCR_DESCRIPTION, + executionSpec.getDescription()); + mapExecutionSpec(executionSpecNode, executionSpec); + flowNode.setProperty(SLC_SPEC, executionSpecNode); + } + + // flow values + for (String attr : efd.getValues().keySet()) { + ExecutionSpecAttribute esa = executionSpec.getAttributes() + .get(attr); + if (esa instanceof PrimitiveSpecAttribute) { + PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa; + // if spec reference there will be no node at this stage + Node valueNode = JcrUtils.getOrAdd(flowNode, attr); + valueNode.setProperty(SLC_TYPE, psa.getType()); + SlcJcrUtils.setPrimitiveAsProperty(valueNode, SLC_VALUE, + (PrimitiveValue) efd.getValues().get(attr)); + } + } + + return flowNode; + } + + /** + * Base can be either an execution spec node, or an execution flow node (in + * case the execution spec is internal) + */ + protected void mapExecutionSpec(Node baseNode, ExecutionSpec executionSpec) + throws RepositoryException { + for (String attrName : executionSpec.getAttributes().keySet()) { + ExecutionSpecAttribute esa = executionSpec.getAttributes().get( + attrName); + Node attrNode = baseNode.addNode(attrName); + // booleans + attrNode.addMixin(SlcTypes.SLC_EXECUTION_SPEC_ATTRIBUTE); + attrNode.setProperty(SLC_IS_IMMUTABLE, esa.getIsImmutable()); + attrNode.setProperty(SLC_IS_CONSTANT, esa.getIsConstant()); + attrNode.setProperty(SLC_IS_HIDDEN, esa.getIsHidden()); + + if (esa instanceof PrimitiveSpecAttribute) { + attrNode.addMixin(SlcTypes.SLC_PRIMITIVE_SPEC_ATTRIBUTE); + PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa; + SlcJcrUtils.setPrimitiveAsProperty(attrNode, SLC_VALUE, psa); + attrNode.setProperty(SLC_TYPE, psa.getType()); + } else if (esa instanceof RefSpecAttribute) { + attrNode.addMixin(SlcTypes.SLC_REF_SPEC_ATTRIBUTE); + RefSpecAttribute rsa = (RefSpecAttribute) esa; + attrNode.setProperty(SLC_TYPE, rsa.getTargetClassName()); + Object value = rsa.getValue(); + if (rsa.getChoices() != null) { + Integer index = null; + int count = 0; + for (RefValueChoice choice : rsa.getChoices()) { + String name = choice.getName(); + if (value != null && name.equals(value.toString())) + index = count; + Node choiceNode = attrNode.addNode(choice.getName()); + choiceNode.addMixin(NodeType.MIX_TITLE); + choiceNode.setProperty(Property.JCR_TITLE, + choice.getName()); + if (choice.getDescription() != null + && !choice.getDescription().trim().equals("")) + choiceNode.setProperty(Property.JCR_DESCRIPTION, + choice.getDescription()); + count++; + } + + if (index != null) + attrNode.setProperty(SLC_VALUE, index); + } + } + } + } + + public synchronized void executionFlowRemoved(ModuleDescriptor module, + ExecutionFlowDescriptor executionFlow) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + Node moduleNode = agentNode.getNode(SlcJcrUtils + .getModuleNodeName(module)); + String relativePath = getExecutionFlowRelativePath(executionFlow); + if (moduleNode.hasNode(relativePath)) + moduleNode.getNode(relativePath).remove(); + agentNode.getSession().save(); + } catch (RepositoryException e) { + throw new SlcException("Cannot remove flow " + executionFlow + + " from module " + module, e); + } + } + + /* + * UTILITIES + */ + /** @return the relative path, never starts with '/' */ + @SuppressWarnings("deprecation") + protected String getExecutionFlowRelativePath( + ExecutionFlowDescriptor executionFlow) { + String relativePath = executionFlow.getPath() == null ? executionFlow + .getName() : executionFlow.getPath() + '/' + + executionFlow.getName(); + // we assume that it is more than one char long + if (relativePath.charAt(0) == '/') + relativePath = relativePath.substring(1); + // FIXME quick hack to avoid duplicate '/' + relativePath = relativePath.replaceAll("//", "/"); + return relativePath; + } + + /* + * BEAN + */ + public void setAgent(JcrAgent agent) { + this.agent = agent; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setModulesManager(ExecutionModulesManager modulesManager) { + this.modulesManager = modulesManager; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionProcess.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionProcess.java new file mode 100644 index 000000000..180d8f008 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionProcess.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr.execution; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.ProcessThread; +import org.argeo.slc.execution.ExecutionProcess; +import org.argeo.slc.execution.ExecutionStep; +import org.argeo.slc.execution.RealizedFlow; +import org.argeo.slc.jcr.SlcJcrUtils; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; + +/** Execution process implementation based on a JCR node. */ +public class JcrExecutionProcess implements ExecutionProcess, SlcNames { + private final static Log log = LogFactory.getLog(JcrExecutionProcess.class); + private final Node node; + + private Long nextLogLine = 1l; + + public JcrExecutionProcess(Node node) { + this.node = node; + } + + public synchronized String getUuid() { + try { + return node.getProperty(SLC_UUID).getString(); + } catch (RepositoryException e) { + throw new SlcException("Cannot get uuid for " + node, e); + } + } + + public synchronized String getStatus() { + try { + return node.getProperty(SLC_STATUS).getString(); + } catch (RepositoryException e) { + log.error("Cannot get status: " + e); + // we should re-throw exception because this information can + // probably used for monitoring in case there are already unexpected + // exceptions + return UNKOWN; + } + } + + public synchronized void setStatus(String status) { + try { + node.setProperty(SLC_STATUS, status); + // last modified properties needs to be manually updated + // see https://issues.apache.org/jira/browse/JCR-2233 + JcrUtils.updateLastModified(node); + node.getSession().save(); + } catch (RepositoryException e) { + JcrUtils.discardUnderlyingSessionQuietly(node); + // we should re-throw exception because this information can + // probably used for monitoring in case there are already unexpected + // exceptions + log.error("Cannot set status " + status + ": " + e); + } + } + + /** + * Synchronized in order to make sure that there is no concurrent + * modification of {@link #nextLogLine}. + */ + public synchronized void addSteps(List steps) { + try { + steps: for (ExecutionStep step : steps) { + String type; + if (step.getType().equals(ExecutionStep.TRACE)) + type = SlcTypes.SLC_LOG_TRACE; + else if (step.getType().equals(ExecutionStep.DEBUG)) + type = SlcTypes.SLC_LOG_DEBUG; + else if (step.getType().equals(ExecutionStep.INFO)) + type = SlcTypes.SLC_LOG_INFO; + else if (step.getType().equals(ExecutionStep.WARNING)) + type = SlcTypes.SLC_LOG_WARNING; + else if (step.getType().equals(ExecutionStep.ERROR)) + type = SlcTypes.SLC_LOG_ERROR; + else + // skip + continue steps; + + String relPath = SLC_LOG + '/' + + step.getThread().replace('/', '_') + '/' + + step.getLocation().replace('.', '/'); + String path = node.getPath() + '/' + relPath; + // clean special character + // TODO factorize in JcrUtils + path = path.replace('@', '_'); + + Node location = JcrUtils.mkdirs(node.getSession(), path); + Node logEntry = location.addNode(Long.toString(nextLogLine), + type); + logEntry.setProperty(SLC_MESSAGE, step.getLog()); + Calendar calendar = new GregorianCalendar(); + calendar.setTime(step.getTimestamp()); + logEntry.setProperty(SLC_TIMESTAMP, calendar); + + // System.out.println("Logged " + logEntry.getPath()); + + nextLogLine++; + } + + // last modified properties needs to be manually updated + // see https://issues.apache.org/jira/browse/JCR-2233 + JcrUtils.updateLastModified(node); + + node.getSession().save(); + } catch (Exception e) { + JcrUtils.discardUnderlyingSessionQuietly(node); + e.printStackTrace(); + } + } + + // public Node getNode() { + // return node; + // } + + public List getRealizedFlows() { + try { + List realizedFlows = new ArrayList(); + Node rootRealizedFlowNode = node.getNode(SLC_FLOW); + // we just manage one level for the time being + NodeIterator nit = rootRealizedFlowNode.getNodes(SLC_FLOW); + while (nit.hasNext()) { + Node realizedFlowNode = nit.nextNode(); + + if (realizedFlowNode.hasNode(SLC_ADDRESS)) { + String flowPath = realizedFlowNode.getNode(SLC_ADDRESS) + .getProperty(Property.JCR_PATH).getString(); + NameVersion moduleNameVersion = SlcJcrUtils + .moduleNameVersion(flowPath); + ((ProcessThread) Thread.currentThread()) + .getExecutionModulesManager().start( + moduleNameVersion); + } + + RealizedFlow realizedFlow = new JcrRealizedFlow( + realizedFlowNode); + if (realizedFlow != null) + realizedFlows.add(realizedFlow); + } + return realizedFlows; + } catch (RepositoryException e) { + throw new SlcException("Cannot get realized flows", e); + } + } + + public String getNodePath() { + try { + return node.getPath(); + } catch (RepositoryException e) { + throw new SlcException("Cannot get process node path for " + node, + e); + } + } + + public Repository getRepository() { + try { + return node.getSession().getRepository(); + } catch (RepositoryException e) { + throw new SlcException("Cannot get process JCR repository for " + + node, e); + } + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrProcessThread.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrProcessThread.java new file mode 100644 index 000000000..8755829e7 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrProcessThread.java @@ -0,0 +1,95 @@ +/* + + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.jcr.execution; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.ArgeoException; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.core.execution.ProcessThread; +import org.argeo.slc.execution.ExecutionModulesManager; +import org.argeo.slc.execution.ExecutionProcess; +import org.argeo.slc.execution.RealizedFlow; +import org.argeo.slc.jcr.SlcNames; + +/** Where the actual execution takes place */ +public class JcrProcessThread extends ProcessThread implements SlcNames { + + public JcrProcessThread(ThreadGroup processesThreadGroup, + ExecutionModulesManager executionModulesManager, + JcrExecutionProcess process) { + super(processesThreadGroup, executionModulesManager, process); + } + + /** Overridden in order to set progress status on realized flow nodes. */ + @Override + protected void process() throws InterruptedException { + Session session = null; + if (getProcess() instanceof JcrExecutionProcess) + try { + session = ((JcrExecutionProcess) getProcess()).getRepository() + .login(); + + List realizedFlows = getProcess() + .getRealizedFlows(); + for (RealizedFlow realizedFlow : realizedFlows) { + Node realizedFlowNode = session + .getNode(((JcrRealizedFlow) realizedFlow).getPath()); + setFlowStatus(realizedFlowNode, ExecutionProcess.RUNNING); + + try { + // + // EXECUTE THE FLOW + // + execute(realizedFlow, true); + + setFlowStatus(realizedFlowNode, + ExecutionProcess.COMPLETED); + } catch (RepositoryException e) { + throw e; + } catch (InterruptedException e) { + setFlowStatus(realizedFlowNode, ExecutionProcess.KILLED); + throw e; + } catch (RuntimeException e) { + setFlowStatus(realizedFlowNode, ExecutionProcess.ERROR); + throw e; + } + } + } catch (RepositoryException e) { + throw new ArgeoException("Cannot process " + + getJcrExecutionProcess().getNodePath(), e); + } finally { + JcrUtils.logoutQuietly(session); + } + else + super.process(); + } + + protected void setFlowStatus(Node realizedFlowNode, String status) + throws RepositoryException { + realizedFlowNode.setProperty(SLC_STATUS, status); + realizedFlowNode.getSession().save(); + } + + protected JcrExecutionProcess getJcrExecutionProcess() { + return (JcrExecutionProcess) getProcess(); + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrRealizedFlow.java b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrRealizedFlow.java new file mode 100644 index 000000000..2dd56e331 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrRealizedFlow.java @@ -0,0 +1,143 @@ +package org.argeo.slc.jcr.execution; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; + +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.DefaultExecutionSpec; +import org.argeo.slc.core.execution.PrimitiveSpecAttribute; +import org.argeo.slc.core.execution.PrimitiveUtils; +import org.argeo.slc.core.execution.RefSpecAttribute; +import org.argeo.slc.execution.ExecutionFlowDescriptor; +import org.argeo.slc.execution.ExecutionSpecAttribute; +import org.argeo.slc.execution.RealizedFlow; +import org.argeo.slc.jcr.SlcJcrUtils; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; + +public class JcrRealizedFlow extends RealizedFlow implements SlcNames { + private static final long serialVersionUID = -3709453850260712001L; + private String path; + + public JcrRealizedFlow(Node node) { + try { + this.path = node.getPath(); + loadFromNode(node); + } catch (RepositoryException e) { + throw new SlcException("Cannot initialize from " + node, e); + } + } + + protected void loadFromNode(Node realizedFlowNode) + throws RepositoryException { + if (realizedFlowNode.hasNode(SLC_ADDRESS)) { + String flowPath = realizedFlowNode.getNode(SLC_ADDRESS) + .getProperty(Property.JCR_PATH).getString(); + // TODO: convert to local path if remote + // FIXME start related module + Node flowNode = realizedFlowNode.getSession().getNode(flowPath); + String flowName = flowNode.getProperty(SLC_NAME).getString(); + String description = null; + if (flowNode.hasProperty(Property.JCR_DESCRIPTION)) + description = flowNode.getProperty(Property.JCR_DESCRIPTION) + .getString(); + + Node executionModuleNode = flowNode.getSession().getNode( + SlcJcrUtils.modulePath(flowPath)); + String executionModuleName = executionModuleNode.getProperty( + SLC_NAME).getString(); + String executionModuleVersion = executionModuleNode.getProperty( + SLC_VERSION).getString(); + + RealizedFlow realizedFlow = this; + realizedFlow.setModuleName(executionModuleName); + realizedFlow.setModuleVersion(executionModuleVersion); + + // retrieve execution spec + DefaultExecutionSpec executionSpec = new DefaultExecutionSpec(); + Map attrs = readExecutionSpecAttributes(realizedFlowNode); + executionSpec.setAttributes(attrs); + + // set execution spec name + if (flowNode.hasProperty(SlcNames.SLC_SPEC)) { + Node executionSpecNode = flowNode.getProperty(SLC_SPEC) + .getNode(); + executionSpec.setBeanName(executionSpecNode.getProperty( + SLC_NAME).getString()); + } + + // explicitly retrieve values + Map values = new HashMap(); + for (String attrName : attrs.keySet()) { + ExecutionSpecAttribute attr = attrs.get(attrName); + Object value = attr.getValue(); + values.put(attrName, value); + } + + ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(flowName, + description, values, executionSpec); + realizedFlow.setFlowDescriptor(efd); + } else { + throw new SlcException("Unsupported realized flow " + + realizedFlowNode); + } + } + + protected Map readExecutionSpecAttributes( + Node node) { + try { + Map attrs = new HashMap(); + for (NodeIterator nit = node.getNodes(); nit.hasNext();) { + Node specAttrNode = nit.nextNode(); + if (specAttrNode + .isNodeType(SlcTypes.SLC_PRIMITIVE_SPEC_ATTRIBUTE)) { + String type = specAttrNode.getProperty(SLC_TYPE) + .getString(); + Object value = null; + if (specAttrNode.hasProperty(SLC_VALUE)) { + String valueStr = specAttrNode.getProperty(SLC_VALUE) + .getString(); + value = PrimitiveUtils.convert(type, valueStr); + } + PrimitiveSpecAttribute specAttr = new PrimitiveSpecAttribute( + type, value); + attrs.put(specAttrNode.getName(), specAttr); + } else if (specAttrNode + .isNodeType(SlcTypes.SLC_REF_SPEC_ATTRIBUTE)) { + if (!specAttrNode.hasProperty(SLC_VALUE)) { + continue; + } + Integer value = (int) specAttrNode.getProperty(SLC_VALUE) + .getLong(); + RefSpecAttribute specAttr = new RefSpecAttribute(); + NodeIterator children = specAttrNode.getNodes(); + int index = 0; + String id = null; + while (children.hasNext()) { + Node child = children.nextNode(); + if (index == value) + id = child.getName(); + index++; + } + specAttr.setValue(id); + attrs.put(specAttrNode.getName(), specAttr); + } + // throw new SlcException("Unsupported spec attribute " + // + specAttrNode); + } + return attrs; + } catch (RepositoryException e) { + throw new SlcException("Cannot read spec attributes from " + node, + e); + } + } + + public String getPath() { + return path; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/jcr/slc.cnd b/org.argeo.slc.core/src/org/argeo/slc/jcr/slc.cnd new file mode 100644 index 000000000..6e23cf5e9 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/jcr/slc.cnd @@ -0,0 +1,139 @@ + + +// COMMON +[slc:nameVersion] > nt:base +mixin +- slc:name (STRING) m +- slc:version (STRING) m + +[slc:categorizedNameVersion] > slc:nameVersion +mixin +// define as mandatory? +- slc:category (STRING) + +// AGENT +[slc:agentFactory] > nt:unstructured, mix:title ++ * (slc:agent) + +[slc:agent] > nt:unstructured, mix:title ++ * (slc:module) + +[slc:module] > slc:nameVersion, mix:title +mixin + +[slc:activableModule] > slc:module +mixin + +[slc:executionModule] > slc:activableModule +mixin + +[slc:executionSpec] > nt:unstructured, mix:referenceable, mix:title +- slc:name (STRING) m ++ * (slc:executionSpecAttribute) * + +[slc:executionSpecAttribute] > nt:base +mixin abstract +- slc:isImmutable (BOOLEAN) +- slc:isConstant (BOOLEAN) +- slc:isHidden (BOOLEAN) + +[slc:primitiveSpecAttribute] > slc:executionSpecAttribute +mixin +- slc:type (STRING) +- slc:value (UNDEFINED) + +[slc:refSpecAttribute] > slc:executionSpecAttribute +orderable +mixin +// typically a class name +- slc:type (STRING) +- slc:value (UNDEFINED) ++ * (mix:title) + +[slc:executionFlow] > nt:unstructured, mix:title +- slc:name (STRING) ! m +// if the execution spec is a referenceable node +- slc:spec (REFERENCE) +// if the execution spec is internal (without name) ++ * (slc:executionSpecAttribute) * + +// PROCESS +[slc:process] > nt:unstructured, mix:created, mix:lastModified +orderable +- slc:uuid (STRING) ! m +- slc:status (STRING) m ++ slc:flow (slc:realizedFlow) ++ slc:log + +// The first part of the relative path is the thread name, rest is location +[slc:logEntry] > nt:unstructured +abstract +- slc:message (STRING) ! +- slc:timestamp (STRING) + +// Log levels are set via types. +// Querying one level also queries the higher levels thanks to the inheritance +// e.g. 'select * from [slc:logWarn]' also returns errors +[slc:logTrace] > slc:logEntry + +[slc:logDebug] > slc:logTrace + +[slc:logInfo] > slc:logDebug + +[slc:logWarning] > slc:logInfo + +[slc:logError] > slc:logWarning + +[slc:realizedFlow] > nt:base +orderable +mixin +// the name of the flow +// - slc:flow (STRING) +// the name of the execution spec +// - slc:spec (STRING) +- slc:started (DATE) +- slc:completed (DATE) +//- slc:status (STRING) ++ slc:address (nt:address) ++ slc:flow (slc:realizedFlow) * +// the realized execution spec attributes ++ * (slc:executionSpecAttribute) * + +// RESULT +[slc:testResult] > nt:unstructured, mix:created, mix:lastModified +- slc:uuid (STRING) ! m +- slc:testCase (STRING) +- slc:completed (DATE) +// Helper to keep a centralize place to have testResultStatus +// when adding more than one result part to a given testResult ++ slc:aggregatedStatus (slc:check) +// DEPRECATED - FOR COMPATIBILITY - DO NOT USE ++ slc:testStatus (slc:check) + +[slc:diffResult] > slc:testResult ++ slc:summary ++ slc:issues + +[slc:resultFolder] > nt:unstructured ++ slc:folderStatus (slc:check) ++ * (slc:resultFolder) * ++ * (slc:testResult) * + +// base node for user defined and managed result tree +// simplify UI management +[slc:myResultRootFolder] > nt:unstructured ++ * (slc:resultFolder) * ++ * (slc:testResult) * + +[slc:check] > nt:unstructured +// true for PASSED, false for FAILED or ERROR +- slc:success (BOOLEAN) ! m +- slc:message (STRING) +// ERROR if set, the check could not be performed because of an unexpected exception +- slc:errorMessage (STRING) +// to ease transition with legacy approach ++ * (slc:property) * + +[slc:property] > nt:unstructured +- slc:name (STRING) ! m +- slc:value (STRING) m diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/BundleRegister.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/BundleRegister.java new file mode 100644 index 000000000..747785fee --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/BundleRegister.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +/** Experimental A structured set of OSGi bundles. */ +public interface BundleRegister { + /** + * @param pkg + * the Java package + * @param version + * the version, can be only major.minor or null + * @return the bundle providing this package or null if none was found + */ + public String bundleProvidingPackage(String pkg, String version); +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/BundlesManager.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/BundlesManager.java new file mode 100644 index 000000000..8c975eeef --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/BundlesManager.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.SlcException; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.util.tracker.ServiceTracker; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.eclipse.gemini.blueprint.context.BundleContextAware; +import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextEvent; +import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextListener; +import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextClosedEvent; +import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextFailedEvent; +import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextRefreshedEvent; +import org.eclipse.gemini.blueprint.util.OsgiBundleUtils; +import org.eclipse.gemini.blueprint.util.OsgiFilterUtils; +import org.springframework.util.Assert; + +/** Wraps low-level access to a {@link BundleContext} */ +public class BundlesManager implements BundleContextAware, FrameworkListener, + InitializingBean, DisposableBean, OsgiBundleApplicationContextListener { + private final static Log log = LogFactory.getLog(BundlesManager.class); + + private BundleContext bundleContext; + + private Long defaultTimeout = 60 * 1000l; + private Long pollingPeriod = 200l; + + // Refresh sync objects + private final Object refreshedPackageSem = new Object(); + private Boolean packagesRefreshed = false; + + public BundlesManager() { + } + + public BundlesManager(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + /** + * Stop the module, update it, refresh it and restart it. All synchronously. + */ + public void upgradeSynchronous(OsgiBundle osgiBundle) { + try { + Bundle bundle = findRelatedBundle(osgiBundle); + + long begin = System.currentTimeMillis(); + + long bStop = begin; + stopSynchronous(bundle); + + long bUpdate = System.currentTimeMillis(); + updateSynchronous(bundle); + + // Refresh in case there are fragments + long bRefresh = System.currentTimeMillis(); + refreshSynchronous(bundle); + + long bStart = System.currentTimeMillis(); + startSynchronous(bundle); + + long aStart = System.currentTimeMillis(); + if (log.isTraceEnabled()) { + log.debug("OSGi upgrade performed in " + (aStart - begin) + + "ms for bundle " + osgiBundle); + log.debug(" stop \t: " + (bUpdate - bStop) + "ms"); + log.debug(" update\t: " + (bRefresh - bUpdate) + "ms"); + log.debug(" refresh\t: " + (bStart - bRefresh) + "ms"); + log.debug(" start\t: " + (aStart - bStart) + "ms"); + log.debug(" TOTAL\t: " + (aStart - begin) + "ms"); + } + + long bAppContext = System.currentTimeMillis(); + String filter = "(Bundle-SymbolicName=" + bundle.getSymbolicName() + + ")"; + // Wait for application context to be ready + // TODO: use service tracker + ServiceReference[] srs = getServiceRefSynchronous( + ApplicationContext.class.getName(), filter); + ServiceReference sr = srs[0]; + long aAppContext = System.currentTimeMillis(); + long end = aAppContext; + + if (log.isTraceEnabled()) { + log.debug("Application context refresh performed in " + + (aAppContext - bAppContext) + "ms for bundle " + + osgiBundle); + } + + if (log.isDebugEnabled()) + log.debug("Bundle '" + bundle.getSymbolicName() + + "' upgraded and ready " + " (upgrade performed in " + + (end - begin) + "ms)."); + + if (log.isTraceEnabled()) { + ApplicationContext applicationContext = (ApplicationContext) bundleContext + .getService(sr); + int beanDefCount = applicationContext.getBeanDefinitionCount(); + log.debug(" " + beanDefCount + " beans in app context of " + + bundle.getSymbolicName() + + ", average init time per bean=" + (end - begin) + / beanDefCount + "ms"); + } + + bundleContext.ungetService(sr); + + } catch (Exception e) { + throw new SlcException("Cannot update bundle " + osgiBundle, e); + } + } + + /** Updates bundle synchronously. */ + protected void updateSynchronous(Bundle bundle) throws BundleException { + bundle.update(); + boolean waiting = true; + + long begin = System.currentTimeMillis(); + do { + int state = bundle.getState(); + if (state == Bundle.INSTALLED || state == Bundle.ACTIVE + || state == Bundle.RESOLVED) + waiting = false; + + sleepWhenPolling(); + checkTimeout(begin, "Update of bundle " + bundle.getSymbolicName() + + " timed out. Bundle state = " + bundle.getState()); + } while (waiting); + + if (log.isTraceEnabled()) + log.debug("Bundle " + bundle.getSymbolicName() + " updated."); + } + + /** Starts bundle synchronously. Does nothing if already started. */ + protected void startSynchronous(Bundle bundle) throws BundleException { + int originalState = bundle.getState(); + if (originalState == Bundle.ACTIVE) + return; + + bundle.start(); + boolean waiting = true; + + long begin = System.currentTimeMillis(); + do { + if (bundle.getState() == Bundle.ACTIVE) + waiting = false; + + sleepWhenPolling(); + checkTimeout(begin, "Start of bundle " + bundle.getSymbolicName() + + " timed out. Bundle state = " + bundle.getState()); + } while (waiting); + + if (log.isTraceEnabled()) + log.debug("Bundle " + bundle.getSymbolicName() + " started."); + } + + /** Stops bundle synchronously. Does nothing if already started. */ + protected void stopSynchronous(Bundle bundle) throws BundleException { + int originalState = bundle.getState(); + if (originalState != Bundle.ACTIVE) + return; + + bundle.stop(); + boolean waiting = true; + + long begin = System.currentTimeMillis(); + do { + if (bundle.getState() != Bundle.ACTIVE + && bundle.getState() != Bundle.STOPPING) + waiting = false; + + sleepWhenPolling(); + checkTimeout(begin, "Stop of bundle " + bundle.getSymbolicName() + + " timed out. Bundle state = " + bundle.getState()); + } while (waiting); + + if (log.isTraceEnabled()) + log.debug("Bundle " + bundle.getSymbolicName() + " stopped."); + } + + /** Refresh bundle synchronously. Does nothing if already started. */ + protected void refreshSynchronous(Bundle bundle) throws BundleException { + ServiceReference packageAdminRef = bundleContext + .getServiceReference(PackageAdmin.class.getName()); + PackageAdmin packageAdmin = (PackageAdmin) bundleContext + .getService(packageAdminRef); + Bundle[] bundles = { bundle }; + + long begin = System.currentTimeMillis(); + synchronized (refreshedPackageSem) { + packagesRefreshed = false; + packageAdmin.refreshPackages(bundles); + try { + refreshedPackageSem.wait(defaultTimeout); + } catch (InterruptedException e) { + // silent + } + if (!packagesRefreshed) { + long now = System.currentTimeMillis(); + throw new SlcException("Packages not refreshed after " + + (now - begin) + "ms"); + } else { + packagesRefreshed = false; + } + } + + if (log.isTraceEnabled()) + log.debug("Bundle " + bundle.getSymbolicName() + " refreshed."); + } + + public void frameworkEvent(FrameworkEvent event) { + if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) { + synchronized (refreshedPackageSem) { + packagesRefreshed = true; + refreshedPackageSem.notifyAll(); + } + } + } + + public ServiceReference[] getServiceRefSynchronous(String clss, + String filter) throws InvalidSyntaxException { + if (log.isTraceEnabled()) + log.debug("Filter: '" + filter + "'"); + ServiceReference[] sfs = null; + boolean waiting = true; + long begin = System.currentTimeMillis(); + do { + sfs = bundleContext.getServiceReferences(clss, filter); + + if (sfs != null) + waiting = false; + + sleepWhenPolling(); + checkTimeout(begin, "Search of services " + clss + " with filter " + + filter + " timed out."); + } while (waiting); + + return sfs; + } + + protected void checkTimeout(long begin, String msg) { + long now = System.currentTimeMillis(); + if (now - begin > defaultTimeout) + throw new SlcException(msg + " (timeout after " + (now - begin) + + "ms)"); + + } + + protected void sleepWhenPolling() { + try { + Thread.sleep(pollingPeriod); + } catch (InterruptedException e) { + throw new SlcException("Polling interrupted"); + } + } + + /** Creates and open a new service tracker. */ + public ServiceTracker newTracker(Class clss) { + ServiceTracker st = new ServiceTracker(bundleContext, clss.getName(), + null); + st.open(); + return st; + } + + @SuppressWarnings(value = { "unchecked" }) + public T getSingleService(Class clss, String filter, + Boolean synchronous) { + if (filter != null) + Assert.isTrue(OsgiFilterUtils.isValidFilter(filter), "valid filter"); + ServiceReference[] sfs; + try { + if (synchronous) + sfs = getServiceRefSynchronous(clss.getName(), filter); + else + sfs = bundleContext + .getServiceReferences(clss.getName(), filter); + } catch (InvalidSyntaxException e) { + throw new SlcException("Cannot retrieve service reference for " + + filter, e); + } + + if (sfs == null || sfs.length == 0) + return null; + else if (sfs.length > 1) + throw new SlcException("More than one execution flow found for " + + filter); + return (T) bundleContext.getService(sfs[0]); + } + + public T getSingleServiceStrict(Class clss, String filter, + Boolean synchronous) { + T service = getSingleService(clss, filter, synchronous); + if (service == null) + throw new SlcException("No execution flow found for " + filter); + else + return service; + } + + public OsgiBundle findRelatedBundle(String moduleName, String moduleVersion) { + OsgiBundle osgiBundle = new OsgiBundle(moduleName, moduleVersion); + if (osgiBundle.getVersion() == null) { + Bundle bundle = findRelatedBundle(osgiBundle); + osgiBundle = new OsgiBundle(bundle); + } + return osgiBundle; + } + + /** + * @param osgiBundle + * cannot be null + * @return the related bundle or null if not found + * @throws SlcException + * if osgiBundle argument is null + */ + public Bundle findRelatedBundle(OsgiBundle osgiBundle) { + if (osgiBundle == null) + throw new SlcException("OSGi bundle cannot be null"); + + Bundle bundle = null; + if (osgiBundle.getInternalBundleId() != null) { + bundle = bundleContext.getBundle(osgiBundle.getInternalBundleId()); + Assert.isTrue( + osgiBundle.getName().equals(bundle.getSymbolicName()), + "symbolic name consistent"); + if (osgiBundle.getVersion() != null) + Assert.isTrue( + osgiBundle.getVersion().equals( + bundle.getHeaders().get( + Constants.BUNDLE_VERSION)), + "version consistent"); + } else if (osgiBundle.getVersion() == null + || osgiBundle.getVersion().equals("0.0.0")) { + bundle = OsgiBundleUtils.findBundleBySymbolicName(bundleContext, + osgiBundle.getName()); + } else {// scan all bundles + bundles: for (Bundle b : bundleContext.getBundles()) { + if (b.getSymbolicName() == null) { + log.warn("Bundle " + b + " has no symbolic name defined."); + continue bundles; + } + + if (b.getSymbolicName().equals(osgiBundle.getName())) { + if (osgiBundle.getVersion() == null) { + bundle = b; + break bundles; + } + + if (b.getHeaders().get(Constants.BUNDLE_VERSION) + .equals(osgiBundle.getVersion())) { + bundle = b; + osgiBundle.setInternalBundleId(b.getBundleId()); + break bundles; + } + } + } + } + return bundle; + } + + /** Find a single bundle based on a symbolic name pattern. */ + public OsgiBundle findFromPattern(String pattern) { + OsgiBundle osgiBundle = null; + for (Bundle b : bundleContext.getBundles()) { + if (b.getSymbolicName().contains(pattern)) { + osgiBundle = new OsgiBundle(b); + break; + } + } + return osgiBundle; + } + + public OsgiBundle getBundle(Long bundleId) { + Bundle bundle = bundleContext.getBundle(bundleId); + return new OsgiBundle(bundle); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void afterPropertiesSet() throws Exception { + bundleContext.addFrameworkListener(this); + } + + public void destroy() throws Exception { + bundleContext.removeFrameworkListener(this); + } + + public void setDefaultTimeout(Long defaultTimeout) { + this.defaultTimeout = defaultTimeout; + } + + /** + * Use with caution since it may interfer with some cached information + * within this object + */ + public BundleContext getBundleContext() { + return bundleContext; + } + + public void setPollingPeriod(Long pollingPeriod) { + this.pollingPeriod = pollingPeriod; + } + + public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) { + if (event instanceof OsgiBundleContextRefreshedEvent) { + log.debug("App context refreshed: " + event); + } else if (event instanceof OsgiBundleContextFailedEvent) { + log.debug("App context failed: " + event); + } + if (event instanceof OsgiBundleContextClosedEvent) { + log.debug("App context closed: " + event); + } + + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/FileSystemBundleRegister.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/FileSystemBundleRegister.java new file mode 100644 index 000000000..a068a4f8c --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/FileSystemBundleRegister.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import java.io.File; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.Constants; + +/** Experimental */ +public class FileSystemBundleRegister implements BundleRegister { + private final static Log log = LogFactory + .getLog(FileSystemBundleRegister.class); + private Properties packagesBundles = null; + + public String bundleProvidingPackage(String pkg, String version) { + if (packagesBundles == null) + return null; + return packagesBundles.getProperty(pkg); + } + + protected void scan(File baseDirectory) { + long begin = System.currentTimeMillis(); + int bundleCount = 0; + int packageCount = 0; + + packagesBundles = new Properties(); + + File[] files = baseDirectory.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + + } else { + try { + JarFile jarFile = new JarFile(file); + Manifest manifest = jarFile.getManifest(); + String symbolicName = manifest.getMainAttributes() + .getValue(Constants.BUNDLE_SYMBOLICNAME); + String exportPackage = manifest.getMainAttributes() + .getValue(Constants.EXPORT_PACKAGE); + + // List exported packages + Set exportedPackages = exportPackageToPackageNames(exportPackage); + + for (String exportedPackage : exportedPackages) { + packagesBundles.put(exportedPackage, symbolicName); + packageCount++; + if (log.isTraceEnabled()) + log.trace("Register " + exportedPackage + "=" + + symbolicName); + } + bundleCount++; + } catch (Exception e) { + log.warn("Cannot scan " + file, e); + if (log.isTraceEnabled()) + e.printStackTrace(); + } + } + } + if (log.isDebugEnabled()) + log.debug("Scanned " + bundleCount + " bundles with " + + packageCount + " packages in " + + (System.currentTimeMillis() - begin) + " ms"); + } + + protected Set exportPackageToPackageNames(String exportPackage) { + Set exportedPackages = new HashSet(); + if (exportPackage == null) + return exportedPackages; + char[] arr = exportPackage.toCharArray(); + + StringBuffer currentPkg = new StringBuffer(""); + boolean skip = false; + boolean inQuote = false; + for (char c : arr) { + if (c == ' ' || c == '\n') { + // ignore + } else if (c == ';') { + if (!skip) + skip = true; + } else if (c == ',') { + if (skip && !inQuote) { + skip = false; + // add new package + exportedPackages.add(currentPkg.toString()); + currentPkg = new StringBuffer(""); + } + } else if (c == '\"') { + inQuote = inQuote ? false : true; + } else { + if (!skip) + currentPkg.append(c); + } + } + + return exportedPackages; + } + + public static void main(String[] args) { + FileSystemBundleRegister fsbr = new FileSystemBundleRegister(); + fsbr.scan(new File( + "/home/mbaudier/dev/src/slc/dist/org.argeo.slc.sdk/target/lib")); + + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/MultipleServiceExporterPostProcessor.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/MultipleServiceExporterPostProcessor.java new file mode 100644 index 000000000..2d8b0311c --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/MultipleServiceExporterPostProcessor.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; + +/** Publishes beans of the application context as OSGi services. */ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +public class MultipleServiceExporterPostProcessor implements + ApplicationListener, Ordered { + private final static Log log = LogFactory + .getLog(MultipleServiceExporterPostProcessor.class); + + private List interfaces = new ArrayList(); + + private int order = Ordered.LOWEST_PRECEDENCE; + + private BundleContext bundleContext = null; + + // private Class osgiServiceFactoryClass = OsgiServiceFactoryBean.class; + // private Boolean useServiceProviderContextClassLoader = false; + + public void onApplicationEvent(ApplicationEvent event) { + Map beans = new HashMap(); + if (event instanceof ContextRefreshedEvent) { + if (bundleContext != null) { + for (Class clss : interfaces) { + ApplicationContext ac = ((ContextRefreshedEvent) event) + .getApplicationContext(); + beans.putAll(ac.getBeansOfType(clss, false, false)); + } + + int count = 0; + for (String beanName : beans.keySet()) { + Object bean = beans.get(beanName); + List classes = new ArrayList(); + for (Class clss : interfaces) { + if (clss.isAssignableFrom(bean.getClass())) { + classes.add(clss.getName()); + } + } + Properties props = new Properties(); + Bundle bundle = bundleContext.getBundle(); + props.put(Constants.BUNDLE_SYMBOLICNAME, + bundle.getSymbolicName()); + props.put(Constants.BUNDLE_VERSION, bundle.getVersion()); + // retrocompatibility with pre-1.0: + props.put("org.eclipse.gemini.blueprint.bean.name", beanName); + bundleContext.registerService( + classes.toArray(new String[classes.size()]), bean, + new Hashtable(props)); + count++; + } + if (log.isTraceEnabled()) + log.trace("Published " + count + " " + interfaces + + " as OSGi services from bundle " + + bundleContext.getBundle().getSymbolicName() + " " + + bundleContext.getBundle().getVersion()); + // note: the services will be automatically unregistered when + // the bundle will be stopped + } + } + } + + // public void postProcessBeanFactory( + // ConfigurableListableBeanFactory beanFactory) throws BeansException { + // if (!(beanFactory instanceof BeanDefinitionRegistry)) { + // throw new SlcException("Can only work on " + // + BeanDefinitionRegistry.class); + // } + // + // long begin = System.currentTimeMillis(); + // + // // Merge all beans implementing these interfaces + // Set beanNames = new HashSet(); + // for (Class clss : interfaces) { + // String[] strs = beanFactory.getBeanNamesForType(clss, true, false); + // beanNames.addAll(Arrays.asList(strs)); + // } + // + // // Register service factory beans for them + // for (String beanName : beanNames) { + // MutablePropertyValues mpv = new MutablePropertyValues(); + // mpv.addPropertyValue("interfaces", interfaces.toArray()); + // mpv.addPropertyValue("targetBeanName", beanName); + // if (useServiceProviderContextClassLoader) + // mpv.addPropertyValue("contextClassLoader", + // ExportContextClassLoader.SERVICE_PROVIDER); + // RootBeanDefinition bd = new RootBeanDefinition( + // osgiServiceFactoryClass, mpv); + // + // String exporterBeanName = "osgiService." + beanName; + // if (log.isTraceEnabled()) + // log.debug("Registering OSGi service exporter " + // + exporterBeanName); + // ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition( + // exporterBeanName, bd); + // } + // + // long end = System.currentTimeMillis(); + // if (log.isTraceEnabled()) + // log.debug("Multiple services exported in " + (end - begin) + // + " ms in bundle."); + // + // } + + public void setInterfaces(List interfaces) { + this.interfaces = interfaces; + } + + // public void setOsgiServiceFactoryClass(Class osgiServiceFactoryClass) { + // this.osgiServiceFactoryClass = osgiServiceFactoryClass; + // } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + // public void setUseServiceProviderContextClassLoader( + // Boolean useServiceProviderContextClassLoader) { + // this.useServiceProviderContextClassLoader = + // useServiceProviderContextClassLoader; + // } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiBundle.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiBundle.java new file mode 100644 index 000000000..cb11615b6 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiBundle.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import org.argeo.slc.DefaultNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.core.build.ResourceDistribution; +import org.argeo.slc.deploy.DeploymentData; +import org.argeo.slc.deploy.Module; +import org.argeo.slc.deploy.ModuleDescriptor; +import org.argeo.slc.deploy.TargetData; +import org.argeo.slc.execution.RealizedFlow; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.springframework.core.io.Resource; + +/** A deployed OSGi bundle. */ +public class OsgiBundle extends DefaultNameVersion implements Module { + private ResourceDistribution distribution; + + private Long internalBundleId; + + private String title; + private String description; + + public OsgiBundle() { + + } + + public OsgiBundle(String name, String version) { + super(name, version); + } + + public OsgiBundle(NameVersion nameVersion) { + super(nameVersion); + } + + public OsgiBundle(Bundle bundle) { + super(bundle.getSymbolicName(), getVersionSafe(bundle)); + internalBundleId = bundle.getBundleId(); + } + + /** + * Initialize from a {@link RealizedFlow}. + * + * @deprecated introduce an unnecessary dependency. TODO: create a separate + * helper. + */ + public OsgiBundle(RealizedFlow realizedFlow) { + super(realizedFlow.getModuleName(), realizedFlow.getModuleVersion()); + } + + /** Utility to avoid NPE. */ + private static String getVersionSafe(Bundle bundle) { + Object versionObj = bundle.getHeaders().get(Constants.BUNDLE_VERSION); + if (versionObj != null) + return versionObj.toString(); + else + return null; + } + + /** Unique deployed system id. TODO: use internal bundle id when available? */ + public String getDeployedSystemId() { + return getName() + ":" + getVersion(); + } + + /** + * OSGi bundle are self-contained and do not require additional deployment + * data. + * + * @return always null + */ + public DeploymentData getDeploymentData() { + return null; + } + + /** The related distribution. */ + public Distribution getDistribution() { + return distribution; + } + + /** + * The related distribution, a jar file with OSGi metadata referenced by a + * {@link Resource}. + */ + public ResourceDistribution getResourceDistribution() { + return distribution; + } + + /** TODO: reference the {@link OsgiRuntime} as target data? */ + public TargetData getTargetData() { + throw new UnsupportedOperationException(); + } + + public void setResourceDistribution(ResourceDistribution distribution) { + this.distribution = distribution; + } + + /** + * Bundle ID used by the OSGi runtime. To be used for optimization when + * looking in the bundle context. Can therefore be null. + */ + public Long getInternalBundleId() { + return internalBundleId; + } + + /** Only package access for the time being. e.g. from {@link BundlesManager} */ + void setInternalBundleId(Long internalBundleId) { + this.internalBundleId = internalBundleId; + } + + /** Value of the Bundle-Name directive. */ + public String getTitle() { + return title; + } + + public void setTitle(String label) { + this.title = label; + } + + /** Value of the Bundle-Description directive. */ + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public ModuleDescriptor getModuleDescriptor() { + ModuleDescriptor moduleDescriptor = new ModuleDescriptor(); + moduleDescriptor.setName(getName()); + moduleDescriptor.setVersion(getVersion()); + moduleDescriptor.setDescription(description); + moduleDescriptor.setTitle(title); + return moduleDescriptor; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModule.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModule.java new file mode 100644 index 000000000..df7ae9bc1 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModule.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.core.execution.AbstractSpringExecutionModule; +import org.argeo.slc.execution.ExecutionContext; + +@Deprecated +public class OsgiExecutionModule extends AbstractSpringExecutionModule { + private final static Log log = LogFactory.getLog(OsgiExecutionModule.class); + + public OsgiExecutionModule() { + log.error("######## ERROR - DEPRECATED APPROACH USED ########"); + log.error(OsgiExecutionModule.class.getName() + " is deprecated. "); + log + .error("It will be removed in the next release. Remove its bean definition."); + log + .error("And replace: "); + log + .error("by: "); + log.error("in osgi.xml.\n\n"); + } + + public void setExecutionContext(ExecutionContext executionContext) { + // do nothing, just for compatibility + } + + /* + * private BundleContext bundleContext; + * + * @Override public void execute(ExecutionFlowDescriptor + * executionFlowDescriptor) { if (descriptorConverter != null) + * executionContext.addVariables(descriptorConverter + * .convertValues(executionFlowDescriptor)); + * + * ExecutionFlow flow = findExecutionFlow(getName(), getVersion(), + * executionFlowDescriptor.getName()); flow.run(); } + * + * @Override protected Map listFlows() { String + * filter = "(org.argeo.slc.execution.module.name=" + getName() + ")"; + * ServiceReference[] sfs; try { sfs = + * bundleContext.getServiceReferences(ExecutionFlow.class .getName(), + * filter); } catch (InvalidSyntaxException e) { throw new SlcException( + * "Cannot retrieve service reference for flow " + filter, e); } + * + * Map flows = new HashMap(); + * for (ServiceReference sf : sfs) { ExecutionFlow flow = (ExecutionFlow) + * bundleContext.getService(sf); flows.put(flow.getName(), flow); } return + * flows; } + * + * public String getName() { return + * bundleContext.getBundle().getSymbolicName(); } + * + * public String getVersion() { return + * bundleContext.getBundle().getHeaders().get("Bundle-Version") .toString(); + * } + * + * public void setBundleContext(BundleContext bundleContext) { + * this.bundleContext = bundleContext; } + * + * protected ExecutionFlow findExecutionFlow(String moduleName, String + * moduleVersion, String flowName) { String filter = + * "(&(org.argeo.slc.execution.module.name=" + moduleName + + * ")(org.argeo.slc.execution.flow.name=" + flowName + "))"; + * log.debug("OSGi filter: " + filter); + * + * Assert.isTrue(OsgiFilterUtils.isValidFilter(filter), "valid filter"); + * ServiceReference[] sfs; try { sfs = + * bundleContext.getServiceReferences(ExecutionFlow.class .getName(), + * filter); } catch (InvalidSyntaxException e) { throw new + * SlcException("Cannot retrieve service reference for " + filter, e); } + * + * if (sfs == null || sfs.length == 0) throw new + * SlcException("No execution flow found for " + filter); else if + * (sfs.length > 1) throw new + * SlcException("More than one execution flow found for " + filter); return + * (ExecutionFlow) bundleContext.getService(sfs[0]); } + */ + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModulesManager.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModulesManager.java new file mode 100644 index 000000000..68fcead71 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionModulesManager.java @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.DefaultNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.AbstractExecutionModulesManager; +import org.argeo.slc.core.execution.DefaultExecutionFlowDescriptorConverter; +import org.argeo.slc.deploy.Module; +import org.argeo.slc.deploy.ModuleDescriptor; +import org.argeo.slc.execution.ExecutionContext; +import org.argeo.slc.execution.ExecutionFlow; +import org.argeo.slc.execution.ExecutionFlowDescriptor; +import org.argeo.slc.execution.ExecutionFlowDescriptorConverter; +import org.argeo.slc.execution.ExecutionModuleDescriptor; +import org.argeo.slc.execution.ExecutionModulesListener; +import org.argeo.slc.execution.RealizedFlow; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleException; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; +import org.osgi.framework.launch.Framework; +import org.springframework.context.ApplicationContext; +import org.eclipse.gemini.blueprint.service.importer.OsgiServiceLifecycleListener; + +/** Execution modules manager implementation based on an OSGi runtime. */ +public class OsgiExecutionModulesManager extends + AbstractExecutionModulesManager implements + OsgiServiceLifecycleListener, BundleListener { + + private final static Log log = LogFactory + .getLog(OsgiExecutionModulesManager.class); + + private BundlesManager bundlesManager; + private Map executionContexts = new HashMap(); + private Map executionFlowDescriptorConverters = new HashMap(); + private Map> executionFlows = new HashMap>(); + private ExecutionFlowDescriptorConverter defaultDescriptorConverter = new DefaultExecutionFlowDescriptorConverter(); + + private List executionModulesListeners = new ArrayList(); + + private Boolean registerFlowsToJmx = false; + + public void init() throws Exception { + bundlesManager.getBundleContext().addBundleListener(this); + + final String module = System.getProperty(UNIQUE_LAUNCH_MODULE_PROPERTY); + final String flow = System.getProperty(UNIQUE_LAUNCH_FLOW_PROPERTY); + if (module != null) { + // launch a flow and stops + new Thread("Unique Flow") { + @Override + public void run() { + executeFlowAndExit(module, null, flow); + } + }.start(); + } + } + + public void destroy() { + bundlesManager.getBundleContext().removeBundleListener(this); + } + + /** Executes a single flow and stops the JVM */ + protected void executeFlowAndExit(final String module, + final String version, final String flow) { + if (log.isDebugEnabled()) + log.debug("Launch unique flow " + flow + " from module " + module); + try { + OsgiBundle osgiBundle = bundlesManager.findFromPattern(module); + if (osgiBundle == null) + throw new SlcException("No OSGi bundle found for " + module); + // Bundle moduleBundle = + // bundlesManager.findRelatedBundle(osgiBundle); + start(osgiBundle); + + RealizedFlow lastLaunch = findRealizedFlow(module, flow); + if (lastLaunch == null) + throw new SlcException("Cannot find launch for " + module + " " + + flow); + execute(lastLaunch); + } catch (Exception e) { + log.error( + "Error in unique flow " + flow + " from module " + module, + e); + } finally { + if (log.isDebugEnabled()) + log.debug("Shutdown OSGi runtime..."); + Framework framework = (Framework) bundlesManager.getBundleContext() + .getBundle(0); + try { + // shutdown framework + framework.stop(); + // wait 1 min for shutdown + framework.waitForStop(60 * 1000); + // close VM + System.exit(0); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + // public void startExectionModule(String moduleName, String moduleVersion) + // { + // try { + // ServiceReference[] sr = bundlesManager.getServiceRefSynchronous( + // ApplicationContext.class.getName(), + // "org.springframework.context.service.name=" + moduleName); + // // bundlesManager.startSynchronous(moduleBundle); + // if (sr == null || sr.length == 0) + // throw new SlcException( + // "Cannot find execution module application context " + // + moduleName); + // } catch (InvalidSyntaxException e) { + // throw new SlcException("Cannot start exeuction module " + // + moduleName, e); + // } + // } + + public synchronized ExecutionModuleDescriptor getExecutionModuleDescriptor( + String moduleName, String version) { + ExecutionModuleDescriptor md = new ExecutionModuleDescriptor(); + OsgiBundle osgiBundle = null; + DefaultNameVersion nameVersion = new DefaultNameVersion(moduleName, + version); + bundles: for (Iterator iterator = executionContexts + .keySet().iterator(); iterator.hasNext();) { + OsgiBundle ob = iterator.next(); + if (nameVersion.getVersion() != null) { + if (ob.equals(nameVersion)) { + osgiBundle = ob; + break bundles; + } + } else { + if (ob.getName().equals(nameVersion.getName())) { + osgiBundle = ob; + break bundles; + } + } + } + if (osgiBundle == null) + throw new SlcException("No execution module registered for " + + nameVersion); + md.setName(osgiBundle.getName()); + md.setVersion(osgiBundle.getVersion()); + md.setTitle(osgiBundle.getTitle()); + md.setDescription(osgiBundle.getDescription()); + + ExecutionFlowDescriptorConverter executionFlowDescriptorConverter = getExecutionFlowDescriptorConverter( + moduleName, version); + if (executionFlowDescriptorConverter == null) + throw new SlcException("No flow converter found."); + executionFlowDescriptorConverter.addFlowsToDescriptor(md, + listFlows(moduleName, version)); + return md; + } + + public synchronized List listExecutionModules() { + List descriptors = new ArrayList(); + + for (Iterator iterator = executionContexts.keySet() + .iterator(); iterator.hasNext();) { + OsgiBundle osgiBundle = iterator.next(); + ExecutionModuleDescriptor md = new ExecutionModuleDescriptor(); + setMetadataFromBundle(md, + bundlesManager.findRelatedBundle(osgiBundle)); + descriptors.add(md); + } + return descriptors; + } + + protected synchronized Map listFlows( + String moduleName, String moduleVersion) { + + Map flows = new HashMap(); + OsgiBundle key = bundlesManager.findRelatedBundle(moduleName, + moduleVersion); + if (!executionFlows.containsKey(key)) + return flows; + Set flowsT = executionFlows.get(key); + for (ExecutionFlow flow : flowsT) + flows.put(flow.getName(), flow); + return flows; + } + + protected ExecutionFlow findExecutionFlow(String moduleName, + String moduleVersion, String flowName) { + String filter = moduleVersion == null || moduleVersion.equals("0.0.0") ? "(&(Bundle-SymbolicName=" + + moduleName + + ")(org.eclipse.gemini.blueprint.bean.name=" + + flowName + "))" + : "(&(Bundle-SymbolicName=" + moduleName + ")(Bundle-Version=" + + moduleVersion + + ")(org.eclipse.gemini.blueprint.bean.name=" + flowName + + "))"; + return bundlesManager.getSingleServiceStrict(ExecutionFlow.class, + filter, true); + } + + protected ExecutionContext findExecutionContext(String moduleName, + String moduleVersion) { + String filter = moduleFilter(moduleName, moduleVersion); + return bundlesManager.getSingleServiceStrict(ExecutionContext.class, + filter, true); + } + + protected ExecutionFlowDescriptorConverter findExecutionFlowDescriptorConverter( + String moduleName, String moduleVersion) { + String filter = moduleFilter(moduleName, moduleVersion); + return bundlesManager.getSingleService( + ExecutionFlowDescriptorConverter.class, filter, false); + } + + /** Only based on symbolic name if version is null or "0.0.0" */ + protected String moduleFilter(String moduleName, String moduleVersion) { + return moduleVersion == null || moduleVersion.equals("0.0.0") ? "(Bundle-SymbolicName=" + + moduleName + ")" + : "(&(Bundle-SymbolicName=" + moduleName + ")(Bundle-Version=" + + moduleVersion + "))"; + + } + + /** + * Builds a minimal realized flow, based on the provided information + * (typically from the command line). + * + * @param module + * a bundle id, or a pattern contained in a bundle symbolic name + * @param module + * the execution flow name + * @return a minimal realized flow, to be used in an execution + */ + public RealizedFlow findRealizedFlow(String module, String executionName) { + // First check whether we have a bundleId + Long bundleId = null; + try { + bundleId = Long.parseLong(module); + } catch (NumberFormatException e) { + // silent + } + + // Look for bundle names containing pattern + OsgiBundle bundle = null; + if (bundleId != null) { + bundle = bundlesManager.getBundle(bundleId); + } else { + bundle = bundlesManager.findFromPattern(module); + } + + if (bundle != null) { + RealizedFlow launch = new RealizedFlow(); + launch.setModuleName(bundle.getName()); + launch.setModuleVersion(bundle.getVersion()); + ExecutionFlowDescriptor descriptor = new ExecutionFlowDescriptor(); + descriptor.setName(executionName); + launch.setFlowDescriptor(descriptor); + return launch; + } else { + log.warn("Could not find any execution module matching these requirements."); + return null; + } + } + + public void upgrade(NameVersion nameVersion) { + OsgiBundle osgiBundle = new OsgiBundle(nameVersion); + bundlesManager.upgradeSynchronous(osgiBundle); + } + + protected synchronized ExecutionFlowDescriptorConverter getExecutionFlowDescriptorConverter( + String moduleName, String moduleVersion) { + return findExecutionFlowDescriptorConverter(moduleName, moduleVersion); + // OsgiBundle osgiBundle = new OsgiBundle(moduleName, moduleVersion); + // return getExecutionFlowDescriptorConverter(osgiBundle); + } + + protected synchronized ExecutionFlowDescriptorConverter getExecutionFlowDescriptorConverter( + OsgiBundle osgiBundle) { + if (executionFlowDescriptorConverters.containsKey(osgiBundle)) + return executionFlowDescriptorConverters.get(osgiBundle); + else + return defaultDescriptorConverter; + } + + public ModuleDescriptor getModuleDescriptor(String moduleName, + String version) { + return getExecutionModuleDescriptor(moduleName, version); + } + + public List listModules() { + Bundle[] bundles = bundlesManager.getBundleContext().getBundles(); + List lst = new ArrayList(); + for (Bundle bundle : bundles) { + ModuleDescriptor moduleDescriptor = new ModuleDescriptor(); + setMetadataFromBundle(moduleDescriptor, bundle); + lst.add(moduleDescriptor); + } + return lst; + } + + public void start(NameVersion nameVersion) { + try { + Bundle bundle = bundlesManager.findRelatedBundle(new OsgiBundle( + nameVersion)); + if (bundle == null) + throw new SlcException("Could not find bundle for " + + nameVersion); + + bundlesManager.startSynchronous(bundle); + if (isSpringInstrumented(bundle)) { + // Wait for Spring application context to be ready + String filter = "(Bundle-SymbolicName=" + + bundle.getSymbolicName() + ")"; + try { + bundlesManager.getServiceRefSynchronous( + ApplicationContext.class.getName(), filter); + } catch (Exception e) { + // stop if application context not found + bundle.stop(); + throw e; + } + } + } catch (Exception e) { + throw new SlcException("Cannot start " + nameVersion, e); + } + } + + /** Do it calmly in order to avoid NPE */ + private Boolean isSpringInstrumented(Bundle bundle) { + Dictionary headers = bundle.getHeaders(); + if (headers != null && headers.get("Spring-Context") != null) + return true; + Enumeration springEntryPaths = bundle + .getEntryPaths("/META-INF/spring"); + if (springEntryPaths != null && springEntryPaths.hasMoreElements()) + return true; + return false; + } + + public void stop(NameVersion nameVersion) { + try { + Bundle bundle = bundlesManager.findRelatedBundle(new OsgiBundle( + nameVersion)); + bundlesManager.stopSynchronous(bundle); + } catch (BundleException e) { + throw new SlcException("Cannot stop " + nameVersion, e); + } + } + + protected void setMetadataFromBundle(ModuleDescriptor md, Bundle bundle) { + Bundle bdl = bundle; + if (bdl == null) { + if (md.getName() == null || md.getVersion() == null) + throw new SlcException("Name and version not available."); + + Bundle[] bundles = bundlesManager.getBundleContext().getBundles(); + for (Bundle b : bundles) { + if (b.getSymbolicName().equals(md.getName()) + && md.getVersion().equals( + getHeaderSafe(b, Constants.BUNDLE_VERSION))) { + bdl = b; + break; + } + } + + } + + if (bdl == null) + throw new SlcException("Cannot find bundle."); + + md.setName(bdl.getSymbolicName()); + md.setVersion(getHeaderSafe(bdl, Constants.BUNDLE_VERSION)); + md.setTitle(getHeaderSafe(bdl, Constants.BUNDLE_NAME)); + md.setDescription(getHeaderSafe(bdl, Constants.BUNDLE_DESCRIPTION)); + + // copy manifets header to meta data + Dictionary headers = bundle.getHeaders(); + Enumeration keys = headers.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + Object value = headers.get(key); + if (value != null) + md.getMetadata().put(key.toString(), value.toString()); + } + + // check if started + if (bundle.getState() == Bundle.ACTIVE + || bundle.getState() == Bundle.STARTING) + md.setStarted(true); + else + md.setStarted(false); + } + + private String getHeaderSafe(Bundle bundle, Object key) { + Object obj = bundle.getHeaders().get(key); + if (obj == null) + return null; + else + return obj.toString(); + } + + /* + * REGISTRATION + */ + + /** Registers an execution context. */ + public synchronized void register(ExecutionContext executionContext, + Map properties) { + OsgiBundle osgiBundle = asOsgiBundle(properties); + Bundle bundle = bundlesManager.findRelatedBundle(osgiBundle); + osgiBundle.setTitle(getHeaderSafe(bundle, Constants.BUNDLE_NAME)); + osgiBundle.setDescription(getHeaderSafe(bundle, + Constants.BUNDLE_DESCRIPTION)); + executionContexts.put(osgiBundle, executionContext); + if (log.isTraceEnabled()) + log.trace("Registered execution context from " + osgiBundle); + // Notify + ModuleDescriptor md = osgiBundle.getModuleDescriptor(); + md.setStarted(true); + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionModuleAdded(md); + } + + /** Unregisters an execution context. */ + public synchronized void unregister(ExecutionContext executionContext, + Map properties) { + OsgiBundle osgiBundle = asOsgiBundle(properties); + if (executionContexts.containsKey(osgiBundle)) { + executionContexts.remove(osgiBundle); + if (log.isTraceEnabled()) + log.trace("Removed execution context from " + osgiBundle); + // Notify + ModuleDescriptor md = osgiBundle.getModuleDescriptor(); + md.setStarted(false); + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionModuleRemoved(md); + } + } + + /** Registers an execution flow. */ + public synchronized void register(ExecutionFlow executionFlow, + Map properties) { + OsgiBundle osgiBundle = asOsgiBundle(properties); + if (!executionFlows.containsKey(osgiBundle)) { + executionFlows.put(osgiBundle, new HashSet()); + } + executionFlows.get(osgiBundle).add(executionFlow); + if (log.isTraceEnabled()) + log.trace("Registered " + executionFlow + " from " + osgiBundle); + + // notifications + if (registerFlowsToJmx) + registerMBean(osgiBundle, executionFlow); + ExecutionFlowDescriptorConverter efdc = getExecutionFlowDescriptorConverter(osgiBundle); + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionFlowAdded(osgiBundle.getModuleDescriptor(), + efdc.getExecutionFlowDescriptor(executionFlow)); + } + + /** Unregisters an execution flow. */ + public synchronized void unregister(ExecutionFlow executionFlow, + Map properties) { + OsgiBundle osgiBundle = asOsgiBundle(properties); + if (executionFlows.containsKey(osgiBundle)) { + Set flows = executionFlows.get(osgiBundle); + flows.remove(executionFlow); + if (log.isTraceEnabled()) + log.trace("Removed " + executionFlow + " from " + osgiBundle); + if (flows.size() == 0) { + executionFlows.remove(osgiBundle); + if (log.isTraceEnabled()) + log.trace("Removed flows set from " + osgiBundle); + } + + // notifications + if (registerFlowsToJmx) + unregisterMBean(osgiBundle, executionFlow); + ExecutionFlowDescriptorConverter efdc = getExecutionFlowDescriptorConverter(osgiBundle); + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionFlowRemoved(osgiBundle.getModuleDescriptor(), + efdc.getExecutionFlowDescriptor(executionFlow)); + } + } + + /** Registers an execution module listener. */ + public synchronized void register( + ExecutionModulesListener executionModulesListener, + Map properties) { + // sync with current state + for (OsgiBundle osgiBundle : executionContexts.keySet()) { + executionModulesListener.executionModuleAdded(osgiBundle + .getModuleDescriptor()); + } + for (OsgiBundle osgiBundle : executionFlows.keySet()) { + ExecutionFlowDescriptorConverter efdc = getExecutionFlowDescriptorConverter(osgiBundle); + for (ExecutionFlow executionFlow : executionFlows.get(osgiBundle)) + executionModulesListener.executionFlowAdded( + osgiBundle.getModuleDescriptor(), + efdc.getExecutionFlowDescriptor(executionFlow)); + } + executionModulesListeners.add(executionModulesListener); + } + + /** Unregisters an execution module listener. */ + public synchronized void unregister( + ExecutionModulesListener executionModulesListener, + Map properties) { + executionModulesListeners.remove(executionModulesListener); + } + + /* + * INTERFACE IMPLEMENTATIONS + */ + + public void bundleChanged(BundleEvent evt) { + Bundle bundle = evt.getBundle(); + if (bundle.getHeaders().get( + ExecutionModuleDescriptor.SLC_EXECUTION_MODULE) != null) { + OsgiBundle osgiBundle = new OsgiBundle(bundle); + if (evt.getType() == BundleEvent.INSTALLED) + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionModuleAdded(osgiBundle + .getModuleDescriptor()); + else if (evt.getType() == BundleEvent.UNINSTALLED) + for (ExecutionModulesListener listener : executionModulesListeners) + listener.executionModuleRemoved(osgiBundle + .getModuleDescriptor()); + } + + } + + @SuppressWarnings({ "rawtypes" }) + public synchronized void bind(Object service, Map properties) + throws Exception { + if (service instanceof ExecutionFlowDescriptorConverter) { + ExecutionFlowDescriptorConverter executionFlowDescriptorConverter = (ExecutionFlowDescriptorConverter) service; + OsgiBundle osgiBundle = asOsgiBundle(properties); + executionFlowDescriptorConverters.put(osgiBundle, + executionFlowDescriptorConverter); + if (log.isTraceEnabled()) + log.debug("Registered execution flow descriptor converter from " + + osgiBundle); + } else { + // ignore + } + } + + @SuppressWarnings("rawtypes") + public synchronized void unbind(Object service, Map properties) + throws Exception { + if (service instanceof ExecutionFlowDescriptorConverter) { + OsgiBundle osgiBundle = asOsgiBundle(properties); + if (executionFlowDescriptorConverters.containsKey(osgiBundle)) { + executionFlowDescriptorConverters.remove(osgiBundle); + if (log.isTraceEnabled()) + log.debug("Removed execution flow descriptor converter from " + + osgiBundle); + } + } else { + // ignore + } + } + + /* + * JMX + */ + protected MBeanServer getMBeanServer() { + return ManagementFactory.getPlatformMBeanServer(); + } + + public void registerMBean(Module module, ExecutionFlow executionFlow) { + try { + StandardMBean mbean = new StandardMBean(executionFlow, + ExecutionFlow.class); + getMBeanServer().registerMBean(mbean, + flowMBeanName(module, executionFlow)); + } catch (Exception e) { + String msg = "Cannot register execution flow " + executionFlow + + " as mbean"; + throw new SlcException(msg, e); + } + } + + public void unregisterMBean(Module module, ExecutionFlow executionFlow) { + try { + getMBeanServer().unregisterMBean( + flowMBeanName(module, executionFlow)); + } catch (Exception e) { + String msg = "Cannot unregister execution flow " + executionFlow + + " as mbean"; + throw new SlcException(msg, e); + } + } + + @SuppressWarnings("deprecation") + protected ObjectName flowMBeanName(Module module, + ExecutionFlow executionFlow) { + String executionModulesPrefix = "SLCExecutionModules"; + String path = executionFlow.getPath(); + String name = executionFlow.getName(); + if (path == null && name.indexOf('/') >= 0) { + path = name.substring(0, name.lastIndexOf('/')); + name = name.substring(name.lastIndexOf('/')); + } + + StringBuffer buf = new StringBuffer(executionModulesPrefix + ":" + + "module=" + module.getName() + " [" + module.getVersion() + + "],"); + + if (path != null && !path.equals("")) { + int depth = 0; + for (String token : path.split("/")) { + if (!token.equals("")) { + buf.append("path").append(depth).append('='); + // in order to have directories first + buf.append('/'); + buf.append(token).append(','); + depth++; + } + } + } + buf.append("name=").append(name); + try { + return new ObjectName(buf.toString()); + } catch (Exception e) { + throw new SlcException("Cannot generate object name based on " + + buf, e); + } + } + + /* + * UTILITIES + */ + @SuppressWarnings("rawtypes") + private OsgiBundle asOsgiBundle(Map properties) { + String bundleSymbolicName = checkAndGet(Constants.BUNDLE_SYMBOLICNAME, + properties); + String bundleVersion = checkAndGet(Constants.BUNDLE_VERSION, properties); + return new OsgiBundle(bundleSymbolicName, bundleVersion); + } + + @SuppressWarnings("rawtypes") + private String checkAndGet(Object key, Map properties) { + if (!properties.containsKey(key) || properties.get(key) == null) + throw new SlcException(key + " not set in " + properties); + else + return properties.get(key).toString(); + } + + public void setBundlesManager(BundlesManager bundlesManager) { + this.bundlesManager = bundlesManager; + } + + public void setDefaultDescriptorConverter( + ExecutionFlowDescriptorConverter defaultDescriptorConverter) { + this.defaultDescriptorConverter = defaultDescriptorConverter; + } + + public void setRegisterFlowsToJmx(Boolean registerFlowsToJmx) { + this.registerFlowsToJmx = registerFlowsToJmx; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionResources.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionResources.java new file mode 100644 index 000000000..57f33cfc6 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiExecutionResources.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.SlcException; +import org.argeo.slc.core.execution.FileExecutionResources; +import org.osgi.framework.BundleContext; +import org.springframework.core.io.Resource; +import org.eclipse.gemini.blueprint.context.BundleContextAware; +import org.eclipse.gemini.blueprint.io.OsgiBundleResource; + +/** Write access to resources in an OSGi context */ +public class OsgiExecutionResources extends FileExecutionResources implements + BundleContextAware { + private final static Log log = LogFactory + .getLog(OsgiExecutionResources.class); + + private BundleContext bundleContext; + + @Override + protected File fileFromResource(Resource resource) { + File file = super.fileFromResource(resource); + if (file != null) + return file; + + if (!(resource instanceof OsgiBundleResource)) + return null; + + OsgiBundleResource osgiBundleResource = (OsgiBundleResource) resource; + try { + return osgiBundleResource.getFile(); + } catch (IOException e) { + if (log.isTraceEnabled()) + log.trace("Resource " + resource + + " is not available on the file system: " + e); + } + + // TODO: ability to access resources in other bundles + String location = bundleContext.getBundle().getLocation(); + String base = null; + if (location.startsWith("reference:file:")) + base = location.substring("reference:file:".length()); + else if (location.startsWith("initial@reference:file:")) { + // TODO: Equinox specific? + String relPath = location.substring("initial@reference:file:" + .length()); + // if (relPath.startsWith("../"))// relative to the framework jar + // relPath = relPath.substring("../".length()); + // String framework = + // System.getProperty("osgi.framework").substring( + // "file:".length()); + // log.debug(framework); + String installArea = System.getProperty("osgi.install.area") + .substring("file:".length()); + // log.debug(installArea); + base = installArea + '/' + relPath; + // int sepIndex = framework.lastIndexOf(File.separatorChar); + // framework = framework.substring(0, sepIndex); + // base = framework + '/' + relPath; + } else { + return null; + } + + String path = base + '/' + osgiBundleResource.getPathWithinContext(); + try { + file = new File(path).getCanonicalFile(); + } catch (IOException e) { + throw new SlcException("Cannot determine canonical path for " + + path, e); + } + + if (!file.exists()) + throw new SlcException(file + + " was retrieved in bundle located at '" + location + + "' for resource " + resource + " but it does not exist"); + + if (log.isTraceEnabled()) + log.debug("OSGi local resource: " + file + " from " + resource); + return file; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiRuntime.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiRuntime.java new file mode 100644 index 000000000..2414649cd --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/OsgiRuntime.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.StreamReadable; +import org.argeo.slc.UnsupportedException; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.core.build.VersionedResourceDistribution; +import org.argeo.slc.deploy.DeploymentData; +import org.argeo.slc.deploy.DynamicRuntime; +import org.argeo.slc.deploy.TargetData; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.eclipse.gemini.blueprint.context.BundleContextAware; + +public class OsgiRuntime implements BundleContextAware, ResourceLoaderAware, + DynamicRuntime { + private String uuid = UUID.randomUUID().toString(); + private BundleContext bundleContext; + private ResourceLoader resourceLoader; + + public List listModules() { + List modules = new ArrayList(); + Bundle[] bundles = bundleContext.getBundles(); + for (Bundle bundle : bundles) { + OsgiBundle osgiBundle = new OsgiBundle(bundle); + modules.add(osgiBundle); + String location = bundle.getLocation(); + if (location != null) { + Resource resource = resourceLoader.getResource(location); + osgiBundle + .setResourceDistribution(new VersionedResourceDistribution( + osgiBundle.getName(), osgiBundle.getVersion(), + resource)); + } + } + return modules; + } + + public OsgiBundle installModule(Distribution distribution) { + if (!(distribution instanceof StreamReadable)) + throw new UnsupportedException("distribution", distribution); + + StreamReadable sr = (StreamReadable) distribution; + Bundle bundle; + try { + bundle = bundleContext.installBundle(sr.toString(), sr + .getInputStream()); + } catch (BundleException e) { + throw new SlcException( + "Cannot install OSGi bundle " + distribution, e); + } + return new OsgiBundle(bundle); + } + + public void updateModule(NameVersion nameVersion) { + Bundle bundle = findBundle(nameVersion); + try { + bundle.update(); + } catch (BundleException e) { + throw new SlcException("Cannot update " + bundle, e); + } + } + + public void uninstallModule(NameVersion nameVersion) { + Bundle bundle = findBundle(nameVersion); + try { + bundle.uninstall(); + } catch (BundleException e) { + throw new SlcException("Cannot uninstall " + bundle, e); + } + } + + public void startModule(NameVersion nameVersion) { + Bundle bundle = findBundle(nameVersion); + try { + bundle.start(); + // TODO: use bundle manager + } catch (BundleException e) { + throw new SlcException("Cannot uninstall " + bundle, e); + } + } + + protected Bundle findBundle(NameVersion nameVersion) { + Bundle[] bundles = bundleContext.getBundles(); + for (Bundle bundle : bundles) { + OsgiBundle osgiBundle = new OsgiBundle(bundle); + if (osgiBundle.equals(nameVersion)) { + return bundle; + } + } + throw new SlcException("Could not find bundle " + nameVersion); + } + + public void shutdown() { + // FIXME use framework + throw new UnsupportedException(); + } + + public String getDeployedSystemId() { + return uuid; + } + + public DeploymentData getDeploymentData() { + throw new UnsupportedException(); + } + + public Distribution getDistribution() { + throw new UnsupportedException(); + } + + public TargetData getTargetData() { + throw new UnsupportedException(); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/AbstractOsgiModularDistribution.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/AbstractOsgiModularDistribution.java new file mode 100644 index 000000000..a38adadcc --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/AbstractOsgiModularDistribution.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.DefaultNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.UnsupportedException; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.build.ModularDistribution; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.springframework.beans.factory.InitializingBean; +import org.eclipse.gemini.blueprint.context.BundleContextAware; + +public abstract class AbstractOsgiModularDistribution implements + ModularDistribution, BundleContextAware, InitializingBean { + private final static Log log = LogFactory + .getLog(AbstractOsgiModularDistribution.class); + + private BundleContext bundleContext; + private EclipseUpdateSite eclipseUpdateSite; + + /** Initialized by the object itself. */ + private SortedMap distributions = new TreeMap(); + + protected abstract void fillDistributions( + SortedMap distributions) + throws Exception; + + public Distribution getModuleDistribution(String moduleName, + String moduleVersion) { + return distributions.get(new DefaultNameVersion(moduleName, + moduleVersion)); + } + + public String getDistributionId() { + return bundleContext.getBundle().getSymbolicName() + + "-" + + bundleContext.getBundle().getHeaders() + .get(Constants.BUNDLE_VERSION); + } + + public Set listModulesNameVersions() { + return distributions.keySet(); + } + + public Iterator nameVersions() { + return distributions.keySet().iterator(); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void afterPropertiesSet() throws Exception { + fillDistributions(distributions); + if (log.isDebugEnabled()) + log.debug("Distribution " + getName() + ":" + getVersion() + + " loaded (" + distributions.size() + " modules)"); + } + + protected String findVersion(String name) { + Set versions = new HashSet(); + for (NameVersion key : distributions.keySet()) { + if (key.getName().equals(name)) + versions.add(key.getVersion()); + } + + if (versions.size() == 0) + throw new SlcException("Cannot find version for name " + name); + else if (versions.size() > 1) + throw new SlcException("Found more than one version for name " + + name + ": " + versions); + else + return versions.iterator().next(); + + } + + public Object getModulesDescriptor(String descriptorType) { + if (descriptorType.equals("eclipse")) + return writeEclipseUpdateSite(); + else + throw new UnsupportedException("descriptorType", descriptorType); + } + + protected Set writePlainUrlList() { + return distributions.keySet(); + } + + protected String writeEclipseUpdateSite() { + if (eclipseUpdateSite == null) + throw new SlcException("No eclipse update site declared."); + + StringBuffer buf = new StringBuffer(""); + buf.append(""); + + List usedCategories = new ArrayList(); + for (EclipseUpdateSiteFeature feature : eclipseUpdateSite.getFeatures()) { + + String featureId = feature.getName(); + String featureVersion = findVersion(featureId); + buf.append("\n"); + + for (EclipseUpdateSiteCategory category : feature.getCategories()) { + usedCategories.add(category); + buf.append(" \n"); + } + buf.append("\n\n"); + } + + for (EclipseUpdateSiteCategory category : usedCategories) { + buf.append("\n"); + buf.append(" ").append(category.getDescription()) + .append("\n"); + buf.append("\n\n"); + } + + buf.append(""); + return buf.toString(); + } + + public String getName() { + return bundleContext.getBundle().getSymbolicName(); + } + + public String getVersion() { + return bundleContext.getBundle().getHeaders() + .get(Constants.BUNDLE_VERSION).toString(); + } + + @Override + public String toString() { + return new DefaultNameVersion(this).toString(); + } + + public void setEclipseUpdateSite(EclipseUpdateSite eclipseUpdateSite) { + this.eclipseUpdateSite = eclipseUpdateSite; + } + + public BundleContext getBundleContext() { + return bundleContext; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/BundleModularDistribution.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/BundleModularDistribution.java new file mode 100644 index 000000000..51595750e --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/BundleModularDistribution.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +import java.net.URL; +import java.util.Enumeration; +import java.util.SortedMap; +import java.util.StringTokenizer; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +import org.apache.commons.io.IOUtils; +import org.argeo.slc.DefaultNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.core.build.VersionedResourceDistribution; +import org.osgi.framework.Constants; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.ResourceLoader; + +public class BundleModularDistribution extends AbstractOsgiModularDistribution + implements ResourceLoaderAware { + private ResourceLoader resourceLoader; + + private String libDirectory = "/lib"; + + @SuppressWarnings(value = { "unchecked" }) + protected void fillDistributions( + SortedMap distributions) + throws Exception { + Enumeration urls = (Enumeration) getBundleContext() + .getBundle().findEntries(libDirectory, "*.jar", false); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + JarInputStream in = null; + try { + in = new JarInputStream(url.openStream()); + Manifest mf = in.getManifest(); + String name = mf.getMainAttributes().getValue( + Constants.BUNDLE_SYMBOLICNAME); + // Skip additional specs such as + // ; singleton:=true + if (name.indexOf(';') > -1) { + name = new StringTokenizer(name, " ;").nextToken(); + } + + String version = mf.getMainAttributes().getValue( + Constants.BUNDLE_VERSION); + DefaultNameVersion nameVersion = new DefaultNameVersion(name, + version); + distributions.put(nameVersion, + new VersionedResourceDistribution(name, version, + resourceLoader.getResource(url.toString()))); + } finally { + IOUtils.closeQuietly(in); + } + } + } + + public void setLibDirectory(String libDirectory) { + this.libDirectory = libDirectory; + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + /* + * @SuppressWarnings(value = { "unchecked" }) protected URL + * findModule(String moduleName, String version) { Enumeration urls = + * (Enumeration) bundleContext.getBundle() .findEntries(libDirectory, + * moduleName + "*", false); + * + * if (!urls.hasMoreElements()) throw new SlcException("Cannot find module " + * + moduleName); + * + * URL url = urls.nextElement(); + * + * // TODO: check version as well if (urls.hasMoreElements()) throw new + * SlcException("More than one module with name " + moduleName); return url; + * } + */ + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSite.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSite.java new file mode 100644 index 000000000..e38d9c024 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSite.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +import java.util.ArrayList; +import java.util.List; + +public class EclipseUpdateSite { + private List features = new ArrayList(); + + public List getFeatures() { + return features; + } + + public void setFeatures(List features) { + this.features = features; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteCategory.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteCategory.java new file mode 100644 index 000000000..c71656236 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteCategory.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +public class EclipseUpdateSiteCategory { + private String name; + private String label; + private String description; + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteFeature.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteFeature.java new file mode 100644 index 000000000..f28c0cb12 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/EclipseUpdateSiteFeature.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +import java.util.ArrayList; +import java.util.List; + +public class EclipseUpdateSiteFeature { + private String name; + private List categories = new ArrayList(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getCategories() { + return categories; + } + + public void setCategories(List categories) { + this.categories = categories; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/build/OsgiRuntimeModularDistribution.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/OsgiRuntimeModularDistribution.java new file mode 100644 index 000000000..434ef0e09 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/build/OsgiRuntimeModularDistribution.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.build; + +import java.net.URL; +import java.util.SortedMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.NameVersion; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.core.build.VersionedResourceDistribution; +import org.argeo.slc.osgi.OsgiBundle; +import org.osgi.framework.Bundle; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.eclipse.gemini.blueprint.util.OsgiBundleUtils; + +public class OsgiRuntimeModularDistribution extends + AbstractOsgiModularDistribution implements ResourceLoaderAware { + private final static Log log = LogFactory + .getLog(OsgiRuntimeModularDistribution.class); + + private ResourceLoader resourceLoader; + + protected void fillDistributions( + SortedMap distributions) + throws Exception { + + String frameworkUrl = System.getProperty("osgi.framework"); + String frameworkBaseUrl = null; + if (frameworkUrl != null) + frameworkBaseUrl = frameworkUrl.substring(0, frameworkUrl + .lastIndexOf('/')); + bundles: for (Bundle bundle : getBundleContext().getBundles()) { + OsgiBundle osgiBundle = new OsgiBundle(bundle); + + String originalLocation = bundle.getLocation(); + + if (OsgiBundleUtils.isSystemBundle(bundle)) { + continue bundles; + } + + String location = originalLocation; + if (originalLocation.startsWith("reference:file:")) + location = originalLocation.substring("reference:".length()); + + if (frameworkBaseUrl != null + && originalLocation.startsWith("initial@reference:file:")) { + location = frameworkBaseUrl + + '/' + + originalLocation.substring("initial@reference:file:" + .length()); + } + + try { + URL url = new URL(location); + Resource res = resourceLoader.getResource(url.toString()); + distributions.put(osgiBundle, + new VersionedResourceDistribution(osgiBundle, res)); + + if (log.isTraceEnabled()) + log.debug("Added url " + url + " from original location " + + originalLocation); + } catch (Exception e) { + log.warn("Cannot interpret location " + location + + " of bundle " + bundle + ": " + e); + } + } + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/deploy/OsgiResourceSet.java b/org.argeo.slc.core/src/org/argeo/slc/osgi/deploy/OsgiResourceSet.java new file mode 100644 index 000000000..d95137e23 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/deploy/OsgiResourceSet.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed 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.argeo.slc.osgi.deploy; + +import org.argeo.slc.core.deploy.DefaultResourceSet; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.springframework.core.io.ResourceLoader; +import org.eclipse.gemini.blueprint.context.BundleContextAware; +import org.eclipse.gemini.blueprint.io.OsgiBundleResourceLoader; +import org.eclipse.gemini.blueprint.io.OsgiBundleResourcePatternResolver; +import org.eclipse.gemini.blueprint.util.OsgiBundleUtils; + +/** + * Retrieves ressources from an OSGi bundle either the active one or another one + * referenced by its symbolic name. + */ +public class OsgiResourceSet extends DefaultResourceSet implements + BundleContextAware { + private BundleContext bundleContext; + private Bundle bundle = null; + private String bundleSymbolicName = null; + + private OsgiBundleResourceLoader osgiBundleResourceLoader = null; + + @Override + public void afterPropertiesSet() throws Exception { + osgiBundleResourceLoader = new OsgiBundleResourceLoader(getBundle()); + if (getResourcePatternResolver() == null) + setResourcePatternResolver(new OsgiBundleResourcePatternResolver( + osgiBundleResourceLoader)); + super.afterPropertiesSet(); + } + + public Bundle getBundle() { + if (bundle != null) + return bundle; + else if (bundleSymbolicName != null)// do not cache + return OsgiBundleUtils.findBundleBySymbolicName(bundleContext, + bundleSymbolicName); + else + // containing bundle + return bundleContext.getBundle(); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + @Override + public ResourceLoader getResourceLoaderToUse() { + return osgiBundleResourceLoader; + } + + public void setBundle(Bundle bundle) { + this.bundle = bundle; + } + + public void setBundleSymbolicName(String bundleSymbolicName) { + this.bundleSymbolicName = bundleSymbolicName; + } + +} diff --git a/org.argeo.slc.core/src/org/argeo/slc/osgi/execution.xml b/org.argeo.slc.core/src/org/argeo/slc/osgi/execution.xml new file mode 100644 index 000000000..cc0aac069 --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/osgi/execution.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file