summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/java/shell/modules/node-impl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/LICENSE205
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/META-INF/MANIFEST.MF53
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/NOTICE9
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/pom.xml121
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java129
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java143
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java142
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java97
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java109
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java550
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointMatcher.java273
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportReferenceImpl.java73
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java80
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportReferenceImpl.java72
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java70
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java234
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java163
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java114
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java346
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java400
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java265
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java66
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java57
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java140
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java262
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointDescription.java682
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointListener.java126
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointPermission.java693
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportReference.java45
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportRegistration.java71
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportReference.java44
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportRegistration.java67
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteConstants.java216
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java124
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java178
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java44
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.ValidationSchema18
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory17
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/org/osgi/service/remoteserviceadmin/osgi-remoteserviceadmin.xsd92
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/CalculatorService.java36
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorActivator.java75
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java114
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceImpl.java96
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/AddService.java31
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/DivideService.java31
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/MultiplyService.java31
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/SubtractService.java31
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/AddServiceImpl.java37
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/DivideServiceImpl.java37
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java37
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/OperationsActivator.java88
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java37
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java156
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiTestCase.java99
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/OSGiTestBundles.java362
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/MANIFEST.MF20
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/sca-contribution.xml24
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/blueprint/calculator-module.xml43
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/calculator-component.xml36
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml55
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/bundle.componentType63
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/calculator.composite53
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/MANIFEST.MF22
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/sca-contribution.xml23
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/add-component.xml25
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/blueprint/operations-module.xml40
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/divide-component.xml25
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/multiply-component.xml25
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/subtract-component.xml25
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/bundle.componentType54
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/operations.composite43
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/MANIFEST.MF12
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/sca-contribution.xml24
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/OSGI-INF/sca/calculator.composite70
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/LICENSE205
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/META-INF/MANIFEST.MF40
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/NOTICE6
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/pom.xml115
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationATOMProcessor.java160
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessor.java237
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeFactoryImpl.java534
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeImpl.java382
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeUtil.java43
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManager.java65
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManagerMBean.java30
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor18
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory18
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory18
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/org/apache/tuscany/sca/node/impl/node-impl-validation-messages.properties22
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorld.java30
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorldImpl.java30
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessorTestCase.java86
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeImplTestCase.java218
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeUtilTestCase.java48
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/HelloWorld.composite30
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/META-INF/sca-contribution.xml24
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/node1.xml58
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/test-node-factory.config19
-rw-r--r--sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/test.properties22
99 files changed, 11028 insertions, 0 deletions
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/LICENSE b/sandbox/sebastien/java/shell/modules/node-impl-osgi/LICENSE
new file mode 100644
index 0000000000..8aa906c321
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/LICENSE
@@ -0,0 +1,205 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/META-INF/MANIFEST.MF b/sandbox/sebastien/java/shell/modules/node-impl-osgi/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..82b79149f8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/META-INF/MANIFEST.MF
@@ -0,0 +1,53 @@
+Manifest-Version: 1.0
+Bundle-Name: Apache Tuscany SCA OSGi Node Implementation
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 2.0.0
+SCA-Version: 1.1
+Bundle-ManifestVersion: 2
+Bundle-Activator: org.apache.tuscany.sca.node.osgi.impl.NodeActivator
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Description: Apache Tuscany SCA Node Implementation
+Import-Package: javax.xml.namespace,
+ javax.xml.stream,
+ org.apache.tuscany.sca.assembly;version="2.0.0",
+ org.apache.tuscany.sca.assembly.builder;version="2.0.0",
+ org.apache.tuscany.sca.common.java.collection;version="2.0.0",
+ org.apache.tuscany.sca.common.java.io;version="2.0.0",
+ org.apache.tuscany.sca.common.xml.stax;version="2.0.0",
+ org.apache.tuscany.sca.contribution;version="2.0.0",
+ org.apache.tuscany.sca.contribution.processor;version="2.0.0",
+ org.apache.tuscany.sca.contribution.resolver;version="2.0.0",
+ org.apache.tuscany.sca.core;version="2.0.0",
+ org.apache.tuscany.sca.core.assembly;version="2.0.0",
+ org.apache.tuscany.sca.core.invocation;version="2.0.0",
+ org.apache.tuscany.sca.definitions;version="2.0.0",
+ org.apache.tuscany.sca.definitions.util;version="2.0.0",
+ org.apache.tuscany.sca.definitions.xml;version="2.0.0",
+ org.apache.tuscany.sca.deployment;version="2.0.0",
+ org.apache.tuscany.sca.extensibility.equinox;version="2.0.0",
+ org.apache.tuscany.sca.implementation.osgi;version="2.0.0",
+ org.apache.tuscany.sca.interfacedef;version="2.0.0",
+ org.apache.tuscany.sca.interfacedef.java;version="2.0.0",
+ org.apache.tuscany.sca.monitor;version="2.0.0",
+ org.apache.tuscany.sca.node;version="2.0.0",
+ org.apache.tuscany.sca.node.configuration;version="2.0.0",
+ org.apache.tuscany.sca.node.impl;version="2.0.0",
+ org.apache.tuscany.sca.policy;version="2.0.0",
+ org.apache.tuscany.sca.provider;version="2.0.0",
+ org.apache.tuscany.sca.runtime;version="2.0.0",
+ org.apache.tuscany.sca.work;version="2.0.0",
+ org.oasisopen.sca;version="2.0.0",
+ org.oasisopen.sca.annotation;version="2.0.0";resolution:=optional,
+ org.osgi.framework;version="1.4.0",
+ org.osgi.framework.hooks.service;version="1.0.0";resolution:=optional,
+ org.osgi.service.cm;version="1.2.1",
+ org.osgi.service.component;version="1.1.0";resolution:=optional,
+ org.osgi.service.event;version="1.2.0",
+ org.osgi.service.packageadmin;version="1.2.0";resolution:=optional,
+ org.osgi.service.remoteserviceadmin;version="1.0.0",
+ org.osgi.util.tracker;version="1.3.0";resolution:=optional
+Bundle-SymbolicName: org.apache.tuscany.sca.node.osgi.impl
+Bundle-DocURL: http://www.apache.org/
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Export-Package: org.osgi.service.remoteserviceadmin;version="1.0.0"
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/NOTICE b/sandbox/sebastien/java/shell/modules/node-impl-osgi/NOTICE
new file mode 100644
index 0000000000..8c74c2267e
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/NOTICE
@@ -0,0 +1,9 @@
+${pom.name}
+Copyright (c) 2005 - 2010 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed at
+the OSGi Alliance (http://www.osgi.org/).
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/pom.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/pom.xml
new file mode 100644
index 0000000000..c5af2a491b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/pom.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<project>
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-modules</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>tuscany-node-impl-osgi</artifactId>
+ <name>Apache Tuscany SCA OSGi Node Implementation</name>
+
+ <repositories>
+ <repository>
+ <id>tuscany.repo</id>
+ <name>Tuscany Maven 2.x Repository</name>
+ <url>http://svn.apache.org/repos/asf/tuscany/maven</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>osgi</artifactId>
+ <version>3.5.0-v20090520</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.osgi</groupId>
+ <artifactId>services</artifactId>
+ <version>3.2.0-v20090520-1800</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Equinox Declarative Services -->
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>ds</artifactId>
+ <version>1.1.0-v20090601</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>util</artifactId>
+ <version>1.0.100-v20090520-1800</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-extensibility-equinox</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-contribution-osgi</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-node-impl</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-node-launcher-equinox</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-implementation-osgi</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-implementation-osgi-runtime</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-binding-rmi-runtime</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java
new file mode 100644
index 0000000000..36feb6526a
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.osgi.impl;
+
+import static org.apache.tuscany.sca.node.osgi.impl.NodeManager.isSCABundle;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.RemoteServiceAdminImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.TopologyManagerImpl;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.DiscoveryActivator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * Bundle activator to receive the BundleContext
+ */
+public class NodeActivator implements BundleActivator, SynchronousBundleListener {
+ private final static Logger logger = Logger.getLogger(NodeActivator.class.getName());
+ private static BundleContext bundleContext;
+ private boolean inited;
+ private NodeManager manager;
+
+ private DiscoveryActivator discoveryActivator = new DiscoveryActivator();
+ private RemoteServiceAdminImpl remoteAdmin;
+ private TopologyManagerImpl controller;
+
+ private void init() {
+ synchronized (this) {
+ if (inited) {
+ return;
+ }
+ manager = new NodeManager(bundleContext);
+ manager.start();
+ bundleContext.addBundleListener(manager);
+ inited = true;
+ }
+ }
+
+ public void start(BundleContext context) throws Exception {
+ try {
+ bundleContext = context;
+
+ // FIXME: We should try to avoid aggressive initialization
+ init();
+
+ remoteAdmin = new RemoteServiceAdminImpl(context);
+ remoteAdmin.start();
+
+ discoveryActivator.start(context);
+
+ controller = new TopologyManagerImpl(context);
+ controller.start();
+
+ boolean found = false;
+ for (Bundle b : context.getBundles()) {
+ if (isSCABundle(b)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ init();
+ } else {
+ context.addBundleListener(this);
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ if (inited) {
+ context.removeBundleListener(this);
+ controller.stop();
+ controller = null;
+
+ discoveryActivator.stop(context);
+ discoveryActivator = null;
+
+ remoteAdmin.stop();
+ remoteAdmin = null;
+
+ manager.stop();
+ bundleContext.removeBundleListener(manager);
+ manager = null;
+ bundleContext = null;
+ inited = false;
+ }
+ }
+
+ public static BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.STARTING) {
+ if (isSCABundle(event.getBundle())) {
+ bundleContext.removeBundleListener(this);
+ init();
+ }
+ }
+
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java
new file mode 100644
index 0000000000..6914e09b8e
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.osgi.impl;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * Managing the mapping between OSGi bundles and SCA implementation.osgi
+ */
+public class NodeManager implements SynchronousBundleListener, ServiceListener {
+ private static final Logger logger = Logger.getLogger(NodeManager.class.getName());
+ private BundleContext bundleContext;
+ OSGiNodeFactoryImpl factory;
+
+ public NodeManager(BundleContext bundleContext) {
+ super();
+ this.bundleContext = bundleContext;
+ this.factory = new OSGiNodeFactoryImpl(this.bundleContext);
+ }
+
+ public void start() {
+ for (Bundle b : bundleContext.getBundles()) {
+ if ((b.getState() & Bundle.ACTIVE) != 0) {
+ // Process the active bundles
+ bundleStarted(b);
+ }
+ }
+ }
+
+ public void stop() {
+ if (factory != null) {
+ factory.destroy();
+ }
+ }
+
+ public static boolean isSCABundle(Bundle bundle) {
+ Dictionary<?, ?> headers = bundle.getHeaders();
+ // OSGi RFC 119 SCA
+ if (headers.get("SCA-Composite") != null) {
+ return true;
+ }
+
+ URL bundleComposite = bundle.getResource("OSGI-INF/sca/bundle.composite");
+ if (bundleComposite != null) {
+ return true;
+ }
+
+ /* FIXME: What if there is a META-INF/sca-contribution.xml? There are two cases:
+ * 1. The file contains deployable elements
+ * 2. The file doesn't contain deployable elements
+ */
+
+ /*
+ * FIXME: Do we want to use all of the .composite files under OSGI-INF/sca?
+ */
+
+ /*
+ Enumeration<?> entries = bundle.findEntries("OSGI-INF/sca", "bundle.composite", false);
+ if (entries != null && entries.hasMoreElements()) {
+ return true;
+ }
+
+ // OSGi Declarative Services
+ if (headers.get("Service-Component") != null) {
+ return true;
+ }
+
+ // OSGI RFC 124: BluePrint Service
+ if (headers.get("Bundle-Blueprint") != null) {
+ return true;
+ }
+
+ entries = bundle.findEntries("OSGI-INF/blueprint", "*.xml", false);
+ if (entries != null && entries.hasMoreElements()) {
+ return true;
+ }
+ */
+ return false;
+ }
+
+ private void bundleStarted(Bundle bundle) {
+ if (!isSCABundle(bundle)) {
+ return;
+ }
+ try {
+ NodeConfiguration configuration = factory.getConfiguration(bundle, null);
+ Node node = factory.createNode(configuration);
+ node.start();
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
+
+ private void bundleStopping(Bundle bundle) {
+ Node node = factory.getNodes().get(bundle);
+ if (node == null) {
+ return;
+ }
+ node.stop();
+ }
+
+ public void serviceChanged(ServiceEvent event) {
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ int type = event.getType();
+ if (type == BundleEvent.STOPPING) {
+ bundleStopping(event.getBundle());
+ } else if (type == BundleEvent.STARTED) {
+ bundleStarted(event.getBundle());
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java
new file mode 100644
index 0000000000..c79fc04c4d
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java
@@ -0,0 +1,142 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.tuscany.sca.node.osgi.impl;
+
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.logging.Level;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.extensibility.equinox.OSGiExtensionPointRegistry;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The OSGi based NodeFactory
+ *
+ * @version $Rev$ $Date$
+ */
+public class OSGiNodeFactoryImpl extends NodeFactoryImpl {
+ private ServiceRegistration registration;
+ private BundleContext bundleContext;
+
+ public OSGiNodeFactoryImpl() {
+ super();
+ Bundle bundle = FrameworkUtil.getBundle(OSGiNodeFactoryImpl.class);
+ if (bundle != null) {
+ this.bundleContext = bundle.getBundleContext();
+ autoDestroy = false;
+ setNodeFactory(this);
+ } else {
+ throw new IllegalStateException(OSGiNodeFactoryImpl.class + " is not loaded by OSGi");
+ }
+ }
+ /**
+ * Constructs a new Node controller
+ */
+ public OSGiNodeFactoryImpl(BundleContext bundleContext) {
+ super();
+ this.bundleContext = bundleContext;
+ autoDestroy = false;
+ setNodeFactory(this);
+ }
+
+ protected NodeConfiguration getConfiguration(Bundle bundle, String compositeContent) {
+ init();
+
+ // Create a node configuration
+ FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ NodeConfigurationFactory configurationFactory = modelFactories.getFactory(NodeConfigurationFactory.class);
+ NodeConfiguration configuration = configurationFactory.createNodeConfiguration();
+
+ URL location = bundle.getEntry("/");
+ String uri = bundle.getSymbolicName();
+ configuration.setURI(uri).addContribution(uri, location);
+
+ if (compositeContent != null) {
+ configuration.addDeploymentComposite(uri, new StringReader(compositeContent));
+ } else {
+ String compositeURI = (String)bundle.getHeaders().get("SCA-Composite");
+ if (compositeURI == null) {
+ compositeURI = "OSGI-INF/sca/bundle.composite";
+ }
+ if (compositeURI != null) {
+ configuration.addDeploymentComposite(uri, compositeURI);
+ }
+ }
+ // Set the bundle
+ // configuration.getExtensions().add(bundle);
+ return configuration;
+ }
+
+ public synchronized void init() {
+ if (!inited) {
+ // Register the ExtensionPointRegistry as an OSGi service
+ Dictionary<Object, Object> props = new Hashtable<Object, Object>();
+ registry = createExtensionPointRegistry();
+ registry.start();
+ registration =
+ bundleContext.registerService(ExtensionPointRegistry.class.getName(), registry, props);
+
+ // Call super.init after the extension point registry is registered
+ super.init();
+ }
+ }
+
+ public synchronized void destroy() {
+ if (inited) {
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ super.destroy();
+ }
+
+ }
+
+ @Override
+ protected Object getNodeKey(NodeConfiguration configuration) {
+ // each bundle might have more than one nodes started
+ return super.getNodeKey(configuration);
+ }
+
+ @Override
+ protected ExtensionPointRegistry createExtensionPointRegistry() {
+ return new OSGiExtensionPointRegistry(bundleContext);
+ }
+
+ @Override
+ protected boolean isSchemaValidationEnabled() {
+ return logger.isLoggable(Level.FINE);
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java
new file mode 100644
index 0000000000..5a4b5aa836
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class AbstractOSGiServiceHandler implements LifeCycleListener {
+ protected ExtensionPointRegistry registry;
+ protected BundleContext context;
+ protected NodeFactoryImpl nodeFactory;
+ protected EndpointIntrospector introspector;
+ protected ServiceTracker discoveryTracker;
+ protected String domainRegistry;
+ protected String domainURI;
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ protected AbstractOSGiServiceHandler(BundleContext context) {
+ this.context = context;
+ this.domainRegistry = context.getProperty("org.osgi.sca.domain.registry");
+ this.domainURI = context.getProperty("org.osgi.sca.domain.uri");
+ }
+
+ protected ExtensionPointRegistry getExtensionPointRegistry() {
+ if (registry == null) {
+ ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null);
+ tracker.open();
+ // tracker.waitForService(1000);
+ registry = (ExtensionPointRegistry)tracker.getService();
+ tracker.close();
+ }
+ return registry;
+ }
+
+ protected synchronized void init() {
+ if (nodeFactory == null) {
+ this.nodeFactory = (NodeFactoryImpl)NodeFactory.getInstance();
+ this.nodeFactory.init();
+ this.discoveryTracker = LocalDiscoveryService.getTracker(context);
+ discoveryTracker.open();
+ this.introspector = new EndpointIntrospector(context, getExtensionPointRegistry(), discoveryTracker);
+ }
+ }
+
+ public void start() {
+ init();
+ }
+
+ public void stop() {
+ if (nodeFactory != null) {
+ discoveryTracker.close();
+ discoveryTracker = null;
+ introspector = null;
+ nodeFactory = null;
+ registry = null;
+ context = null;
+ }
+ }
+
+ public void setDomainRegistry(String domainRegistry) {
+ this.domainRegistry = domainRegistry;
+ }
+
+ public void setDomainURI(String domainURI) {
+ this.domainURI = domainURI;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java
new file mode 100644
index 0000000000..8ecc5f7ea8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.interfacedef.Interface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+/**
+ * Implementation of {@link EndpointDescription}
+ */
+public class EndpointHelper {
+ private EndpointHelper() {
+ }
+
+ public static EndpointDescription createEndpointDescription(BundleContext bundleContext, Endpoint endpoint) {
+ return new EndpointDescription(getProperties(bundleContext, endpoint));
+ }
+
+ private static List<String> getInterfaces(Endpoint endpoint) {
+ Interface intf = endpoint.getComponentServiceInterfaceContract().getInterface();
+ JavaInterface javaInterface = (JavaInterface)intf;
+ return Collections.singletonList(javaInterface.getName());
+ }
+
+ private static Map<String, Object> getProperties(BundleContext bundleContext, Endpoint endpoint) {
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ if (!endpoint.isRemote()) {
+ String uuid = OSGiHelper.getFrameworkUUID(bundleContext);
+ props.put(RemoteConstants.ENDPOINT_FRAMEWORK_UUID, uuid);
+ }
+
+ for (Object ext : endpoint.getService().getExtensions()) {
+ if (ext instanceof OSGiProperty) {
+ OSGiProperty prop = (OSGiProperty)ext;
+ props.put(prop.getName(), prop.getStringValue());
+ }
+ }
+
+ String serviceID = (String)props.get(Constants.SERVICE_ID);
+ if (serviceID != null) {
+ props.put(RemoteConstants.ENDPOINT_SERVICE_ID, Long.parseLong(serviceID));
+ }
+
+ // FIXME: [rfeng] We need to calculate the intents supported by this endpoint
+ /*
+ QName bindingTypeName = endpoint.getBinding().getType();
+ Definitions definitions = null;
+ if(definitions!=null) {
+ for(BindingType bindingType: definitions.getBindingTypes()) {
+ if(bindingType.getType().equals(bindingTypeName)) {
+ bindingType.getAlwaysProvidedIntents();
+ }
+ }
+ */
+
+ String intents = (String)props.get(RemoteConstants.SERVICE_EXPORTED_INTENTS);
+ String extraIntents = (String)props.get(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA);
+ if (intents == null) {
+ intents = "";
+ }
+ if (extraIntents != null) {
+ intents = intents + " " + extraIntents;
+ }
+
+ props.put(RemoteConstants.SERVICE_INTENTS, intents.trim());
+
+ props.put(RemoteConstants.ENDPOINT_ID, endpoint.getURI());
+ // FIXME: [rfeng] How to pass in the remote service id from the endpoint XML
+ props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, new String[] {"org.osgi.sca"});
+ props.put(Endpoint.class.getName(), endpoint);
+ List<String> interfaces = getInterfaces(endpoint);
+ props.put(Constants.OBJECTCLASS, interfaces.toArray(new String[interfaces.size()]));
+ return props;
+ }
+
+ public static Endpoint getEndpoint(EndpointDescription endpointDescription) {
+ return (Endpoint)endpointDescription.getProperties().get(Endpoint.class.getName());
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java
new file mode 100644
index 0000000000..d00696a822
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java
@@ -0,0 +1,550 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.assembly.Base.SCA11_TUSCANY_NS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SCA_BINDINGS;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.createOSGiProperty;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getStringArray;
+import static org.osgi.framework.Constants.OBJECTCLASS;
+import static org.osgi.framework.Constants.SERVICE_ID;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.tuscany.sca.assembly.AssemblyFactory;
+import org.apache.tuscany.sca.assembly.Base;
+import org.apache.tuscany.sca.assembly.Binding;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Composite;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Reference;
+import org.apache.tuscany.sca.assembly.Service;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.ContributionFactory;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ContributionResolveException;
+import org.apache.tuscany.sca.contribution.resolver.ExtensibleModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolverExtensionPoint;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.deployment.Deployer;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptionsFactory;
+import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService.ExtenderConfiguration;
+import org.apache.tuscany.sca.policy.Intent;
+import org.apache.tuscany.sca.policy.PolicyFactory;
+import org.apache.tuscany.sca.policy.PolicySet;
+import org.oasisopen.sca.ServiceRuntimeException;
+import org.oasisopen.sca.annotation.PolicySets;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Introspect an OSGi Service to create an SCA composite that contains a single component with
+ * implementation.osgi
+ */
+public class EndpointIntrospector {
+ // private BundleContext context;
+ private AssemblyFactory assemblyFactory;
+ private ContributionFactory contributionFactory;
+ private OSGiImplementationFactory implementationFactory;
+ private PolicyFactory policyFactory;
+ private ExtensionPointRegistry registry;
+ private FactoryExtensionPoint factories;
+ private ModelResolverExtensionPoint modelResolvers;
+ // private StAXArtifactProcessor<Composite> compositeProcessor;
+ private JavaInterfaceFactory javaInterfaceFactory;
+ private Deployer deployer;
+ private ServiceTracker discoveryTracker;
+
+ /**
+ * @param name
+ * @return
+ */
+ private static QName getQName(String name) {
+ QName qname;
+ if (name.startsWith("{")) {
+ int i = name.indexOf('}');
+ if (i != -1) {
+ qname = new QName(name.substring(1, i), name.substring(i + 1));
+ } else {
+ throw new IllegalArgumentException("Invalid qname: " + name);
+ }
+ } else {
+ // Default to SCA namespace
+ qname = new QName("", name);
+ }
+ return qname;
+ }
+
+ /**
+ * @param context TODO
+ * @param registry
+ */
+ public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry, ServiceTracker discoveryTracker) {
+ super();
+ this.registry = registry;
+ // this.context = context;
+ this.discoveryTracker = discoveryTracker;
+ this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ this.modelResolvers = registry.getExtensionPoint(ModelResolverExtensionPoint.class);
+// this.compositeProcessor =
+// registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class).getProcessor(Composite.class);
+ this.assemblyFactory = factories.getFactory(AssemblyFactory.class);
+ this.contributionFactory = factories.getFactory(ContributionFactory.class);
+ this.policyFactory = factories.getFactory(PolicyFactory.class);
+ this.implementationFactory = factories.getFactory(OSGiImplementationFactory.class);
+ this.javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class);
+ this.deployer = registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(Deployer.class);
+ }
+
+ private Intent getIntent(String intent) {
+ QName name = getQName(intent);
+ Intent i = policyFactory.createIntent();
+ i.setName(name);
+ return i;
+ }
+
+ private List<Intent> getIntents(String[] intents) {
+ if (intents == null || intents.length == 0) {
+ return Collections.emptyList();
+ }
+ List<Intent> intentList = new ArrayList<Intent>();
+ for (String i : intents) {
+ Intent intent = getIntent(i);
+ if (intent != null) {
+ intentList.add(intent);
+ }
+ }
+ return intentList;
+ }
+
+ /**
+ * Any property in the map overrides the service reference properties, regardless of
+ * case. That is, if the map contains a key then it will override any case variant
+ * of this key in the Service Reference.<p>
+ * If the map contains the objectClass or service. id property key in any case
+ * variant, then these properties must not override the Service References value. This
+ * implies that the map can provide the service.exported. interfaces, property allowing
+ * the Topology Manager to export any registered service, also services not specifically
+ * marked to be exported.
+ * @param reference
+ * @param props
+ * @return
+ */
+ private Map<String, Object> getProperties(ServiceReference reference, Map<String, Object> props) {
+ String[] names = reference.getPropertyKeys();
+ Map<String, Object> properties = new HashMap<String, Object>();
+ if (names != null) {
+ for (String name : names) {
+ properties.put(name, reference.getProperty(name));
+ }
+ }
+ if (props != null) {
+ // Create a map of names (key = lowcase name, value = name)
+ Map<String, String> nameMap = new HashMap<String, String>();
+ if (names != null) {
+ for (String name : names) {
+ nameMap.put(name.toLowerCase(), name);
+ }
+ }
+ for (Map.Entry<String, Object> p : props.entrySet()) {
+ if (OBJECTCLASS.equalsIgnoreCase(p.getKey())) {
+ throw new IllegalArgumentException(OBJECTCLASS + " property cannot be overridden.");
+ } else if (SERVICE_ID.equalsIgnoreCase(p.getKey())) {
+ throw new IllegalArgumentException(SERVICE_ID + " property cannot be overridden.");
+ }
+ String key = nameMap.get(p.getKey().toLowerCase());
+ if (key != null) {
+ properties.put(key, p.getValue());
+ } else {
+ properties.put(p.getKey(), p.getValue());
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Parse the Stringp[] to support values that are separated by comma
+ * @param interfaces
+ * @return
+ */
+ private String[] parse(String[] interfaces) {
+ if (interfaces == null) {
+ return null;
+ }
+ List<String> names = new ArrayList<String>();
+ for (String i : interfaces) {
+ String[] parts = i.split(",");
+ for (String p : parts) {
+ names.add(p.trim());
+ }
+ }
+ return names.toArray(new String[names.size()]);
+ }
+
+ /**
+ * Introspect a local OSGi Service represented by the ServiceReference to create
+ * an SCA service with the required intents and bindings
+ * @param reference The service reference for a local OSGi service
+ * @param props Addiontal properties
+ * @return An SCA contribution with a deployable composite for the SCA service
+ * @throws Exception
+ */
+ public Contribution introspect(ServiceReference reference, Map<String, Object> props) throws Exception {
+ Bundle bundle = reference.getBundle();
+ Map<String, Object> properties = getProperties(reference, props);
+ Collection<OSGiProperty> osgiProps = implementationFactory.createOSGiProperties(reference);
+ Long sid = (Long)reference.getProperty(SERVICE_ID);
+
+ String[] requiredIntents = getStringArray(properties.get(RemoteConstants.SERVICE_EXPORTED_INTENTS));
+ List<Intent> intents = getIntents(requiredIntents);
+ String[] requiredIntentsExtra = getStringArray(properties.get(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA));
+ List<Intent> extraIntents = getIntents(requiredIntentsExtra);
+ Set<Intent> allIntents = new HashSet<Intent>(intents);
+ allIntents.addAll(extraIntents);
+
+ String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
+ Collection<Binding> bindings = loadBindings(bindingNames);
+
+ String[] remoteInterfaces = getStringArray(reference.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
+ if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) {
+ remoteInterfaces = getStringArray(reference.getProperty(OBJECTCLASS));
+ } else {
+ remoteInterfaces = parse(remoteInterfaces);
+ String[] objectClasses = getStringArray(reference.getProperty(OBJECTCLASS));
+ Set<String> objectClassSet = new HashSet<String>(Arrays.asList(objectClasses));
+ if (!objectClassSet.containsAll(Arrays.asList(remoteInterfaces))) {
+ throw new IllegalArgumentException(
+ "The exported interfaces are not a subset of the types" + " listed in the objectClass service property from the Service Reference");
+ }
+ }
+
+ Contribution contribution = generateContribution(bundle, sid, remoteInterfaces, bindings, allIntents, osgiProps);
+ return contribution;
+ }
+
+ public String instrospectSCAConfig(ServiceReference reference, Map<String, Object> props, ComponentService service){
+
+ ServiceDescriptionsFactory serviceDescriptionFactory = registry.getExtensionPoint(ServiceDescriptionsFactory.class);
+ SCAConfig scaConfig = serviceDescriptionFactory.createSCAConfig();
+
+ // add the binding configurations
+ List<Binding> bindings = scaConfig.getBindings();
+ bindings.addAll(service.getBindings());
+
+ // add the intent configurations
+ List<Intent> intents = scaConfig.getIntents();
+ intents.addAll(service.getRequiredIntents());
+
+ // add the policy set configurations
+ List<PolicySet> policySets = scaConfig.getPolicySets();
+ policySets.addAll(service.getPolicySets());
+
+ // set up the target namespace
+ // TODO - there is a bug in the spec which only allow bindings from one
+ // namsepace to be included in sca-config element. Here we just
+ // the first bindings namespace
+ Map<String, Object> properties = getProperties(reference, props);
+ String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
+ if (bindingNames.length > 0){
+ QName firstBindingQName = getQName(bindingNames[0]);
+ scaConfig.setTargetNamespace(firstBindingQName.getNamespaceURI());
+ }
+
+ // write the sca config out to XML
+ String scaConfigXMLString = "";
+
+ try {
+ Writer writer = new StringWriter();
+ deployer.saveXMLDocument(scaConfig, writer, deployer.createMonitor());
+ scaConfigXMLString = writer.toString();
+ } catch (Exception ex){
+ throw new ServiceRuntimeException(ex);
+ }
+
+ return scaConfigXMLString;
+ }
+
+ /*
+ public Contribution loadContribution(Bundle bundle, Composite composite) {
+ try {
+ URL root = bundle.getEntry("/");
+ Contribution contribution = deployer.loadContribution(root.toURI(), root, deployer.createMonitor());
+ deployer.attachDeploymentComposite(contribution, composite, false);
+ return contribution;
+ } catch (Exception e) {
+ throw new ServiceRuntimeException(e);
+ }
+ }
+ */
+
+ /**
+ * Generate a contribution that contains the composite for the exported service
+ * @param bundle The OSGi bundle
+ * @param sid The service id
+ * @param remoteInterfaces
+ * @param bindings
+ * @param allIntents
+ * @return
+ * @throws ClassNotFoundException
+ * @throws InvalidInterfaceException
+ */
+ private Contribution generateContribution(Bundle bundle,
+ Long sid,
+ String[] remoteInterfaces,
+ Collection<Binding> bindings,
+ Set<Intent> allIntents,
+ Collection<OSGiProperty> osgiProps) throws ClassNotFoundException,
+ InvalidInterfaceException {
+ String id = "osgi.service." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ OSGiProperty serviceID = implementationFactory.createOSGiProperty();
+ serviceID.setName(SERVICE_ID);
+ // The service.id is Long
+ serviceID.setValue(String.valueOf(sid));
+
+ for (String intf : remoteInterfaces) {
+ Service service = assemblyFactory.createService();
+ JavaInterfaceContract interfaceContract = createJavaInterfaceContract(bundle, intf);
+ String name = intf.substring(intf.lastIndexOf('.') + 1);
+ service.setName(name);
+ service.setInterfaceContract(interfaceContract);
+
+ implementation.getServices().add(service);
+
+ ComponentService componentService = assemblyFactory.createComponentService();
+ componentService.setName(service.getName());
+ componentService.getExtensions().add(serviceID);
+ componentService.getExtensions().addAll(osgiProps);
+
+ component.getServices().add(componentService);
+ componentService.setService(service);
+ }
+
+ for (ComponentService componentService : component.getServices()) {
+ componentService.getRequiredIntents().addAll(allIntents);
+ componentService.getBindings().addAll(bindings);
+ }
+
+ // FIXME: Should we scan the owning bundle to create the SCA contribution?
+ Contribution contribution = loadContribution(bundle, id, composite);
+ return contribution;
+ }
+
+ private Contribution loadContribution(Bundle bundle, String id, Composite composite) {
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setClassLoader(OSGiHelper.createBundleClassLoader(bundle));
+ contribution.setURI(id);
+ contribution.setLocation(bundle.getEntry("/").toString());
+ deployer.attachDeploymentComposite(contribution, composite, false);
+ ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
+ contribution.setModelResolver(modelResolver);
+ // compositeProcessor.resolve(composite, modelResolver, new ProcessorContext(registry));
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+ /**
+ * @param bundle
+ * @param endpoint
+ * @return
+ * @throws Exception
+ */
+ public Contribution introspect(Bundle bundle, EndpointDescription endpoint) throws Exception {
+ Collection<Binding> bindings = Collections.emptyList();
+ Collection<String> interfaces = Collections.emptyList();
+ Collection<Intent> intents = Collections.emptyList();
+ Endpoint ep = (Endpoint)endpoint.getProperties().get(Endpoint.class.getName());
+ Collection<OSGiProperty> osgiProps = implementationFactory.createOSGiProperties(endpoint.getProperties());
+ if (ep != null) {
+ bindings = Collections.singletonList(ep.getBinding());
+ interfaces = Collections.singletonList(((JavaInterface)ep.getComponentServiceInterfaceContract().getInterface()).getName());
+ // FIXME: [rfeng] We need to build the in-memory composite so that intents are calculated at the ep level
+ intents = ep.getService().getRequiredIntents();
+ } else {
+ Map<String, Object> properties = endpoint.getProperties();
+ interfaces = endpoint.getInterfaces();
+ String[] requiredIntents = getStringArray(properties.get(RemoteConstants.SERVICE_INTENTS));
+ intents = getIntents(requiredIntents);
+
+ String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
+ bindings = loadBindings(bindingNames);
+ }
+
+ Contribution contribution = generateContribution(bundle, interfaces, bindings, intents, osgiProps);
+ return contribution;
+ }
+
+ private Contribution generateContribution(Bundle bundle,
+ Collection<String> remoteInterfaces,
+ Collection<Binding> bindings,
+ Collection<Intent> intents,
+ Collection<OSGiProperty> osgiProps) throws ClassNotFoundException,
+ InvalidInterfaceException, ContributionResolveException {
+ String id = "osgi.reference." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(Base.SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+ // component.setAutowire(Boolean.TRUE);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ int count = 0;
+ for (String intf : remoteInterfaces) {
+ Reference reference = assemblyFactory.createReference();
+ JavaInterfaceContract interfaceContract = createJavaInterfaceContract(bundle, intf);
+
+ reference.setName("ref" + (count++));
+ reference.setInterfaceContract(interfaceContract);
+
+ implementation.getReferences().add(reference);
+
+ ComponentReference componentReference = assemblyFactory.createComponentReference();
+ componentReference.setName(reference.getName());
+ componentReference.getExtensions().addAll(osgiProps);
+ component.getReferences().add(componentReference);
+ componentReference.setReference(reference);
+ componentReference.setWiredByImpl(true);
+ }
+
+ for (ComponentReference componentReference : component.getReferences()) {
+ componentReference.getRequiredIntents().addAll(intents);
+ componentReference.getBindings().addAll(bindings);
+ }
+
+ Contribution contribution = loadContribution(bundle, id, composite);
+ return contribution;
+ }
+
+ private JavaInterfaceContract createJavaInterfaceContract(Bundle bundle, String intf)
+ throws ClassNotFoundException, InvalidInterfaceException {
+ JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
+ Class<?> interfaceClass = bundle.loadClass(intf);
+ JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface();
+ // [rfeng] For OSGi, the interfaces should be marked as remotable
+ javaInterface.setRemotable(true);
+ // [rfeng] We need to mark the interface to be remotable before the createJavaInterface() is called
+ javaInterfaceFactory.createJavaInterface(javaInterface, interfaceClass);
+ interfaceContract.setInterface(javaInterface);
+ if (javaInterface.getCallbackClass() != null) {
+ JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(javaInterface.getCallbackClass());
+ callbackInterface.setRemotable(true);
+ interfaceContract.setCallbackInterface(callbackInterface);
+ }
+ return interfaceContract;
+ }
+
+ private Collection<Binding> loadBindings(String[] qnames) throws IOException, ContributionReadException,
+ XMLStreamException {
+ if (qnames == null || qnames.length == 0) {
+ return Collections.emptyList();
+ }
+ QName[] bindingNames = new QName[qnames.length];
+ int index = 0;
+ for (String name : qnames) {
+ bindingNames[index++] = getQName(name);
+ }
+
+ LocalDiscoveryService discoveryService = (LocalDiscoveryService)discoveryTracker.getService();
+
+ Map<QName, Binding> bindingMap = new HashMap<QName, Binding>();
+ if (discoveryService != null) {
+ for (ExtenderConfiguration config : discoveryService.getConfigurations()) {
+ for (SCAConfig sc : config.getSCAConfigs()) {
+ for (QName bindingName : bindingNames) {
+ if ("".equals(bindingName.getNamespaceURI()) ||
+ sc.getTargetNamespace().equals(bindingName.getNamespaceURI())) {
+ for (Binding binding : sc.getBindings()) {
+ if (bindingName.getLocalPart().equals(binding.getName())) {
+ // We need to check duplications
+ if (bindingMap.put(bindingName, binding) != null) {
+ throw new ServiceRuntimeException("Duplicate binding found: " + bindingName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (QName bindingName : bindingNames) {
+ if (!bindingMap.containsKey(bindingName)) {
+ throw new ServiceRuntimeException("Binding cannot be resolved: " + bindingName);
+ }
+ }
+ return bindingMap.values();
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointMatcher.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointMatcher.java
new file mode 100644
index 0000000000..f978ba658d
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointMatcher.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.common.java.collection.CollectionMap;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+/**
+ * Matching endpoint descriptions against the sevice listeners using OSGi filiters
+ */
+public class EndpointMatcher {
+ private static final Logger logger = Logger.getLogger(EndpointMatcher.class.getName());
+ private final EndpointMap endpointDescriptions = new EndpointMap();
+ private final ListenerMap listeners = new ListenerMap();
+ private final BundleContext context;
+ private final BlockingQueue<ImportAction> importQueue = new ArrayBlockingQueue<ImportAction>(256, true);
+
+ public EndpointMatcher(BundleContext context) {
+ super();
+ this.context = context;
+ }
+
+ public static boolean matches(String filter, EndpointDescription endpointDescription) {
+ Filter f = null;
+ try {
+ f = FrameworkUtil.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ Hashtable<String, Object> props = new Hashtable<String, Object>(endpointDescription.getProperties());
+ return f.match(props);
+ }
+
+ private void importEndpoint(ListenerInfo listener, EndpointDescription ep) {
+ ImportAction request = new ImportAction(ImportAction.Type.Add, listener, ep);
+ try {
+ importQueue.put(request);
+ } catch (InterruptedException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private void unimportEndpoint(ListenerInfo listener, EndpointDescription ep) {
+ ImportAction request = new ImportAction(ImportAction.Type.Remove, listener, ep);
+ try {
+ importQueue.put(request);
+ } catch (InterruptedException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public synchronized void added(ListenerInfo listener) {
+ String filter = listener.getFilter();
+ listeners.putValue(filter, listener);
+ for (EndpointDescription ep : getEndpoints(filter)) {
+ importEndpoint(listener, ep);
+ }
+ }
+
+ public synchronized Collection<String> added(Collection<ListenerInfo> listeners) {
+ for (ListenerInfo listener : listeners) {
+ if (accepts(listener)) {
+ if (!listener.isRemoved() && listener.getBundleContext().getBundle().getBundleId() != 0L) {
+ added(listener);
+ }
+ }
+ }
+ return getFilters();
+ }
+
+ private boolean accepts(ListenerInfo listener) {
+ BundleContext context = listener.getBundleContext();
+ return context != null && listener.getFilter() != null && context != this.context;
+ }
+
+ public synchronized void removed(ListenerInfo listener) {
+ String filter = listener.getFilter();
+ if (accepts(listener))
+ if (listeners.removeValue(filter, listener, true)) {
+ // Find the corresponding ImportRegistration with the listener
+ for (EndpointDescription ep : getEndpoints(filter)) {
+ unimportEndpoint(listener, ep);
+ }
+ if (getListeners(filter).isEmpty()) {
+ // No more listeners on the this filter, clean up the endpoint descriptionss
+ endpointDescriptions.remove(filter);
+ }
+
+ }
+ }
+
+ public synchronized Collection<String> removed(Collection<ListenerInfo> listeners) {
+ for (ListenerInfo listener : listeners) {
+ removed(listener);
+ }
+ return getFilters();
+ }
+
+ public synchronized void added(EndpointDescription endpointDescription) {
+ for (Map.Entry<String, Collection<ListenerInfo>> entry : listeners.entrySet()) {
+ if (matches(entry.getKey(), endpointDescription)) {
+ endpointDescriptions.putValue(entry.getKey(), endpointDescription);
+ for (ListenerInfo listener : entry.getValue()) {
+ importEndpoint(listener, endpointDescription);
+ }
+ }
+ }
+ }
+
+ public synchronized void added(EndpointDescription endpointDescription, String matchedFilter) {
+ if (endpointDescriptions.putValue(matchedFilter, endpointDescription)) {
+ Collection<ListenerInfo> listenerInfos = listeners.get(matchedFilter);
+ if (listenerInfos != null) {
+ for (ListenerInfo listener : listenerInfos) {
+ importEndpoint(listener, endpointDescription);
+ }
+ }
+ }
+ }
+
+ public synchronized void removed(EndpointDescription endpointDescription, String matchedFilter) {
+ if (endpointDescriptions.removeValue(matchedFilter, endpointDescription, true)) {
+ for (ListenerInfo listener : getListeners(matchedFilter)) {
+ unimportEndpoint(listener, endpointDescription);
+ }
+ }
+ }
+
+ public synchronized Set<String> getFilters() {
+ return new HashSet<String>(listeners.keySet());
+ }
+
+ public synchronized void clear() {
+ endpointDescriptions.clear();
+ listeners.clear();
+ importQueue.clear();
+ }
+
+ public synchronized Collection<ListenerInfo> getListeners(String filter) {
+ Collection<ListenerInfo> collection = listeners.get(filter);
+ if (collection == null) {
+ return Collections.emptySet();
+ } else {
+ return collection;
+ }
+ }
+
+ public synchronized Collection<EndpointDescription> getEndpoints(String filter) {
+ Collection<EndpointDescription> collection = endpointDescriptions.get(filter);
+ if (collection == null) {
+ return Collections.emptySet();
+ } else {
+ return collection;
+ }
+ }
+
+ public CollectionMap<Class<?>, ListenerInfo> groupListeners(EndpointDescription endpointDescription,
+ String matchedFilter) {
+ Collection<ListenerInfo> snapshot = new HashSet<ListenerInfo>(getListeners(matchedFilter));
+
+ // Try to partition the listeners by the interface classes
+ List<String> interfaceNames = endpointDescription.getInterfaces();
+ CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = new CollectionMap<Class<?>, ListenerInfo>();
+ for (String i : interfaceNames) {
+ for (Iterator<ListenerInfo> it = snapshot.iterator(); it.hasNext();) {
+ try {
+ ListenerInfo listener = it.next();
+ if (listener.isRemoved()) {
+ it.remove();
+ continue;
+ }
+ if (!matchedFilter.equals(listener.getFilter())) {
+ continue;
+ }
+ try {
+ // The classloading can be synchronzed against the serviceListeners
+ Class<?> interfaceClass = listener.getBundleContext().getBundle().loadClass(i);
+ interfaceToListeners.putValue(interfaceClass, listener);
+ } catch (IllegalStateException e) {
+ logger.log(Level.WARNING, e.getMessage(), e);
+ // Ignore the exception
+ }
+ } catch (ClassNotFoundException e) {
+ // Ignore the listener as it cannot load the interface class
+ }
+ }
+ }
+ return interfaceToListeners;
+ }
+
+ public BlockingQueue<ImportAction> getImportQueue() {
+ return importQueue;
+ }
+
+ private static class ListenerMap extends CollectionMap<String, ListenerInfo> {
+ private static final long serialVersionUID = -8612202123531331219L;
+
+ @Override
+ protected Collection<ListenerInfo> createCollection() {
+ return new HashSet<ListenerInfo>();
+ }
+ }
+
+ private static class EndpointMap extends CollectionMap<String, EndpointDescription> {
+ private static final long serialVersionUID = -6261405398109798549L;
+
+ @Override
+ protected Collection<EndpointDescription> createCollection() {
+ return new HashSet<EndpointDescription>();
+ }
+ }
+
+ /**
+ * Representation of an import/unimport request
+ */
+ public static class ImportAction {
+ enum Type {
+ Add, Remove
+ };
+
+ public final Type type;
+ public final ListenerInfo listenerInfo;
+ public final EndpointDescription endpointDescription;
+
+ /**
+ * @param type
+ * @param listenerInfo
+ * @param endpointDescription
+ */
+ public ImportAction(Type type, ListenerInfo listenerInfo, EndpointDescription endpointDescription) {
+ super();
+ this.type = type;
+ this.listenerInfo = listenerInfo;
+ this.endpointDescription = endpointDescription;
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportReferenceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportReferenceImpl.java
new file mode 100644
index 0000000000..13c7dd3cf3
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportReferenceImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+
+/**
+ *
+ */
+public class ExportReferenceImpl implements ExportReference {
+ private Node node;
+ private final ServiceReference exportedService;
+ private final EndpointDescription endpointDescription;
+ private int count;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ExportReferenceImpl(Node node, ServiceReference exportedService, EndpointDescription endpointDescription) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ }
+
+ public ServiceReference getExportedService() {
+ return exportedService;
+ }
+
+ public EndpointDescription getExportedEndpoint() {
+ return endpointDescription;
+ }
+
+ public synchronized ExportRegistration register() {
+ count++;
+ return new ExportRegistrationImpl(this);
+ }
+
+ public synchronized void unregister() {
+ if (count > 0) {
+ count--;
+ }
+ if (count == 0) {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java
new file mode 100644
index 0000000000..39cbd7b9e6
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+
+/**
+ * Implementation of {@link ExportRegistration}
+ */
+public class ExportRegistrationImpl implements ExportRegistration {
+ private ExportReferenceImpl exportReference;
+ private Throwable exception;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ExportRegistrationImpl(ExportReferenceImpl exportReference, Throwable exception) {
+ super();
+ this.exportReference = exportReference;
+ this.exception = exception;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ExportRegistrationImpl(ExportReferenceImpl exportReference) {
+ this(exportReference, null);
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.ExportRegistration#close()
+ */
+ public void close() {
+ if (exportReference != null) {
+ exportReference.unregister();
+ }
+ exception = null;
+ exportReference = null;
+ }
+
+ public ServiceReference getExportedService() {
+ return exportReference.getExportedService();
+ }
+
+ public EndpointDescription getEndpointDescription() {
+ return exportReference.getExportedEndpoint();
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public ExportReference getExportReference() throws IllegalStateException {
+ return exportReference;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportReferenceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportReferenceImpl.java
new file mode 100644
index 0000000000..9c5b1818c8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportReferenceImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+
+/**
+ * Implementation of ImportReference. It keeps a reference count of ImportRegistrations
+ */
+public class ImportReferenceImpl implements ImportReference {
+ private Node node;
+ private final ServiceReference importedService;
+ private final EndpointDescription endpointDescription;
+ private int count = 0;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ImportReferenceImpl(Node node, ServiceReference importedService, EndpointDescription endpointDescription) {
+ super();
+ this.node = node;
+ this.importedService = importedService;
+ this.endpointDescription = endpointDescription;
+ }
+
+ public ServiceReference getImportedService() {
+ return importedService;
+ }
+
+ public EndpointDescription getImportedEndpoint() {
+ return endpointDescription;
+ }
+
+ public synchronized ImportRegistration register() {
+ count++;
+ return new ImportRegistrationImpl(this);
+ }
+
+ public synchronized void unregister() {
+ if (count > 0) {
+ count--;
+ }
+ if (count == 0) {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java
new file mode 100644
index 0000000000..9506128286
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+
+/**
+ *
+ */
+public class ImportRegistrationImpl implements ImportRegistration {
+ private ImportReferenceImpl importReference;
+ private Throwable exception;
+
+ /**
+ * @param importReference
+ */
+ public ImportRegistrationImpl(ImportReferenceImpl importReference) {
+ super();
+ this.importReference = importReference;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ImportRegistrationImpl(ImportReferenceImpl importReference, Throwable exception) {
+ super();
+ this.importReference = importReference;
+ this.exception = exception;
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.ImportRegistration#close()
+ */
+ public void close() {
+ if (importReference != null) {
+ importReference.unregister();
+ }
+ exception = null;
+ importReference = null;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public ImportReference getImportReference() {
+ return importReference;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java
new file mode 100644
index 0000000000..460d291f53
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ */
+public class OSGiHelper {
+ public final static String FRAMEWORK_UUID = "org.osgi.framework.uuid";
+
+ private OSGiHelper() {
+ }
+
+ /**
+ * In OSGi, the value of String+ can be a single String, String[] or Collection<String>
+ * @param value
+ * @return
+ */
+ public static String[] getStringArray(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return new String[] {(String)value};
+ } else if (value instanceof Collection) {
+ Collection<String> collection = (Collection)value;
+ return collection.toArray(new String[collection.size()]);
+ }
+ return (String[])value;
+
+ }
+
+ public static Collection<String> getStringCollection(Object value) {
+ String[] values = getStringArray(value);
+ if (values == null) {
+ return null;
+ } else {
+ return Arrays.asList(values);
+ }
+ }
+
+ public static String[] getStringArray(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringArray(propertyValue);
+ }
+
+ public static Collection<String> getStringCollection(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringCollection(propertyValue);
+ }
+
+ public static Filter createFilter(BundleContext context, String filterValue) {
+ if (filterValue == null) {
+ return null;
+ }
+ try {
+ return context.createFilter(filterValue);
+ } catch (InvalidSyntaxException ex) {
+ throw new IllegalArgumentException("Invalid Filter: " + filterValue, ex);
+ }
+ }
+
+ /**
+ * Get a collection of resources that are configured by the given header
+ * @param bundle The bundle
+ * @param header
+ * @param defaultValue
+ * @return
+ */
+ public static Collection<URL> getConfiguration(Bundle bundle, String header, String defaultValue) {
+ String value = (String)bundle.getHeaders().get(header);
+ if (value == null) {
+ return Collections.emptyList();
+ }
+ value = value.trim();
+ String paths[] = value.split("( |\t|\n|\r|\f|,)+");
+ if ("".equals(value) || paths.length == 0) {
+ if (defaultValue != null) {
+ paths = new String[] {defaultValue};
+ } else {
+ paths = new String[0];
+ }
+ }
+ Collection<URL> files = new HashSet<URL>();
+ for (String path : paths) {
+ if ("".equals(path)) {
+ // Skip empty ones
+ continue;
+ }
+ if (path.endsWith("/")) {
+ path = path + "*.xml";
+ }
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ int lastIndex = path.lastIndexOf('/');
+ String root = path.substring(0, lastIndex);
+ if ("".equals(root)) {
+ root = "/";
+ }
+ String pattern = path.substring(lastIndex + 1);
+ Enumeration<URL> entries = bundle.findEntries(root, pattern, false);
+ if (entries != null) {
+ while (entries.hasMoreElements()) {
+ files.add(entries.nextElement());
+ }
+ }
+ }
+ return files;
+ }
+
+ public static Collection<OSGiProperty> getOSGiProperties(ExtensionPointRegistry registry, ServiceReference reference) {
+ FactoryExtensionPoint factoryExtensionPoint = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ OSGiImplementationFactory implementationFactory =
+ factoryExtensionPoint.getFactory(OSGiImplementationFactory.class);
+ return implementationFactory.createOSGiProperties(reference);
+ }
+
+ public static OSGiProperty createOSGiProperty(ExtensionPointRegistry registry, String name, Object value) {
+ FactoryExtensionPoint factoryExtensionPoint = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ OSGiImplementationFactory implementationFactory =
+ factoryExtensionPoint.getFactory(OSGiImplementationFactory.class);
+ return implementationFactory.createOSGiProperty(name, value);
+ }
+
+ public synchronized static String getFrameworkUUID(BundleContext bundleContext) {
+ String uuid = null;
+ if (bundleContext != null) {
+ uuid = bundleContext.getProperty(FRAMEWORK_UUID);
+ } else {
+ uuid = System.getProperty(FRAMEWORK_UUID);
+ }
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ }
+ System.setProperty(FRAMEWORK_UUID, uuid);
+ return uuid;
+ }
+
+ public static ClassLoader createBundleClassLoader(Bundle bundle) {
+ return new BundleClassLoader(bundle);
+ }
+
+ private static class BundleClassLoader extends ClassLoader {
+ private Bundle bundle;
+
+ public BundleClassLoader(Bundle bundle) {
+ super(null);
+ this.bundle = bundle;
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return bundle.loadClass(name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return bundle.getResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ Enumeration<URL> urls = bundle.getResources(name);
+ if (urls == null) {
+ List<URL> list = Collections.emptyList();
+ return Collections.enumeration(list);
+ } else {
+ return urls;
+ }
+ }
+ }
+
+ /**
+ * Find out what elements are added between the oldValues and newValues
+ * @param oldValues
+ * @param newValues
+ * @return
+ */
+ public static Collection<String> getAddedItems(Collection<String> oldValues, Collection<String> newValues) {
+ if (newValues == null) {
+ newValues = Collections.emptySet();
+ }
+
+ Collection<String> deltaInterest = new HashSet<String>(newValues);
+ if (oldValues == null) {
+ oldValues = Collections.emptySet();
+ }
+ deltaInterest.removeAll(oldValues);
+ return deltaInterest;
+ }
+
+ public static Collection<String> getRemovedItems(Collection<String> oldValues, Collection<String> newValues) {
+ return getAddedItems(newValues, oldValues);
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java
new file mode 100644
index 0000000000..081a3a60c0
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointHelper.createEndpointDescription;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.createOSGiProperty;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getFrameworkUUID;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getOSGiProperties;
+import static org.osgi.framework.Constants.SERVICE_ID;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.assembly.Binding;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptionsFactory;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.impl.NodeImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class OSGiServiceExporter extends AbstractOSGiServiceHandler implements ServiceTrackerCustomizer {
+ private final static Logger logger = Logger.getLogger(OSGiServiceExporter.class.getName());
+
+ private Map<EndpointDescription, ExportReferenceImpl> exportReferences =
+ new ConcurrentHashMap<EndpointDescription, ExportReferenceImpl>();
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ public OSGiServiceExporter(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ init();
+ }
+
+ public void stop() {
+ exportReferences.clear();
+ super.stop();
+ }
+
+ public Object addingService(ServiceReference reference) {
+ return exportService(reference, null);
+ }
+
+ public List<ExportRegistration> exportService(ServiceReference reference, Map<String, Object> properties) {
+ // FIXME: [rfeng] We need to check if a corresponding endpoint has been exported
+ try {
+ Contribution contribution = introspector.introspect(reference, properties);
+ if (contribution != null) {
+
+ NodeConfiguration configuration = nodeFactory.createNodeConfiguration();
+ if (domainRegistry != null) {
+ configuration.setDomainRegistryURI(domainRegistry);
+ }
+ if (domainURI != null) {
+ configuration.setDomainURI(domainURI);
+ }
+ configuration.setURI(contribution.getURI());
+ // configuration.getExtensions().add(reference.getBundle());
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentService service = component.getServices().get(0);
+ service.getExtensions().addAll(getOSGiProperties(registry, reference));
+ service.getExtensions().add(createOSGiProperty(registry,
+ ENDPOINT_FRAMEWORK_UUID,
+ getFrameworkUUID(reference.getBundle()
+ .getBundleContext())));
+ service.getExtensions().add(createOSGiProperty(registry, ENDPOINT_SERVICE_ID, reference
+ .getProperty(SERVICE_ID)));
+
+ // create the org.osgi.sca.config.xml property
+ service.getExtensions().add(createSCAConfigXMLProperty(reference, properties, service));
+
+ // FIXME: Configure the domain and node URI
+ NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
+ node.start();
+ List<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>();
+ for (Endpoint endpoint : service.getEndpoints()) {
+ EndpointDescription endpointDescription = createEndpointDescription(context, endpoint);
+ synchronized (this) {
+ ExportReferenceImpl exportReference = exportReferences.get(endpointDescription);
+ if (exportReference == null) {
+ exportReference = new ExportReferenceImpl(node, reference, endpointDescription);
+ }
+ ExportRegistration exportRegistration = exportReference.register();
+ exportedServices.add(exportRegistration);
+ }
+ }
+ return exportedServices;
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ ExportRegistration exportRegistration = new ExportRegistrationImpl(null, e);
+ return Collections.singletonList(exportRegistration);
+ }
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ removedService(reference, service);
+ exportService(reference, null);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ List<ExportRegistration> exportedServices = (List<ExportRegistration>)service;
+ for (ExportRegistration exportRegistration : exportedServices) {
+ exportRegistration.close();
+ }
+ }
+
+ private OSGiProperty createSCAConfigXMLProperty(ServiceReference reference, Map<String, Object> props, ComponentService service){
+
+ // create the <sca-config> element
+ String scaConfigXMLString = introspector.instrospectSCAConfig(reference, props, service);
+
+ // wrap the <sca-config> element in an <xml> element
+ String scaConfigXMLStringWithoutXMLHeader = scaConfigXMLString.substring(scaConfigXMLString.indexOf("><") + 1);
+ scaConfigXMLString = "<xml>" + scaConfigXMLStringWithoutXMLHeader + "</xml>";
+
+ // create and return the OSGI property
+ return createOSGiProperty(registry, "org.osgi.sca.config.xml", scaConfigXMLString);
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java
new file mode 100644
index 0000000000..f24e9af7c1
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.impl.NodeImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class OSGiServiceImporter extends AbstractOSGiServiceHandler {
+ private final static Logger logger = Logger.getLogger(OSGiServiceImporter.class.getName());
+ private Map<EndpointDescription, ImportReferenceImpl> importReferences =
+ new ConcurrentHashMap<EndpointDescription, ImportReferenceImpl>();
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ public OSGiServiceImporter(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ // Defer init() to importService()
+ }
+
+ public void stop() {
+ importReferences.clear();
+ super.stop();
+ }
+
+ public ImportRegistration importService(Bundle bundle, EndpointDescription endpointDescription) {
+ init();
+ try {
+ Contribution contribution = introspector.introspect(bundle, endpointDescription);
+ if (contribution != null) {
+
+ NodeConfiguration configuration = nodeFactory.createNodeConfiguration();
+ if (domainRegistry != null) {
+ configuration.setDomainRegistryURI(domainRegistry);
+ }
+ if (domainURI != null) {
+ configuration.setDomainURI(domainURI);
+ }
+ configuration.setURI(contribution.getURI());
+ // configuration.getExtensions().add(bundle);
+ // FIXME: Configure the domain and node URI
+ NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
+ node.start();
+
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentReference componentReference = component.getReferences().get(0);
+ ServiceReference serviceReference =
+ context.getServiceReference("(sca.reference=" + component.getURI()
+ + "#reference("
+ + componentReference.getName()
+ + ")");
+ synchronized (this) {
+ ImportReferenceImpl importReference = importReferences.get(endpointDescription);
+ if (importReference == null) {
+ importReference = new ImportReferenceImpl(node, serviceReference, endpointDescription);
+ importReferences.put(endpointDescription, importReference);
+ }
+ return importReference.register();
+ }
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ return new ImportRegistrationImpl(null, e);
+ }
+ }
+
+ public void unimportService(ImportRegistration importRegistration) {
+ if (importRegistration != null) {
+ importRegistration.close();
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java
new file mode 100644
index 0000000000..94955ada38
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.definitions.Definitions;
+import org.apache.tuscany.sca.deployment.Deployer;
+import org.apache.tuscany.sca.policy.BindingType;
+import org.apache.tuscany.sca.policy.Intent;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * SCA Implementation of {@link RemoteServiceAdmin}
+ */
+public class RemoteServiceAdminImpl implements RemoteServiceAdmin, ManagedService {
+ private BundleContext context;
+ private ServiceRegistration registration;
+ private ServiceRegistration managedService;
+ private ServiceTracker listeners;
+
+ private OSGiServiceExporter exporter;
+ private OSGiServiceImporter importer;
+
+ private Collection<ImportRegistration> importRegistrations = new ArrayList<ImportRegistration>();
+ private Collection<ExportRegistration> exportedRegistrations = new ArrayList<ExportRegistration>();
+
+ public RemoteServiceAdminImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ this.exporter = new OSGiServiceExporter(context);
+ this.importer = new OSGiServiceImporter(context);
+ exporter.start();
+ importer.start();
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(RemoteConstants.REMOTE_CONFIGS_SUPPORTED, new String[] {"org.osgi.sca"});
+
+ ExtensionPointRegistry registry = exporter.getExtensionPointRegistry();
+ UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class);
+ Deployer deployer = utilities.getUtility(Deployer.class);
+ Definitions definitions = deployer.getSystemDefinitions();
+
+ String[] intents = new String[definitions.getIntents().size()];
+ int i = 0;
+ for (Intent intent : definitions.getIntents()) {
+ intents[i++] = intent.toString();
+ }
+
+ String[] bindingTypes = new String[definitions.getBindingTypes().size()];
+ i = 0;
+ for (BindingType bindingType : definitions.getBindingTypes()) {
+ bindingTypes[i++] = bindingType.getType().toString();
+ }
+
+ // FIXME: We should ask SCA domain for the supported intents
+ props.put(RemoteConstants.REMOTE_INTENTS_SUPPORTED, intents);
+ // FIXME: We should ask SCA domain for the supported binding types
+ props.put("org.osgi.sca.binding.types", bindingTypes);
+ registration = context.registerService(RemoteServiceAdmin.class.getName(), this, props);
+
+ props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_PID, RemoteServiceAdminImpl.class.getName());
+ managedService = context.registerService(ManagedService.class.getName(), this, props);
+ listeners = new ServiceTracker(this.context, RemoteServiceAdminListener.class.getName(), null);
+ listeners.open();
+ }
+
+ public void stop() {
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ if (managedService != null) {
+ try {
+ managedService.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ managedService = null;
+ }
+ if (listeners != null) {
+ listeners.close();
+ listeners = null;
+ }
+ for (ExportRegistration exportRegistration : exportedRegistrations) {
+ exportRegistration.close();
+ }
+ exportedRegistrations.clear();
+ for (ImportRegistration importRegistration : importRegistrations) {
+ importRegistration.close();
+ }
+ importRegistrations.clear();
+ if (importer != null) {
+ importer.stop();
+ importer = null;
+ }
+ if (exporter != null) {
+ exporter.stop();
+ exporter = null;
+ }
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.RemoteServiceAdmin#exportService(org.osgi.framework.ServiceReference,
+ * java.util.Map)
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref, Map properties) {
+ List<ExportRegistration> exportRegistrations = exporter.exportService(ref, properties);
+ if (exportRegistrations != null) {
+ exportRegistrations.addAll(exportedRegistrations);
+ fireExportEvents(ref.getBundle(), exportRegistrations);
+ }
+ return exportRegistrations;
+ }
+
+ private void fireExportEvents(Bundle source, List<ExportRegistration> exportRegistrations) {
+ for (ExportRegistration registration : exportRegistrations) {
+ RemoteServiceAdminEvent rsaEvent =
+ new RemoteServiceAdminEvent(RemoteServiceAdminEvent.EXPORT_REGISTRATION, source, registration
+ .getExportReference(), registration.getException());
+ EventAdmin eventAdmin = getEventAdmin();
+ if (eventAdmin != null) {
+ eventAdmin.postEvent(wrap(rsaEvent));
+ }
+ for (Object listener : listeners.getServices()) {
+ RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener;
+ rsaListener.remoteAdminEvent(rsaEvent);
+ }
+ }
+ }
+
+ private EventAdmin getEventAdmin() {
+ ServiceReference reference = context.getServiceReference(EventAdmin.class.getName());
+ if (reference == null) {
+ return null;
+ } else {
+ return (EventAdmin)context.getService(reference);
+ }
+ }
+
+ private Event wrap(RemoteServiceAdminEvent rsaEvent) {
+ int type = rsaEvent.getType();
+ String eventType = null;
+ switch (type) {
+ case RemoteServiceAdminEvent.EXPORT_ERROR:
+ eventType = "EXPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
+ eventType = "EXPORT_REGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
+ eventType = "EXPORT_UNREGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_WARNING:
+ eventType = "EXPORT_WARNING";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_ERROR:
+ eventType = "IMPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
+ eventType = "IMPORT_REGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
+ eventType = "EXPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_WARNING:
+ eventType = "IMPORT_UNREGISTRATION";
+ break;
+ }
+ String topic = "org/osgi/service/remoteserviceadmin/" + eventType;
+ Map<String, Object> props = new HashMap<String, Object>();
+ /*
+ * <ul>
+ <li>bundle (Bundle) The Remote Service Admin bundle
+ <li>bundle-id (Long) The id of the Blueprint bundle.
+ <li>bundle-symbolicname (String) The Bundle Symbolic Name of the
+ Remote Service Admin bundle.
+ <li>bundle-version - (Version) The version of the Blueprint bundle.
+ <li>cause The exception, if present.
+ <li>import.registration An imported endpoint, if present
+ <li>export.registration An exported endpoint, if present
+ <li>service.remote.id Remote service UUID, if present
+ <li>service.remote.uuid Remote service UUID, if present
+ <li>service.remote.uri (String) The URI of the endpoint, if present
+ <li>objectClass (String[]) The interface names, if present
+ <li>service.imported.configs (String+) The configuration types of the
+ imported services, if present
+ <li>timestamp (Long) The time when the event occurred
+ <li>event (RemoteServiceAdminEvent) The RemoteServiceAdminEvent
+ object that caused this event.
+ </ul>
+ */
+ Bundle rsaBundle = context.getBundle();
+ props.put("bundle", rsaBundle);
+ props.put("bundle-id", rsaBundle.getBundleId());
+ props.put("bundle-symbolicname", rsaBundle.getSymbolicName());
+ props.put("bundle-version", rsaBundle.getHeaders().get(Constants.BUNDLE_VERSION));
+ props.put("cause", rsaEvent.getException());
+ props.put("import.registration", rsaEvent.getImportReference());
+ props.put("export.registration", rsaEvent.getExportReference());
+ EndpointDescription ep = null;
+ if (rsaEvent.getImportReference() != null) {
+ ep = rsaEvent.getImportReference().getImportedEndpoint();
+ }
+ if (rsaEvent.getExportReference() != null) {
+ ep = rsaEvent.getExportReference().getExportedEndpoint();
+ }
+
+ if (ep != null) {
+ props.put("endpoint.service.id", ep.getServiceId());
+ props.put("endpoint.framework.uuid", ep.getFrameworkUUID());
+ props.put("endpoint.id", ep.getId());
+ props.put("objectClass", ep.getInterfaces());
+ props.put("service.imported.configs", ep.getConfigurationTypes());
+ Object bindings = ep.getProperties().get("org.osgi.sca.bindings");
+ if (bindings != null) {
+ props.put("org.osgi.sca.bindings", bindings);
+ }
+ Object config = ep.getProperties().get("org.osgi.sca.config.url");
+ if (config != null) {
+ props.put("org.osgi.sca.config.url", config);
+ }
+ config = ep.getProperties().get("org.osgi.sca.config.xml");
+ if (config != null) {
+ props.put("org.osgi.sca.config.xml", config);
+ } }
+ props.put("timestamp", Long.valueOf(System.currentTimeMillis()));
+ props.put("event", rsaEvent);
+ return new Event(topic, props);
+ }
+
+ private void fireImportEvents(Bundle source, ImportRegistration registration) {
+ RemoteServiceAdminEvent rsaEvent =
+ new RemoteServiceAdminEvent(RemoteServiceAdminEvent.IMPORT_REGISTRATION, source, registration
+ .getImportReference(), registration.getException());
+ EventAdmin eventAdmin = getEventAdmin();
+ if (eventAdmin != null) {
+ eventAdmin.postEvent(wrap(rsaEvent));
+ }
+ for (Object listener : listeners.getServices()) {
+ RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener;
+ rsaListener.remoteAdminEvent(rsaEvent);
+ }
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.RemoteServiceAdmin#getExportedServices()
+ */
+ public Collection<ExportReference> getExportedServices() {
+ Collection<ExportReference> exportedServices = new ArrayList<ExportReference>();
+ for (ExportRegistration registration : exportedRegistrations) {
+ exportedServices.add(registration.getExportReference());
+ }
+ return exportedServices;
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.RemoteServiceAdmin#getImportedEndpoints()
+ */
+ public Collection<ImportReference> getImportedEndpoints() {
+ Collection<ImportReference> importedEndpoints = new ArrayList<ImportReference>();
+ for (ImportRegistration registration : importRegistrations) {
+ importedEndpoints.add(registration.getImportReference());
+ }
+ return importedEndpoints;
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.RemoteServiceAdmin#importService(org.apache.tuscany.sca.dosgi.discovery.EndpointDescription)
+ */
+ public ImportRegistration importService(EndpointDescription endpoint) {
+ Bundle bundle = (Bundle)endpoint.getProperties().get(Bundle.class.getName());
+ ImportRegistration importReg = importer.importService(bundle, endpoint);
+ if (importReg != null) {
+ fireImportEvents(bundle, importReg);
+ importRegistrations.add(importReg);
+ }
+ return importReg;
+ }
+
+ public synchronized void updated(Dictionary props) throws ConfigurationException {
+ if (props == null) {
+ // It can be null in Apache Felix
+ return;
+ }
+ String domainRegistry = (String)props.get("org.osgi.sca.domain.registry");
+ String domainURI = (String)props.get("org.osgi.sca.domain.uri");
+ if (domainRegistry != null) {
+ exporter.setDomainRegistry(domainRegistry);
+ importer.setDomainRegistry(domainRegistry);
+ }
+ if (domainURI != null) {
+ exporter.setDomainURI(domainURI);
+ importer.setDomainURI(domainURI);
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java
new file mode 100644
index 0000000000..a3f3636cce
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.common.java.collection.CollectionMap;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointMatcher.ImportAction;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Implementation of Remote Controller
+ */
+public class TopologyManagerImpl implements ListenerHook, RemoteServiceAdminListener, EndpointListener,
+ ServiceTrackerCustomizer, LifeCycleListener /*, EventHook */{
+ private final static Logger logger = Logger.getLogger(TopologyManagerImpl.class.getName());
+ public final static String ENDPOINT_LOCAL = "service.local";
+
+ private BundleContext context;
+ private ServiceTracker remoteAdmins;
+
+ private volatile ServiceRegistration registration;
+ private ServiceRegistration endpointListener;
+
+ private ServiceTracker remotableServices;
+
+ private EndpointMatcher endpointMatcher;
+
+ private CollectionMap<ServiceReference, ExportRegistration> exportedServices =
+ new CollectionMap<ServiceReference, ExportRegistration>();
+ private CollectionMap<ImportKey, ImportRegistration> importedServices =
+ new CollectionMap<ImportKey, ImportRegistration>();
+
+ private Filter remotableServiceFilter;
+
+ public TopologyManagerImpl(BundleContext context) {
+ this.context = context;
+ this.endpointMatcher = new EndpointMatcher(context);
+ }
+
+ public void start() {
+ String filter =
+ "(& (!(" + SERVICE_IMPORTED
+ + "=*)) ("
+ + SERVICE_EXPORTED_INTERFACES
+ + "=*) ("
+ + SERVICE_EXPORTED_CONFIGS
+ + "=org.osgi.sca) )";
+ try {
+ remotableServiceFilter = context.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+
+ endpointListener = context.registerService(EndpointListener.class.getName(), this, null);
+ remoteAdmins = new ServiceTracker(this.context, RemoteServiceAdmin.class.getName(), null);
+ remoteAdmins.open();
+
+ // DO NOT register EventHook.class.getName() as it cannot report existing services
+ String interfaceNames[] =
+ new String[] {ListenerHook.class.getName(), RemoteServiceAdminListener.class.getName()};
+ // The registration will trigger the added() method before registration is assigned
+ registration = context.registerService(interfaceNames, this, null);
+
+ remotableServices = new ServiceTracker(context, remotableServiceFilter, this);
+ remotableServices.open(true);
+
+ Thread thread = new Thread(new ImportTask());
+ thread.start();
+ }
+
+ public Object addingService(ServiceReference reference) {
+ exportService(reference);
+ return reference.getBundle().getBundleContext().getService(reference);
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ exportService(reference);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ }
+
+ private void unexportService(ServiceReference reference) {
+ // Call remote admin to unexport the service
+ Collection<ExportRegistration> exportRegistrations = exportedServices.get(reference);
+ if (exportRegistrations != null) {
+ for (Iterator<ExportRegistration> i = exportRegistrations.iterator(); i.hasNext();) {
+ ExportRegistration exported = i.next();
+ exported.close();
+ i.remove();
+ }
+ }
+ }
+
+ private void exportService(ServiceReference reference) {
+ // Call remote admin to export the service
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ // Ignore
+ logger.warning("No RemoteAdmin services are available.");
+ } else {
+ for (Object ra : admins) {
+ RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
+ Collection<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference, null);
+ if (exportRegistrations != null && !exportRegistrations.isEmpty()) {
+ exportedServices.putValues(reference, exportRegistrations);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection)
+ */
+ public void added(Collection listeners) {
+ try {
+ synchronized (endpointMatcher) {
+ Collection<String> oldFilters = endpointMatcher.getFilters();
+ Collection<String> newFilters = endpointMatcher.added(listeners);
+ if (!OSGiHelper.getAddedItems(oldFilters, newFilters).isEmpty()) {
+ updateEndpointListenerScope(newFilters);
+ }
+ }
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ if (e instanceof Error) {
+ throw (Error)e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void updateEndpointListenerScope(Collection<String> filters) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(ENDPOINT_LISTENER_SCOPE, filters);
+ endpointListener.setProperties(props);
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#removed(java.util.Collection)
+ */
+ public void removed(Collection listeners) {
+ try {
+ synchronized (endpointMatcher) {
+ Collection<String> oldFilters = endpointMatcher.getFilters();
+ Collection<String> newFilters = endpointMatcher.removed(listeners);
+ if (!OSGiHelper.getRemovedItems(oldFilters, newFilters).isEmpty()) {
+ updateEndpointListenerScope(newFilters);
+ }
+ }
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ if (e instanceof Error) {
+ throw (Error)e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener#remoteAdminEvent(org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent)
+ */
+ public void remoteAdminEvent(RemoteServiceAdminEvent event) {
+ switch (event.getType()) {
+ case RemoteServiceAdminEvent.EXPORT_ERROR:
+ case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
+ case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
+ case RemoteServiceAdminEvent.EXPORT_WARNING:
+ break;
+ case RemoteServiceAdminEvent.IMPORT_ERROR:
+ case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
+ case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
+ case RemoteServiceAdminEvent.IMPORT_WARNING:
+ break;
+ }
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.EndpointListener#addEndpoint(org.osgi.service.remoteserviceadmin.EndpointDescription,
+ * java.lang.String)
+ */
+ public void endpointAdded(EndpointDescription endpoint, String matchedFilter) {
+ endpointMatcher.added(endpoint, matchedFilter);
+ // importService(endpoint, matchedFilter);
+ }
+
+ /**
+ * @see org.osgi.remoteserviceadmin.EndpointListener#removeEndpoint(org.osgi.service.remoteserviceadmin.EndpointDescription)
+ */
+ public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) {
+ endpointMatcher.removed(endpoint, matchedFilter);
+ // unimportService(endpoint);
+ }
+
+ private void importService(EndpointDescription endpoint, String matchedFilter) {
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ logger.warning("No Remote Service Admin services are available.");
+ return;
+ }
+
+ CollectionMap<Class<?>, ListenerInfo> interfaceToListeners =
+ endpointMatcher.groupListeners(endpoint, matchedFilter);
+ for (Map.Entry<Class<?>, Collection<ListenerInfo>> e : interfaceToListeners.entrySet()) {
+ Class<?> interfaceClass = e.getKey();
+ Collection<ListenerInfo> listeners = e.getValue();
+ // Get a listener
+ ListenerInfo listener = listeners.iterator().next();
+ Bundle bundle = listener.getBundleContext().getBundle();
+ if (bundle.getBundleId() == 0L) {
+ // Skip system bundles
+ continue;
+ }
+ try {
+ Filter filter = listener.getBundleContext().createFilter(matchedFilter);
+ if (!filter.match(new Hashtable<String, Object>(endpoint.getProperties()))) {
+ continue;
+ }
+ } catch (InvalidSyntaxException ex) {
+ logger.log(Level.SEVERE, ex.getMessage(), ex);
+ continue;
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
+ props.put(Bundle.class.getName(), bundle);
+ props.put(Constants.OBJECTCLASS, new String[] {interfaceClass.getName()});
+ EndpointDescription description = new EndpointDescription(props);
+
+ if (admins != null) {
+ for (Object ra : admins) {
+ RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
+ ImportRegistration importRegistration = remoteAdmin.importService(description);
+ if (importRegistration != null) {
+ importedServices.putValue(new ImportKey(description, listener), importRegistration);
+ }
+ }
+ }
+ }
+ }
+
+ private void unimportService(EndpointDescription endpoint, ListenerInfo listenerInfo) {
+ // Call remote admin to unimport the service
+ Collection<ImportRegistration> importRegistrations =
+ importedServices.get(new ImportKey(endpoint, listenerInfo));
+ if (importRegistrations != null) {
+ for (Iterator<ImportRegistration> i = importRegistrations.iterator(); i.hasNext();) {
+ ImportRegistration imported = i.next();
+ imported.close();
+ i.remove();
+ }
+ }
+ }
+
+ public void stop() {
+ remotableServices.close();
+
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ if (remoteAdmins != null) {
+ remoteAdmins.close();
+ remoteAdmins = null;
+ }
+ if (endpointMatcher != null) {
+ endpointMatcher.clear();
+ }
+ }
+
+ private class ImportTask implements Runnable {
+ public void run() {
+ while (registration != null) {
+ BlockingQueue<EndpointMatcher.ImportAction> queue = endpointMatcher.getImportQueue();
+ ImportAction action = null;
+ try {
+ action = queue.poll(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ if (action != null) {
+ if (action.type == ImportAction.Type.Add) {
+ importService(action.endpointDescription, action.listenerInfo.getFilter());
+ } else if (action.type == ImportAction.Type.Remove) {
+ unimportService(action.endpointDescription, action.listenerInfo);
+ }
+ }
+ }
+ }
+ }
+
+ private static class ImportKey {
+ private EndpointDescription endpointDescription;
+
+ /**
+ * @param endpointDescription
+ * @param listenerInfo
+ */
+ private ImportKey(EndpointDescription endpointDescription, ListenerInfo listenerInfo) {
+ super();
+ this.endpointDescription = endpointDescription;
+ this.listenerInfo = listenerInfo;
+ }
+
+ private ListenerInfo listenerInfo;
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((endpointDescription == null) ? 0 : endpointDescription.hashCode());
+ result = prime * result + ((listenerInfo == null) ? 0 : listenerInfo.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ImportKey other = (ImportKey)obj;
+ if (endpointDescription == null) {
+ if (other.endpointDescription != null)
+ return false;
+ } else if (!endpointDescription.equals(other.endpointDescription))
+ return false;
+ if (listenerInfo == null) {
+ if (other.listenerInfo != null)
+ return false;
+ } else if (!listenerInfo.equals(other.listenerInfo))
+ return false;
+ return true;
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
new file mode 100644
index 0000000000..fd57dbded8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ *
+ */
+public abstract class AbstractDiscoveryService implements Discovery, LifeCycleListener {
+ protected final static int ADDED = 0x1;
+ protected final static int REMOVED = 0x2;
+ protected final static int MODIFIED = 0x4;
+
+ protected final static Logger logger = Logger.getLogger(AbstractDiscoveryService.class.getName());
+
+ protected BundleContext context;
+ protected ExtensionPointRegistry registry;
+ // private WorkScheduler workScheduler;
+
+ private Map<EndpointListener, Collection<String>> listenersToFilters =
+ new ConcurrentHashMap<EndpointListener, Collection<String>>();
+
+ protected Map<EndpointDescription, Bundle> endpointDescriptions =
+ new ConcurrentHashMap<EndpointDescription, Bundle>();
+ private ServiceTracker trackerTracker;
+
+ public AbstractDiscoveryService(BundleContext context) {
+ super();
+ this.context = context;
+ }
+
+ public void start() {
+ getExtensionPointRegistry();
+ // UtilityExtensionPoint utilityExtensionPoint = registry.getExtensionPoint(UtilityExtensionPoint.class);
+ // this.workScheduler = utilityExtensionPoint.getUtility(WorkScheduler.class);
+
+ // track the registration of EndpointListener
+ trackerTracker = new ServiceTracker(this.context, EndpointListener.class.getName(), null) {
+ public Object addingService(ServiceReference reference) {
+ Object result = super.addingService(reference);
+ cacheTracker(reference, result);
+ return result;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ super.modifiedService(reference, service);
+ updateTracker(reference, service);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ super.removedService(reference, service);
+ clearTracker(service);
+ }
+ };
+
+ trackerTracker.open();
+ }
+
+ public void stop() {
+ trackerTracker.close();
+ }
+
+ protected ExtensionPointRegistry getExtensionPointRegistry() {
+ NodeFactoryImpl factory = (NodeFactoryImpl)NodeFactory.getInstance();
+ factory.init();
+ ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null);
+ tracker.open();
+ // tracker.waitForService(1000);
+ registry = (ExtensionPointRegistry)tracker.getService();
+ tracker.close();
+ return registry;
+ }
+
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary headers = context.getBundle().getHeaders();
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(PRODUCT_NAME, "Apache Tuscany SCA");
+ props.put(PRODUCT_VERSION, headers.get(Constants.BUNDLE_VERSION));
+ props.put(VENDOR_NAME, headers.get(Constants.BUNDLE_VENDOR));
+ // props.put(SUPPORTED_PROTOCOLS, new String[] {"local", "org.osgi.sca"});
+ return props;
+ }
+
+ private void cacheTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ Collection<String> filters = null;
+ synchronized (this) {
+ filters = addTracker(reference, listener, EndpointListener.ENDPOINT_LISTENER_SCOPE);
+ // Take a snapshot of the endpoints
+ triggerCallbacks(null, filters, listener);
+ }
+ }
+ }
+
+ private void clearTracker(Object service) {
+ if (service instanceof EndpointListener) {
+ synchronized (this) {
+ removeTracker((EndpointListener)service);
+ }
+ }
+ }
+
+ private void updateTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ Collection<String> oldFilters = null;
+ Collection<String> newFilters = null;
+ synchronized (this) {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("updating listener: " + listener);
+ }
+ oldFilters = removeTracker(listener);
+ newFilters = addTracker(reference, listener, EndpointListener.ENDPOINT_LISTENER_SCOPE);
+ triggerCallbacks(oldFilters, newFilters, listener);
+ }
+ }
+ }
+
+ private void triggerCallbacks(Collection<String> oldInterest,
+ Collection<String> newInterest,
+ EndpointListener listener) {
+ // compute delta between old & new interfaces/filters and
+ // trigger callbacks for any entries in servicesInfo that
+ // match any *additional* interface/filters
+ Collection<String> deltaInterest = getDelta(oldInterest, newInterest);
+
+ Iterator<String> i = deltaInterest.iterator();
+ while (i.hasNext()) {
+ String next = i.next();
+ for (EndpointDescription sd : endpointDescriptions.keySet()) {
+ triggerCallbacks(listener, next, sd, ADDED);
+ }
+ }
+ // Find removed filters
+ deltaInterest = getDelta(newInterest, oldInterest);
+
+ i = deltaInterest.iterator();
+ while (i.hasNext()) {
+ String next = i.next();
+ for (EndpointDescription sd : endpointDescriptions.keySet()) {
+ triggerCallbacks(listener, next, sd, REMOVED);
+ }
+ }
+ }
+
+ private Collection<String> getDelta(Collection<String> oldInterest, Collection<String> newInterest) {
+ if (newInterest == null) {
+ newInterest = Collections.emptySet();
+ }
+
+ Collection<String> deltaInterest = new ArrayList<String>(newInterest);
+ if (oldInterest == null) {
+ oldInterest = Collections.emptySet();
+ }
+ deltaInterest.removeAll(oldInterest);
+ return deltaInterest;
+ }
+
+ /**
+ * Notify the endpoint listener
+ * @param listener
+ * @param matchedFilter
+ * @param endpoint
+ * @param type
+ */
+ private static void notify(EndpointListener listener, String matchedFilter, EndpointDescription endpoint, int type) {
+ switch (type) {
+ case ADDED:
+ listener.endpointAdded(endpoint, matchedFilter);
+ break;
+ case REMOVED:
+ listener.endpointRemoved(endpoint, matchedFilter);
+ break;
+ case MODIFIED:
+ listener.endpointRemoved(endpoint, matchedFilter);
+ listener.endpointAdded(endpoint, matchedFilter);
+ break;
+ }
+ }
+
+ private void triggerCallbacks(EndpointListener listener,
+ String matchedFilter,
+ EndpointDescription endpoint,
+ int type) {
+ // workScheduler.scheduleWork(new Notifier(listener, matchedFilter, endpoint, type));
+ notify(listener, matchedFilter, endpoint, type);
+ }
+
+ private boolean filterMatches(String filterValue, EndpointDescription sd) {
+ Filter filter = OSGiHelper.createFilter(context, filterValue);
+ Hashtable<String, Object> props = new Hashtable<String, Object>(sd.getProperties());
+ // Add two faked properties to make the filter match
+ props.put(Constants.OBJECTCLASS, sd.getInterfaces());
+ props.put(RemoteConstants.SERVICE_IMPORTED, "true");
+ return filter != null ? filter.match(props) : false;
+ }
+
+ private Collection<String> removeTracker(EndpointListener listener) {
+ return listenersToFilters.remove(listener);
+ }
+
+ private Collection<String> addTracker(ServiceReference reference, EndpointListener listener, String property) {
+ Collection<String> collection = OSGiHelper.getStringCollection(reference, property);
+ if (collection != null && !collection.isEmpty()) {
+ listenersToFilters.put(listener, new ArrayList<String>(collection));
+ }
+ return collection;
+ }
+
+ protected void endpointChanged(EndpointDescription sd, int type) {
+ synchronized (this) {
+ for (Map.Entry<EndpointListener, Collection<String>> entry : listenersToFilters.entrySet()) {
+ for (String filter : entry.getValue()) {
+ if (filterMatches(filter, sd)) {
+ triggerCallbacks(entry.getKey(), filter, sd, type);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java
new file mode 100644
index 0000000000..3ad1e53fee
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+/**
+ * Every Discovery Provider registers a service implementing this interface.
+ * This service is registered with extra properties identified at the beginning
+ * of this interface to denote the name of the product providing Discovery
+ * functionality, its version, vendor, used protocols etc..
+ * <p>
+ * Discovery allows to publish services exposed for remote access as well as to
+ * search for remote services.
+ * <p>
+ * Discovery service implementations usually rely on some discovery protocols or
+ * other information distribution means.
+ *
+ * @ThreadSafe
+ */
+public interface Discovery {
+
+ /**
+ * ServiceRegistration property for the name of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String PRODUCT_NAME = "osgi.remote.discovery.product";
+
+ /**
+ * ServiceRegistration property for the version of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String PRODUCT_VERSION = "osgi.remote.discovery.product.version";
+
+ /**
+ * ServiceRegistration property for the Discovery product vendor name.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String VENDOR_NAME = "osgi.remote.discovery.vendor";
+
+ /**
+ * ServiceRegistration property that lists the discovery protocols used by
+ * this Discovery service.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>.
+ */
+ String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols";
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java
new file mode 100644
index 0000000000..32bd10b77b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class DiscoveryActivator implements BundleActivator {
+ private List<AbstractDiscoveryService> discoveryServices = new ArrayList<AbstractDiscoveryService>();
+ private List<ServiceRegistration> discoveryServiceRegistrations = new ArrayList<ServiceRegistration>();
+
+ public void start(BundleContext context) {
+ discoveryServices.add(new LocalDiscoveryService(context));
+
+ discoveryServices.add(new DomainDiscoveryService(context));
+
+ for (AbstractDiscoveryService service : discoveryServices) {
+ service.start();
+ ServiceRegistration registration =
+ context.registerService(Discovery.class.getName(), service, service.getProperties());
+ discoveryServiceRegistrations.add(registration);
+ }
+ }
+
+ public void stop(BundleContext context) {
+ for (ServiceRegistration registration : discoveryServiceRegistrations) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ }
+ for (AbstractDiscoveryService service : discoveryServices) {
+ service.stop();
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java
new file mode 100644
index 0000000000..0cbdda7b56
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointHelper.createEndpointDescription;
+
+import java.util.Dictionary;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Implementation;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.runtime.DomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.EndpointListener;
+import org.apache.tuscany.sca.runtime.EndpointRegistry;
+import org.apache.tuscany.sca.runtime.ExtensibleDomainRegistryFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+/**
+ * Discovery service based on the distributed SCA domain
+ */
+public class DomainDiscoveryService extends AbstractDiscoveryService implements EndpointListener {
+ private DomainRegistryFactory domainRegistryFactory;
+ private EndpointRegistry endpointRegistry;
+
+ public DomainDiscoveryService(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ super.start();
+ this.domainRegistryFactory = ExtensibleDomainRegistryFactory.getInstance(registry);
+ domainRegistryFactory.addListener(this);
+
+ // [rfeng] Starting of the endpoint registry takes a long time and it leaves the bundle
+ // state to be starting. When the registry is started, remote endpoints come in and that
+ // triggers the classloading from this bundle.
+ Thread thread = new Thread() {
+ public void run() {
+ startEndpointRegistry();
+ }
+ };
+ thread.start();
+ }
+
+ private synchronized void startEndpointRegistry() {
+ // The following code forced the start() of the domain registry in absense of services
+ String domainRegistry = context.getProperty("org.osgi.sca.domain.registry");
+ if (domainRegistry == null) {
+ domainRegistry = NodeConfiguration.DEFAULT_DOMAIN_REGISTRY_URI;
+ }
+ String domainURI = context.getProperty("org.osgi.sca.domain.uri");
+ if (domainURI == null) {
+ domainURI = NodeConfiguration.DEFAULT_DOMAIN_URI;
+ }
+ if (domainRegistry != null) {
+ endpointRegistry = domainRegistryFactory.getEndpointRegistry(domainRegistry, domainURI);
+ }
+ }
+
+ public void endpointAdded(Endpoint endpoint) {
+ Implementation impl = endpoint.getComponent().getImplementation();
+
+ /*
+ if (!(impl instanceof OSGiImplementation)) {
+ return;
+ }
+ */
+
+ BundleContext bundleContext = null;
+ // Remote endpoint doesn't have a bundle
+ if (!endpoint.isRemote()) {
+ OSGiImplementation osgiImpl = (OSGiImplementation)impl;
+ Bundle bundle = osgiImpl.getBundle();
+ bundleContext = bundle != null ? bundle.getBundleContext() : null;
+ }
+
+ // Notify the endpoint listeners
+ EndpointDescription description = createEndpointDescription(bundleContext, endpoint);
+ // Set the owning bundle to runtime bundle to avoid NPE
+ synchronized (this) {
+ endpointDescriptions.put(description, context.getBundle());
+ endpointChanged(description, ADDED);
+ }
+ }
+
+ public void endpointRemoved(Endpoint endpoint) {
+ EndpointDescription description = createEndpointDescription(context, endpoint);
+ synchronized (this) {
+ endpointDescriptions.remove(description);
+ endpointChanged(description, REMOVED);
+ }
+ }
+
+ public void endpointUpdated(Endpoint oldEndpoint, Endpoint newEndpoint) {
+ // FIXME: This is a quick and dirty way for the update
+ endpointRemoved(oldEndpoint);
+ endpointAdded(newEndpoint);
+ }
+
+ public void stop() {
+ if (domainRegistryFactory != null) {
+ domainRegistryFactory.removeListener(this);
+ if (endpointRegistry instanceof LifeCycleListener) {
+ ((LifeCycleListener)endpointRegistry).stop();
+ }
+ domainRegistryFactory = null;
+ endpointRegistry = null;
+ super.stop();
+ }
+ }
+
+ @Override
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary<String, Object> props = super.getProperties();
+ props.put(SUPPORTED_PROTOCOLS, new String[] {"org.osgi.sca"});
+ return props;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
new file mode 100644
index 0000000000..dc9ae096dd
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
@@ -0,0 +1,262 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getConfiguration;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID;
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import javax.xml.namespace.QName;
+
+import org.apache.tuscany.sca.assembly.Binding;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.deployment.Deployer;
+import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescription;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper;
+import org.apache.tuscany.sca.policy.Intent;
+import org.apache.tuscany.sca.policy.PolicySet;
+import org.oasisopen.sca.ServiceRuntimeException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleTrackerCustomizer {
+ private Deployer deployer;
+ private BundleTracker bundleTracker;
+ private Collection<ExtenderConfiguration> extenders = new ArrayList<ExtenderConfiguration>();
+
+ public LocalDiscoveryService(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ super.start();
+
+ UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class);
+ this.deployer = utilities.getUtility(Deployer.class);
+ bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STARTING, this);
+ bundleTracker.open();
+ }
+
+ public static ServiceTracker getTracker(BundleContext context) {
+ Filter filter = null;
+ try {
+ filter =
+ context.createFilter("(& (" + Discovery.SUPPORTED_PROTOCOLS
+ + "=local) ("
+ + Constants.OBJECTCLASS
+ + "="
+ + Discovery.class.getName()
+ + "))");
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return new ServiceTracker(context, filter, null);
+ }
+
+ private EndpointDescription createEndpointDescription(ServiceDescription sd) {
+ Map<String, Object> props = new HashMap<String, Object>(sd.getProperties());
+ props.put(Constants.OBJECTCLASS, sd.getInterfaces().toArray(new String[sd.getInterfaces().size()]));
+ if (!props.containsKey(ENDPOINT_SERVICE_ID)) {
+ props.put(ENDPOINT_SERVICE_ID, Long.valueOf(System.currentTimeMillis()));
+ }
+ if (!props.containsKey(ENDPOINT_FRAMEWORK_UUID)) {
+ props.put(ENDPOINT_FRAMEWORK_UUID, OSGiHelper.getFrameworkUUID(context));
+ }
+ if (!props.containsKey(ENDPOINT_ID)) {
+ props.put(ENDPOINT_ID, UUID.randomUUID().toString());
+ }
+
+ EndpointDescription sed = new EndpointDescription(props);
+ return sed;
+ }
+
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Map.Entry<EndpointDescription, Bundle>> i = endpointDescriptions.entrySet().iterator(); i.hasNext();) {
+ Entry<EndpointDescription, Bundle> entry = i.next();
+ if (entry.getValue().equals(bundle)) {
+ serviceDescriptionRemoved(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
+ private void serviceDescriptionAdded(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, ADDED);
+ }
+
+ private void serviceDescriptionRemoved(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, REMOVED);
+ }
+
+ public void stop() {
+ if (bundleTracker != null) {
+ bundleTracker.close();
+ }
+ super.stop();
+ }
+
+ public Object addingBundle(Bundle bundle, BundleEvent event) {
+ if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null || bundle.getBundleId() == 0) {
+ // Ignore fragments
+ return null;
+ }
+ Collection<URL> scaConfigs = getConfiguration(bundle, "SCA-Configuration", "OSGI-INF/sca-config/*.xml");
+ Collection<URL> descriptions = getConfiguration(bundle, "Remote-Service", null);
+ if (scaConfigs.isEmpty() && descriptions.isEmpty()) {
+ return null;
+ }
+ ExtenderConfiguration extender = new ExtenderConfiguration();
+ for (URL url : scaConfigs) {
+ try {
+ SCAConfig scaConfig = deployer.loadXMLDocument(url, deployer.createMonitor());
+ extender.scaConfigs.add(scaConfig);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ throw new ServiceRuntimeException(e);
+ }
+ }
+ for (URL url : descriptions) {
+ try {
+ ServiceDescriptions sds = deployer.loadXMLDocument(url, deployer.createMonitor());
+ extender.remoteServiceDescriptions.add(sds);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ // throw new ServiceRuntimeException(e);
+ }
+ }
+
+ // Add to the extenders before notifying the listeners (the endpoints may references to the config)
+ ExtenderConfiguration.validate(extenders, extender);
+ this.extenders.add(extender);
+
+ // Notify
+ for (ServiceDescriptions sds : extender.getRemoteServiceDescriptions()) {
+ for (ServiceDescription sd : sds) {
+ EndpointDescription sed = createEndpointDescription(sd);
+ endpointDescriptions.put(sed, bundle);
+ serviceDescriptionAdded(sed);
+ }
+ }
+
+ return extender;
+ }
+
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+ // STARTING --> ACTIVE
+ }
+
+ public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+ if (object instanceof ExtenderConfiguration) {
+ extenders.remove((ExtenderConfiguration)object);
+ removeServicesDeclaredInBundle(bundle);
+ }
+ }
+
+ public Collection<ExtenderConfiguration> getConfigurations() {
+ return extenders;
+ }
+
+ public static class ExtenderConfiguration {
+ private Collection<SCAConfig> scaConfigs = new ArrayList<SCAConfig>();
+ private Collection<ServiceDescriptions> remoteServiceDescriptions = new ArrayList<ServiceDescriptions>();
+
+ public Collection<ServiceDescriptions> getRemoteServiceDescriptions() {
+ return remoteServiceDescriptions;
+ }
+
+ public Collection<SCAConfig> getSCAConfigs() {
+ return scaConfigs;
+ }
+
+ public static void validate(Collection<ExtenderConfiguration> configs, ExtenderConfiguration newConfig) {
+ Map<QName, Binding> bindings = new HashMap<QName, Binding>();
+ Map<QName, Intent> intents = new HashMap<QName, Intent>();
+ Map<QName, PolicySet> policySets = new HashMap<QName, PolicySet>();
+
+ for (ExtenderConfiguration config : configs) {
+ for (SCAConfig c : config.getSCAConfigs()) {
+ addBindings(bindings, c);
+ addIntents(intents, c);
+ addPolicySets(policySets, c);
+ }
+ }
+ for (SCAConfig c : newConfig.getSCAConfigs()) {
+ addBindings(bindings, c);
+ addIntents(intents, c);
+ addPolicySets(policySets, c);
+ }
+ }
+
+ private static void addIntents(Map<QName, Intent> intents, SCAConfig c) {
+ for (Intent i: c.getIntents()) {
+ if (intents.put(i.getName(), i) != null) {
+ throw new ServiceRuntimeException("Duplicate intent: " + i.getName());
+ }
+ }
+ }
+
+ private static void addPolicySets(Map<QName, PolicySet> policySets, SCAConfig c) {
+ for (PolicySet ps: c.getPolicySets()) {
+ if (policySets.put(ps.getName(), ps) != null) {
+ throw new ServiceRuntimeException("Duplicate policySet: " + ps.getName());
+ }
+ }
+ }
+
+ private static void addBindings(Map<QName, Binding> bindings, SCAConfig c) {
+ for (Binding b : c.getBindings()) {
+ QName name = new QName(c.getTargetNamespace(), b.getName());
+ if (bindings.put(name, b) != null) {
+ throw new ServiceRuntimeException("Duplicate binding: " + name);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary<String, Object> props = super.getProperties();
+ props.put(SUPPORTED_PROTOCOLS, new String[] {"local"});
+ return props;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointDescription.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointDescription.java
new file mode 100644
index 0000000000..ee7e5ba1bd
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointDescription.java
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2010). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.*;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+
+/**
+ * A description of an endpoint that provides sufficient information for a
+ * compatible distribution provider to create a connection to this endpoint
+ *
+ * An Endpoint Description is easy to transfer between different systems because
+ * it is property based where the property keys are strings and the values are
+ * simple types. This allows it to be used as a communications device to convey
+ * available endpoint information to nodes in a network.
+ *
+ * An Endpoint Description reflects the perspective of an <i>importer</i>. That
+ * is, the property keys have been chosen to match filters that are created by
+ * client bundles that need a service. Therefore the map must not contain any
+ * <code>service.exported.*</code> property and must contain the corresponding
+ * <code>service.imported.*</code> ones.
+ *
+ * The <code>service.intents</code> property must contain the intents provided
+ * by the service itself combined with the intents added by the exporting
+ * distribution provider. Qualified intents appear fully expanded on this
+ * property.
+ *
+ * @Immutable
+ * @version $Revision$
+ */
+
+public class EndpointDescription {
+ private final Map<String, Object> properties;
+ private final List<String> interfaces;
+ private final long serviceId;
+ private final String frameworkUUID;
+ private final String id;
+
+ /**
+ * Create an Endpoint Description from a Map.
+ *
+ * <p>
+ * The {@link RemoteConstants#ENDPOINT_ID endpoint.id},
+ * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS service.imported.configs}
+ * and <code>objectClass</code> properties must be set.
+ *
+ * @param properties The map from which to create the Endpoint Description.
+ * The keys in the map must be type <code>String</code> and, since
+ * the keys are case insensitive, there must be no duplicates with
+ * case variation.
+ * @throws IllegalArgumentException When the properties are not proper for
+ * an Endpoint Description.
+ */
+
+ public EndpointDescription(Map<String, Object> properties) {
+ Map<String, Object> props = new TreeMap<String, Object>(
+ String.CASE_INSENSITIVE_ORDER);
+ try {
+ props.putAll(properties);
+ }
+ catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "non-String key in properties");
+ iae.initCause(e);
+ throw iae;
+ }
+ if (props.size() < properties.size()) {
+ throw new IllegalArgumentException(
+ "duplicate keys with different cases in properties: "
+ + new ArrayList<String>(props.keySet())
+ .removeAll(properties.keySet()));
+ }
+
+ if (!props.containsKey(SERVICE_IMPORTED)) {
+ props.put(SERVICE_IMPORTED, Boolean.toString(true));
+ }
+ this.properties = Collections.unmodifiableMap(props);
+ /* properties must be initialized before calling the following methods */
+ interfaces = verifyObjectClassProperty();
+ serviceId = verifyLongProperty(ENDPOINT_SERVICE_ID);
+ frameworkUUID = verifyStringProperty(ENDPOINT_FRAMEWORK_UUID);
+ id = verifyStringProperty(ENDPOINT_ID);
+ if (id == null) {
+ throw new IllegalArgumentException(ENDPOINT_ID
+ + " property must be set");
+ }
+ if (getConfigurationTypes().isEmpty()) {
+ throw new IllegalArgumentException(SERVICE_IMPORTED_CONFIGS
+ + " property must be set and non-empty");
+ }
+ }
+
+ /**
+ * Create an Endpoint Description based on a Service Reference and a Map of
+ * properties. The properties in the map take precedence over the properties
+ * in the Service Reference.
+ *
+ * <p>
+ * This method will automatically set the
+ * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID endpoint.framework.uuid}
+ * and {@link RemoteConstants#ENDPOINT_SERVICE_ID endpoint.service.id}
+ * properties based on the specified Service Reference as well as the
+ * {@link RemoteConstants#SERVICE_IMPORTED service.imported} property if
+ * they are not specified as properties.
+ * <p>
+ * The {@link RemoteConstants#ENDPOINT_ID endpoint.id},
+ * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS service.imported.configs}
+ * and <code>objectClass</code> properties must be set.
+ *
+ * @param reference A service reference that can be exported.
+ * @param properties Map of properties. This argument can be
+ * <code>null</code>. The keys in the map must be type
+ * <code>String</code> and, since the keys are case insensitive,
+ * there must be no duplicates with case variation.
+ * @throws IllegalArgumentException When the properties are not proper for
+ * an Endpoint Description
+ */
+ public EndpointDescription(final ServiceReference reference,
+ final Map<String, Object> properties) {
+ Map<String, Object> props = new TreeMap<String, Object>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ if (properties != null) {
+ try {
+ props.putAll(properties);
+ }
+ catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "non-String key in properties");
+ iae.initCause(e);
+ throw iae;
+ }
+ if (props.size() < properties.size()) {
+ throw new IllegalArgumentException(
+ "duplicate keys with different cases in properties: "
+ + new ArrayList<String>(props.keySet())
+ .removeAll(properties.keySet()));
+ }
+ }
+
+ for (String key : reference.getPropertyKeys()) {
+ if (!props.containsKey(key)) {
+ props.put(key, reference.getProperty(key));
+ }
+ }
+
+ if (!props.containsKey(ENDPOINT_SERVICE_ID)) {
+ props.put(ENDPOINT_SERVICE_ID, reference.getProperty(Constants.SERVICE_ID));
+ }
+ if (!props.containsKey(ENDPOINT_FRAMEWORK_UUID)) {
+ String uuid = null;
+ try {
+ uuid = AccessController
+ .doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return reference.getBundle().getBundleContext()
+ .getProperty("org.osgi.framework.uuid");
+ }
+ });
+ }
+ catch (SecurityException e) {
+ // if we don't have permission, we can't get the property
+ }
+ if (uuid != null) {
+ props.put(ENDPOINT_FRAMEWORK_UUID, uuid);
+ }
+ }
+ if (!props.containsKey(SERVICE_IMPORTED)) {
+ props.put(SERVICE_IMPORTED, Boolean.toString(true));
+ }
+ this.properties = Collections.unmodifiableMap(props);
+ /* properties must be initialized before calling the following methods */
+ interfaces = verifyObjectClassProperty();
+ serviceId = verifyLongProperty(ENDPOINT_SERVICE_ID);
+ frameworkUUID = verifyStringProperty(ENDPOINT_FRAMEWORK_UUID);
+ id = verifyStringProperty(ENDPOINT_ID);
+ if (id == null) {
+ throw new IllegalArgumentException(ENDPOINT_ID
+ + " property must be set");
+ }
+ if (getConfigurationTypes().isEmpty()) {
+ throw new IllegalArgumentException(SERVICE_IMPORTED_CONFIGS
+ + " property must be set and non-empty");
+ }
+ }
+
+ /**
+ * Verify and obtain the interface list from the properties.
+ *
+ * @return A list with the interface names.
+ * @throws IllegalArgumentException If the objectClass property is not set
+ * or is empty or if the package version property values are
+ * malformed.
+ */
+ private List<String> verifyObjectClassProperty() {
+ Object o = properties.get(Constants.OBJECTCLASS);
+ if (!(o instanceof String[])) {
+ throw new IllegalArgumentException(
+ "objectClass value must be of type String[]");
+ }
+ String[] objectClass = (String[]) o;
+ if (objectClass.length < 1) {
+ throw new IllegalArgumentException("objectClass is empty");
+ }
+ for (String interf : objectClass) {
+ int index = interf.lastIndexOf('.');
+ if (index == -1) {
+ continue;
+ }
+ String packageName = interf.substring(0, index);
+ try {
+ /* Make sure any package version properties are well formed */
+ getPackageVersion(packageName);
+ }
+ catch (IllegalArgumentException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "Improper version for package " + packageName);
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+ return Collections.unmodifiableList(Arrays.asList(objectClass));
+ }
+
+ /**
+ * Verify and obtain a required String property.
+ *
+ * @param propName The name of the property
+ * @return The value of the property or null if the property is not set.
+ * @throws IllegalArgumentException when the property doesn't have the
+ * correct data type.
+ */
+ private String verifyStringProperty(String propName) {
+ Object r = properties.get(propName);
+ try {
+ return (String) r;
+ }
+ catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "property value is not a String: " + propName);
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+
+ /**
+ * Verify and obtain a required long property.
+ *
+ * @param propName The name of the property
+ * @return The value of the property or 0 if the property is not set.
+ * @throws IllegalArgumentException when the property doesn't have the
+ * correct data type.
+ */
+ private long verifyLongProperty(String propName) {
+ Object r = properties.get(propName);
+ if (r == null) {
+ return 0l;
+ }
+ try {
+ return ((Long) r).longValue();
+ }
+ catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "property value is not a Long: " + propName);
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+
+ /**
+ * Returns the endpoint's id.
+ *
+ * The id is an opaque id for an endpoint. No two different endpoints must
+ * have the same id. Two Endpoint Descriptions with the same id must
+ * represent the same endpoint.
+ *
+ * The value of the id is stored in the
+ * {@link RemoteConstants#ENDPOINT_ID} property.
+ *
+ * @return The id of the endpoint, never <code>null</code>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Provide the list of interfaces implemented by the exported service.
+ *
+ * The value of the interfaces is derived from the <code>objectClass</code>
+ * property.
+ *
+ * @return An unmodifiable list of Java interface names implemented by this
+ * endpoint.
+ */
+ public List<String> getInterfaces() {
+ return interfaces;
+ }
+
+ /**
+ * Provide the version of the given package name.
+ *
+ * The version is encoded by prefixing the given package name with
+ * {@link RemoteConstants#ENDPOINT_PACKAGE_VERSION_
+ * endpoint.package.version.}, and then using this as an endpoint property
+ * key. For example:
+ *
+ * <pre>
+ * endpoint.package.version.com.acme
+ * </pre>
+ *
+ * The value of this property is in String format and will be converted to a
+ * <code>Version</code> object by this method.
+ *
+ * @param packageName The name of the package for which a version is
+ * requested.
+ * @return The version of the specified package or
+ * <code>Version.emptyVersion</code> if the package has no version
+ * in this Endpoint Description.
+ * @throws IllegalArgumentException If the version property value is not
+ * String.
+ */
+ public Version getPackageVersion(String packageName) {
+ String key = ENDPOINT_PACKAGE_VERSION_ + packageName;
+ Object value = properties.get(key);
+ String version;
+ try {
+ version = (String) value;
+ }
+ catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(key
+ + " property value is not a String");
+ iae.initCause(e);
+ throw iae;
+ }
+ return Version.parseVersion(version);
+ }
+
+ /**
+ * Returns the service id for the service exported through this endpoint.
+ *
+ * This is the service id under which the framework has registered the
+ * service. This field together with the Framework UUID is a globally unique
+ * id for a service.
+ *
+ * The value of the remote service id is stored in the
+ * {@link RemoteConstants#ENDPOINT_SERVICE_ID} endpoint property.
+ *
+ * @return Service id of a service or 0 if this Endpoint Description does
+ * not relate to an OSGi service.
+ *
+ */
+ public long getServiceId() {
+ return serviceId;
+ }
+
+ /**
+ * Returns the configuration types.
+ *
+ * A distribution provider exports a service with an endpoint. This endpoint
+ * uses some kind of communications protocol with a set of configuration
+ * parameters. There are many different types but each endpoint is
+ * configured by only one configuration type. However, a distribution
+ * provider can be aware of different configuration types and provide
+ * synonyms to increase the change a receiving distribution provider can
+ * create a connection to this endpoint.
+ *
+ * This value of the configuration types is stored in the
+ * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS} service property.
+ *
+ * @return An unmodifiable list of the configuration types used for the
+ * associated endpoint and optionally synonyms.
+ */
+ public List<String> getConfigurationTypes() {
+ return getStringPlusProperty(SERVICE_IMPORTED_CONFIGS);
+ }
+
+ /**
+ * Return the list of intents implemented by this endpoint.
+ *
+ * The intents are based on the service.intents on an imported service,
+ * except for any intents that are additionally provided by the importing
+ * distribution provider. All qualified intents must have been expanded.
+ *
+ * This value of the intents is stored in the
+ * {@link RemoteConstants#SERVICE_INTENTS} service property.
+ *
+ * @return An unmodifiable list of expanded intents that are provided by
+ * this endpoint.
+ */
+ public List<String> getIntents() {
+ return getStringPlusProperty(SERVICE_INTENTS);
+ }
+
+ /**
+ * Reads a 'String+' property from the properties map, which may be of type
+ * String, String[] or Collection<String> and returns it as an unmodifiable
+ * List.
+ *
+ * @param key The property
+ * @return An unmodifiable list
+ */
+ private List<String> getStringPlusProperty(String key) {
+ Object value = properties.get(key);
+ if (value == null) {
+ return Collections.EMPTY_LIST;
+ }
+
+ if (value instanceof String) {
+ return Collections.singletonList((String) value);
+ }
+
+ if (value instanceof String[]) {
+ String[] values = (String[]) value;
+ List<String> result = new ArrayList<String>(values.length);
+ for (String v : values) {
+ if (v != null) {
+ result.add(v);
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ if (value instanceof Collection< ? >) {
+ Collection< ? > values = (Collection< ? >) value;
+ List<String> result = new ArrayList<String>(values.size());
+ for (Iterator< ? > iter = values.iterator(); iter.hasNext();) {
+ Object v = iter.next();
+ if (v instanceof String) {
+ result.add((String) v);
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Return the framework UUID for the remote service, if present.
+ *
+ * The value of the remote framework uuid is stored in the
+ * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID} endpoint property.
+ *
+ * @return Remote Framework UUID, or null if this endpoint is not associated
+ * with an OSGi framework having a framework uuid.
+ */
+ public String getFrameworkUUID() {
+ return frameworkUUID;
+ }
+
+ /**
+ * Returns all endpoint properties.
+ *
+ * @return An unmodifiable map referring to the properties of this Endpoint
+ * Description.
+ */
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ /**
+ * Answers if this Endpoint Description refers to the same service instance
+ * as the given Endpoint Description.
+ *
+ * Two Endpoint Descriptions point to the same service if they have the same
+ * id or their framework UUIDs and remote service ids are equal.
+ *
+ * @param other The Endpoint Description to look at
+ * @return True if this endpoint description points to the same service as
+ * the other
+ */
+ public boolean isSameService(EndpointDescription other) {
+ if (this.equals(other)) {
+ return true;
+ }
+
+ if (this.getFrameworkUUID() == null) {
+ return false;
+ }
+
+ return (this.getServiceId() == other.getServiceId())
+ && this.getFrameworkUUID().equals(
+ other.getFrameworkUUID());
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return An integer which is a hash code value for this object.
+ */
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * Compares this <code>EndpointDescription</code> object to another object.
+ *
+ * <p>
+ * An Endpoint Description is considered to be <b>equal to</b> another
+ * Endpoint Description if their ids are equal.
+ *
+ * @param other The <code>EndpointDescription</code> object to be compared.
+ * @return <code>true</code> if <code>object</code> is a
+ * <code>EndpointDescription</code> and is equal to this object;
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof EndpointDescription)) {
+ return false;
+ }
+ return getId().equals(
+ ((EndpointDescription) other).getId());
+ }
+
+ /**
+ * Tests the properties of this <code>EndpointDescription</code> against
+ * the given filter using a case insensitive match.
+ *
+ * @param filter The filter to test.
+ * @return <code>true</code> If the properties of this
+ * <code>EndpointDescription</code> match the filter,
+ * <code>false</code> otherwise.
+ * @throws IllegalArgumentException If <code>filter</code> contains an
+ * invalid filter string that cannot be parsed.
+ */
+ public boolean matches(String filter) {
+ Filter f;
+ try {
+ f = FrameworkUtil.createFilter(filter);
+ }
+ catch (InvalidSyntaxException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(e
+ .getMessage());
+ iae.initCause(e);
+ throw iae;
+ }
+ Dictionary<String, Object> d = new UnmodifiableDictionary<String, Object>(
+ properties);
+ /*
+ * we can use matchCase here since properties already supports case
+ * insensitive key lookup.
+ */
+ return f.matchCase(d);
+ }
+
+ /**
+ * Returns the string representation of this EndpointDescription.
+ *
+ * @return String form of this EndpointDescription.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append('{');
+ Iterator<Map.Entry<String, Object>> iter = properties.entrySet()
+ .iterator();
+ boolean comma = false;
+ while (iter.hasNext()) {
+ Map.Entry<String, Object> entry = iter.next();
+ if (comma) {
+ sb.append(", ");
+ }
+ else {
+ comma = true;
+ }
+ sb.append(entry.getKey());
+ sb.append('=');
+ Object value = entry.getValue();
+ if (value != null) {
+ Class< ? > valueType = value.getClass();
+ if (Object[].class.isAssignableFrom(valueType)) {
+ append(sb, (Object[]) value);
+ continue;
+ }
+ }
+ sb.append(value);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Append the specified Object array to the specified StringBuffer.
+ *
+ * @param sb Receiving StringBuffer.
+ * @param value Object array to append to the specified StringBuffer.
+ */
+ private static void append(StringBuffer sb, Object[] value) {
+ sb.append('[');
+ boolean comma = false;
+ final int length = value.length;
+ for (int i = 0; i < length; i++) {
+ if (comma) {
+ sb.append(", ");
+ }
+ else {
+ comma = true;
+ }
+ sb.append(String.valueOf(value[i]));
+ }
+ sb.append(']');
+ }
+
+ /**
+ * Unmodifiable Dictionary wrapper for a Map. This class is also used by
+ * EndpointPermission.
+ */
+ static class UnmodifiableDictionary<K, V> extends Dictionary<K, V> {
+ private final Map<K, V> wrapped;
+
+ UnmodifiableDictionary(Map<K, V> wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ public Enumeration<V> elements() {
+ return Collections.enumeration(wrapped.values());
+ }
+
+ public V get(Object key) {
+ return wrapped.get(key);
+ }
+
+ public boolean isEmpty() {
+ return wrapped.isEmpty();
+ }
+
+ public Enumeration<K> keys() {
+ return Collections.enumeration(wrapped.keySet());
+ }
+
+ public V put(K key, V value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public V remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int size() {
+ return wrapped.size();
+ }
+
+ public String toString() {
+ return wrapped.toString();
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointListener.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointListener.java
new file mode 100644
index 0000000000..941b899fbb
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointListener.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+/**
+ * A white board service that represents a listener for endpoints.
+ *
+ * An Endpoint Listener represents a participant in the distributed model that
+ * is interested in Endpoint Descriptions.
+ *
+ * This white board service can be used in many different scenarios. However,
+ * the primary use case is to allow a remote manager to be informed of Endpoint
+ * Descriptions available in the network and inform the network about available
+ * Endpoint Descriptions.
+ *
+ * Both the network bundle and the manager bundle register an Endpoint Listener
+ * service. The manager informs the network bundle about Endpoints that it
+ * creates. The network bundles then uses a protocol like SLP to announce these
+ * local end-points to the network.
+ *
+ * If the network bundle discovers a new Endpoint through its discovery
+ * protocol, then it sends an Endpoint Description to all the Endpoint
+ * Listener services that are registered (except its own) that have specified an
+ * interest in that endpoint.
+ *
+ * Endpoint Listener services can express their <i>scope</i> with the service
+ * property {@link #ENDPOINT_LISTENER_SCOPE}. This service property is a list of
+ * filters. An Endpoint Description should only be given to a Endpoint Listener
+ * when there is at least one filter that matches the Endpoint Description
+ * properties. given to it.
+ *
+ * This filter model is quite flexible. For example, a discovery bundle is only
+ * interested in locally originating Endpoint Descriptions. The following filter
+ * ensure that it only sees local endpoints.
+ *
+ * <pre>
+ * (org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72)
+ * </pre>
+ *
+ * In the same vein, a manager that is only interested in remote Endpoint
+ * Descriptions can use a filter like:
+ *
+ * <pre>
+ * (!(org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72))
+ * </pre>
+ *
+ * Where in both cases, the given UUID is the UUID of the local framework that
+ * can be found in the Framework properties.
+ *
+ * The Endpoint Listener's scope maps very well to the service hooks. A manager
+ * can just register all filters found from the Listener Hook as its scope. This
+ * will automatically provide it with all known endpoints that match the given
+ * scope, without having to inspect the filter string.
+ *
+ * In general, when an Endpoint Description is discovered, it should be
+ * dispatched to all registered Endpoint Listener services. If a new Endpoint
+ * Listener is registered, it should be informed about all currently known
+ * Endpoints that match its scope. If a getter of the Endpoint Listener service
+ * is unregistered, then all its registered Endpoint Description objects must be
+ * removed.
+ *
+ * The Endpoint Listener models a <i>best effort</i> approach. Participating
+ * bundles should do their utmost to keep the listeners up to date, but
+ * implementers should realize that many endpoints come through unreliable
+ * discovery processes.
+ *
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface EndpointListener {
+ /**
+ * Specifies the interest of this listener with filters. This listener is
+ * only interested in Endpoint Descriptions where its properties match the
+ * given filter. The type of this property must be <code>String+</code>.
+ */
+ String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope";
+
+ /**
+ * Register an endpoint with this listener.
+ *
+ * If the endpoint matches one of the filters registered with the
+ * {@link #ENDPOINT_LISTENER_SCOPE} service property then this filter should
+ * be given as the <code>matchedFilter</code> parameter.
+ *
+ * When this service is first registered or it is modified, it should
+ * receive all known endpoints matching the filter.
+ *
+ * @param endpoint The Endpoint Description to be published
+ * @param matchedFilter The filter from the {@link #ENDPOINT_LISTENER_SCOPE}
+ * that matched the endpoint, must not be <code>null</code>.
+ */
+ void endpointAdded(EndpointDescription endpoint, String matchedFilter);
+
+ /**
+ * Remove the registration of an endpoint.
+ *
+ * If an endpoint that was registered with the
+ * {@link #endpointAdded(EndpointDescription, String)} method is no longer
+ * available then this method should be called. This will remove the
+ * endpoint from the listener.
+ *
+ * It is not necessary to remove endpoints when the service is unregistered
+ * or modified in such a way that not all endpoints match the interest
+ * filter anymore.
+ *
+ * @param endpoint The Endpoint Description that is no longer valid.
+ * @param matchedFilter The filter from the {@link #ENDPOINT_LISTENER_SCOPE}
+ * that matched the endpoint, must not be <code>null</code>.
+ */
+ void endpointRemoved(EndpointDescription endpoint, String matchedFilter);
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointPermission.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointPermission.java
new file mode 100644
index 0000000000..433e22e543
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/EndpointPermission.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2010). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import static org.osgi.service.remoteserviceadmin.RemoteConstants.*;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * A bundle's authority to export, import or read an Endpoint.
+ * <ul>
+ * <li>The <code>export</code> action allows a bundle to export a service as an
+ * Endpoint.</li>
+ * <li>The <code>import</code> action allows a bundle to import a service from
+ * an Endpoint.</li>
+ * <li>The <code>read</code> action allows a bundle to read references to an
+ * Endpoint.</li>
+ * </ul>
+ * Permission to read an Endpoint is required in order to detect events
+ * regarding an Endpoint. Untrusted bundles should not be able to detect the
+ * presence of certain Endpoints unless they have the appropriate
+ * <code>EndpointPermission</code> to read the specific service.
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+
+public final class EndpointPermission extends Permission {
+ static final long serialVersionUID = -7662148639076511574L;
+ /**
+ * The action string <code>read</code>.
+ */
+ public final static String READ = "read";
+ /**
+ * The action string <code>import</code>. The <code>import</code> action
+ * implies the <code>read</code> action.
+ */
+ public final static String IMPORT = "import";
+ /**
+ * The action string <code>export</code>. The <code>export</code> action
+ * implies the <code>read</code> action.
+ */
+ public final static String EXPORT = "export";
+
+ private final static int ACTION_READ = 0x00000001;
+ private final static int ACTION_IMPORT = 0x00000002;
+ private final static int ACTION_EXPORT = 0x00000004;
+ private final static int ACTION_ALL = ACTION_EXPORT
+ | ACTION_IMPORT
+ | ACTION_READ;
+ final static int ACTION_NONE = 0;
+
+ /**
+ * The actions mask.
+ */
+ transient int action_mask;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private volatile String actions = null;
+
+ /**
+ * The endpoint used by this EndpointPermission. Must be null if not
+ * constructed with a endpoint.
+ */
+ transient final EndpointDescription endpoint;
+
+ /**
+ * This dictionary holds the properties of the permission, used to match a
+ * filter in implies.
+ */
+ private transient final Dictionary<String, Object> properties;
+
+ /**
+ * If this EndpointPermission was not constructed with an
+ * EndpointDescription, this holds a Filter matching object used to evaluate
+ * the filter in implies or null for wildcard.
+ */
+ transient Filter filter;
+
+ /**
+ * Create a new EndpointPermission with the specified filter.
+ *
+ * <p>
+ * The filter will be evaluated against the endpoint properties of a
+ * requested EndpointPermission.
+ *
+ * <p>
+ * There are three possible actions: <code>read</code>, <code>import</code>
+ * and <code>export</code>. The <code>read</code> action allows the owner of
+ * this permission to see the presence of distributed services. The
+ * <code>import</code> action allows the owner of this permission to import
+ * an endpoint. The <code>export</code> action allows the owner of this
+ * permission to export a service.
+ *
+ * @param filterString The filter string or &quot;*&quot; to match all
+ * endpoints.
+ * @param actions The actions <code>read</code>, <code>import</code>, or
+ * <code>export</code>.
+ * @throws IllegalArgumentException If the filter has an invalid syntax or
+ * the actions are not valid.
+ */
+ public EndpointPermission(String filterString, String actions) {
+ this(filterString, parseActions(actions));
+ }
+
+ /**
+ * Creates a new requested <code>EndpointPermission</code> object to be used
+ * by code that must perform <code>checkPermission</code>.
+ * <code>EndpointPermission</code> objects created with this constructor
+ * cannot be added to an <code>EndpointPermission</code> permission
+ * collection.
+ *
+ * @param endpoint The requested endpoint.
+ * @param localFrameworkUUID The UUID of the local framework. This is used
+ * to support matching the
+ * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID
+ * endpoint.framework.uuid} endpoint property to the
+ * <code>&lt;&lt;LOCAL&gt;&gt;</code> value in the filter expression.
+ * @param actions The actions <code>read</code>, <code>import</code>, or
+ * <code>export</code>.
+ * @throws IllegalArgumentException If the endpoint is <code>null</code> or
+ * the actions are not valid.
+ */
+ public EndpointPermission(EndpointDescription endpoint,
+ String localFrameworkUUID, String actions) {
+ super(createName(endpoint));
+ setTransients(null, parseActions(actions));
+ Map<String, Object> props;
+ if ((localFrameworkUUID != null)
+ && localFrameworkUUID.equals(endpoint.getFrameworkUUID())) {
+ props = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
+ props.putAll(endpoint.getProperties());
+ props.put(ENDPOINT_FRAMEWORK_UUID, new String[] {
+ endpoint.getFrameworkUUID(), "<<LOCAL>>"});
+ }
+ else {
+ props = endpoint.getProperties();
+ }
+ this.endpoint = endpoint;
+ this.properties = new EndpointDescription.UnmodifiableDictionary<String, Object>(
+ props);
+ }
+
+ /**
+ * Create a permission name from a EndpointDescription.
+ *
+ * @param endpoint EndpointDescription to use to create permission name.
+ * @return permission name.
+ */
+ private static String createName(EndpointDescription endpoint) {
+ if (endpoint == null) {
+ throw new IllegalArgumentException("invalid endpoint: null");
+ }
+ StringBuffer sb = new StringBuffer("(" + ENDPOINT_ID + "=");
+ sb.append(endpoint.getId());
+ sb.append(")");
+ return sb.toString();
+ }
+
+ /**
+ * Package private constructor used by EndpointPermissionCollection.
+ *
+ * @param name class name
+ * @param mask action mask
+ */
+ EndpointPermission(String name, int mask) {
+ super(name);
+ setTransients(parseFilter(name), mask);
+ this.endpoint = null;
+ this.properties = null;
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param mask action mask
+ */
+ private void setTransients(Filter f, int mask) {
+ if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+ throw new IllegalArgumentException("invalid action string");
+ }
+ action_mask = mask;
+ filter = f;
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions Action string.
+ * @return action mask.
+ */
+ private static int parseActions(String actions) {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null) {
+ return mask;
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return mask;
+
+ while (i != -1) {
+ char c;
+
+ // skip whitespace
+ while ((i != -1)
+ && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
+ || c == '\f' || c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 5 && (a[i - 5] == 'i' || a[i - 5] == 'I')
+ && (a[i - 4] == 'm' || a[i - 4] == 'M')
+ && (a[i - 3] == 'p' || a[i - 3] == 'P')
+ && (a[i - 2] == 'o' || a[i - 2] == 'O')
+ && (a[i - 1] == 'r' || a[i - 1] == 'R')
+ && (a[i] == 't' || a[i] == 'T')) {
+ matchlen = 6;
+ mask |= ACTION_IMPORT | ACTION_READ;
+
+ }
+ else
+ if (i >= 5 && (a[i - 5] == 'e' || a[i - 5] == 'E')
+ && (a[i - 4] == 'x' || a[i - 4] == 'X')
+ && (a[i - 3] == 'p' || a[i - 3] == 'P')
+ && (a[i - 2] == 'o' || a[i - 2] == 'O')
+ && (a[i - 1] == 'r' || a[i - 1] == 'R')
+ && (a[i] == 't' || a[i] == 'T')) {
+ matchlen = 6;
+ mask |= ACTION_EXPORT | ACTION_READ;
+
+ }
+ else {
+ if (i >= 3 && (a[i - 3] == 'r' || a[i - 3] == 'R')
+ && (a[i - 2] == 'e' || a[i - 2] == 'E')
+ && (a[i - 1] == 'a' || a[i - 1] == 'A')
+ && (a[i] == 'd' || a[i] == 'D')) {
+ matchlen = 4;
+ mask |= ACTION_READ;
+
+ }
+ else {
+ // parse error
+ throw new IllegalArgumentException(
+ "invalid permission: " + actions);
+ }
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarfread". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma) {
+ switch (a[i - matchlen]) {
+ case ',' :
+ seencomma = true;
+ /* FALLTHROUGH */
+ case ' ' :
+ case '\r' :
+ case '\n' :
+ case '\f' :
+ case '\t' :
+ break;
+ default :
+ throw new IllegalArgumentException(
+ "invalid permission: " + actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma) {
+ throw new IllegalArgumentException("invalid permission: " + actions);
+ }
+
+ return mask;
+ }
+
+ /**
+ * Parse filter string into a Filter object.
+ *
+ * @param filterString The filter string to parse.
+ * @return a Filter for this bundle.
+ * @throws IllegalArgumentException If the filter syntax is invalid.
+ */
+ private static Filter parseFilter(String filterString) {
+ if (filterString == null) {
+ throw new IllegalArgumentException("invalid filter: null");
+ }
+ filterString = filterString.trim();
+ if (filterString.equals("*")) {
+ return null; // wildcard
+ }
+ try {
+ return FrameworkUtil.createFilter(filterString);
+ }
+ catch (InvalidSyntaxException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "invalid filter");
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+
+ /**
+ * Determines if a <code>EndpointPermission</code> object "implies" the
+ * specified permission.
+ *
+ * @param p The target permission to check.
+ * @return <code>true</code> if the specified permission is implied by this
+ * object; <code>false</code> otherwise.
+ */
+ public boolean implies(Permission p) {
+ if (!(p instanceof EndpointPermission)) {
+ return false;
+ }
+ EndpointPermission requested = (EndpointPermission) p;
+ if (endpoint != null) {
+ return false;
+ }
+ // if requested permission has a filter, then it is an invalid argument
+ if (requested.filter != null) {
+ return false;
+ }
+ return implies0(requested, ACTION_NONE);
+ }
+
+ /**
+ * Internal implies method. Used by the implies and the permission
+ * collection implies methods.
+ *
+ * @param requested The requested EndpointPermission which has already be
+ * validated as a proper argument. The requested EndpointPermission
+ * must not have a filter expression.
+ * @param effective The effective actions with which to start.
+ * @return <code>true</code> if the specified permission is implied by this
+ * object; <code>false</code> otherwise.
+ */
+ boolean implies0(EndpointPermission requested, int effective) {
+ /* check actions first - much faster */
+ effective |= action_mask;
+ final int desired = requested.action_mask;
+ if ((effective & desired) != desired) {
+ return false;
+ }
+ /* if we have no filter */
+ Filter f = filter;
+ if (f == null) {
+ // it's "*"
+ return true;
+ }
+ return f.matchCase(requested.getProperties());
+ }
+
+ /**
+ * Returns the canonical string representation of the actions. Always
+ * returns present actions in the following canonical order:
+ * <code>read</code>, <code>import</code>, <code>export</code>.
+ *
+ * @return The canonical string representation of the actions.
+ */
+ public String getActions() {
+ String result = actions;
+ if (result == null) {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ int mask = action_mask;
+ if ((mask & ACTION_READ) == ACTION_READ) {
+ sb.append(READ);
+ comma = true;
+ }
+
+ if ((mask & ACTION_IMPORT) == ACTION_IMPORT) {
+ if (comma)
+ sb.append(',');
+ sb.append(IMPORT);
+ }
+
+ if ((mask & ACTION_EXPORT) == ACTION_EXPORT) {
+ if (comma)
+ sb.append(',');
+ sb.append(EXPORT);
+ }
+
+ actions = result = sb.toString();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new <code>PermissionCollection</code> object for storing
+ * <code>EndpointPermission<code> objects.
+ *
+ * @return A new <code>PermissionCollection</code> object suitable for
+ * storing <code>EndpointPermission</code> objects.
+ */
+ public PermissionCollection newPermissionCollection() {
+ return new EndpointPermissionCollection();
+ }
+
+ /**
+ * Determines the equality of two EndpointPermission objects.
+ *
+ * Checks that specified object has the same name, actions and endpoint as
+ * this <code>EndpointPermission</code>.
+ *
+ * @param obj The object to test for equality.
+ * @return true if obj is a <code>EndpointPermission</code>, and has the
+ * same name, actions and endpoint as this
+ * <code>EndpointPermission</code> object; <code>false</code>
+ * otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof EndpointPermission)) {
+ return false;
+ }
+
+ EndpointPermission ep = (EndpointPermission) obj;
+
+ return (action_mask == ep.action_mask)
+ && getName().equals(ep.getName())
+ && ((endpoint == ep.endpoint) || ((endpoint != null)
+ && (ep.endpoint != null) && endpoint
+ .equals(ep.endpoint)));
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return Hash code value for this object.
+ */
+ public int hashCode() {
+ int h = 31 * 17 + getName().hashCode();
+ h = 31 * h + getActions().hashCode();
+ if (endpoint != null) {
+ h = 31 * h + endpoint.hashCode();
+ }
+ return h;
+ }
+
+ /**
+ * WriteObject is called to save the state of this permission to a stream.
+ * The actions are serialized, and the superclass takes care of the name.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException {
+ if (endpoint != null) {
+ throw new NotSerializableException("cannot serialize");
+ }
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null) {
+ getActions();
+ }
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of this permission from a
+ * stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ // Read in the action, then initialize the rest
+ s.defaultReadObject();
+ setTransients(parseFilter(getName()), parseActions(actions));
+ }
+
+ /**
+ * Called by <code><@link EndpointPermission#implies(Permission)></code>.
+ *
+ * @return a dictionary of properties for this permission.
+ */
+ private Dictionary<String, Object> getProperties() {
+ return properties;
+ }
+}
+
+/**
+ * Stores a set of EndpointPermission permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class EndpointPermissionCollection extends PermissionCollection {
+ static final long serialVersionUID = 662615640374640621L;
+ /**
+ * Table of permissions.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private Map<String, EndpointPermission> permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private boolean all_allowed;
+
+ /**
+ * Creates an empty EndpointPermissions object.
+ */
+ public EndpointPermissionCollection() {
+ permissions = new HashMap<String, EndpointPermission>();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds a permission to this permission collection.
+ *
+ * @param permission The Permission object to add.
+ * @throws IllegalArgumentException If the specified permission is not a
+ * EndpointPermission object.
+ * @throws SecurityException If this
+ * <code>EndpointPermissionCollection</code> object has been marked
+ * read-only.
+ */
+ public void add(final Permission permission) {
+ if (!(permission instanceof EndpointPermission)) {
+ throw new IllegalArgumentException("invalid permission: "
+ + permission);
+ }
+ if (isReadOnly()) {
+ throw new SecurityException("attempt to add a Permission to a "
+ + "readonly PermissionCollection");
+ }
+
+ final EndpointPermission ep = (EndpointPermission) permission;
+ if (ep.endpoint != null) {
+ throw new IllegalArgumentException("cannot add to collection: "
+ + ep);
+ }
+
+ final String name = ep.getName();
+ synchronized (this) {
+ /* select the bucket for the permission */
+ Map<String, EndpointPermission> pc = permissions;
+ final EndpointPermission existing = (EndpointPermission) pc
+ .get(name);
+
+ if (existing != null) {
+ final int oldMask = existing.action_mask;
+ final int newMask = ep.action_mask;
+ if (oldMask != newMask) {
+ pc.put(name,
+ new EndpointPermission(name, oldMask | newMask));
+ }
+ }
+ else {
+ pc.put(name, ep);
+ }
+
+ if (!all_allowed) {
+ if (name.equals("*")) {
+ all_allowed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if a set of permissions implies the permissions expressed in
+ * <code>permission</code>.
+ *
+ * @param permission The Permission object to compare.
+ * @return <code>true</code> if <code>permission</code> is a proper subset
+ * of a permission in the set; <code>false</code> otherwise.
+ */
+ public boolean implies(final Permission permission) {
+ if (!(permission instanceof EndpointPermission)) {
+ return false;
+ }
+ final EndpointPermission requested = (EndpointPermission) permission;
+ /* if requested permission has a filter, then it is an invalid argument */
+ if (requested.filter != null) {
+ return false;
+ }
+ int effective = EndpointPermission.ACTION_NONE;
+ Collection<EndpointPermission> perms;
+ synchronized (this) {
+ final int desired = requested.action_mask;
+ /* short circuit if the "*" Permission was added */
+ if (all_allowed) {
+ EndpointPermission ep = permissions.get("*");
+ if (ep != null) {
+ effective |= ep.action_mask;
+ if ((effective & desired) == desired) {
+ return true;
+ }
+ }
+ }
+ perms = permissions.values();
+ }
+
+ /* iterate one by one over permissions */
+ for (Iterator<EndpointPermission> iter = perms.iterator(); iter
+ .hasNext();) {
+ if (iter.next().implies0(requested, effective)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an enumeration of all the <code>EndpointPermission</code> objects
+ * in the container.
+ *
+ * @return Enumeration of all the EndpointPermission objects.
+ */
+ public synchronized Enumeration<Permission> elements() {
+ List<Permission> all = new ArrayList<Permission>(permissions.values());
+ return Collections.enumeration(all);
+ }
+
+ /* serialization logic */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("permissions", HashMap.class),
+ new ObjectStreamField("all_allowed", Boolean.TYPE) };
+
+ private synchronized void writeObject(ObjectOutputStream out)
+ throws IOException {
+ ObjectOutputStream.PutField pfields = out.putFields();
+ pfields.put("permissions", permissions);
+ pfields.put("all_allowed", all_allowed);
+ out.writeFields();
+ }
+
+ private synchronized void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gfields = in.readFields();
+ permissions = (HashMap<String, EndpointPermission>) gfields.get(
+ "permissions", new HashMap<String, EndpointPermission>());
+ all_allowed = gfields.get("all_allowed", false);
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportReference.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportReference.java
new file mode 100644
index 0000000000..03b3235c59
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportReference.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Export Reference associates a service with a local endpoint.
+ *
+ * The Export Reference can be used to reference an exported service. When the
+ * service is no longer exported, all methods must return <code>null</code>;
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface ExportReference {
+ /**
+ * Return the service being exported.
+ *
+ * @return The service being exported, must be <code>null</code> when this
+ * registration is unregistered.
+ */
+ ServiceReference getExportedService();
+
+ /**
+ * Return the Endpoint Description that is created for this registration.
+ *
+ * @return the local Endpoint Description
+ */
+ EndpointDescription getExportedEndpoint();
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportRegistration.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportRegistration.java
new file mode 100644
index 0000000000..e8c6c66cbf
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ExportRegistration.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Export Registration associates a service to a local endpoint.
+ *
+ * The Export Registration can be used to delete the endpoint associated with an
+ * this registration. It is created with the
+ * {@link RemoteServiceAdmin#exportService(ServiceReference,java.util.Map)}
+ * method.
+ *
+ * When this Export Registration has been unregistered, the methods must all
+ * return <code>null</code>.
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface ExportRegistration {
+ /**
+ * Return the Export Reference for the exported service.
+ *
+ * @return An Export Reference for this registration
+ * @throws IllegalStateException Thrown when this object was not properly
+ * initialized, see {@link #getException()}
+ */
+ ExportReference getExportReference();
+
+ /**
+ * Delete the local endpoint and disconnect any remote distribution
+ * providers. After this method returns, all the methods must return
+ * <code>null</code>.
+ *
+ * This method has no effect when the endpoint is already destroyed or being
+ * destroyed.
+ */
+ void close();
+
+ /**
+ * Exception for any error during the import process.
+ *
+ * If the Remote Admin for some reasons is unable to create a registration,
+ * then it must return a <code>Throwable</code> from this method. In this
+ * case, all other methods must return on this interface must throw an
+ * Illegal State Exception. If no error occurred, this method must return
+ * <code>null</code>.
+ *
+ * The error must be set before this Import Registration is returned.
+ * Asynchronously occurring errors must be reported to the log.
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or <code>null</code> if no exception occurred.
+ */
+ Throwable getException();
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportReference.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportReference.java
new file mode 100644
index 0000000000..5fa5673341
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportReference.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Import Reference associates an active proxy service to a remote endpoint.
+ *
+ * The Import Reference can be used to reference an imported service. When the
+ * service is no longer imported, all methods must return <code>null</code>;
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface ImportReference {
+ /**
+ * Answer the associated Service Reference for the proxy to the endpoint.
+ *
+ * @return A Service Reference to the proxy for the endpoint.
+ */
+ ServiceReference getImportedService();
+
+ /**
+ * Answer the associated remote Endpoint Description.
+ *
+ * @return A Endpoint Description for the remote endpoint.
+ */
+ EndpointDescription getImportedEndpoint();
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportRegistration.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportRegistration.java
new file mode 100644
index 0000000000..1fc1c6f6ba
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/ImportRegistration.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+/**
+ * An Import Registration associates an active proxy service to a remote
+ * endpoint.
+ *
+ * The Import Registration can be used to delete the proxy associated with an
+ * endpoint. It is created with the{@link RemoteServiceAdmin#importService}
+ * method.
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface ImportRegistration {
+ /**
+ * Answer the associated Export Reference.
+ *
+ * @return An Import Reference for this registration
+ * @throws IllegalStateException Thrown when this object was not properly
+ * initialized, see {@link #getException()}
+ */
+ ImportReference getImportReference();
+
+ /**
+ * Unregister this Import Registration. This must close the connection to
+ * the end endpoint unregister the proxy. After this method returns, all
+ * other methods must return null.
+ *
+ * This method has no effect when the service is already unregistered or in
+ * the process off.
+ */
+ void close();
+
+ /**
+ * Exception for any error during the import process.
+ *
+ * If the Remote Admin for some reasons is unable to create a registration,
+ * then it must return a <code>Throwable</code> from this method. In this
+ * case, all other methods must return on this interface must thrown an
+ * Illegal State Exception. If no error occurred, this method must return
+ * <code>null</code>.
+ *
+ * The error must be set before this Import Registration is returned.
+ * Asynchronously occurring errors must be reported to the log.
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or <code>null</code> if no exception occurred.
+ */
+ Throwable getException();
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteConstants.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteConstants.java
new file mode 100644
index 0000000000..cf2194ec12
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteConstants.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+/**
+ * Provide the definition of the constants used in the Remote Services API.
+ *
+ * @Immutable
+ * @version $Revision$
+ */
+public class RemoteConstants {
+ private RemoteConstants() {
+ }
+
+ /**
+ * Service property identifying the configuration types supported by a
+ * distribution provider. Registered by the distribution provider on one of
+ * its services to indicate the supported configuration types.
+ * <p>
+ * The value of this property must be of type <code>String</code>,
+ * <code>String[]</code>, or <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String REMOTE_CONFIGS_SUPPORTED = "remote.configs.supported";
+
+ /**
+ * Service property identifying the intents supported by a distribution
+ * provider. Registered by the distribution provider on one of its services
+ * to indicate the vocabulary of implemented intents.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>,
+ * <code>String[]</code>, or <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String REMOTE_INTENTS_SUPPORTED = "remote.intents.supported";
+
+ /**
+ * Service property identifying the configuration types that should be used
+ * to export the service. Each configuration type represents the
+ * configuration parameters for an endpoint. A distribution provider should
+ * create an endpoint for each configuration type that it supports.
+ *
+ * <p>
+ * This property may be supplied in the <code>properties</code>
+ * <code>Dictionary</code> object passed to the
+ * <code>BundleContext.registerService</code> method. The value of this
+ * property must be of type <code>String</code>, <code>String[]</code>, or
+ * <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String SERVICE_EXPORTED_CONFIGS = "service.exported.configs";
+
+ /**
+ * Service property identifying the intents that the distribution provider
+ * must implement to distribute the service. Intents listed in this property
+ * are reserved for intents that are critical for the code to function
+ * correctly, for example, ordering of messages. These intents should not be
+ * configurable.
+ *
+ * <p>
+ * This property may be supplied in the <code>properties</code>
+ * <code>Dictionary</code> object passed to the
+ * <code>BundleContext.registerService</code> method. The value of this
+ * property must be of type <code>String</code>, <code>String[]</code>, or
+ * <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String SERVICE_EXPORTED_INTENTS = "service.exported.intents";
+
+ /**
+ * Service property identifying the extra intents that the distribution
+ * provider must implement to distribute the service. This property is
+ * merged with the <code>service.exported.intents</code> property before the
+ * distribution provider interprets the listed intents; it has therefore the
+ * same semantics but the property should be configurable so the
+ * administrator can choose the intents based on the topology. Bundles
+ * should therefore make this property configurable, for example through the
+ * Configuration Admin service.
+ *
+ * <p>
+ * This property may be supplied in the <code>properties</code>
+ * <code>Dictionary</code> object passed to the
+ * <code>BundleContext.registerService</code> method. The value of this
+ * property must be of type <code>String</code>, <code>String[]</code>, or
+ * <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra";
+
+ /**
+ * Service property marking the service for export. It defines the
+ * interfaces under which this service can be exported. This list must be a
+ * subset of the types under which the service was registered. The single
+ * value of an asterisk (&quot;*&quot;, &#92;u002A) indicates all the
+ * interface types under which the service was registered excluding the
+ * non-interface types. It is strongly recommended to only export interface
+ * types and not concrete classes due to the complexity of creating proxies
+ * for some type of concrete classes.
+ *
+ * <p>
+ * This property may be supplied in the <code>properties</code>
+ * <code>Dictionary</code> object passed to the
+ * <code>BundleContext.registerService</code> method. The value of this
+ * property must be of type <code>String</code>, <code>String[]</code>, or
+ * <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String SERVICE_EXPORTED_INTERFACES = "service.exported.interfaces";
+
+ /**
+ * Service property identifying the service as imported. This service
+ * property must be set by a distribution provider to any value when it
+ * registers the endpoint proxy as an imported service. A bundle can use
+ * this property to filter out imported services.
+ *
+ * <p>
+ * The value of this property may be of any type.
+ */
+ public static final String SERVICE_IMPORTED = "service.imported";
+
+ /**
+ * Service property identifying the configuration types used to import the
+ * service. Any associated properties for this configuration types must be
+ * properly mapped to the importing system. For example, a URL in these
+ * properties must point to a valid resource when used in the importing
+ * framework. If multiple configuration types are listed in this property,
+ * then they must be synonyms for exactly the same remote endpoint that is
+ * used to export this service.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>,
+ * <code>String[]</code>, or <code>Collection&lt;String&gt;</code>.
+ *
+ * @see #SERVICE_EXPORTED_CONFIGS
+ */
+ public static final String SERVICE_IMPORTED_CONFIGS = "service.imported.configs";
+
+ /**
+ * Service property identifying the intents that this service implement.
+ * This property has a dual purpose:
+ * <ul>
+ * <li>A bundle can use this service property to notify the distribution
+ * provider that these intents are already implemented by the exported
+ * service object.</li>
+ * <li>A distribution provider must use this property to convey the combined
+ * intents of: The exporting service, and, the intents that the exporting
+ * distribution provider adds, and the intents that the importing
+ * distribution provider adds.</li>
+ * </ul>
+ * To export a service, a distribution provider must expand any qualified
+ * intents. Both the exporting and importing distribution providers must
+ * recognize all intents before a service can be distributed.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>,
+ * <code>String[]</code>, or <code>Collection&lt;String&gt;</code>.
+ */
+ public static final String SERVICE_INTENTS = "service.intents";
+
+ /* above are from Ch 13 Remote Service spec. */
+
+ /**
+ * Endpoint property identifying the id for this endpoint. This service
+ * property must always be set.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>.
+ */
+ public final static String ENDPOINT_ID = "endpoint.id";
+
+ /**
+ * Endpoint property identifying the service id of the exported service. Can
+ * be absent or 0 if the corresponding endpoint is not for an OSGi service.
+ *
+ * <p>
+ * The value of this property must be of type <code>Long</code>.
+ */
+ public final static String ENDPOINT_SERVICE_ID = "endpoint.service.id";
+
+ /**
+ * Endpoint property identifying the universally unique id of the exporting
+ * framework. Can be absent if the corresponding endpoint is not for an OSGi
+ * service.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>.
+ */
+ public final static String ENDPOINT_FRAMEWORK_UUID = "endpoint.framework.uuid";
+
+ /**
+ * Prefix for an endpoint property identifying the interface Java package
+ * version for an interface. For example, the property
+ * endpoint.package.version.com.acme=1.3 describes the version of the
+ * package for the com.acme.Foo interface. This endpoint property for an
+ * interface package does not have to be set. If not set, the value must be
+ * assumed to be 0.
+ *
+ * <p>
+ * Since endpoint properties are stored in a case insensitive map, case
+ * variants of a package name are folded together.
+ *
+ * <p>
+ * The value of this property must be of type <code>String</code>.
+ */
+ public final static String ENDPOINT_PACKAGE_VERSION_ = "endpoint.package.version.";
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java
new file mode 100644
index 0000000000..98f56a07ae
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A Remote Service Admin manages the import and export of services.
+ *
+ * A Distribution Provider can expose a control interface. This interface allows
+ * the a remote manager to control the export and import of services.
+ *
+ * The API allows a remote manager to export a service, to import a service, and
+ * find out about the current imports and exports.
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+public interface RemoteServiceAdmin {
+
+ /**
+ * Export a service to a given Endpoint. The Remote Service Admin must
+ * create an Endpoint from the given description that can be used by other
+ * Distrbution Providers to connect to this Remote Service Admin and use the
+ * exported service. This method can return null if the service could not be
+ * exported because the Endpoint could not be implemented by this Remote
+ * Service Admin.
+ *
+ * The properties on a Service Reference are case insensitive while the
+ * properties on a <code>properties</code> are case sensitive. A value in
+ * the <code>properties</code> must therefore override any case variant in
+ * the properties of the Service Reference.
+ *
+ * <p>
+ * If the caller does not have the appropriate
+ * <code>EndpointPermission[endpoint,EXPORT]</code> for an Endpoint, and the
+ * Java Runtime Environment supports permissions, then the
+ * {@link ExportRegistration#getException() getException} method on the
+ * corresponding returned {@link ExportRegistration} will return a
+ * <code>SecurityException</code>.
+ *
+ * @param reference The Service Reference to export.
+ * @param properties The properties to create a local Endpoint that can be
+ * implemented by this Remote Service Admin. If this is null, the
+ * Endpoint will be determined by the properties on the service. The
+ * properties are the same as given for an exported service. They are
+ * overlaid over any properties the service defines (case
+ * insensitive). This parameter can be <code>null</code>, this should
+ * be treated as an empty map.
+ *
+ * @return An Export Registration that combines the Endpoint Description and
+ * the Service Reference. Is never <code>null</code>.
+ * @throws IllegalArgumentException If any of the properties has a value
+ * that is not syntactically correct or if the service properties
+ * and the overlaid properties do not contain a
+ * {@link RemoteConstants#SERVICE_EXPORTED_INTERFACES} entry.
+ * @throws UnsupportedOperationException If any of the intents expressed
+ * through the properties is not supported by the distribution
+ * provider.
+ */
+ Collection<ExportRegistration> exportService(ServiceReference reference,
+ Map<String, Object> properties);
+
+ /**
+ * Import a service from an Endpoint. The Remote Service Admin must use the
+ * given Endpoint to create a proxy. This method can return null if the
+ * service could not be imported.
+ *
+ * @param endpoint The Endpoint Description to be used for import.
+ * @return An Import Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code> if the Endpoint could
+ * not be imported.
+ * @throws SecurityException If the caller does not have the appropriate
+ * <code>EndpointPermission[endpoint,IMPORT]</code> for the
+ * Endpoint, and the Java Runtime Environment supports permissions.
+ */
+ ImportRegistration importService(EndpointDescription endpoint);
+
+ /**
+ * Return the currently active Export References.
+ *
+ * <p>
+ * If the caller does not have the appropriate
+ * <code>EndpointPermission[endpoint,READ]</code> for an Endpoint, and the
+ * Java Runtime Environment supports permissions, then returned collection
+ * will not contain a reference to the exported Endpoint.
+ *
+ * @return A <code>Collection</code> of {@link ExportReference}s that are
+ * currently active.
+ */
+ Collection<ExportReference> getExportedServices();
+
+ /**
+ * Return the currently active Import References.
+ *
+ * <p>
+ * If the caller does not have the appropriate
+ * <code>EndpointPermission[endpoint,READ]</code> for an Endpoint, and the
+ * Java Runtime Environment supports permissions, then returned collection
+ * will not contain a reference to the imported Endpoint.
+ *
+ * @return A <code>Collection</code> of {@link ImportReference}s that are
+ * currently active.
+ */
+ Collection<ImportReference> getImportedEndpoints();
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java
new file mode 100644
index 0000000000..8f4ac717c8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Provides the event information for a Remote Admin event.
+ *
+ * @Immutable
+ * @version $Revision$
+ */
+public class RemoteServiceAdminEvent {
+ /**
+ * Add an import registration. The Remote Services Admin will call this
+ * method when it imports a service. When this service is registered, the
+ * Remote Service Admin must notify the listener of all existing Import
+ * Registrations.
+ *
+ */
+ public static final int IMPORT_REGISTRATION = 1;
+
+ /**
+ * Add an export registration. The Remote Services Admin will call this
+ * method when it exports a service. When this service is registered, the
+ * Remote Service Admin must notify the listener of all existing Export
+ * Registrations.
+ */
+ public static final int EXPORT_REGISTRATION = 2;
+
+ /**
+ * Remove an export registration. The Remote Services Admin will call this
+ * method when it removes the export of a service.
+ *
+ */
+ public static final int EXPORT_UNREGISTRATION = 3;
+
+ /**
+ * Remove an import registration. The Remote Services Admin will call this
+ * method when it removes the import of a service.
+ *
+ */
+ public static final int IMPORT_UNREGISTRATION = 4;
+
+ /**
+ * A fatal importing error occurred. The Import Registration has been
+ * closed.
+ */
+ public static final int IMPORT_ERROR = 5;
+
+ /**
+ * A fatal exporting error occurred. The Export Registration has been
+ * closed.
+ */
+ public static final int EXPORT_ERROR = 6;
+
+ /**
+ * A problematic situation occurred, the export is still active.
+ */
+ public static final int EXPORT_WARNING = 7;
+ /**
+ * A problematic situation occurred, the import is still active.
+ */
+ public static final int IMPORT_WARNING = 8;
+
+ private final ImportReference importReference;
+ private final ExportReference exportReference;
+ private final Throwable exception;
+ private final int type;
+ private final Bundle source;
+
+ /**
+ * Private constructor.
+ *
+ * @param type The event type
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param importReference The importReference, can be <code>null</code>.
+ * @param exportReference The exportReference, can be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>
+ */
+ private RemoteServiceAdminEvent(int type, Bundle source,
+ ImportReference importReference, ExportReference exportReference,
+ Throwable exception) {
+ if (source == null) {
+ throw new NullPointerException("source must not be null");
+ }
+ this.type = type;
+ this.source = source;
+ this.importReference = importReference;
+ this.exportReference = exportReference;
+ this.exception = exception;
+ }
+
+ /**
+ * Create a Remote Service Admin Event for an export notification.
+ *
+ * @param type The event type.
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param exportReference The exportReference, can not be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>.
+ */
+ public RemoteServiceAdminEvent(int type, Bundle source,
+ ExportReference exportReference, Throwable exception) {
+ this(type, source, null, exportReference, exception);
+ }
+
+ /**
+ * Create a Remote Service Admin Event for an import notification.
+ *
+ * @param type The event type.
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param importReference The importReference, can not be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>.
+ */
+ public RemoteServiceAdminEvent(int type, Bundle source,
+ ImportReference importReference, Throwable exception) {
+ this(type, source, importReference, null, exception);
+ }
+
+ /**
+ * Return the Import Reference for this event.
+ *
+ * @return The Import Reference or <code>null</code>.
+ */
+ public ImportReference getImportReference() {
+ return importReference;
+ }
+
+ /**
+ * Return the Export Reference for this event.
+ *
+ * @return The Export Reference or <code>null</code>.
+ */
+ public ExportReference getExportReference() {
+ return exportReference;
+ }
+
+ /**
+ * Return the exception for this event.
+ *
+ * @return The exception or <code>null</code>.
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * Return the type of this event.
+ *
+ * @return The type of this event.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Return the bundle source of this event.
+ *
+ * @return The bundle source of this event.
+ */
+ public Bundle getSource() {
+ return source;
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java
new file mode 100644
index 0000000000..2941f8b117
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/java/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.
+ *
+ * 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.osgi.service.remoteserviceadmin;
+
+/**
+ * A {@link RemoteServiceAdminEvent} listener is notified synchronously of any
+ * export or import registrations and unregistrations.
+ *
+ * <p>
+ * If the Java Runtime Environment supports permissions, then filtering is done.
+ * <code>RemoteServiceAdminEvent</code> objects are only delivered to the
+ * listener if the bundle which defines the listener object's class has the
+ * appropriate <code>EndpointPermission[endpoint,READ]</code> for the endpoint
+ * referenced by the event.
+ *
+ *
+ * @see RemoteServiceAdminEvent
+ * @ThreadSafe
+ * @version $Revision$
+ */
+
+public interface RemoteServiceAdminListener {
+ /**
+ * Receive notification of any export or import registrations and
+ * unregistrations as well as errors and warnings.
+ *
+ * @param event The {@link RemoteServiceAdminEvent} object.
+ */
+ void remoteAdminEvent(RemoteServiceAdminEvent event);
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.ValidationSchema b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.ValidationSchema
new file mode 100644
index 0000000000..1991eba409
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.ValidationSchema
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+org/osgi/service/remoteserviceadmin/osgi-remoteserviceadmin.xsd
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory
new file mode 100644
index 0000000000..e44fce66a5
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# org.apache.tuscany.sca.node.osgi.impl.OSGiNodeFactoryImpl;ranking=100
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/org/osgi/service/remoteserviceadmin/osgi-remoteserviceadmin.xsd b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/org/osgi/service/remoteserviceadmin/osgi-remoteserviceadmin.xsd
new file mode 100644
index 0000000000..dc88df965c
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/main/resources/org/osgi/service/remoteserviceadmin/osgi-remoteserviceadmin.xsd
@@ -0,0 +1,92 @@
+<!--
+ *
+ * Copyright (c) OSGi Alliance (2008, 2010). All Rights Reserved.
+ *
+ * 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.
+ *
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:rsa="http://www.osgi.org/xmlns/rsa/v1.0.0"
+ targetNamespace="http://www.osgi.org/xmlns/rsa/v1.0.0" elementFormDefault="qualified" version="1.0.0">
+ <element name="endpoint-descriptions" type="rsa:Tendpoint-descriptions" />
+ <complexType name="Tendpoint-descriptions">
+ <sequence>
+ <element name="endpoint-description" type="rsa:Tendpoint-description" minOccurs="1" maxOccurs="unbounded" />
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </sequence>
+ <anyAttribute />
+ </complexType>
+ <complexType name="Tendpoint-description">
+ <sequence>
+ <element name="property" type="rsa:Tproperty" minOccurs="1" maxOccurs="unbounded" />
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </sequence>
+ <anyAttribute />
+ </complexType>
+ <complexType name="Tproperty" mixed="true">
+ <sequence>
+ <choice minOccurs="0" maxOccurs="1">
+ <element name="array" type="rsa:Tmulti-value" />
+ <element name="list" type="rsa:Tmulti-value" />
+ <element name="set" type="rsa:Tmulti-value" />
+ <element name="xml" type="rsa:Txml" />
+ </choice>
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="value" type="string" use="optional" />
+ <attribute name="value-type" type="rsa:Tvalue-types" default="String" use="optional" />
+ <anyAttribute />
+ </complexType>
+ <complexType name="Tmulti-value">
+ <sequence>
+ <element name="value" minOccurs="0" maxOccurs="unbounded" type="rsa:Tvalue" />
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </sequence>
+ <anyAttribute />
+ </complexType>
+ <complexType name="Tvalue" mixed="true">
+ <sequence>
+ <element name="xml" minOccurs="0" maxOccurs="1" type="rsa:Txml" />
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </sequence>
+ <anyAttribute />
+ </complexType>
+ <simpleType name="Tvalue-types">
+ <restriction base="string">
+ <enumeration value="String" />
+ <enumeration value="long" />
+ <enumeration value="Long" />
+ <enumeration value="double" />
+ <enumeration value="Double" />
+ <enumeration value="float" />
+ <enumeration value="Float" />
+ <enumeration value="int" />
+ <enumeration value="Integer" />
+ <enumeration value="byte" />
+ <enumeration value="Byte" />
+ <enumeration value="char" />
+ <enumeration value="Character" />
+ <enumeration value="boolean" />
+ <enumeration value="Boolean" />
+ <enumeration value="short" />
+ <enumeration value="Short" />
+ </restriction>
+ </simpleType>
+ <complexType name="Txml">
+ <sequence>
+ <any namespace="##other" minOccurs="1" maxOccurs="1" processContents="lax" />
+ </sequence>
+ <anyAttribute />
+ </complexType>
+ <attribute name="must-understand" type="boolean" default="false" />
+</schema> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/CalculatorService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/CalculatorService.java
new file mode 100644
index 0000000000..cc562b7c2f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/CalculatorService.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * The Calculator service interface.
+ */
+@Remotable
+public interface CalculatorService {
+
+ double add(double n1, double n2);
+
+ double subtract(double n1, double n2);
+
+ double multiply(double n1, double n2);
+
+ double divide(double n1, double n2);
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorActivator.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorActivator.java
new file mode 100644
index 0000000000..cf1b78931a
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorActivator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package calculator.dosgi.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.logging.Logger;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import calculator.dosgi.CalculatorService;
+import calculator.dosgi.operations.AddService;
+
+/**
+ *
+ */
+public class CalculatorActivator implements BundleActivator {
+ private Logger logger = Logger.getLogger(CalculatorActivator.class.getName());
+
+ private Bundle getBundle(BundleContext bundleContext, Class<?> cls) {
+ PackageAdmin packageAdmin = null;
+ // PackageAdmin is used to resolve bundles
+ ServiceReference ref = bundleContext.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (ref != null) {
+ packageAdmin = (PackageAdmin)bundleContext.getService(ref);
+ Bundle bundle = packageAdmin.getBundle(cls);
+ if (bundle != null) {
+ logger.info(cls.getName() + " is loaded by bundle: " + bundle.getSymbolicName());
+ }
+ bundleContext.ungetService(ref);
+ return bundle;
+ }
+ return null;
+ }
+
+ public void start(BundleContext context) throws Exception {
+ logger.info("Starting " + context.getBundle());
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("sca.service", "CalculatorComponent#service-name(Calculator)");
+ props.put("calculator", "Calculator");
+ logger.info("Registering " + CalculatorService.class.getName());
+ CalculatorService calculator = new CalculatorServiceImpl(context);
+ context.registerService(CalculatorService.class.getName(), calculator, props);
+
+ getBundle(context, AddService.class);
+
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ logger.info("Stopping " + context.getBundle());
+ // Registered services will be automatically unregistered
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java
new file mode 100644
index 0000000000..5f9db16ca9
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.impl;
+
+import org.osgi.service.component.ComponentContext;
+
+import calculator.dosgi.CalculatorService;
+import calculator.dosgi.operations.AddService;
+import calculator.dosgi.operations.DivideService;
+import calculator.dosgi.operations.MultiplyService;
+import calculator.dosgi.operations.SubtractService;
+
+/**
+ * An implementation of the Calculator service.
+ */
+public class CalculatorServiceDSImpl implements CalculatorService {
+ private AddService addService;
+ private SubtractService subtractService;
+ private MultiplyService multiplyService;
+ private DivideService divideService;
+
+ public CalculatorServiceDSImpl() {
+ super();
+ System.out.println("CalculatorServiceDSImpl()");
+ }
+
+ protected void activate(ComponentContext context) {
+ System.out.println("Activating " + context);
+ }
+
+ protected void deactivate(ComponentContext context) {
+ System.out.println("Deactivating " + context);
+ }
+
+ /*
+ * The following setters can be used for DS injection
+ */
+ public void setAddService(AddService addService) {
+ System.out.println("setAddService()");
+ this.addService = addService;
+ }
+
+ public void setSubtractService(SubtractService subtractService) {
+ this.subtractService = subtractService;
+ }
+
+ public void setDivideService(DivideService divideService) {
+ this.divideService = divideService;
+ }
+
+ public void setMultiplyService(MultiplyService multiplyService) {
+ this.multiplyService = multiplyService;
+ }
+
+ /*
+ * The following setters can be used for DS injection
+ */
+ public void unsetAddService(AddService addService) {
+ System.out.println("unsetAddService()");
+ this.addService = null;
+ }
+
+ public void unsetSubtractService(SubtractService subtractService) {
+ this.subtractService = null;
+ }
+
+ public void unsetDivideService(DivideService divideService) {
+ this.divideService = null;
+ }
+
+ public void unsetMultiplyService(MultiplyService multiplyService) {
+ this.multiplyService = null;
+ }
+ private <T> T getService(Class<T> cls) {
+ for (Object s : new Object[] {addService, subtractService, multiplyService, divideService}) {
+ if (cls.isInstance(s)) {
+ return cls.cast(s);
+ }
+ }
+ throw new IllegalStateException(cls.getSimpleName() + " is not available");
+ }
+
+ public double add(double n1, double n2) {
+ return getService(AddService.class).add(n1, n2);
+ }
+
+ public double subtract(double n1, double n2) {
+ return getService(SubtractService.class).subtract(n1, n2);
+ }
+
+ public double multiply(double n1, double n2) {
+ return getService(MultiplyService.class).multiply(n1, n2);
+ }
+
+ public double divide(double n1, double n2) {
+ return getService(DivideService.class).divide(n1, n2);
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceImpl.java
new file mode 100644
index 0000000000..0960938086
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/impl/CalculatorServiceImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.impl;
+
+import static org.osgi.framework.Constants.OBJECTCLASS;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.util.tracker.ServiceTracker;
+
+import calculator.dosgi.CalculatorService;
+import calculator.dosgi.operations.AddService;
+import calculator.dosgi.operations.DivideService;
+import calculator.dosgi.operations.MultiplyService;
+import calculator.dosgi.operations.SubtractService;
+
+/**
+ * An implementation of the Calculator service.
+ */
+public class CalculatorServiceImpl implements CalculatorService {
+ private ServiceTracker remoteServices;
+ private ServiceTracker localServices;
+
+ public CalculatorServiceImpl() {
+ super();
+ }
+
+ public CalculatorServiceImpl(BundleContext context) {
+ super();
+ Filter remoteFilter = null, localFilter = null;
+ try {
+ remoteFilter =
+ context.createFilter("(&(" + OBJECTCLASS + "=calculator.dosgi.operations.*) (sca.reference=*))");
+ localFilter =
+ context.createFilter("(&(" + OBJECTCLASS + "=calculator.dosgi.operations.*) (!(sca.reference=*)))");
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ this.remoteServices = new ServiceTracker(context, remoteFilter, null);
+ remoteServices.open();
+ this.localServices = new ServiceTracker(context, localFilter, null);
+ localServices.open();
+ }
+
+ private <T> T getService(Class<T> cls) {
+ Object[] remoteObjects = remoteServices.getServices();
+ if (remoteObjects != null) {
+ for (Object s : remoteObjects) {
+ if (cls.isInstance(s)) {
+ System.out.println("Remote service: " + s);
+ return cls.cast(s);
+ }
+ }
+ }
+ for (Object s : localServices.getServices()) {
+ if (cls.isInstance(s)) {
+ System.out.println("Local service: " + s);
+ return cls.cast(s);
+ }
+ }
+ throw new IllegalStateException(cls.getSimpleName() + " is not available");
+ }
+
+ public double add(double n1, double n2) {
+ return getService(AddService.class).add(n1, n2);
+ }
+
+ public double subtract(double n1, double n2) {
+ return getService(SubtractService.class).subtract(n1, n2);
+ }
+
+ public double multiply(double n1, double n2) {
+ return getService(MultiplyService.class).multiply(n1, n2);
+ }
+
+ public double divide(double n1, double n2) {
+ return getService(DivideService.class).divide(n1, n2);
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/AddService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/AddService.java
new file mode 100644
index 0000000000..971500782f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/AddService.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * The interface for the add service
+ */
+@Remotable
+public interface AddService {
+
+ double add(double n1, double n2);
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/DivideService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/DivideService.java
new file mode 100644
index 0000000000..49b8a1c0bf
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/DivideService.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * The interface for the divide service
+ */
+@Remotable
+public interface DivideService {
+
+ double divide(double n1, double n2);
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/MultiplyService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/MultiplyService.java
new file mode 100644
index 0000000000..f4e59d12ea
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/MultiplyService.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * The interface for the multiply service
+ */
+@Remotable
+public interface MultiplyService {
+
+ double multiply(double n1, double n2);
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/SubtractService.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/SubtractService.java
new file mode 100644
index 0000000000..bfb9b820f7
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/SubtractService.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * The interface for the subtract service
+ */
+@Remotable
+public interface SubtractService {
+
+ double subtract(double n1, double n2);
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/AddServiceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/AddServiceImpl.java
new file mode 100644
index 0000000000..66b2977241
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/AddServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import calculator.dosgi.operations.AddService;
+
+/**
+ * An implementation of the Add service
+ */
+public class AddServiceImpl implements AddService {
+
+ public double add(double n1, double n2) {
+ Logger logger = Logger.getLogger("calculator");
+ logger.log(Level.INFO, "Adding " + n1 + " and " + n2);
+ return n1 + n2;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/DivideServiceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/DivideServiceImpl.java
new file mode 100644
index 0000000000..a3c21b2b96
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/DivideServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import calculator.dosgi.operations.DivideService;
+
+/**
+ * An implementation of the Divide service.
+ */
+public class DivideServiceImpl implements DivideService {
+
+ public double divide(double n1, double n2) {
+ Logger logger = Logger.getLogger("calculator");
+ logger.log(Level.INFO, "Dividing " + n1 + " with " + n2);
+ return n1 / n2;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java
new file mode 100644
index 0000000000..7922d2d392
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import calculator.dosgi.operations.MultiplyService;
+
+/**
+ * An implementation of the Multiply service.
+ */
+public class MultiplyServiceImpl implements MultiplyService {
+
+ public double multiply(double n1, double n2) {
+ Logger logger = Logger.getLogger("calculator");
+ logger.log(Level.INFO, "Multiplying " + n1 + " with " + n2);
+ return n1 * n2;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/OperationsActivator.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/OperationsActivator.java
new file mode 100644
index 0000000000..5348d19f34
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/OperationsActivator.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package calculator.dosgi.operations.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.logging.Logger;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import calculator.dosgi.operations.AddService;
+import calculator.dosgi.operations.DivideService;
+import calculator.dosgi.operations.MultiplyService;
+import calculator.dosgi.operations.SubtractService;
+
+/**
+ *
+ */
+public class OperationsActivator implements BundleActivator {
+ private Logger logger = Logger.getLogger(OperationsActivator.class.getName());
+
+ public void start(BundleContext context) throws Exception {
+ logger.info("Starting " + context.getBundle());
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+
+ logger.info("Registering " + AddService.class.getName());
+ props.put("sca.service", "AddComponent#service-name(Add)");
+ context.registerService(AddService.class.getName(), new AddServiceImpl(), props);
+
+ logger.info("Registering " + SubtractService.class.getName());
+ props.put("sca.service", "SubtractComponent#service-name(Subtract)");
+ context.registerService(SubtractService.class.getName(), new SubtractServiceImpl(), props);
+
+ logger.info("Registering " + MultiplyService.class.getName());
+ props.put("sca.service", "MultiplyComponent#service-name(Multiply)");
+ context.registerService(MultiplyService.class.getName(), new MultiplyServiceImpl(), props);
+
+ logger.info("Registering " + DivideService.class.getName());
+ props.put("sca.service", "DivideComponent#service-name(Divide)");
+ context.registerService(DivideService.class.getName(), new DivideServiceImpl(), props);
+
+ getBundle(context, AddService.class);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ logger.info("Stopping " + context.getBundle());
+ // Registered services will be automatically unregistered
+ }
+
+ private Bundle getBundle(BundleContext bundleContext, Class<?> cls) {
+ PackageAdmin packageAdmin = null;
+ // PackageAdmin is used to resolve bundles
+ ServiceReference ref = bundleContext.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (ref != null) {
+ packageAdmin = (PackageAdmin)bundleContext.getService(ref);
+ Bundle bundle = packageAdmin.getBundle(cls);
+ if (bundle != null) {
+ logger.info(cls.getName() + " is loaded by bundle: " + bundle.getSymbolicName());
+ }
+ bundleContext.ungetService(ref);
+ return bundle;
+ }
+ return null;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java
new file mode 100644
index 0000000000..4bbe83b14f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package calculator.dosgi.operations.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import calculator.dosgi.operations.SubtractService;
+
+/**
+ * An implementation of the subtract service.
+ */
+public class SubtractServiceImpl implements SubtractService {
+
+ public double subtract(double n1, double n2) {
+ Logger logger = Logger.getLogger("calculator");
+ logger.log(Level.INFO, "Subtracting " + n1 + " from " + n2);
+ return n1 - n2;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java
new file mode 100644
index 0000000000..268f16bc9f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package calculator.dosgi.test;
+
+import static calculator.dosgi.test.OSGiTestBundles.bundleStatus;
+import static calculator.dosgi.test.OSGiTestBundles.generateCalculatorBundle;
+import static calculator.dosgi.test.OSGiTestBundles.generateCalculatorSCABundle;
+import static calculator.dosgi.test.OSGiTestBundles.generateOperationsBundle;
+
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.tuscany.sca.node.equinox.launcher.EquinoxHost;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+import calculator.dosgi.CalculatorService;
+
+/**
+ *
+ */
+public class CalculatorOSGiNodeTestCase {
+ private static EquinoxHost host;
+ private static BundleContext context;
+ private static Bundle calculatorBundle;
+ private static Bundle operationsBundle;
+ private static Bundle scaBundle;
+ private static Boolean client;
+
+ public static URL getCodeLocation(final Class<?> anchorClass) {
+ return AccessController.doPrivileged(new PrivilegedAction<URL>() {
+ public URL run() {
+ return anchorClass.getProtectionDomain().getCodeSource().getLocation();
+ }
+ });
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ try {
+ host = new EquinoxHost();
+ context = host.start();
+ String prop = System.getProperty("client");
+ if (prop != null) {
+ client = Boolean.valueOf(prop);
+ }
+
+ if (client == null || client.booleanValue()) {
+ System.out.println("Generating calculator.dosgi bundle...");
+ calculatorBundle = context.installBundle("reference:" + generateCalculatorBundle().toString());
+ }
+
+ if (client == null || !client.booleanValue()) {
+ System.out.println("Generating calculator.dosgi.operations bundle...");
+ operationsBundle = context.installBundle("reference:" + generateOperationsBundle().toString());
+ }
+
+ scaBundle = context.installBundle("reference:" + generateCalculatorSCABundle().toString());
+
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals("org.eclipse.equinox.ds") || b.getSymbolicName()
+ .startsWith("org.apache.tuscany.sca.")) {
+ try {
+ if (b.getHeaders().get(Constants.FRAGMENT_HOST) == null) {
+ b.start();
+ }
+ } catch (Exception e) {
+ System.out.println(bundleStatus(b, false));
+ e.printStackTrace();
+ }
+ System.out.println(bundleStatus(b, false));
+ }
+ }
+
+ if (scaBundle != null) {
+ scaBundle.start();
+ }
+
+ if (calculatorBundle != null) {
+ calculatorBundle.start();
+ System.out.println(bundleStatus(calculatorBundle, false));
+ }
+
+ if (operationsBundle != null) {
+ operationsBundle.start();
+ System.out.println(bundleStatus(operationsBundle, false));
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ @Test
+ public void testOSGi() {
+ if (client == null || client.booleanValue()) {
+ ServiceReference ref =
+ calculatorBundle.getBundleContext().getServiceReference(CalculatorService.class.getName());
+ Assert.assertNotNull(ref);
+ Object service = context.getService(ref);
+ Assert.assertNotNull(service);
+ CalculatorService calculator = OSGiTestBundles.cast(service, CalculatorService.class);
+ System.out.println("2.0 + 1.0 = " + calculator.add(2.0, 1.0));
+ System.out.println("2.0 - 1.0 = " + calculator.subtract(2.0, 1.0));
+ System.out.println("2.0 * 1.0 = " + calculator.multiply(2.0, 1.0));
+ System.out.println("2.0 / 1.0 = " + calculator.divide(2.0, 1.0));
+ }
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ if (host != null) {
+ if (scaBundle != null) {
+ scaBundle.stop();
+ }
+ if (client != null && !client.booleanValue()) {
+ System.out.println("Press Enter to stop the node...");
+ System.in.read();
+ }
+ host.stop();
+ context = null;
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiTestCase.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiTestCase.java
new file mode 100644
index 0000000000..2af5552279
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/CalculatorOSGiTestCase.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package calculator.dosgi.test;
+
+import static calculator.dosgi.test.OSGiTestBundles.bundleStatus;
+import static calculator.dosgi.test.OSGiTestBundles.generateCalculatorBundle;
+import static calculator.dosgi.test.OSGiTestBundles.generateOperationsBundle;
+
+import org.apache.tuscany.sca.node.equinox.launcher.EquinoxHost;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import calculator.dosgi.CalculatorService;
+
+/**
+ *
+ */
+public class CalculatorOSGiTestCase {
+ private static EquinoxHost host;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ try {
+ host = new EquinoxHost();
+ BundleContext context = host.start();
+ Bundle calculatorBundle = context.installBundle("reference:" + generateCalculatorBundle().toString());
+ Bundle operationsBundle = context.installBundle("reference:" + generateOperationsBundle().toString());
+
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals("org.eclipse.equinox.ds")) {
+ System.out.println(bundleStatus(b, false));
+ b.start();
+ System.out.println(bundleStatus(b, false));
+ }
+ }
+
+ calculatorBundle.start();
+ System.out.println(bundleStatus(calculatorBundle, false));
+ operationsBundle.start();
+ System.out.println(bundleStatus(operationsBundle, false));
+
+ // Sleep for 1 sec so that the DS is available
+ Thread.sleep(1000);
+ // Use the DS version
+ String filter = "(component.name=CalculatorComponent)";
+ System.out.println(filter);
+ ServiceReference ref =
+ calculatorBundle.getBundleContext().getServiceReferences(CalculatorService.class.getName(), filter)[0];
+ CalculatorService calculator = OSGiTestBundles.cast(context.getService(ref), CalculatorService.class);
+ System.out.println("2.0 + 1.0 = " + calculator.add(2.0, 1.0));
+ System.out.println("2.0 - 1.0 = " + calculator.subtract(2.0, 1.0));
+ System.out.println("2.0 * 1.0 = " + calculator.multiply(2.0, 1.0));
+ System.out.println("2.0 / 1.0 = " + calculator.divide(2.0, 1.0));
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ @Test
+ public void testOSGi() {
+
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ if (host != null) {
+ host.stop();
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/OSGiTestBundles.java b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/OSGiTestBundles.java
new file mode 100644
index 0000000000..bdb484e6bb
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/java/calculator/dosgi/test/OSGiTestBundles.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package calculator.dosgi.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Constants;
+
+import calculator.dosgi.CalculatorService;
+import calculator.dosgi.impl.CalculatorActivator;
+import calculator.dosgi.impl.CalculatorServiceDSImpl;
+import calculator.dosgi.impl.CalculatorServiceImpl;
+import calculator.dosgi.operations.AddService;
+import calculator.dosgi.operations.DivideService;
+import calculator.dosgi.operations.MultiplyService;
+import calculator.dosgi.operations.SubtractService;
+import calculator.dosgi.operations.impl.AddServiceImpl;
+import calculator.dosgi.operations.impl.DivideServiceImpl;
+import calculator.dosgi.operations.impl.MultiplyServiceImpl;
+import calculator.dosgi.operations.impl.OperationsActivator;
+import calculator.dosgi.operations.impl.SubtractServiceImpl;
+
+/**
+ *
+ * Utility class to create OSGi bundles
+ *
+ * @version $Rev$ $Date$
+ */
+public class OSGiTestBundles {
+ private static class InvocationHandlerImpl implements InvocationHandler {
+ private Object instance;
+
+ public InvocationHandlerImpl(Object instance) {
+ super();
+ this.instance = instance;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Method m = instance.getClass().getMethod(method.getName(), method.getParameterTypes());
+ return m.invoke(instance, args);
+ }
+
+ }
+
+ private static String getPackageName(Class<?> cls) {
+ String name = cls.getName();
+ int index = name.lastIndexOf('.');
+ return index == -1 ? "" : name.substring(0, index);
+ }
+
+ public static URL createBundle(String jarName, String mfFile, String[][] resources, Class<?>... classes)
+ throws IOException {
+ InputStream is = OSGiTestBundles.class.getClassLoader().getResourceAsStream(mfFile);
+ Manifest manifest = new Manifest(is);
+ is.close();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ JarOutputStream jarOut = new JarOutputStream(out, manifest);
+
+ for (Class<?> cls : classes) {
+ addClass(jarOut, cls);
+ }
+
+ if (resources != null) {
+ for (String resource[] : resources) {
+ if (resource.length >= 1) {
+ String r1 = resource[0];
+ String r2 = resource.length > 1 ? resource[1] : r1;
+ addResource(jarOut, OSGiTestBundles.class.getClassLoader(), r1, r2);
+ }
+ }
+ }
+
+ jarOut.close();
+ out.close();
+
+ File jar = new File(jarName);
+ FileOutputStream fileOut = new FileOutputStream(jar);
+ fileOut.write(out.toByteArray());
+ fileOut.close();
+
+ return jar.toURI().toURL();
+ }
+
+ public static URL createBundle(String jarName,
+ String bundleName,
+ String exports,
+ String imports,
+ String[] resources,
+ Class<?>... classes) throws IOException {
+
+ Class<?> activator = null;
+ Set<String> packages = new HashSet<String>();
+ StringBuffer exportPackages = new StringBuffer();
+ if (exports != null) {
+ exportPackages.append(exports);
+ }
+ for (Class<?> cls : classes) {
+ if (BundleActivator.class.isAssignableFrom(cls)) {
+ activator = cls;
+ }
+ if (exports == null && cls.isInterface()) {
+ String pkg = getPackageName(cls);
+ if (packages.add(pkg)) {
+ exportPackages.append(pkg).append(",");
+ }
+ }
+ }
+ int len = exportPackages.length();
+ if (len > 0 && exportPackages.charAt(len - 1) == ',') {
+ exportPackages.deleteCharAt(len - 1);
+ }
+
+ Manifest manifest = new Manifest();
+ // This attribute Manifest-Version is required so that the MF will be added to the jar
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, bundleName);
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, "1.0.0");
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_NAME, bundleName);
+ manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE, exportPackages.toString());
+ StringBuffer importPackages = new StringBuffer();
+ if (imports != null) {
+ importPackages.append(imports).append(",org.osgi.framework");
+ } else {
+ importPackages.append("org.osgi.framework");
+ }
+ manifest.getMainAttributes().putValue(Constants.IMPORT_PACKAGE, importPackages.toString());
+
+ if (activator != null) {
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_ACTIVATOR, activator.getName());
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ JarOutputStream jarOut = new JarOutputStream(out, manifest);
+
+ for (Class<?> cls : classes) {
+ addClass(jarOut, cls);
+ }
+
+ if (resources != null) {
+ for (String resource : resources) {
+ addResource(jarOut, OSGiTestBundles.class.getClassLoader(), resource, null);
+ }
+ }
+
+ jarOut.close();
+ out.close();
+
+ File jar = new File(jarName);
+ FileOutputStream fileOut = new FileOutputStream(jar);
+ fileOut.write(out.toByteArray());
+ fileOut.close();
+
+ return jar.toURI().toURL();
+ }
+
+ private static void addClass(JarOutputStream jarOut, Class<?> javaClass) throws IOException, FileNotFoundException {
+ String classFile = javaClass.getName().replace('.', '/') + ".class";
+ URL url = javaClass.getResource(javaClass.getSimpleName() + ".class");
+ addEntry(jarOut, url, classFile);
+ }
+
+ private static void addResource(JarOutputStream jarOut, ClassLoader cl, String resourceName, String entryName)
+ throws IOException, FileNotFoundException {
+ URL url = cl.getResource(resourceName);
+ if (entryName == null) {
+ entryName = resourceName;
+ }
+ addEntry(jarOut, url, entryName);
+ }
+
+ private static void addEntry(JarOutputStream jarOut, URL url, String resourceName) throws IOException,
+ FileNotFoundException {
+ String path = url.getPath();
+
+ ZipEntry ze = new ZipEntry(resourceName);
+
+ jarOut.putNextEntry(ze);
+ FileInputStream file = new FileInputStream(path);
+ byte[] fileContents = new byte[file.available()];
+ file.read(fileContents);
+ jarOut.write(fileContents);
+ jarOut.closeEntry();
+ file.close();
+ }
+
+ static URL generateCalculatorBundle() throws IOException {
+ return createBundle("target/test-classes/calculator-bundle.jar",
+ "calculator/dosgi/META-INF/MANIFEST.MF",
+ new String[][] {
+ {
+ "calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml",
+ "OSGI-INF/remote-service/calculator-service-descriptions.xml"},
+ {"calculator/dosgi/OSGI-INF/calculator-component.xml",
+ "OSGI-INF/calculator-component.xml"},
+ /*
+ {"calculator/dosgi/bundle.componentType",
+ "OSGI-INF/sca/bundle.componentType"},
+ {"calculator/dosgi/calculator.composite", "OSGI-INF/sca/bundle.composite"},
+ {"calculator/dosgi/META-INF/sca-contribution.xml",
+ "META-INF/sca-contribution.xml"}
+ */
+ },
+ CalculatorService.class,
+ // Package the interfaces so that the operations bundle can be remote
+ AddService.class,
+ SubtractService.class,
+ MultiplyService.class,
+ DivideService.class,
+ CalculatorServiceImpl.class,
+ CalculatorServiceDSImpl.class,
+ CalculatorActivator.class);
+ }
+
+ /**
+ * Create the OSGi bundle for calculator SCA
+ * @return
+ * @throws IOException
+ */
+ static URL generateCalculatorSCABundle() throws IOException {
+ return createBundle("target/test-classes/calculator-sca-bundle.jar",
+ "calculator/dosgi/sca/META-INF/MANIFEST.MF",
+ new String[][] {
+ {"calculator/dosgi/sca/OSGI-INF/sca/calculator.composite",
+ "OSGI-INF/sca/bundle.composite"},
+ {"calculator/dosgi/sca/META-INF/sca-contribution.xml",
+ "META-INF/sca-contribution.xml"},
+ {"calculator/dosgi/bundle.componentType",
+ "OSGI-INF/sca/calculator.dosgi/bundle.componentType"},
+ {"calculator/dosgi/operations/bundle.componentType",
+ "OSGI-INF/sca/calculator.dosgi.operations/bundle.componentType"},
+ });
+ }
+
+ /**
+ * Create the OSGi bundle for calculator operations
+ * @return
+ * @throws IOException
+ */
+ static URL generateOperationsBundle() throws IOException {
+ return createBundle("target/test-classes/operations-bundle.jar",
+ "calculator/dosgi/operations/META-INF/MANIFEST.MF",
+ new String[][] {
+ {"calculator/dosgi/operations/OSGI-INF/add-component.xml",
+ "OSGI-INF/add-component.xml"},
+ {"calculator/dosgi/operations/OSGI-INF/subtract-component.xml",
+ "OSGI-INF/subtract-component.xml"},
+ {"calculator/dosgi/operations/OSGI-INF/multiply-component.xml",
+ "OSGI-INF/multiply-component.xml"},
+ {"calculator/dosgi/operations/OSGI-INF/divide-component.xml",
+ "OSGI-INF/divide-component.xml"},
+ /*
+ {"calculator/dosgi/operations/bundle.componentType",
+ "OSGI-INF/sca/bundle.componentType"},
+ {"calculator/dosgi/operations/operations.composite",
+ "OSGI-INF/sca/bundle.composite"},
+ {"calculator/dosgi/operations/META-INF/sca-contribution.xml",
+ "META-INF/sca-contribution.xml"}
+ */
+ },
+ OperationsActivator.class,
+ AddService.class,
+ AddServiceImpl.class,
+ SubtractService.class,
+ SubtractServiceImpl.class,
+ MultiplyService.class,
+ MultiplyServiceImpl.class,
+ DivideService.class,
+ DivideServiceImpl.class);
+ }
+
+ /**
+ * Returns a string representation of the given bundle.
+ *
+ * @param b
+ * @param verbose
+ * @return
+ */
+ public static String bundleStatus(Bundle bundle, boolean verbose) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(bundle.getBundleId()).append(" ").append(bundle.getSymbolicName());
+ int s = bundle.getState();
+ if ((s & Bundle.UNINSTALLED) != 0) {
+ sb.append(" UNINSTALLED");
+ }
+ if ((s & Bundle.INSTALLED) != 0) {
+ sb.append(" INSTALLED");
+ }
+ if ((s & Bundle.RESOLVED) != 0) {
+ sb.append(" RESOLVED");
+ }
+ if ((s & Bundle.STARTING) != 0) {
+ sb.append(" STARTING");
+ }
+ if ((s & Bundle.STOPPING) != 0) {
+ sb.append(" STOPPING");
+ }
+ if ((s & Bundle.ACTIVE) != 0) {
+ sb.append(" ACTIVE");
+ }
+
+ if (verbose) {
+ sb.append(" ").append(bundle.getLocation());
+ sb.append(" ").append(bundle.getHeaders());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * A utility to cast the object to the given interface. If the class for the object
+ * is loaded by a different classloader, a proxy will be created.
+ *
+ * @param <T>
+ * @param obj
+ * @param cls
+ * @return
+ */
+ public static <T> T cast(Object obj, Class<T> cls) {
+ if (cls.isInstance(obj)) {
+ return cls.cast(obj);
+ } else {
+ return cls.cast(Proxy.newProxyInstance(cls.getClassLoader(),
+ new Class<?>[] {cls},
+ new InvocationHandlerImpl(obj)));
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/MANIFEST.MF b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..4b7b7ec71b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Export-Package: calculator.dosgi;version="1.0.0",
+ calculator.dosgi.operations;version="1.0.0"
+Bundle-Version: 1.0.0
+Bundle-Name: calculator.dosgi
+Bundle-Activator: calculator.dosgi.impl.CalculatorActivator
+Bundle-ManifestVersion: 2
+Import-Package: org.oasisopen.sca.annotation;version="2.0.0",
+ org.osgi.framework,
+ org.osgi.service.component;resolution:=optional,
+ org.osgi.service.packageadmin,
+ org.osgi.util.tracker
+Bundle-SymbolicName: calculator.dosgi
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Service-Component: OSGI-INF/calculator-component.xml
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/sca-contribution.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/sca-contribution.xml
new file mode 100644
index 0000000000..b48c048000
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/META-INF/sca-contribution.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+
+<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:c="http://calculator.dosgi">
+ <deployable composite="c:CalculatorComposite" />
+</contribution>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/blueprint/calculator-module.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/blueprint/calculator-module.xml
new file mode 100644
index 0000000000..fd834e12ef
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/blueprint/calculator-module.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!-- A sample module-context.xml for OSGI RFC 124 (BluePrint Service) -->
+<components xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+ <component id="CalculatorComponent" class="calculator.dosgi.impl.CalculatorServiceImpl">
+ <property name="addService" ref="AddService" />
+ <property name="subtractService" ref="SubtractService" />
+ <property name="multiplyService" ref="MultiplyService" />
+ <property name="divideService" ref="DivideService" />
+ </component>
+
+ <!-- We can derive the SCA services for the implementation.osgi -->
+ <service id="CalculatorService" ref="CalculatorComponent" interface="calculator.dosgi.CalculatorService">
+ </service>
+
+ <!-- We can derive the SCA references for the implementation.osgi -->
+ <reference id="AddService" interface="calculator.dosgi.operations.AddService">
+ </reference>
+ <reference id="SubtractService" interface="calculator.dosgi.operations.SubtractService">
+ </reference>
+ <reference id="MultiplyService" interface="calculator.dosgi.operations.MultiplyService">
+ </reference>
+ <reference id="DivideService" interface="calculator.dosgi.operations.DivideService">
+ </reference>
+
+</components> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/calculator-component.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/calculator-component.xml
new file mode 100644
index 0000000000..5daaa59aae
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/calculator-component.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<scr:component name="CalculatorComponent"
+ xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+ <implementation class="calculator.dosgi.impl.CalculatorServiceDSImpl" />
+ <service>
+ <provide interface="calculator.dosgi.CalculatorService" />
+ </service>
+
+ <reference name="addService" interface="calculator.dosgi.operations.AddService" bind="setAddService" unbind="unsetAddService"
+ policy="dynamic" />
+ <reference name="subtractService" interface="calculator.dosgi.operations.SubtractService" bind="setSubtractService"
+ unbind="unsetSubtractService" policy="dynamic" />
+ <reference name="multiplyService" interface="calculator.dosgi.operations.MultiplyService" bind="setMultiplyService"
+ unbind="unsetMultiplyService" policy="dynamic" />
+ <reference name="divideService" interface="calculator.dosgi.operations.DivideService" bind="setDivideService"
+ unbind="unsetDivideService" policy="dynamic" />
+
+</scr:component>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml
new file mode 100644
index 0000000000..8306fa9834
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/OSGI-INF/remote-service/calculator-service-descriptions.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<!-- A consumer-side service description file for OSGi Remote Service Admin -->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912">
+ <!-- Describe a remote OSGi service -->
+ <endpoint-description>
+ <property name="objectClass" value="calculator.dosgi.operations.AddService" />
+ <property name="service.intents" value="sca:SOAP sca:HTTP" />
+ <property name="service.imported.configs" value="org.osgi.sca"/>
+ <property name="osgi.remote.configuration.type" value="org.osgi.sca" />
+ <property name="osgi.remote.configuration.sca.componentType" value="OSGI-INF/sca/bundle.componentType" />
+ <property name="osgi.remote.configuration.sca.reference" value="addService" />
+ </endpoint-description>
+ <endpoint-description>
+ <property name="objectClass" value="calculator.dosgi.operations.SubtractService" />
+ <property name="service.imported.configs" value="org.osgi.sca"/>
+ <property name="service.intents" value="sca:SOAP sca:HTTP" />
+ <property name="osgi.remote.configuration.type" value="org.osgi.sca" />
+ <property name="osgi.remote.configuration.sca.componentType" value="OSGI-INF/sca/bundle.componentType" />
+ <property name="osgi.remote.configuration.sca.reference" value="subtractService" />
+ </endpoint-description>
+ <endpoint-description>
+ <property name="objectClass" value="calculator.dosgi.operations.MultiplyService" />
+ <property name="service.intents" value="sca:SOAP sca:HTTP" />
+ <property name="service.imported.configs" value="org.osgi.sca"/>
+ <property name="osgi.remote.configuration.type" value="org.osgi.sca" />
+ <property name="osgi.remote.configuration.sca.componentType" value="OSGI-INF/sca/bundle.componentType" />
+ <property name="osgi.remote.configuration.sca.reference" value="multiplyService" />
+ </endpoint-description>
+ <endpoint-description>
+ <property name="objectClass" value="calculator.dosgi.operations.DivideService" />
+ <property name="service.imported.configs" value="org.osgi.sca"/>
+ <property name="service.intents" value="sca:SOAP sca:HTTP" />
+ <property name="osgi.remote.configuration.type" value="org.osgi.sca" />
+ <property name="osgi.remote.configuration.sca.componentType" value="OSGI-INF/sca/bundle.componentType" />
+ <property name="osgi.remote.configuration.sca.reference" value="divideService" />
+ </endpoint-description>
+</endpoint-descriptions> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/bundle.componentType b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/bundle.componentType
new file mode 100644
index 0000000000..401809c197
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/bundle.componentType
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1">
+ <!-- The service elment defines an SCA view of the OSGi service -->
+ <service name="Calculator">
+ <!-- The interface will be mapped into the OSGi service class -->
+ <interface.java interface="calculator.dosgi.CalculatorService"/>
+ <!-- The list of OSGi properties -->
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </service>
+
+ <!-- The reference elment defines an SCA proxy to a remote OSGi service -->
+ <reference name="addService">
+ <interface.java interface="calculator.dosgi.operations.AddService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </reference>
+ <reference name="subtractService">
+ <interface.java interface="calculator.dosgi.operations.SubtractService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </reference>
+ <reference name="multiplyService">
+ <interface.java interface="calculator.dosgi.operations.MultiplyService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </reference>
+ <reference name="divideService">
+ <interface.java interface="calculator.dosgi.operations.DivideService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </reference>
+</componentType>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/calculator.composite b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/calculator.composite
new file mode 100644
index 0000000000..830c6867ed
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/calculator.composite
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://calculator.dosgi"
+ name="CalculatorComposite">
+
+ <component name="CalculatorComponent">
+ <tuscany:implementation.osgi bundleSymbolicName="calculator.dosgi" bundleVersion="1.0.0" />
+ <reference name="addService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/AddService"/>
+ </reference>
+ <reference name="subtractService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService"/>
+ </reference>
+ <reference name="multiplyService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService"/>
+ </reference>
+ <reference name="divideService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/>
+ </reference>
+
+ <!--
+ <reference name="addService" target="OperationsComponent/AddService">
+ </reference>
+ <reference name="subtractService" target="OperationsComponent/SubtractService">
+ </reference>
+ <reference name="multiplyService" target="OperationsComponent/MultiplyService">
+ </reference>
+ <reference name="divideService" target="OperationsComponent/DivideService">
+ </reference>
+ -->
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/MANIFEST.MF b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..2345c785a7
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Export-Package: calculator.dosgi.operations;version="1.0.0"
+Bundle-Version: 1.0.0
+Bundle-Name: calculator.dosgi.operations
+Bundle-Activator: calculator.dosgi.operations.impl.OperationsActivator
+Bundle-ManifestVersion: 2
+Import-Package: calculator.dosgi.operations;version="1.0.0",
+ org.oasisopen.sca.annotation;version="2.0.0",
+ org.osgi.framework,
+ org.osgi.service.component;resolution:=optional,
+ org.osgi.service.packageadmin
+Bundle-SymbolicName: calculator.dosgi.operations
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Service-Component: OSGI-INF/add-component.xml,
+ OSGI-INF/subtract-component.xml,
+ OSGI-INF/multiply-component.xml,
+ OSGI-INF/divide-component.xml
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/sca-contribution.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/sca-contribution.xml
new file mode 100644
index 0000000000..d24999ab3e
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/META-INF/sca-contribution.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:c="http://calculator.dosgi">
+ <deployable composite="c:OperationsComposite" />
+</contribution>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/add-component.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/add-component.xml
new file mode 100644
index 0000000000..99845257ff
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/add-component.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<scr:component name="AddComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+ <implementation class="calculator.dosgi.operations.impl.AddServiceImpl" />
+ <service>
+ <provide interface="calculator.dosgi.operations.AddService" />
+ </service>
+</scr:component>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/blueprint/operations-module.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/blueprint/operations-module.xml
new file mode 100644
index 0000000000..f6b5f4690e
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/blueprint/operations-module.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!-- A sample module-context.xml for OSGI RFC 124 (BluePrint Service) -->
+<components xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+ <component id="AddComponent" class="calculator.dosgi.operations.impl.AddServiceImpl">
+ </component>
+ <component id="SubtractComponent" class="calculator.dosgi.operations.impl.SubtractServiceImpl">
+ </component>
+ <component id="MultiplyComponent" class="calculator.dosgi.operations.impl.MultiplyServiceImpl">
+ </component>
+ <component id="DivideComponent" class="calculator.dosgi.operations.impl.DivideServiceImpl">
+ </component>
+
+ <!-- We can derive the SCA services for the implementation.osgi -->
+ <service id="AddService" ref="AddComponent" interface="calculator.dosgi.operations.AddService">
+ </service>
+ <service id="SubtractService" ref="SubtractComponent" interface="calculator.dosgi.operations.SubtractService">
+ </service>
+ <service id="MultiplyService" ref="MultiplyComponent" interface="calculator.dosgi.operations.MultiplyService">
+ </service>
+ <service id="DivideService" ref="DivideComponent" interface="calculator.dosgi.operations.DivideService">
+ </service>
+</components> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/divide-component.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/divide-component.xml
new file mode 100644
index 0000000000..322d4daf2f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/divide-component.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<scr:component name="DivideComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+ <implementation class="calculator.dosgi.operations.impl.DivideServiceImpl" />
+ <service>
+ <provide interface="calculator.dosgi.operations.DivideService" />
+ </service>
+</scr:component>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/multiply-component.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/multiply-component.xml
new file mode 100644
index 0000000000..b9ca777bd8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/multiply-component.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<scr:component name="MultiplyComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+ <implementation class="calculator.dosgi.operations.impl.MultiplyServiceImpl" />
+ <service>
+ <provide interface="calculator.dosgi.operations.MultiplyService" />
+ </service>
+</scr:component>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/subtract-component.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/subtract-component.xml
new file mode 100644
index 0000000000..1472f5a976
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/OSGI-INF/subtract-component.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<scr:component name="SubtractComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+ <implementation class="calculator.dosgi.operations.impl.SubtractServiceImpl" />
+ <service>
+ <provide interface="calculator.dosgi.operations.SubtractService" />
+ </service>
+</scr:component>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/bundle.componentType b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/bundle.componentType
new file mode 100644
index 0000000000..fced5b7840
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/bundle.componentType
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1">
+
+
+ <!-- The service elment defines an SCA proxy to a remote OSGi service -->
+ <service name="AddService">
+ <interface.java interface="calculator.dosgi.operations.AddService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </service>
+ <service name="SubtractService">
+ <interface.java interface="calculator.dosgi.operations.SubtractService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </service>
+ <service name="MultiplyService">
+ <interface.java interface="calculator.dosgi.operations.MultiplyService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </service>
+ <service name="DivideService">
+ <interface.java interface="calculator.dosgi.operations.DivideService"/>
+ <extensions>
+ <t:osgi.property name="prop1">1</t:osgi.property>
+ <t:osgi.property name="prop2">ABC</t:osgi.property>
+ </extensions>
+ </service>
+</componentType>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/operations.composite b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/operations.composite
new file mode 100644
index 0000000000..c7250912b0
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/operations/operations.composite
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://calculator.dosgi"
+ name="OperationsComposite">
+
+ <component name="OperationsComponent">
+ <tuscany:implementation.osgi bundleSymbolicName="calculator.dosgi.operations" bundleVersion="1.0.0" />
+ <service name="AddService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/AddService"/>
+ </service>
+ <service name="SubtractService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService"/>
+ </service>
+ <service name="MultiplyService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService"/>
+ </service>
+ <service name="DivideService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/>
+ </service>
+
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/MANIFEST.MF b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..60dc54014a
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-Version: 1.0.0
+Bundle-Name: calculator.dosgi.sca
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: calculator.dosgi.sca
+Bundle-Vendor: The Apache Software Foundation
+Eclipse-LazyStart: false
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
+Import-Package: calculator.dosgi;version="1.0.0",
+ calculator.dosgi.operations;version="1.0.0"
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/sca-contribution.xml b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/sca-contribution.xml
new file mode 100644
index 0000000000..b48c048000
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/META-INF/sca-contribution.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+
+<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:c="http://calculator.dosgi">
+ <deployable composite="c:CalculatorComposite" />
+</contribution>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/OSGI-INF/sca/calculator.composite b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/OSGI-INF/sca/calculator.composite
new file mode 100644
index 0000000000..1be863568c
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl-osgi/src/test/resources/calculator/dosgi/sca/OSGI-INF/sca/calculator.composite
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://calculator.dosgi"
+ name="CalculatorComposite">
+
+ <component name="CalculatorComponent">
+ <tuscany:implementation.osgi bundleSymbolicName="calculator.dosgi" bundleVersion="1.0.0" />
+ <reference name="addService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/AddService"/>
+ </reference>
+ <reference name="subtractService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService"/>
+ </reference>
+ <reference name="multiplyService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService"/>
+ </reference>
+ <reference name="divideService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/>
+ </reference>
+
+ <!--
+ <reference name="addService" target="OperationsComponent/AddService">
+ </reference>
+ <reference name="subtractService" target="OperationsComponent/SubtractService">
+ </reference>
+ <reference name="multiplyService" target="OperationsComponent/MultiplyService">
+ </reference>
+ <reference name="divideService" target="OperationsComponent/DivideService">
+ </reference>
+ -->
+ </component>
+
+ <component name="OperationsComponent">
+ <tuscany:implementation.osgi bundleSymbolicName="calculator.dosgi.operations" bundleVersion="1.0.0" />
+ <service name="AddService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/AddService"/>
+ </service>
+ <service name="SubtractService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService"/>
+ </service>
+ <service name="MultiplyService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService"/>
+ </service>
+ <service name="DivideService">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/>
+ </service>
+
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/LICENSE b/sandbox/sebastien/java/shell/modules/node-impl/LICENSE
new file mode 100644
index 0000000000..8aa906c321
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/LICENSE
@@ -0,0 +1,205 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/META-INF/MANIFEST.MF b/sandbox/sebastien/java/shell/modules/node-impl/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..15eeb4cf15
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/META-INF/MANIFEST.MF
@@ -0,0 +1,40 @@
+Manifest-Version: 1.0
+Export-Package: org.apache.tuscany.sca.node.impl;version="2.0.0"
+SCA-Version: 1.1
+Bundle-Name: Apache Tuscany SCA Node Implementation
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 2.0.0
+Bundle-ManifestVersion: 2
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Description: Apache Tuscany SCA Node Implementation
+Import-Package: javax.management;resolution:=optional,
+ javax.xml.namespace,
+ javax.xml.stream,
+ org.apache.tuscany.sca.assembly;version="2.0.0",
+ org.apache.tuscany.sca.assembly.builder;version="2.0.0",
+ org.apache.tuscany.sca.common.java.io;version="2.0.0",
+ org.apache.tuscany.sca.common.xml.stax;version="2.0.0",
+ org.apache.tuscany.sca.context;version="2.0.0",
+ org.apache.tuscany.sca.contribution;version="2.0.0",
+ org.apache.tuscany.sca.contribution.processor;version="2.0.0",
+ org.apache.tuscany.sca.contribution.resolver;version="2.0.0",
+ org.apache.tuscany.sca.core;version="2.0.0",
+ org.apache.tuscany.sca.core.assembly;version="2.0.0",
+ org.apache.tuscany.sca.core.invocation;version="2.0.0",
+ org.apache.tuscany.sca.definitions;version="2.0.0",
+ org.apache.tuscany.sca.definitions.util;version="2.0.0",
+ org.apache.tuscany.sca.definitions.xml;version="2.0.0",
+ org.apache.tuscany.sca.deployment;version="2.0.0",
+ org.apache.tuscany.sca.extensibility;version="2.0.0",
+ org.apache.tuscany.sca.management;version="2.0.0",
+ org.apache.tuscany.sca.monitor;version="2.0.0",
+ org.apache.tuscany.sca.node;version="2.0.0",
+ org.apache.tuscany.sca.node.configuration;version="2.0.0",
+ org.apache.tuscany.sca.node.impl;version="2.0.0",
+ org.apache.tuscany.sca.runtime;version="2.0.0",
+ org.apache.tuscany.sca.work;version="2.0.0",
+ org.oasisopen.sca;version="2.0.0",
+ org.oasisopen.sca.annotation;version="2.0.0";resolution:=optional
+Bundle-SymbolicName: org.apache.tuscany.sca.node.impl
+Bundle-DocURL: http://www.apache.org/
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/NOTICE b/sandbox/sebastien/java/shell/modules/node-impl/NOTICE
new file mode 100644
index 0000000000..ad2ba40961
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/NOTICE
@@ -0,0 +1,6 @@
+${pom.name}
+Copyright (c) 2005 - 2010 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/pom.xml b/sandbox/sebastien/java/shell/modules/node-impl/pom.xml
new file mode 100644
index 0000000000..5d55b13fc8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<project>
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-modules</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>tuscany-node-impl</artifactId>
+ <name>Apache Tuscany SCA Node Implementation</name>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-assembly</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-contribution</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-node-api</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-core</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-assembly-xml</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-deployment</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-builder</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-binding-sca-runtime</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-core-databinding</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-assembly-xsd</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-implementation-java-runtime</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-binding-ws-wsdlgen</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationATOMProcessor.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationATOMProcessor.java
new file mode 100644
index 0000000000..d11eb317cf
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationATOMProcessor.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.node.configuration.xml;
+
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.tuscany.sca.contribution.processor.BaseStAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ContributionResolveException;
+import org.apache.tuscany.sca.contribution.processor.ContributionWriteException;
+import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.node.configuration.ContributionConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory;
+
+/**
+ * Implements a StAX artifact processor for configured node implementations.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NodeConfigurationATOMProcessor extends BaseStAXArtifactProcessor implements
+ StAXArtifactProcessor<NodeConfiguration> {
+
+ private static final String ATOM_NS = "http://www.w3.org/2005/Atom";
+ private static final QName FEED_QNAME = new QName(ATOM_NS, "feed");
+ private static final QName ENTRY_QNAME = new QName(ATOM_NS, "entry");
+ private static final QName ID_QNAME = new QName(ATOM_NS, "id");
+ private static final QName LINK_QNAME = new QName(ATOM_NS, "link");
+ private static final QName CONTENT_QNAME = new QName(ATOM_NS, "content");
+ private static final String HREF = "href";
+
+ private NodeConfigurationFactory factory;
+
+ public NodeConfigurationATOMProcessor(FactoryExtensionPoint modelFactories) {
+ this.factory = modelFactories.getFactory(NodeConfigurationFactory.class);
+ }
+
+ public QName getArtifactType() {
+ return null;
+ }
+
+ public Class<NodeConfiguration> getModelType() {
+ // Returns the type of model processed by this processor
+ return NodeConfiguration.class;
+ }
+
+ public NodeConfiguration read(XMLStreamReader reader, ProcessorContext context) throws ContributionReadException, XMLStreamException {
+
+ NodeConfiguration config = factory.createNodeConfiguration();
+
+ // Read a feed containing links to the composite and the contributions assigned to
+ // the node
+ ContributionConfiguration contribution = null;
+ boolean id = false;
+ QName name = null;
+
+ while (reader.hasNext()) {
+ int event = reader.getEventType();
+ switch (event) {
+
+ case START_ELEMENT:
+ name = reader.getName();
+
+ if (ENTRY_QNAME.equals(name)) {
+
+ // Read an <entry>
+ contribution = factory.createContributionConfiguration();
+ } else if (ID_QNAME.equals(name)) {
+
+ // Read an <id>
+ id = true;
+
+ } else if (LINK_QNAME.equals(name)) {
+
+ // Read a <link>
+ String href = getString(reader, HREF);
+
+ if (contribution != null) {
+ contribution.setLocation(href);
+ }
+ } else if (CONTENT_QNAME.equals(name)) {
+ // Read a <content>
+ } else if (FEED_QNAME.equals(name)) {
+ // Read a <feed>
+ }
+ break;
+
+ case XMLStreamConstants.CHARACTERS:
+
+ // Read characters inside an <id> element
+ if (id) {
+ if (contribution != null) {
+ contribution.setURI(reader.getText());
+ }
+ }
+ break;
+
+ case END_ELEMENT:
+ name = reader.getName();
+
+ // Clear current state when reading reaching end element
+ if (ENTRY_QNAME.equals(name)) {
+ if (contribution != null) {
+ config.getContributions().add(contribution);
+ }
+
+ contribution = null;
+
+ } else if (ID_QNAME.equals(name)) {
+ id = false;
+
+ } else if (FEED_QNAME.equals(name)) {
+
+ // We've reached the end of the feed
+ return config;
+ }
+ break;
+ }
+
+ // Read the next element
+ if (reader.hasNext()) {
+ reader.next();
+ }
+ }
+ return config;
+ }
+
+ public void resolve(NodeConfiguration implementation, ModelResolver resolver, ProcessorContext context) throws ContributionResolveException {
+ }
+
+ public void write(NodeConfiguration implementation, XMLStreamWriter writer, ProcessorContext context) throws ContributionWriteException,
+ XMLStreamException {
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessor.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessor.java
new file mode 100644
index 0000000000..46f46a3757
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessor.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.node.configuration.xml;
+
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.StringTokenizer;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.tuscany.sca.common.xml.stax.StAXHelper;
+import org.apache.tuscany.sca.contribution.processor.BaseStAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ContributionResolveException;
+import org.apache.tuscany.sca.contribution.processor.ContributionWriteException;
+import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.node.configuration.BindingConfiguration;
+import org.apache.tuscany.sca.node.configuration.ContributionConfiguration;
+import org.apache.tuscany.sca.node.configuration.DeploymentComposite;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory;
+
+/**
+ * Implements a StAX artifact processor for node implementations.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NodeConfigurationProcessor extends BaseStAXArtifactProcessor implements
+ StAXArtifactProcessor<NodeConfiguration> {
+ private static final String SCA11_TUSCANY_NS = "http://tuscany.apache.org/xmlns/sca/1.1";
+ private static final QName NODE = new QName(SCA11_TUSCANY_NS, "node");
+ private static final QName CONTRIBUTION = new QName(SCA11_TUSCANY_NS, "contribution");
+ private static final QName BINDING = new QName(SCA11_TUSCANY_NS, "binding");
+ private static final QName BASE_URI = new QName(SCA11_TUSCANY_NS, "baseURI");
+ private static final QName DEPLOYMENT_COMPOSITE = new QName(SCA11_TUSCANY_NS, "deploymentComposite");
+
+ private static final String SCA11_NS = "http://docs.oasis-open.org/ns/opencsa/sca/200912";
+ private static final QName COMPOSITE = new QName(SCA11_NS, "composite");
+
+ private StAXArtifactProcessor processor;
+ private NodeConfigurationFactory nodeConfigurationFactory;
+ private StAXHelper helper;
+
+ public NodeConfigurationProcessor(ExtensionPointRegistry registry,
+ StAXArtifactProcessor processor) {
+ FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ this.nodeConfigurationFactory = modelFactories.getFactory(NodeConfigurationFactory.class);
+ this.processor = processor;
+ this.helper = StAXHelper.getInstance(registry);
+ }
+
+ public QName getArtifactType() {
+ // Returns the QName of the XML element processed by this processor
+ return NODE;
+ }
+
+ public Class<NodeConfiguration> getModelType() {
+ // Returns the type of model processed by this processor
+ return NodeConfiguration.class;
+ }
+
+ public NodeConfiguration read(XMLStreamReader reader, ProcessorContext context) throws ContributionReadException, XMLStreamException {
+
+ NodeConfiguration node = null;
+ ContributionConfiguration contribution = null;
+ DeploymentComposite composite = null;
+ BindingConfiguration binding = null;
+
+ // Skip to end element
+ while (true) {
+ int event = reader.getEventType();
+ switch (event) {
+ case XMLStreamConstants.START_ELEMENT:
+ QName name = reader.getName();
+ if (NODE.equals(name)) {
+ node = nodeConfigurationFactory.createNodeConfiguration();
+ node.setURI(reader.getAttributeValue(null, "uri"));
+ node.setDomainURI(reader.getAttributeValue(null, "domain"));
+ node.setDomainRegistryURI(reader.getAttributeValue(null, "domainRegistry"));
+ } else if (CONTRIBUTION.equals(name)) {
+ contribution = nodeConfigurationFactory.createContributionConfiguration();
+ contribution.setURI(reader.getAttributeValue(null, "uri"));
+ contribution.setLocation(reader.getAttributeValue(null, "location"));
+ node.getContributions().add(contribution);
+ } else if (BINDING.equals(name)) {
+ binding = nodeConfigurationFactory.createBindingConfiguration();
+ binding.setBindingType(getQName(reader, "name"));
+ String baseURIs = reader.getAttributeValue(null, "baseURIs");
+ if (baseURIs != null) {
+ StringTokenizer tokenizer = new StringTokenizer(baseURIs);
+ while (tokenizer.hasMoreTokens()) {
+ binding.getBaseURIs().add(tokenizer.nextToken());
+ }
+ }
+ node.getBindings().add(binding);
+ } else if (DEPLOYMENT_COMPOSITE.equals(name)) {
+ composite = nodeConfigurationFactory.createDeploymentComposite();
+ composite.setLocation(reader.getAttributeValue(null, "location"));
+ if (contribution != null) {
+ contribution.getDeploymentComposites().add(composite);
+ }
+ } else if(BASE_URI.equals(name)) {
+ // We also support <baseURI> element
+ String baseURI = reader.getElementText();
+ if (baseURI != null && binding != null) {
+ baseURI = baseURI.trim();
+ binding.addBaseURI(baseURI);
+ }
+ // getElementText() moves the event to END_ELEMENT
+ continue;
+ } else if (COMPOSITE.equals(name)) {
+ /*
+ Object model = processor.read(reader);
+ if (model instanceof Composite) {
+ // FIXME: We need to capture the text here
+ // composite.setComposite((Composite)model);
+ }
+ */
+ StringWriter sw = new StringWriter();
+ XMLStreamWriter writer = helper.createXMLStreamWriter(sw);
+ helper.save(reader, writer);
+ composite.setContent(sw.toString());
+ } else {
+ node.getExtensions().add(processor.read(reader, context));
+ }
+ break;
+
+ case END_ELEMENT:
+ name = reader.getName();
+ if (NODE.equals(name)) {
+ return node;
+ } else if (CONTRIBUTION.equals(name)) {
+ contribution = null;
+ } else if (DEPLOYMENT_COMPOSITE.equals(name)) {
+ composite = null;
+ } else if (BINDING.equals(name)) {
+ binding = null;
+ }
+ }
+ if (reader.hasNext()) {
+ reader.next();
+ } else {
+ break;
+ }
+ }
+
+ return node;
+ }
+
+ public void resolve(NodeConfiguration node, ModelResolver resolver, ProcessorContext context) throws ContributionResolveException {
+ }
+
+ public void write(NodeConfiguration node, XMLStreamWriter writer, ProcessorContext context) throws ContributionWriteException,
+ XMLStreamException {
+
+ writeStart(writer,
+ NODE.getNamespaceURI(),
+ NODE.getLocalPart(),
+ new XAttr("uri", node.getURI()),
+ new XAttr("domainRegistry", node.getDomainRegistryURI()),
+ new XAttr("domain", node.getDomainURI()));
+
+ for (ContributionConfiguration c : node.getContributions()) {
+ writeStart(writer,
+ CONTRIBUTION.getNamespaceURI(),
+ CONTRIBUTION.getLocalPart(),
+ new XAttr("uri", c.getURI()),
+ new XAttr("location", c.getLocation()));
+ for (DeploymentComposite dc : c.getDeploymentComposites()) {
+ writeStart(writer,
+ DEPLOYMENT_COMPOSITE.getNamespaceURI(),
+ DEPLOYMENT_COMPOSITE.getLocalPart(),
+ new XAttr("location", dc.getLocation()),
+ new XAttr("contribution", dc.getContributionURI()));
+ if (dc.getContent() != null) {
+ XMLStreamReader reader = helper.createXMLStreamReader(new StringReader(dc.getContent()));
+ reader.nextTag(); // Move to the first element
+ helper.save(reader, writer);
+ reader.close();
+ }
+ writeEnd(writer);
+ }
+ writeEnd(writer);
+ }
+
+ for (BindingConfiguration b : node.getBindings()) {
+ StringBuffer uris = new StringBuffer();
+ for (String uri : b.getBaseURIs()) {
+ uris.append(uri).append(' ');
+ }
+ if (uris.length() > 0) {
+ uris.deleteCharAt(uris.length() - 1); // Remove the trailing space
+ } else {
+ uris = null;
+ }
+ String baseURIs = (uris == null) ? null : uris.toString();
+ writeStart(writer,
+ BINDING.getNamespaceURI(),
+ BINDING.getLocalPart(),
+ new XAttr("name", b.getBindingType()),
+ new XAttr("baseURIs", baseURIs));
+ writeEnd(writer);
+ }
+
+ for(Object o: node.getExtensions()) {
+ processor.write(o, writer, context);
+ }
+
+ writeEnd(writer);
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeFactoryImpl.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeFactoryImpl.java
new file mode 100644
index 0000000000..3c34c5ce25
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeFactoryImpl.java
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.impl;
+
+import static java.lang.System.currentTimeMillis;
+import static org.apache.tuscany.sca.common.java.io.IOHelper.createURI;
+import static org.apache.tuscany.sca.common.java.io.IOHelper.openStream;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.tuscany.sca.assembly.AssemblyFactory;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Composite;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.EndpointReference;
+import org.apache.tuscany.sca.assembly.Implementation;
+import org.apache.tuscany.sca.common.java.io.IOHelper;
+import org.apache.tuscany.sca.contribution.Artifact;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
+import org.apache.tuscany.sca.contribution.processor.ValidationSchemaExtensionPoint;
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.core.assembly.RuntimeAssemblyFactory;
+import org.apache.tuscany.sca.core.invocation.ExtensibleProxyFactory;
+import org.apache.tuscany.sca.core.invocation.ProxyFactory;
+import org.apache.tuscany.sca.deployment.Deployer;
+import org.apache.tuscany.sca.extensibility.ServiceDiscovery;
+import org.apache.tuscany.sca.monitor.Monitor;
+import org.apache.tuscany.sca.monitor.MonitorFactory;
+import org.apache.tuscany.sca.monitor.Problem;
+import org.apache.tuscany.sca.monitor.Problem.Severity;
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.configuration.BindingConfiguration;
+import org.apache.tuscany.sca.node.configuration.ContributionConfiguration;
+import org.apache.tuscany.sca.node.configuration.DefaultNodeConfigurationFactory;
+import org.apache.tuscany.sca.node.configuration.DeploymentComposite;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory;
+import org.apache.tuscany.sca.runtime.DomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.EndpointReferenceBinder;
+import org.apache.tuscany.sca.runtime.EndpointRegistry;
+import org.apache.tuscany.sca.runtime.ExtensibleDomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.RuntimeProperties;
+import org.apache.tuscany.sca.work.WorkScheduler;
+import org.oasisopen.sca.ServiceRuntimeException;
+
+/**
+ * This class provides a node factory that can create multiple nodes that share the same
+ * extension point registry
+ */
+public class NodeFactoryImpl extends NodeFactory {
+ protected static final Logger logger = Logger.getLogger(NodeImpl.class.getName());
+
+ protected boolean inited;
+ protected Map<Object, Node> nodes = new ConcurrentHashMap<Object, Node>();
+
+ protected Deployer deployer;
+ protected ExtensionPointRegistry registry;
+ protected ProxyFactory proxyFactory;
+ protected MonitorFactory monitorFactory;
+
+ /**
+ * Automatically destroy the factory when last node is stopped. Subclasses
+ * can set this flag.
+ */
+ protected boolean autoDestroy = true;
+
+ @Override
+ public Node createNode(NodeConfiguration configuration) {
+ if (configuration.getURI() == null) {
+ // Make sure a unique node URI is created for the same node factory
+ configuration.setURI(generateNodeURI());
+ }
+ return new NodeImpl(this, configuration);
+ }
+
+ protected Node removeNode(NodeConfiguration configuration) {
+ Node node = nodes.remove(getNodeKey(configuration));
+ if (autoDestroy && nodes.isEmpty()) {
+ destroy();
+ }
+ return node;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Node createNode(List<?> contributions) {
+ NodeConfigurationFactory nodeConfigurationFactory = new DefaultNodeConfigurationFactory();
+ return new NodeImpl(this, nodeConfigurationFactory.createNodeConfiguration(), new ArrayList<Contribution>((List<Contribution>)contributions));
+ }
+
+ protected void addNode(NodeConfiguration configuration, Node node) {
+ nodes.put(getNodeKey(configuration), node);
+ }
+
+ @Override
+ public NodeConfiguration loadConfiguration(InputStream xml, URL base) {
+ try {
+ init();
+ InputStreamReader reader = new InputStreamReader(xml, "UTF-8");
+ ProcessorContext context = deployer.createProcessorContext();
+ NodeConfiguration config = deployer.loadXMLDocument(reader, context.getMonitor());
+ if (base != null && config != null) {
+ // Resolve the contribution location against the node.xml
+ for (ContributionConfiguration c : config.getContributions()) {
+ String location = c.getLocation();
+ if (location != null) {
+ URL url = new URL(base, location);
+ url = IOHelper.normalize(url);
+ c.setLocation(url.toString());
+ }
+ }
+ }
+ return config;
+ } catch (Throwable e) {
+ throw new ServiceRuntimeException(e);
+ }
+ }
+
+ public Map<Object, Node> getNodes() {
+ return nodes;
+ }
+
+ public List<Node> getNodesInDomain(String domainName) {
+ List<Node> domainNodes = new ArrayList<Node>();
+ if (nodes != null) {
+ for (Node n : nodes.values()) {
+ if (domainName.equals(((NodeImpl)n).getConfiguration().getDomainURI())) {
+ domainNodes.add(n);
+ }
+ }
+ }
+ return domainNodes;
+ }
+
+ protected Object getNodeKey(NodeConfiguration configuration) {
+ return new NodeKey(configuration);
+ }
+
+ public synchronized void destroy() {
+ if (inited) {
+ for (Node node : nodes.values()) {
+ node.stop();
+ }
+ nodes.clear();
+ deployer.stop();
+ registry.stop();
+ super.destroy();
+ inited = false;
+ }
+ }
+
+ /**
+ * Analyze problems reported by the artifact processors and builders.
+ *
+ * @throws Exception
+ */
+ private void analyzeProblems(Monitor monitor) throws Throwable {
+ try {
+ for (Problem problem : monitor.getProblems()) {
+ if ((problem.getSeverity() == Severity.ERROR)) {
+ if (problem.getCause() != null) {
+ throw problem.getCause();
+ } else {
+ throw new ServiceRuntimeException(problem.toString());
+ }
+ }
+ }
+ } finally {
+ // FIXME: Clear problems so that the monitor is clean again
+ monitor.reset();
+ }
+ }
+
+ private boolean attachDeploymentComposite(Contribution contribution, Reader xml, String location, boolean attached, ProcessorContext context)
+ throws XMLStreamException, ContributionReadException {
+
+ // Read the composite model
+ Composite composite = deployer.loadXMLDocument(xml, context.getMonitor());
+
+ // Replace the deployable composites with the deployment composites
+ // Clear the deployable composites if it's the first deployment composite
+ deployer.attachDeploymentComposite(contribution, composite, attached);
+ if (!attached) {
+ attached = true;
+ }
+ return attached;
+ }
+
+ public ExtensionPointRegistry getExtensionPointRegistry() {
+ if (registry == null) {
+ // Create extension point registry
+ registry = createExtensionPointRegistry();
+ registry.start();
+ }
+ return registry;
+ }
+
+ public synchronized void init() {
+ if (inited) {
+ return;
+ }
+ long start = currentTimeMillis();
+
+ getExtensionPointRegistry();
+
+ UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class);
+ monitorFactory = utilities.getUtility(MonitorFactory.class);
+
+ utilities.getUtility(RuntimeProperties.class).setProperties(properties);
+
+ // Use the runtime-enabled assembly factory
+ FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ AssemblyFactory assemblyFactory = new RuntimeAssemblyFactory(registry);
+ modelFactories.addFactory(assemblyFactory);
+
+ // Load the Deployer
+ deployer = utilities.getUtility(Deployer.class);
+
+ // Enable schema validation only of the logger level is FINE or higher
+ deployer.setSchemaValidationEnabled(isSchemaValidationEnabled());
+
+ // Initialize the Tuscany module activators
+ // The module activators will be started
+ registry.getExtensionPoint(ModuleActivatorExtensionPoint.class);
+
+ // Initialize runtime
+
+ // Get proxy factory
+ proxyFactory = ExtensibleProxyFactory.getInstance(registry);
+
+ utilities.getUtility(WorkScheduler.class);
+
+ inited = true;
+
+ if (logger.isLoggable(Level.FINE)) {
+ long end = currentTimeMillis();
+ logger.fine("The tuscany runtime started in " + (end - start) + " ms.");
+ }
+ }
+
+ public void start() {
+ init();
+ DomainRegistryFactory domainRegistryFactory = ExtensibleDomainRegistryFactory.getInstance(registry);
+ domainRegistryFactory.getEndpointRegistry(properties.getProperty("reguri"), properties.getProperty("defaultDomainName"));
+ }
+
+ protected ExtensionPointRegistry createExtensionPointRegistry() {
+ return new DefaultExtensionPointRegistry();
+ }
+
+ protected boolean isSchemaValidationEnabled() {
+ String enabled = getSystemProperty(ValidationSchemaExtensionPoint.class.getName() + ".enabled");
+ if (enabled == null) {
+ enabled = "true";
+ }
+ boolean debug = logger.isLoggable(Level.FINE);
+ return "true".equals(enabled) || debug;
+ }
+
+ protected Composite configureNode(NodeConfiguration configuration, List<Contribution> contributions, ProcessorContext context)
+ throws Throwable {
+ if (contributions == null) {
+ // Load contributions
+ contributions = loadContributions(configuration, context);
+ }
+
+ Monitor monitor = context.getMonitor();
+ Map<QName, List<String>> bindingBaseURIs = new HashMap<QName, List<String>>();
+ for (BindingConfiguration config : configuration.getBindings()) {
+ bindingBaseURIs.put(config.getBindingType(), config.getBaseURIs());
+ }
+ List<Contribution> allContributions = getAllContributions(configuration.getDomainURI());
+
+ Composite domainComposite = deployer.build(contributions, allContributions, bindingBaseURIs, monitor);
+ analyzeProblems(monitor);
+
+ // postBuildEndpointReferenceMatching(domainComposite);
+
+ return domainComposite;
+ }
+
+ private List<Contribution> getAllContributions(String domainName) {
+ List<Contribution> contributions = new ArrayList<Contribution>();
+ for (NodeFactory f : getNodeFactories()) {
+ for (Node node : ((NodeFactoryImpl)f).getNodesInDomain(domainName)) {
+ contributions.addAll(((NodeImpl)node).getContributions());
+ }
+ }
+ return contributions;
+ }
+
+ // =============================================
+ // TODO - TUSCANY-3425
+ // post build endpoint reference matching. Give the matching algorithm
+ // a chance to run and report any errors for local references prior to
+ // runtime start. Not in use at the moment as we are getting away with
+ // runtime matching. Leaving here for when we come to sorting out
+ // autowire which still relies on matching in the builder
+ private void postBuildEndpointReferenceMatching(Composite composite){
+ EndpointReferenceBinder endpointReferenceBinder = registry.getExtensionPoint(EndpointReferenceBinder.class);
+ DomainRegistryFactory domainRegistryFactory = ExtensibleDomainRegistryFactory.getInstance(registry);
+
+ // create temporary local registry for all available local endpoints
+ // TODO - need a better way of getting a local registry
+ EndpointRegistry registry = domainRegistryFactory.getEndpointRegistry("vm://tmp", "local");
+
+ // populate the registry with all the endpoints that are currently present in the model
+ populateLocalRegistry(composite, registry);
+
+ // look at all the endpoint references and try to match them to
+ // any local endpoints
+ for (EndpointReference endpointReference : registry.getEndpointReferences()){
+ endpointReferenceBinder.bindBuildTime(registry, endpointReference);
+ }
+
+ // remove the local registry
+ domainRegistryFactory.getEndpointRegistries().remove(registry);
+ }
+
+ private void populateLocalRegistry(Composite composite, EndpointRegistry registry){
+ for (Component component : composite.getComponents()) {
+ // recurse for composite implementations
+ Implementation implementation = component.getImplementation();
+ if (implementation instanceof Composite) {
+ populateLocalRegistry((Composite)implementation, registry);
+ }
+
+ for (ComponentService service : component.getServices()) {
+ for (Endpoint endpoint : service.getEndpoints()){
+ registry.addEndpoint(endpoint);
+ }
+ }
+
+ for (ComponentReference reference : component.getReferences()) {
+ for (EndpointReference endpointReference : reference.getEndpointReferences()){
+ registry.addEndpointReference(endpointReference);
+ }
+ }
+ }
+ }
+
+ // =============================================
+
+ protected List<Contribution> loadContributions(NodeConfiguration configuration, ProcessorContext context) throws Throwable {
+ List<Contribution> contributions = new ArrayList<Contribution>();
+
+ // Load the specified contributions
+ for (ContributionConfiguration contrib : configuration.getContributions()) {
+ URI contributionURI = createURI(contrib.getURI());
+
+ URI uri = createURI(contrib.getLocation());
+ if (uri.getScheme() == null) {
+ uri = new File(contrib.getLocation()).toURI();
+ }
+ URL contributionURL = uri.toURL();
+
+ // Load the contribution
+ logger.log(Level.INFO, "Loading contribution: " + contributionURL);
+ Contribution contribution = deployer.loadContribution(contributionURI, contributionURL, context.getMonitor());
+ contributions.add(contribution);
+
+ boolean attached = false;
+ for (DeploymentComposite dc : contrib.getDeploymentComposites()) {
+ if (dc.getContent() != null) {
+ Reader xml = new StringReader(dc.getContent());
+ attached = attachDeploymentComposite(contribution, xml, null, attached, context);
+ } else if (dc.getLocation() != null) {
+ URI dcURI = createURI(dc.getLocation());
+ if (!dcURI.isAbsolute()) {
+ Composite composite = null;
+ // The location is pointing to an artifact within the contribution
+ for (Artifact a : contribution.getArtifacts()) {
+ if (dcURI.toString().equals(a.getURI())) {
+ composite = (Composite)a.getModel();
+ if (!attached) {
+ contribution.getDeployables().clear();
+ attached = true;
+ }
+ contribution.getDeployables().add(composite);
+ break;
+ }
+ }
+ if (composite == null) {
+ // Not found
+ throw new ServiceRuntimeException("Deployment composite " + dcURI
+ + " cannot be found within contribution "
+ + contribution.getLocation());
+ }
+ } else {
+ URL url = dcURI.toURL();
+ InputStream is = openStream(url);
+ Reader xml = new InputStreamReader(is, "UTF-8");
+ attached = attachDeploymentComposite(contribution, xml, url.toString(), attached, context);
+ }
+ }
+ }
+ analyzeProblems(context.getMonitor());
+ }
+ return contributions;
+ }
+
+ protected static String getSystemProperty(final String name) {
+ return AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty(name);
+ }
+ });
+ }
+
+ protected static class NodeKey {
+ private String domainURI;
+ private String nodeURI;
+
+ public NodeKey(NodeConfiguration configuration) {
+ this.domainURI = configuration.getDomainURI();
+ this.nodeURI = configuration.getURI();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((domainURI == null) ? 0 : domainURI.hashCode());
+ result = prime * result + ((nodeURI == null) ? 0 : nodeURI.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ NodeKey other = (NodeKey)obj;
+ if (domainURI == null) {
+ if (other.domainURI != null)
+ return false;
+ } else if (!domainURI.equals(other.domainURI))
+ return false;
+ if (nodeURI == null) {
+ if (other.nodeURI != null)
+ return false;
+ } else if (!nodeURI.equals(other.nodeURI))
+ return false;
+ return true;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ if (domainURI != null) {
+ buf.append("{").append(domainURI).append("}");
+ }
+ if (nodeURI != null) {
+ buf.append(nodeURI);
+ }
+ return buf.toString();
+ }
+ }
+
+ @Override
+ public void configure(Map<String, Map<String, String>> attributes) {
+ ServiceDiscovery discovery = getExtensionPointRegistry().getServiceDiscovery();
+ for (Map.Entry<String, Map<String, String>> e : attributes.entrySet()) {
+ discovery.setAttribute(e.getKey(), e.getValue());
+ }
+ for (Object o : properties.keySet()) {
+ String p = (String) o;
+ if (p.indexOf('.') > -1) {
+ String serviceType = p.substring(0, p.lastIndexOf('.'));
+ String attribute = p.substring(p.lastIndexOf('.')+1);
+ discovery.setAttribute(serviceType, attribute, properties.getProperty(p));
+ }
+ }
+ super.configure(attributes);
+ }
+
+ /**
+ * Added to allow the node access to the deployer in order to get
+ * to the systemContribution and hence set up the CompositeContext so that
+ * the runtime epr matching algorithm can get at the binding types
+ * @return
+ */
+ public Deployer getDeployer() {
+ return deployer;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeImpl.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeImpl.java
new file mode 100644
index 0000000000..6eafb4df12
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeImpl.java
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.stream.XMLOutputFactory;
+
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Composite;
+import org.apache.tuscany.sca.assembly.CompositeService;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Service;
+import org.apache.tuscany.sca.context.CompositeContext;
+import org.apache.tuscany.sca.context.ThreadMessageContext;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.core.invocation.ProxyFactory;
+import org.apache.tuscany.sca.monitor.Monitor;
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.runtime.ActivationException;
+import org.apache.tuscany.sca.runtime.CompositeActivator;
+import org.apache.tuscany.sca.runtime.DomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.EndpointRegistry;
+import org.apache.tuscany.sca.runtime.ExtensibleDomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.RuntimeComponent;
+import org.apache.tuscany.sca.runtime.RuntimeComponentContext;
+import org.apache.tuscany.sca.runtime.RuntimeComponentService;
+import org.oasisopen.sca.ServiceReference;
+import org.oasisopen.sca.ServiceRuntimeException;
+import org.oasisopen.sca.ServiceUnavailableException;
+
+/**
+ * An SCA Node that is managed by the NodeManager
+ */
+public class NodeImpl implements Node {
+ private static final Logger logger = Logger.getLogger(NodeImpl.class.getName());
+ private ProxyFactory proxyFactory;
+ private CompositeActivator compositeActivator;
+ private CompositeContext compositeContext;
+ private Composite domainComposite;
+ private NodeConfiguration configuration;
+ private NodeFactoryImpl nodeFactory;
+ private List<Contribution> contributions;
+ // private NodeManager mbean;
+
+ /**
+ * Create a node from the configuration
+ * @param manager
+ * @param configuration
+ */
+ public NodeImpl(NodeFactoryImpl nodeFactory, NodeConfiguration configuration) {
+ super();
+ this.configuration = configuration;
+ this.nodeFactory = nodeFactory;
+ }
+
+ /**
+ * Create a node from the configuration and loaded contributions
+ * @param manager
+ * @param configuration
+ * @param contributions
+ */
+ public NodeImpl(NodeFactoryImpl manager, NodeConfiguration configuration, List<Contribution> contributions) {
+ super();
+ this.configuration = configuration;
+ this.nodeFactory = manager;
+ this.contributions = new ArrayList<Contribution>(contributions);
+ }
+
+ public String getURI() {
+ return getConfiguration().getURI();
+ }
+
+ public Node start() {
+ logger.log(Level.INFO, "Starting node: " + configuration.getURI() + " domain: " + configuration.getDomainURI());
+
+ nodeFactory.init();
+ nodeFactory.addNode(configuration, this);
+ this.proxyFactory = nodeFactory.proxyFactory;
+
+ DomainRegistryFactory domainRegistryFactory = ExtensibleDomainRegistryFactory.getInstance(nodeFactory.registry);
+ EndpointRegistry endpointRegistry =
+ domainRegistryFactory.getEndpointRegistry(configuration.getDomainRegistryURI(), configuration
+ .getDomainURI());
+
+ UtilityExtensionPoint utilities = nodeFactory.registry.getExtensionPoint(UtilityExtensionPoint.class);
+ this.compositeActivator = utilities.getUtility(CompositeActivator.class);
+ try {
+ Monitor monitor = nodeFactory.monitorFactory.createMonitor();
+ ProcessorContext context = new ProcessorContext(monitor);
+
+ // Set up the thead context monitor
+ Monitor tcm = nodeFactory.monitorFactory.setContextMonitor(monitor);
+ try {
+ if (contributions == null) {
+ contributions = nodeFactory.loadContributions(configuration, context);
+ }
+ domainComposite = nodeFactory.configureNode(configuration, contributions, context);
+
+ this.compositeContext =
+ new CompositeContext(nodeFactory.registry,
+ endpointRegistry,
+ domainComposite,
+ configuration.getDomainURI(),
+ configuration.getURI(),
+ nodeFactory.getDeployer().getSystemDefinitions());
+
+ CompositeContext.setThreadCompositeContext(compositeContext);
+ } finally {
+ // Reset the thread context monitor
+ nodeFactory.monitorFactory.setContextMonitor(tcm);
+ }
+
+ // Activate the composite
+ compositeActivator.activate(compositeContext, domainComposite);
+
+ // Start the composite
+ compositeActivator.start(compositeContext, domainComposite);
+
+ // FIXME: [rfeng] We should turn the management capability into a system utility.
+ // In certain environment such as Google App Engine, the JMX API is not allowed
+ try {
+ /*
+ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ mbean = new NodeManager(this);
+ mBeanServer.registerMBean(mbean, mbean.getName());
+ */
+ /*
+ LocateRegistry.createRegistry(9999);
+ JMXServiceURL url =
+ new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server");
+ JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mBeanServer);
+ connectorServer.start();
+ */
+ } catch (Throwable e) {
+ // Ignore the error for now
+ // mbean = null;
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+
+ return this;
+
+ } catch (Throwable e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ public void stop() {
+ logger.log(Level.INFO, "Stopping node: " + configuration.getURI());
+
+ try {
+ if (compositeActivator == null) {
+ return;
+ }
+
+ /*
+ if (mbean != null) {
+ try {
+ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ mBeanServer.unregisterMBean(mbean.getName());
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ } finally {
+ mbean = null;
+ }
+ }
+ */
+
+ if( domainComposite != null ) {
+
+ // Stop the composite
+ compositeActivator.stop(compositeContext, domainComposite);
+
+ // Deactivate the composite
+ compositeActivator.deactivate(domainComposite);
+
+ } // end if
+
+ nodeFactory.removeNode(configuration);
+ this.compositeActivator = null;
+ this.proxyFactory = null;
+ this.domainComposite = null;
+ this.compositeContext = null;
+
+ ThreadMessageContext.removeMessageContext();
+ CompositeContext.removeCompositeContext();
+
+ } catch (ActivationException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <B, R extends ServiceReference<B>> R cast(B target) throws IllegalArgumentException {
+ return (R)proxyFactory.cast(target);
+ }
+
+ public <B> B getService(Class<B> businessInterface, String serviceName) {
+
+ ServiceReference<B> serviceReference = getServiceReference(businessInterface, serviceName);
+ if (serviceReference == null) {
+ throw new ServiceRuntimeException("Service not found: " + serviceName);
+ }
+ return serviceReference.getService();
+ }
+
+ public <B> ServiceReference<B> getServiceReference(Class<B> businessInterface, String name) {
+
+ // Extract the component name
+ String componentName;
+ String serviceName;
+ int i = name.indexOf('/');
+ if (i != -1) {
+ componentName = name.substring(0, i);
+ serviceName = name.substring(i + 1);
+
+ } else {
+ componentName = name;
+ serviceName = null;
+ }
+
+ // Lookup the component
+ Component component = null;
+
+ for (Component compositeComponent : domainComposite.getComponents()) {
+ if (compositeComponent.getName().equals(componentName)) {
+ component = compositeComponent;
+ break;
+ }
+ }
+
+ if (component == null) {
+ throw new ServiceUnavailableException("The service " + name + " has not been contributed to the domain");
+ }
+ RuntimeComponentContext componentContext = null;
+
+ // If the component is a composite, then we need to find the
+ // non-composite component that provides the requested service
+ if (component.getImplementation() instanceof Composite) {
+ for (ComponentService componentService : component.getServices()) {
+ String bindingName = null;
+ if (serviceName != null) {
+ int index = serviceName.indexOf('/');
+ if (index != -1) {
+ bindingName = serviceName.substring(index + 1);
+ serviceName = serviceName.substring(0, index);
+ }
+ }
+ if (serviceName == null || serviceName.equals(componentService.getName())) {
+ CompositeService compositeService = (CompositeService)componentService.getService();
+ if (compositeService != null) {
+ componentContext =
+ ((RuntimeComponent)compositeService.getPromotedComponent()).getComponentContext();
+ serviceName = compositeService.getPromotedService().getName();
+ if (bindingName != null) {
+ serviceName = serviceName + "/" + bindingName;
+ }
+ return componentContext.createSelfReference(businessInterface, serviceName);
+ }
+ break;
+ }
+ }
+ // No matching service found
+ throw new ServiceRuntimeException("Composite service not found: " + name);
+ } else {
+ componentContext = ((RuntimeComponent)component).getComponentContext();
+ if (serviceName != null) {
+ return componentContext.createSelfReference(businessInterface, serviceName);
+ } else {
+ return componentContext.createSelfReference(businessInterface);
+ }
+ }
+ }
+
+ public NodeConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ public ExtensionPointRegistry getExtensionPointRegistry() {
+ return nodeFactory.getExtensionPointRegistry();
+ }
+
+ /**
+ * Get the service endpoints in this Node
+ * TODO: needs review, works for the very simple testcase but i expect there are
+ * other endpoints to be included
+ */
+ public List<Endpoint> getServiceEndpoints() {
+ List<Endpoint> endpoints = new ArrayList<Endpoint>();
+ if (compositeActivator != null) {
+ if (domainComposite != null) {
+ for (Component component : domainComposite.getComponents()) {
+ for (Service service : component.getServices()) {
+ // MJE 28/05/2009 - changed to RuntimeComponentService from RuntimeComponentServiceImpl
+ // - no need to access the Impl directly here
+ if (service instanceof RuntimeComponentService) {
+ endpoints.addAll(((RuntimeComponentService)service).getEndpoints());
+ }
+ }
+ }
+ }
+ }
+ return endpoints;
+ }
+
+ public Composite getDomainComposite() {
+ return domainComposite;
+ }
+
+ public String dumpDomainComposite() {
+
+ StAXArtifactProcessorExtensionPoint xmlProcessors =
+ getExtensionPointRegistry().getExtensionPoint(StAXArtifactProcessorExtensionPoint.class);
+ StAXArtifactProcessor<Composite> compositeProcessor =
+ xmlProcessors.getProcessor(Composite.class);
+
+ return writeComposite(getDomainComposite(), compositeProcessor);
+ }
+
+ private String writeComposite(Composite composite, StAXArtifactProcessor<Composite> compositeProcessor){
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ XMLOutputFactory outputFactory =
+ nodeFactory.getExtensionPointRegistry().getExtensionPoint(FactoryExtensionPoint.class)
+ .getFactory(XMLOutputFactory.class);
+
+ try {
+ compositeProcessor.write(composite, outputFactory.createXMLStreamWriter(bos), new ProcessorContext(nodeFactory.registry));
+ } catch(Exception ex) {
+ return ex.toString();
+ }
+
+ String result = bos.toString();
+
+ // write out and nested composites
+ for (Component component : composite.getComponents()) {
+ if (component.getImplementation() instanceof Composite) {
+ result +=
+ "\n<!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->\n" + writeComposite((Composite)component
+ .getImplementation(),
+ compositeProcessor);
+ }
+ }
+
+ return result;
+ }
+
+ public List<Contribution> getContributions() {
+ return contributions;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeUtil.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeUtil.java
new file mode 100644
index 0000000000..4da41442f5
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/impl/NodeUtil.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.impl;
+
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.ContributionFactory;
+
+/**
+ * NodeUtil
+ *
+ * @version $Rev$ $Date$
+ */
+public class NodeUtil {
+
+ private NodeUtil() {
+ }
+
+ static Contribution contribution(ContributionFactory contributionFactory, org.apache.tuscany.sca.node.Contribution c) {
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setURI(c.getURI());
+ contribution.setLocation(c.getLocation());
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManager.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManager.java
new file mode 100644
index 0000000000..12704a3ee5
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManager.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.impl.NodeImpl;
+
+/**
+ * MBean implementation for the node
+ */
+public class NodeManager implements NodeManagerMBean {
+ private NodeImpl node;
+ private ObjectName name;
+
+ public NodeManager(NodeImpl node) {
+ this.node = node;
+ try {
+ this.name = getName(node);
+ } catch (MalformedObjectNameException e) {
+ // Ignore
+ }
+ }
+
+ public String getURI() {
+ return node.getURI();
+ }
+
+ public String getDomainURI() {
+ return node.getConfiguration().getDomainURI();
+ }
+
+ public ObjectName getName() {
+ return name;
+ }
+
+ private static ObjectName getName(NodeImpl node) throws MalformedObjectNameException {
+ String name =
+ Node.class.getPackage().getName() + ":type="
+ + Node.class.getSimpleName()
+ + ",uri="
+ + ObjectName.quote(node.getURI());
+ return ObjectName.getInstance(name);
+
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManagerMBean.java b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManagerMBean.java
new file mode 100644
index 0000000000..3133142c2b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/java/org/apache/tuscany/sca/node/management/NodeManagerMBean.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.management;
+
+
+
+/**
+ * MBean for NodeImpl
+ */
+public interface NodeManagerMBean {
+ String getURI();
+ String getDomainURI();
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor
new file mode 100644
index 0000000000..a9d0f5ef52
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.tuscany.sca.node.configuration.xml.NodeConfigurationProcessor;qname=http://tuscany.apache.org/xmlns/sca/1.1#node,model=org.apache.tuscany.sca.node.configuration.NodeConfiguration
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory
new file mode 100644
index 0000000000..a349dd7c26
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.NodeFactory
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.tuscany.sca.node.impl.NodeFactoryImpl
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory
new file mode 100644
index 0000000000..9b26d0c7c1
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.tuscany.sca.node.configuration.DefaultNodeConfigurationFactory
+
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/org/apache/tuscany/sca/node/impl/node-impl-validation-messages.properties b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/org/apache/tuscany/sca/node/impl/node-impl-validation-messages.properties
new file mode 100644
index 0000000000..0c07a3a30f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/main/resources/org/apache/tuscany/sca/node/impl/node-impl-validation-messages.properties
@@ -0,0 +1,22 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+UnresolvedImport = Unresolved import: Import = {0}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorld.java b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorld.java
new file mode 100644
index 0000000000..e3fcdfd25f
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorld.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package hello;
+
+import org.oasisopen.sca.annotation.Remotable;
+
+/**
+ * HelloWorld interface
+ */
+@Remotable
+public interface HelloWorld {
+ String hello(String name);
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorldImpl.java b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorldImpl.java
new file mode 100644
index 0000000000..c9a7560b12
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/hello/HelloWorldImpl.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package hello;
+
+/**
+ * HelloWorldImpl
+ */
+public class HelloWorldImpl implements HelloWorld {
+ public String hello(String name) {
+ System.out.println("Hello: " + name);
+ return "Hello, " + name;
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessorTestCase.java b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessorTestCase.java
new file mode 100644
index 0000000000..a10f2cccd4
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/configuration/xml/NodeConfigurationProcessorTestCase.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.configuration.xml;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint;
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.DefaultFactoryExtensionPoint;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class NodeConfigurationProcessorTestCase {
+ private static FactoryExtensionPoint factories;
+ private static StAXArtifactProcessor processor;
+
+ private static ProcessorContext context;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ ExtensionPointRegistry registry = new DefaultExtensionPointRegistry();
+ context = new ProcessorContext(registry);
+
+ factories = new DefaultFactoryExtensionPoint(registry);
+ StAXArtifactProcessorExtensionPoint processors =
+ registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class);
+ processor = processors.getProcessor(NodeConfiguration.class);
+ }
+
+ @Test
+ public void testRead() throws Exception {
+ InputStream is = getClass().getResourceAsStream("/org/apache/tuscany/sca/node/configuration/node1.xml");
+ XMLInputFactory xmlInputFactory = factories.getFactory(XMLInputFactory.class);
+ XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(is);
+ is.close();
+ reader.nextTag();
+ NodeConfiguration config = (NodeConfiguration) processor.read(reader, context);
+ StringWriter sw = new StringWriter();
+ XMLOutputFactory xmlOutputFactory = factories.getFactory(XMLOutputFactory.class);
+ xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
+ XMLStreamWriter writer = xmlOutputFactory.createXMLStreamWriter(sw);
+ processor.write(config, writer, context);
+ writer.flush();
+ System.out.println(sw.toString());
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeImplTestCase.java b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeImplTestCase.java
new file mode 100644
index 0000000000..3133931210
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeImplTestCase.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * \"License\"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.impl;
+
+import hello.HelloWorld;
+
+import java.io.File;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.contribution.processor.ValidationSchemaExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.extensibility.ServiceDeclarationParser;
+import org.apache.tuscany.sca.node.Contribution;
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.runtime.DomainRegistryFactoryExtensionPoint;
+import org.apache.tuscany.sca.runtime.RuntimeProperties;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test case for NodeImpl
+ */
+public class NodeImplTestCase {
+ private static String composite =
+ "<composite xmlns=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\"" + " xmlns:tuscany=\"http://tuscany.apache.org/xmlns/sca/1.1\""
+ + " targetNamespace=\"http://sample/composite\""
+ + " xmlns:sc=\"http://sample/composite\""
+ + " name=\"HelloWorld2\">"
+ + " <component name=\"HelloWorld2\">"
+ + " <implementation.java class=\"hello.HelloWorldImpl\"/>"
+ + " </component>"
+ + " </composite>";
+
+ @Test
+ public void testNodeWithCompositeContent() {
+ NodeFactory factory = new NodeFactoryImpl();
+ Contribution contribution = new Contribution("c1", new File("target/test-classes").toURI().toString());
+ Node node = factory.createNode(new StringReader(composite), contribution);
+ testNode2(node);
+ }
+
+ @Test
+ public void testNodeWithRelativeCompositeURI() {
+ NodeFactory factory = new NodeFactoryImpl();
+ Contribution contribution = new Contribution("c1", new File("target/test-classes").toURI().toString());
+ String compositeURI = "HelloWorld.composite";
+ Node node = factory.createNode(compositeURI, contribution);
+ testNode(node);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testNodeWithAbsoluteCompositeURI() throws MalformedURLException {
+ NodeFactory factory = new NodeFactoryImpl();
+ Contribution contribution = new Contribution("c1", new File("target/test-classes").toURL().toString());
+ String compositeURI = new File("target/test-classes/HelloWorld.composite").toURL().toString();
+ Node node = factory.createNode(compositeURI, contribution);
+ testNode(node);
+ }
+
+ @Test
+ public void testDefaultNode() {
+ testNode(new NodeFactoryImpl().createNode());
+ }
+
+ @Test
+ public void testNodeWithURI() {
+ testNode(new NodeFactoryImpl().createNode(URI.create("foo"),"target/test-classes"));
+ }
+ @Test
+ public void testNodeWithURIandComposite() throws MalformedURLException {
+ String compositeURI = new File("target/test-classes/HelloWorld.composite").toURI().toString();
+ testNode(new NodeFactoryImpl().createNode(URI.create("foo"), compositeURI, new String[]{"target/test-classes"}));
+ }
+
+ @Test
+ public void testGetServiceEndpoints() {
+ NodeFactory factory = new NodeFactoryImpl();
+ Contribution contribution = new Contribution("c1", new File("target/test-classes").toURI().toString());
+ NodeImpl node = (NodeImpl)factory.createNode(new StringReader(composite), contribution);
+ node.start();
+ List<Endpoint> es = node.getServiceEndpoints();
+ Assert.assertEquals(1, es.size());
+ Assert.assertEquals("HelloWorld2", es.get(0).getComponent().getName());
+ node.stop();
+ }
+
+ private void testNode(Node node) {
+ node.start();
+ HelloWorld hw = node.getService(HelloWorld.class, "HelloWorld");
+ Assert.assertEquals("Hello, Node", hw.hello("Node"));
+ node.stop();
+ }
+
+ private void testNode2(Node node) {
+ node.start();
+ HelloWorld hw = node.getService(HelloWorld.class, "HelloWorld2");
+ Assert.assertEquals("Hello, Node", hw.hello("Node"));
+ node.stop();
+ }
+
+ @Test
+ public void testNodeFactoryAttributes() {
+ Map<String, Map<String, String>> attrs = new HashMap<String, Map<String, String>>();
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("enabled", "false");
+ attrs.put(ValidationSchemaExtensionPoint.class.getName(), map);
+
+ Map<String, String> map2 = new HashMap<String, String>();
+ map2.put("urn:MyDomain", "multicast://200.0.0.100:50000/MyDomain");
+ attrs.put(DomainRegistryFactoryExtensionPoint.class.getName(), map2);
+
+ NodeFactoryImpl factory = (NodeFactoryImpl)NodeFactory.newInstance(attrs);
+ Assert.assertFalse(factory.getExtensionPointRegistry().getExtensionPoint(ValidationSchemaExtensionPoint.class)
+ .isEnabled());
+
+ DomainRegistryFactoryExtensionPoint domainRegistryFactoryExtensionPoint =
+ factory.getExtensionPointRegistry().getExtensionPoint(DomainRegistryFactoryExtensionPoint.class);
+ Map<String, String> mapping = domainRegistryFactoryExtensionPoint.getDomainRegistryMapping();
+ Assert.assertEquals(1, mapping.size());
+ Assert.assertEquals("multicast://200.0.0.100:50000/MyDomain", mapping.get("urn:MyDomain"));
+ }
+
+ @Test
+ public void testNodeFactoryProperties() throws Exception {
+ NodeFactoryImpl factory = (NodeFactoryImpl)NodeFactory.newInstance();
+ factory.init();
+ UtilityExtensionPoint utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ Properties ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(2, ps.size());
+ Assert.assertEquals("vm", ps.getProperty("defaultScheme"));
+
+ Properties properties = new Properties();
+ properties.setProperty("defaultScheme", "vm");
+ properties.setProperty("foo.bla", "some value");
+ factory = (NodeFactoryImpl)NodeFactory.newInstance(properties);
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(2, ps.size());
+ Assert.assertEquals("some value", ps.getProperty("foo.bla"));
+
+ factory = (NodeFactoryImpl)NodeFactory.newInstance("properties:test.properties");
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(2, ps.size());
+ Assert.assertEquals("xyz", ps.getProperty("foo.bla"));
+
+ factory = (NodeFactoryImpl)NodeFactory.newInstance("uri:foo?k1=v1&k2=v2&defaultScheme=vm");
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(4, ps.size());
+ Assert.assertEquals("vm", ps.getProperty("defaultScheme"));
+ Assert.assertEquals("foo", ps.getProperty("defaultDomainName"));
+ Assert.assertEquals("v1", ps.getProperty("k1"));
+ Assert.assertEquals("v2", ps.getProperty("k2"));
+
+ factory = (NodeFactoryImpl)NodeFactory.newInstance("uri:");
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(1, ps.size());
+ Assert.assertEquals("", ps.getProperty("defaultDomainName"));
+
+ factory = (NodeFactoryImpl)NodeFactory.newInstance("uri:?");
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(1, ps.size());
+ Assert.assertEquals("default", ps.getProperty("defaultDomainName"));
+
+ factory = (NodeFactoryImpl)NodeFactory.newInstance("uri:?foo");
+ factory.init();
+ utilities = factory.getExtensionPointRegistry().getExtensionPoint(UtilityExtensionPoint.class);
+ ps = utilities.getUtility(RuntimeProperties.class).getProperties();
+ Assert.assertEquals(2, ps.size());
+ Assert.assertEquals("default", ps.getProperty("defaultDomainName"));
+ Assert.assertEquals("", ps.getProperty("foo"));
+ }
+
+ @Test
+ public void testLoadNodeFactoryProperties() throws Exception {
+ URL url = getClass().getResource("/org/apache/tuscany/sca/node/configuration/test-node-factory.config");
+ Collection<Map<String, String>> items = ServiceDeclarationParser.load(url, false);
+ for (Map<String, String> attrs : items) {
+ System.out.println(attrs);
+ }
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeUtilTestCase.java b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeUtilTestCase.java
new file mode 100644
index 0000000000..4b9dda4a71
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/java/org/apache/tuscany/sca/node/impl/NodeUtilTestCase.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.impl;
+
+import java.net.URI;
+
+import org.apache.tuscany.sca.common.java.io.IOHelper;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ *
+ */
+public class NodeUtilTestCase {
+ @Test
+ public void testCreateURI() {
+ Assert.assertEquals("/a/b", IOHelper.createURI("/a/b").toString());
+ Assert.assertEquals("/a%20b", IOHelper.createURI("/a b").toString());
+ Assert.assertEquals("file:/a/b", IOHelper.createURI("file:/a/b").toString());
+ Assert.assertEquals("file:/a%20b", IOHelper.createURI("file:/a b").toString());
+ Assert.assertEquals("file:/a%20b", IOHelper.createURI("file:/a%20b").toString());
+ }
+
+ @Test
+ public void testDomainURI() {
+ Assert.assertEquals("foo", NodeFactoryImpl.getDomainURI(URI.create("tuscany:foo")));
+ Assert.assertEquals("foo", NodeFactoryImpl.getDomainURI(URI.create("vm:foo")));
+ Assert.assertEquals("foo", NodeFactoryImpl.getDomainURI(URI.create("bla://foo")));
+ }
+}
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/HelloWorld.composite b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/HelloWorld.composite
new file mode 100644
index 0000000000..ca3c3eda7c
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/HelloWorld.composite
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://sample/composite"
+ xmlns:sc="http://sample/composite"
+ name="HelloWorld">
+
+ <component name="HelloWorld">
+ <implementation.java class="hello.HelloWorldImpl"/>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/META-INF/sca-contribution.xml b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/META-INF/sca-contribution.xml
new file mode 100644
index 0000000000..20627c3d2c
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/META-INF/sca-contribution.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+
+<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:sc="http://sample/composite">
+ <deployable composite="sc:HelloWorld" />
+</contribution>
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/node1.xml b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/node1.xml
new file mode 100644
index 0000000000..4afc52d73b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/node1.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<node xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns="http://tuscany.apache.org/xmlns/sca/1.1"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ uri="http://sample/nodes/TestNode1"
+ domainRegistry = "vm://localhost"
+ domain="http://domain1">
+
+ <!-- Configure the base URIs for a given binding -->
+ <!-- Each base URI is for a protocol supported by the binding -->
+ <binding name="sca:binding.ws" baseURIs="http://localhost:8080/ws https://localhost:8081/ws"/>
+ <binding name="tuscany:binding.rmi" baseURIs="rmi://localhost:8080/rmi"/>
+ <binding name="tuscany:binding.jsonrpc">
+ <baseURI>http://localhost:8080/jsonrpc</baseURI>
+ </binding>
+
+ <!-- Configure a list of contributions -->
+ <contribution uri="http://c1" location="file:///a.jar"/>
+ <contribution uri="http://c2" location="http://locahost/repo/b.jar">
+ <!-- The deployment composite is optional -->
+ <!-- The deployment composite can be defined inline or referenced using a URI -->
+ <!-- Can we have more than one deployment composites -->
+ <deploymentComposite>
+ <sca:composite>
+ <sca:component name="Component1"> <!-- first component -->
+ <sca:implementation.java class="a.b.Component1Impl"/>
+ <sca:reference name="ref1">
+ <tuscany:binding.rmi/>
+ </sca:reference>
+ </sca:component>
+ </sca:composite>
+ </deploymentComposite>
+ </contribution>
+ <contribution uri="http://c3" location="http://locahost/repo/c.jar">
+ <deploymentComposite location="test/Test.composite">
+ </deploymentComposite>
+ </contribution>
+ <option name="debug" value="true"/>
+ <option name="validation">true</option>
+</node> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/test-node-factory.config b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/test-node-factory.config
new file mode 100644
index 0000000000..ec87f6a3e0
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/org/apache/tuscany/sca/node/configuration/test-node-factory.config
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# Configuration properties for NodeFactory
+org.apache.tuscany.sca.contribution.processor.ValidationSchemaExtensionPoint;enabled=true,attr="value"
+org.apache.tuscany.sca.runtime.DomainRegistryFactoryExtensionPoint;urn:MyDomain="multicast://200.0.0.100:50000/MyDomain" \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/test.properties b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/test.properties
new file mode 100644
index 0000000000..df9d47b4eb
--- /dev/null
+++ b/sandbox/sebastien/java/shell/modules/node-impl/src/test/resources/test.properties
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+defaultScheme = vm
+foo.bla = xyz
+
+