Re-organization of the source code:
the runtime project now contains the core, composite and annotations.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1326404 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/DEPENDENCIES b/ipojo/runtime/DEPENDENCIES
new file mode 100644
index 0000000..8675421
--- /dev/null
+++ b/ipojo/runtime/DEPENDENCIES
@@ -0,0 +1,15 @@
+Apache Felix iPOJO Runtime Project
+Copyright 2008-2012 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+
+II. Used Third-Party Software
+
+
+III. Overall License Summary
+- Apache License 2.0
diff --git a/ipojo/runtime/LICENSE b/ipojo/runtime/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ipojo/runtime/LICENSE
@@ -0,0 +1,202 @@
+
+ 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/ipojo/runtime/NOTICE b/ipojo/runtime/NOTICE
new file mode 100644
index 0000000..3325754
--- /dev/null
+++ b/ipojo/runtime/NOTICE
@@ -0,0 +1,10 @@
+Apache Felix iPOJO Runtime Project
+Copyright 2008-2012 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Copyright (c) 2000-2005 INRIA, France Telecom
+Licensed under BSD License.
diff --git a/ipojo/runtime/annotations/DEPENDENCIES b/ipojo/runtime/annotations/DEPENDENCIES
new file mode 100644
index 0000000..f759d7a
--- /dev/null
+++ b/ipojo/runtime/annotations/DEPENDENCIES
@@ -0,0 +1,13 @@
+Apache Felix iPOJO Annotations
+Copyright 2008-2011 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+II. Used Third-Party Software
+
+III. Overall License Summary
+- Apache License 2.0
diff --git a/ipojo/runtime/annotations/LICENSE b/ipojo/runtime/annotations/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ipojo/runtime/annotations/LICENSE
@@ -0,0 +1,202 @@
+
+ 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/ipojo/runtime/annotations/NOTICE b/ipojo/runtime/annotations/NOTICE
new file mode 100644
index 0000000..b1ed786
--- /dev/null
+++ b/ipojo/runtime/annotations/NOTICE
@@ -0,0 +1,7 @@
+Apache Felix iPOJO Annotations
+Copyright 2008-2011 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
diff --git a/ipojo/runtime/annotations/doc/changelog.txt b/ipojo/runtime/annotations/doc/changelog.txt
new file mode 100644
index 0000000..e2c9f16
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/changelog.txt
@@ -0,0 +1,74 @@
+Changes from the 1.6.4 to 1.8.0
+-------------------------------
+** Bug
+ * [FELIX-2568] - Rename Transactionnal to Transactional
+
+** Improvement
+ * [FELIX-1428] - Constructor injection of Configuration properties
+ * [FELIX-2620] - Change iPojo annotation parameters to follow java naming conventions
+ * [FELIX-2621] - Rename annotations to remove collisions
+ * [FELIX-2622] - Support static service properties that are not mirrored into fields
+ * [FELIX-2624] - Support multiple whiteboards using annotations
+ * [FELIX-2630] - Rename @Component attributes to follow the java naming conventions
+ * [FELIX-2631] - Rename @Publisher and @Subscriber attributes to follow the java naming conventions
+ * [FELIX-2633] - Rename JMX annotations
+ * [FELIX-2634] - Rename the @Publisher annotation into @Publishes annotation to avoid collision
+ * [FELIX-2666] - Rename the temporal handler annotation to avoid collision
+ * [FELIX-2742] - Constructor injection of service dependencies
+
+Changes from the 1.6.2 to 1.6.4
+-------------------------------
+** Improvement
+ * [FELIX-2420] - Enum support for @Property annotation
+ * [FELIX-2461] - Allow specifying the targeted service interface in the @ServiceController
+
+Changes from the 1.6.0 to 1.6.2
+-------------------------------
+** Improvement
+ * [FELIX-2296] - Access to ServiceReference in iPOJO service.
+
+Changes from the 1.4.0 to 1.6.0
+-------------------------------
+** Bug
+ * [FELIX-1557] - Cosmetic change of the Bundle-Name and Bundle-SymbolicName in iPOJO annotations.
+
+** Improvement
+ * [FELIX-1426] - Service injection with Dynamic Proxies
+ * [FELIX-1427] - Service injection with Smart Proxies
+ * [FELIX-1906] - Allow calling a method when service properties of an already injected service are modified
+
+** New Feature
+ * [FELIX-2132] - Provides a way to control service exposition from the implementation class
+
+** Wish
+ * [FELIX-1940] - Add @Instance annotation to declare instances without metadata.xml
+
+Changes from 1.2.0 to 1.4.0
+---------------------------
+** Bug
+ * [FELIX-1319] - Issue in the metadata overriding analysis in iPOJO annotations
+
+** Improvement
+ * Update parent pom
+
+
+Changes from 1.0.0 to 1.2.0
+---------------------------
+** Improvement
+ * [FELIX-825] - Provide annotation for iPojo JMX Handler
+ * [FELIX-834] - Provide Annotations for the extender, whiteboard and event admin handlers
+
+** Bug
+ * [FELIX-867] - iPOJO @ServiceProperty can target method
+ * [FELIX-868] - iPOJO @Component don't support factory methods
+
+
+Changes from 0.8.0 to 1.0.0
+---------------------------
+** Improvement
+ * [FELIX-627] - Temporal dependency annotation
+
+
+Version 0.8.0
+-------------
+ * Initial release
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations.html b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations.html
new file mode 100644
index 0000000..1ce24ba
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations.html
@@ -0,0 +1,744 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head>
+
+
+
+ <title>Apache Felix - How to use iPOJO Annotations</title>
+ <link rel="stylesheet" href="how-to-use-ipojo-annotations_files/site.css" type="text/css" media="all">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head><body>
+ <div class="title"><div class="logo"><a href="http://felix.apache.org/site/index.html"><img alt="Apache Felix" src="how-to-use-ipojo-annotations_files/logo.png" border="0"></a></div><div class="header"><a href="http://www.apache.org/"><img alt="Apache" src="how-to-use-ipojo-annotations_files/apache.png" border="0"></a></div></div>
+ <div class="menu">
+<ul>
+ <li><a href="http://felix.apache.org/site/news.html" title="news">news</a></li>
+ <li><a href="http://felix.apache.org/site/license.html" title="license">license</a></li>
+ <li><a href="http://felix.apache.org/site/downloads.cgi" rel="nofollow">downloads</a></li>
+ <li><a href="http://felix.apache.org/site/documentation.html" title="documentation">documentation</a></li>
+ <li><a href="http://felix.apache.org/site/mailinglists.html" title="mailinglists">mailing lists</a></li>
+ <li><a href="http://felix.apache.org/site/contributing.html" title="Contributing">contributing</a></li>
+ <li><a href="http://www.apache.org/" rel="nofollow">asf</a></li>
+ <li><a href="http://www.apache.org/foundation/sponsorship.html" rel="nofollow">sponsorship</a></li>
+ <li><a href="http://www.apache.org/foundation/thanks.html" rel="nofollow">sponsors</a>
+<!-- ApacheCon Ad -->
+<iframe src="how-to-use-ipojo-annotations_files/button.html" style="border-width: 0pt; float: left;" scrolling="no" frameborder="0" height="135" width="135"></iframe>
+<p style="height: 100px;">
+<!-- ApacheCon Ad -->
+</p></li></ul> </div>
+ <div class="main">
+<style type="text/css">
+ @import url(http://people.apache.org/~clement/ipojo/site/superfish.css);
+</style>
+
+<style type="text/css">
+ @import url(http://people.apache.org/~clement/ipojo/site/style.css);
+</style>
+
+<p>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shCore.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushCSharp.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushPhp.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushJScript.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushVb.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushSql.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushXml.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushShell.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushDelphi.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushPython.js"></script>
+<script class="javascript" src="how-to-use-ipojo-annotations_files/shBrushJava.js"></script>
+
+<script type="text/javascript" src="how-to-use-ipojo-annotations_files/jquery-1.js"></script>
+<script type="text/javascript" src="how-to-use-ipojo-annotations_files/hoverIntent.js"></script>
+<script type="text/javascript" src="how-to-use-ipojo-annotations_files/superfish.js"></script>
+<script type="text/javascript" src="how-to-use-ipojo-annotations_files/supersubs.js"></script>
+
+<script type="text/javascript">
+
+ $(document).ready(function(){
+ $("ul.sf-menu").supersubs({
+ minWidth: 14, // minimum width of sub-menus in em units
+ maxWidth: 30, // maximum width of sub-menus in em units
+ extraWidth: 1 // extra width can ensure lines don't sometimes turn over
+ // due to slight rounding differences and font-family
+ }).superfish(); // call supersubs first, then superfish, so that subs are
+ // not display:none when measuring. Call before initialising
+ // containing tabs for same reason.
+ });
+
+</script>
+</p><div class="main">
+<div class="page-header">
+<img src="how-to-use-ipojo-annotations_files/header.png" class="header">
+<a href="http://ipojo.org/"><img src="how-to-use-ipojo-annotations_files/ipojo.png" class="header-logo" width="225"></a>
+<ul class="sf-menu sf-js-enabled sf-shadow" id="ipojo-menu">
+<li class="current">
+<!-- Menu Overview -->
+<a href="" class="sf-with-ul">Overview<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo.html" title="Apache Felix iPOJO">Home</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-why-choose-ipojo.html" title="apache-felix-ipojo-why-choose-ipojo">Why choose iPOJO</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-successstories.html" title="apache-felix-ipojo-successstories">Success stories</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-feature-overview.html" title="Apache Felix iPOJO Feature Overview">Features</a>
+ </li>
+</ul>
+</li>
+
+<li class="">
+<!-- Menu download -->
+</li><li>
+<a href="http://felix.apache.org/site/download.html" title="Download">Download </a>
+</li>
+
+<li class="">
+<!-- Menu Documentation -->
+<a href="" class="sf-with-ul">Documentation<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <!-- sub- menu : getting started -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">Getting Started<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-in-10-minutes.html" title="iPOJO in 10 minutes">iPOJO in 10 minutes</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="" title="How to use iPOJO Annotations">Using Annotations</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-hello-word-maven-based-tutorial.html" title="iPOJO Hello Word (Maven-Based) tutorial">Maven tutorial</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-advanced-tutorial.html" title="iPOJO Advanced Tutorial">Advanced tutorial</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-dosgi.html" title="apache-felix-ipojo-dosgi">Using Distributed OSGi</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-composition-tutorial.html" title="iPOJO Composition Tutorial">Application Composition</a></li>
+ </ul>
+ </li> <!-- end of getting started -->
+ <!-- sub menu : Describing Components -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/describing-components.html" class="sf-with-ul">Describing components<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/service-requirement-handler.html" title="Service Requirement Handler">Requiring a service</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/providing-osgi-services.html" title="Providing OSGi services">Providing a service</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/lifecycle-callback-handler.html" title="Lifecycle Callback Handler">Lifecycle management</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/configuration-handler.html" title="Configuration Handler">Configuration</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/architecture-handler.html" title="Architecture Handler">Introspection</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/controller-lifecycle-handler.html" title="Controller Lifecycle Handler">Impacting the lifecycle</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/event-admin-handlers.html" title="Event Admin Handlers">Asynchronous communication</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-jmx-handler.html" title="iPOJO JMX Handler">JMX management</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/extender-pattern-handler.html" title="Extender Pattern Handler">Extender pattern</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/white-board-pattern-handler.html" title="White Board Pattern Handler">Whiteboard pattern</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/temporal-service-dependency.html" title="Temporal Service Dependency">Temporal dependencies</a></li>
+ </ul>
+ </li> <!-- End of describing components -->
+ <!-- sub- menu : User Guide -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">User Guide<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/using-xml-schemas.html" title="Using XML Schemas">XML Schemas</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-api.html" title="apache-felix-ipojo-api">API</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-testing-components.html" title="apache-felix-ipojo-testing-components">Testing components</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-eclipse-integration.html" title="apache-felix-ipojo-eclipse-integration">Eclipse Integration</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-faq.html" title="iPOJO FAQ">FAQ</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-reference-card.html" title="iPOJO-Reference-Card">Reference Card</a></li>
+ </ul>
+ </li> <!-- end of user guide -->
+ <!-- sub- menu : Dev Guide -->
+ <li class="" style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">Advanced Topics<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/ipojo/api/1.2.0" rel="nofollow">Javadoc</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/how-to-write-your-own-handler.html" title="How to write your own handler">Handler guide</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/how-to-use-ipojo-manipulation-metadata.html" title="How to use iPOJO Manipulation Metadata">Manipulation Metadata </a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/dive-into-the-ipojo-manipulation-depths.html" title="Dive into the iPOJO Manipulation depths">Dive into the iPOJO Manipulation depths</a></li>
+ </ul>
+ </li> <!-- End of Dev guide -->
+</ul>
+</li> <!-- End of doc -->
+<!-- Menu 4 : Tools -->
+<li class="">
+<a href="" class="sf-with-ul">Tools<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-ant-task.html" title="iPOJO Ant Task">Ant Task</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-eclipse-plug-in.html" title="iPOJO Eclipse Plug-in">Eclipse Plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-maven-plug-in.html" title="iPOJO Maven Plug-in">Maven Plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-online-manipulator.html" title="apache-felix-ipojo-online-manipulator">Online Manipulator</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-webconsole-plugin.html" title="iPOJO Webconsole Plugin">Webconsole plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-junit4osgi.html" title="apache-felix-ipojo-junit4osgi">Junit4OSGi</a></li>
+</ul>
+</li><!-- End of tools -->
+<!-- Menu 5 : Misc -->
+<li class="">
+<a href="" class="sf-with-ul">Misc<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-issuestracker.html" title="apache-felix-ipojo-issuestracker">Issues Tracker</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-supportedvms.html" title="apache-felix-ipojo-supportedVMs">Supported JVMs</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-supportedosgi.html" title="apache-felix-ipojo-supportedOSGi">Supported OSGi Implementations</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://ipojo-dark-side.blogspot.com/" rel="nofollow">iPOJO's Dark Side Blog</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/article-presentations.html" title="Article & Presentations">Article & Presentations</a></li>
+</ul>
+</li><!-- End of misc -->
+</ul> <!-- End of the menu -->
+</div> <!-- Page header -->
+
+
+<div class="content">
+
+<h1><a name="HowtouseiPOJOAnnotations-HowtouseiPOJOannotations"></a>How to use iPOJO annotations</h1>
+
+<p><em>You can use annotations to define your component types. This page presents supported iPOJO annotations.</em></p>
+
+<div class="toc">
+<ul>
+ <li><a href="#HowtouseiPOJOAnnotations-GettingiPOJOAnnotations%253A">Getting iPOJO Annotations:</a></li>
+<ul>
+ <li><a href="#HowtouseiPOJOAnnotations-InEclipse">In Eclipse</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-InMaven">In Maven</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-InAnt">In Ant</a></li>
+</ul>
+ <li><a href="#HowtouseiPOJOAnnotations-Anexampleofusage">An example of usage</a></li>
+<ul>
+ <li><a href="#HowtouseiPOJOAnnotations-HelloServiceProvider">Hello Service Provider</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-HelloServiceConsumer">Hello Service Consumer</a></li>
+</ul>
+ <li><a href="#HowtouseiPOJOAnnotations-DefinedAnnotations">Defined Annotations</a></li>
+<ul>
+ <li><a href="#HowtouseiPOJOAnnotations-@Component">@Component</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Provides">@Provides</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Requires">@Requires</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@ServiceProperty">@ServiceProperty</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Property">@Property</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Updated%255CNewsinthe1.3.0SNAPSHOTversion%255C">@Updated <b>[ News in the 1.3.0-SNAPSHOT version ]</b></a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Bind">@Bind</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Unbind">@Unbind</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Validate">@Validate</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-@Invalidate">@Invalidate</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-TemporalDependencies%2528externalhandler%2529">Temporal Dependencies (external handler)</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-ExposinginstancesasaJMXMBean%2528externalhandler%2529">Exposing instances as a JMX MBean (external handler)</a></li>
+</ul>
+ <li><a href="#HowtouseiPOJOAnnotations-AdvancedtopicsandFAQ">Advanced topics and FAQ</a></li>
+<ul>
+ <li><a href="#HowtouseiPOJOAnnotations-Metadatafileandannotationmerge">Metadata file and annotation merge</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-Instancecreation">Instance creation</a></li>
+ <li><a href="#HowtouseiPOJOAnnotations-UsingCustomAnnotations">Using Custom Annotations</a></li>
+</ul>
+</ul></div>
+
+<h2><a name="HowtouseiPOJOAnnotations-GettingiPOJOAnnotations:"></a>Getting iPOJO Annotations:</h2>
+
+<p>iPOJO Annotations are defines inside the
+org.apache.felix.ipojo.annotations project. You can download the Jar
+file of this project from the <a href="http://felix.apache.org/site/download.html" title="Download">download</a> page. Sources are available on the <a href="http://felix.apache.org/site/sourcecode.html" rel="nofollow">Felix trunk</a>.<br>
+Once added to your class path / build path / dependencies, you can use
+the annotations as normal annotations. These annotations are
+automatically processed by the iPOJO manipulator.</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-InEclipse"></a>In Eclipse</h3>
+
+<p>Add the org.apache.felix.ipojo.annotations jar file in your build
+path. Do not forget to use a Java compiler accepting annotations (1.5
+or higher).</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-InMaven"></a>In Maven</h3>
+
+<p>Add the following dependency:</p>
+<div class="code">
+<div class="dp-highlighter"><table class="dp-xml" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="line1"><span></span><span class="tag"><</span><span>dependency</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"> <span class="tag"><</span><span>groupId</span><span class="tag">></span><span>org.apache.felix</span><span class="tag"></</span><span>groupId</span><span class="tag">></span><span> </span></td></tr><tr><td class="line1"> <span class="tag"><</span><span>artifactId</span><span class="tag">></span><span>org.apache.felix.ipojo.annotations</span><span class="tag"></</span><span>artifactId</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"> <span class="tag"><</span><span>version</span><span class="tag">></span><span>1.2.0</span><span class="tag"></</span><span>version</span><span class="tag">></span><span> </span></td></tr><tr><td class="line1"><span class="tag"></</span><span>dependency</span><span class="tag">></span><span> </span></td></tr></tbody></table></div><textarea style="display: none;" name="newcodemacro" class="xml:nocontrols:nogutter" rows="10" readonly="readonly"><dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.annotations</artifactId>
+ <version>1.2.0</version>
+</dependency></textarea>
+<script class="javascript">
+ if(!window.newcodemacro_initialised)
+ {
+ window.newcodemacro_initialised = true;
+ window.oldonloadmethod = window.onload;
+ window.onload = function(){
+ dp.SyntaxHighlighter.HighlightAll('newcodemacro');
+ if(window.oldonloadmethod)
+ {
+ window.oldonloadmethod();
+ }
+ }
+ }
+
+</script>
+</div>
+
+<p>Moreover, you need to set that the source code and the target code
+are Java 1.5 code. To achieve this, just add the following plugin in
+your plugins section:</p>
+<div class="code">
+<div class="dp-highlighter"><table class="dp-xml" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="line1"><span></span><span class="tag"><</span><span>plugin</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"> <span class="tag"><</span><span>groupId</span><span class="tag">></span><span>org.apache.maven.plugins</span><span class="tag"></</span><span>groupId</span><span class="tag">></span><span> </span></td></tr><tr><td class="line1"> <span class="tag"><</span><span>artifactId</span><span class="tag">></span><span>maven-compiler-plugin</span><span class="tag"></</span><span>artifactId</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"> <span class="tag"><</span><span>configuration</span><span class="tag">></span><span> </span></td></tr><tr><td class="line1"> <span class="tag"><</span><span>source</span><span class="tag">></span><span>1.5</span><span class="tag"></</span><span>source</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"> <span class="tag"><</span><span>target</span><span class="tag">></span><span>1.5</span><span class="tag"></</span><span>target</span><span class="tag">></span><span> </span></td></tr><tr><td class="line1"> <span class="tag"></</span><span>configuration</span><span class="tag">></span><span> </span></td></tr><tr><td class="line2"><span class="tag"></</span><span>plugin</span><span class="tag">></span><span> </span></td></tr></tbody></table></div><textarea style="display: none;" name="newcodemacro" class="xml:nocontrols:nogutter" rows="10" readonly="readonly"><plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+</plugin></textarea>
+<script class="javascript">
+ if(!window.newcodemacro_initialised)
+ {
+ window.newcodemacro_initialised = true;
+ window.oldonloadmethod = window.onload;
+ window.onload = function(){
+ dp.SyntaxHighlighter.HighlightAll('newcodemacro');
+ if(window.oldonloadmethod)
+ {
+ window.oldonloadmethod();
+ }
+ }
+ }
+
+</script>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-InAnt"></a>In Ant</h3>
+
+<p>Just add the org.apache.felix.ipojo.annotations jar file in your class path.</p>
+
+<h2><a name="HowtouseiPOJOAnnotations-Anexampleofusage"></a>An example of usage</h2>
+
+<p>To illustrate annotations usage, let taking the tutorial example. In this tutorial, there are two components:</p>
+<ul>
+ <li>The first one provides the hello service</li>
+ <li>The second one uses the provided hello service<br>
+You can download the archive containing the examples and a preconfigured version of Felix <a href="http://people.apache.org/%7Eclement/ipojo/tutorials/annotations/annotation-tutorial.zip" rel="nofollow">here</a>.</li>
+</ul>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-HelloServiceProvider"></a>Hello Service Provider</h3>
+
+<p>The provider uses two annotations. The "component" annotation is
+mandatory and defines that the class defines a component type. Then the
+"provides" annotation just declare that the defined component type
+provides a service.</p>
+<div class="code">
+<div class="dp-highlighter"><table class="dp-c" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="line1"><span></span><span class="keyword">package</span><span> ipojo.example.hello.impl; </span></td></tr><tr><td class="line2"> </td></tr><tr><td class="line1"><span class="keyword">import</span><span> ipojo.example.hello.Hello; </span></td></tr><tr><td class="line2"> </td></tr><tr><td class="line1"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Component; </span></td></tr><tr><td class="line2"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Provides; </span></td></tr><tr><td class="line1"> </td></tr><tr><td class="line2"><span class="comment">/**</span></td></tr><tr><td class="line1"><span class="comment"> * Component implementing the Hello service.</span></td></tr><tr><td class="line2"><span class="comment"> **/</span><span> </span></td></tr><tr><td class="line1"><span class="preprocessor">@Component</span><span> </span></td></tr><tr><td class="line2"><span class="preprocessor">@Provides</span><span> </span></td></tr><tr><td class="line1"><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> HelloImpl </span><span class="keyword">implements</span><span> Hello { </span></td></tr><tr><td class="line2"> <span class="keyword">public</span><span> String sayHello(String name) { </span></td></tr><tr><td class="line1"> <span class="keyword">return</span><span> </span><span class="string">"hello "</span><span> + name; </span></td></tr><tr><td class="line2"> } </td></tr><tr><td class="line1">} </td></tr></tbody></table></div><textarea style="display: none;" name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package ipojo.example.hello.impl;
+
+import ipojo.example.hello.Hello;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+
+/**
+ * Component implementing the Hello service.
+ **/
+@Component
+@Provides
+public class HelloImpl implements Hello {
+ public String sayHello(String name) {
+ return "hello " + name;
+ }
+}</textarea>
+<script class="javascript">
+ if(!window.newcodemacro_initialised)
+ {
+ window.newcodemacro_initialised = true;
+ window.oldonloadmethod = window.onload;
+ window.onload = function(){
+ dp.SyntaxHighlighter.HighlightAll('newcodemacro');
+ if(window.oldonloadmethod)
+ {
+ window.oldonloadmethod();
+ }
+ }
+ }
+
+</script>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-HelloServiceConsumer"></a>Hello Service Consumer</h3>
+
+<p>The Hello Service Consumer use more annotations. First it used the
+component annotation. To defines its "immediate" behavior, it add the
+'immediate' attribute.<br>
+Then, it uses the requires annotation to define a service dependency.
+Finally, it uses the validate and invalidate annotations to define
+lifecycle callbacks.</p>
+<div class="code">
+<div class="dp-highlighter"><table class="dp-c" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="line1"><span></span><span class="keyword">package</span><span> ipojo.example.hello.client; </span></td></tr><tr><td class="line2"> </td></tr><tr><td class="line1"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Component; </span></td></tr><tr><td class="line2"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Invalidate; </span></td></tr><tr><td class="line1"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Requires; </span></td></tr><tr><td class="line2"><span class="keyword">import</span><span> org.apache.felix.ipojo.annotations.Validate; </span></td></tr><tr><td class="line1"> </td></tr><tr><td class="line2"><span class="keyword">import</span><span> ipojo.example.hello.Hello; </span></td></tr><tr><td class="line1"><span class="preprocessor"></span></td></tr><tr><td class="line2"><span class="preprocessor">@Component(name="AnnotedHelloClient", immediate=true)</span><span> </span></td></tr><tr><td class="line1"><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> HelloClient </span><span class="keyword">implements</span><span> Runnable { </span></td></tr><tr><td class="line2"><span class="preprocessor"></span></td></tr><tr><td class="line1"><span class="preprocessor">@Requires</span><span> </span></td></tr><tr><td class="line2"><span class="keyword">private</span><span> Hello[] m_hello; </span><span class="comment">// Service Dependency</span><span> </span></td></tr><tr><td class="line1"> </td></tr><tr><td class="line2"><span class="keyword">private</span><span> </span><span class="keyword">final</span><span> </span><span class="keyword">static</span><span> int DELAY=10000; </span></td></tr><tr><td class="line1"><span class="keyword">private</span><span> </span><span class="keyword">boolean</span><span> end; </span></td></tr><tr><td class="line2"> </td></tr><tr><td class="line1"> <span class="keyword">public</span><span> </span><span class="keyword">void</span><span> run() { </span></td></tr><tr><td class="line2"> <span class="keyword">while</span><span> (!end) { </span></td></tr><tr><td class="line1"> <span class="keyword">try</span><span> { </span></td></tr><tr><td class="line2"> invoke(); </td></tr><tr><td class="line1"> Thread.sleep(DELAY); </td></tr><tr><td class="line2"> } <span class="keyword">catch</span><span> (InterruptedException ie) { } </span></td></tr><tr><td class="line1"> <span class="comment">/* will recheck end */</span><span> </span></td></tr><tr><td class="line2"> } </td></tr><tr><td class="line1">} </td></tr><tr><td class="line2"> </td></tr><tr><td class="line1"><span class="keyword">public</span><span> </span><span class="keyword">void</span><span> invoke() { </span></td></tr><tr><td class="line2"> for (int i = 0; i < m_hello.length; i++) { </td></tr><tr><td class="line1"> System.out.println(m_hello[i]. </td></tr><tr><td class="line2"> sayHello(<span class="string">"Clement"</span><span>)); </span></td></tr><tr><td class="line1"> } </td></tr><tr><td class="line2">} </td></tr><tr><td class="line1"><span class="preprocessor"></span></td></tr><tr><td class="line2"><span class="preprocessor"> @Validate</span><span> </span></td></tr><tr><td class="line1"> <span class="keyword">public</span><span> </span><span class="keyword">void</span><span> starting() { </span></td></tr><tr><td class="line2"> Thread T = new Thread(<span class="keyword">this</span><span>); </span></td></tr><tr><td class="line1"> end = <span class="keyword">false</span><span>; </span></td></tr><tr><td class="line2"> T.start(); </td></tr><tr><td class="line1"> } </td></tr><tr><td class="line2"><span class="preprocessor"></span></td></tr><tr><td class="line1"><span class="preprocessor"> @Invalidate</span><span> </span></td></tr><tr><td class="line2"> <span class="keyword">public</span><span> </span><span class="keyword">void</span><span> stopping() { </span></td></tr><tr><td class="line1"> end = <span class="keyword">true</span><span>; </span></td></tr><tr><td class="line2"> } </td></tr><tr><td class="line1">} </td></tr></tbody></table></div><textarea style="display: none;" name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package ipojo.example.hello.client;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Invalidate;
+import org.apache.felix.ipojo.annotations.Requires;
+import org.apache.felix.ipojo.annotations.Validate;
+
+import ipojo.example.hello.Hello;
+
+@Component(name="AnnotedHelloClient", immediate=true)
+public class HelloClient implements Runnable {
+
+@Requires
+private Hello[] m_hello; // Service Dependency
+
+private final static int DELAY=10000;
+private boolean end;
+
+ public void run() {
+ while (!end) {
+ try {
+ invoke();
+ Thread.sleep(DELAY);
+ } catch (InterruptedException ie) { }
+ /* will recheck end */
+ }
+}
+
+public void invoke() {
+ for (int i = 0; i < m_hello.length; i++) {
+ System.out.println(m_hello[i].
+ sayHello("Clement"));
+ }
+}
+
+ @Validate
+ public void starting() {
+ Thread T = new Thread(this);
+ end = false;
+ T.start();
+ }
+
+ @Invalidate
+ public void stopping() {
+ end = true;
+ }
+}</textarea>
+<script class="javascript">
+ if(!window.newcodemacro_initialised)
+ {
+ window.newcodemacro_initialised = true;
+ window.oldonloadmethod = window.onload;
+ window.onload = function(){
+ dp.SyntaxHighlighter.HighlightAll('newcodemacro');
+ if(window.oldonloadmethod)
+ {
+ window.oldonloadmethod();
+ }
+ }
+ }
+
+</script>
+</div>
+
+
+<h2><a name="HowtouseiPOJOAnnotations-DefinedAnnotations"></a>Defined Annotations</h2>
+
+<p>This section lists defined annotations and how to use them.</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-@Component"></a>@Component</h3>
+
+<p><b>Goal:</b> Defines a component type<br>
+<b>Target:</b> The component implementation class<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>name : defines the component type name (optional, default = the class name)</li>
+ <li>immediate: defines the component type as immediate (optional, default = "false")</li>
+ <li>architecture: enable the architecture exposition (optional, default = "false")</li>
+ <li>propagation: enable configuration property propagation (on provided services) (optional, default = "false")</li>
+ <li>managedservice : set the Managed Service PID. (optional, default = no PID (i.e. the managed service will not be exposed)).</li>
+ <li>factory_method
+: set the factory-method. The specified method must be a static method
+and return a pojo object.(optional, default = iPOJO uses the 'regular'
+constructor).</li>
+ <li>public_factory : set if the component type is public. (optional, default = true).</li>
+</ul>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Provides"></a>@Provides</h3>
+
+<p><b>Goal:</b> Defines that the component type provide services<br>
+<b>Target:</b> The component implementation class<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>specifications: defines the provided interface (optional, default = all implemented interfaces)</li>
+ <li>strategy
+: the service object creation strategy. Possible values : SINGLETON,
+SERVICE, METHOD, INSTANCE or the strategy class name. With SINGLETON
+there is only one POJO per component instance, SERVICE means OSGi
+Service factory, METHOD delegates the creation to the factory-method of
+the component, INSTANCE creates one service object per requiring
+instance. For other strategies, specify the qualified name of the
+CreationStrategy class. (optional, default = SINGLETON)</li>
+</ul>
+
+
+
+<div class="box">
+ <div class="box-blue-header">
+ <div class="box-blue-title">
+ <img src="how-to-use-ipojo-annotations_files/information.gif"> <b>OSGi Service Factory</b>
+ </div>
+ </div>
+ <div class="box-blue-content">
+The <tt>SERVICE</tt> strategy means OSGi service factory. So, one service object per asking bundle will be created.
+ </div>
+ <div class="box-blue-footer"></div>
+</div>
+
+
+
+
+<div class="box">
+ <div class="box-blue-header">
+ <div class="box-blue-title">
+ <img src="how-to-use-ipojo-annotations_files/information.gif"> <b>Changes between the 1.0.0 and the 1.2.0</b>
+ </div>
+ </div>
+ <div class="box-blue-content">
+The <tt>factory</tt> attribute became <tt>strategy</tt>. A third policy is now available (<tt>instance</tt>) allowing to create one service object per asking instance. Moreover, it is also possible to indicates the <tt>CreationStrategy</tt> class name.
+ </div>
+ <div class="box-blue-footer"></div>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Requires"></a>@Requires</h3>
+
+<p><b>Goal:</b> Defines a service dependency<br>
+<b>Target:</b> Field<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>Filter: defines the LDAP filter (optional)</li>
+ <li>Optional: defines if the dependency is optional (optional, default = "false")</li>
+ <li>Id:
+defines the dependency Id (useful to identify bind & unbind
+methods) (optional, default = field name) (if a dependency with the
+same id is already created (by a @bind or @unbind annotation), it
+merges the dependencies).</li>
+ <li>Nullable: enable or disable the
+Null Object injection when the dependency is optional and no providers
+are available (optional, default = "true")</li>
+ <li>Default-Implementation: set the Default-Implmentation (optional, by default iPOJO uses a Null object)</li>
+ <li>Policy: defines the binding policy (accepted value : dynamic, static, dynamic-priority) (optional, default = "dynamic")</li>
+ <li>Comparator: defines the comparator to use to sort service references (optional, default = OSGi Service Reference Comparator)</li>
+ <li>From : defines the specific provider to use</li>
+ <li>Specification
+: the required service specification. This attribute is required for
+Collection field. (optional, default = annotated field type).</li>
+</ul>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@ServiceProperty"></a>@ServiceProperty</h3>
+
+<p><b>Goal:</b> Defines a service property<br>
+<b>Target:</b> Field<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>name: property name (optional, default=field name</li>
+ <li>value: property value (optional, default=no value)</li>
+ <li>mandatory : is the property mandatory? (optional, default=false)</li>
+</ul>
+
+
+
+<div class="box">
+ <div class="box-blue-header">
+ <div class="box-blue-title">
+ <img src="how-to-use-ipojo-annotations_files/information.gif"> <b>Mandatory property</b>
+ </div>
+ </div>
+ <div class="box-blue-content">
+A mandatory property must receive a value either from the component type description (<tt>value</tt> attribute, or the instance configuration.
+ </div>
+ <div class="box-blue-footer"></div>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Property"></a>@Property</h3>
+
+<p><b>Goal:</b> Defines a property<br>
+<b>Target:</b> Field or Method<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>name: property name (optional, default=field name computed by
+removing "set" from the method name (for instance setFoo(String ff)
+will get the Foo name))</li>
+ <li>value: property value (optional, default=no value)</li>
+ <li>mandatory : is the property mandatory? (optional, default=false)</li>
+</ul>
+
+
+
+<div class="box">
+ <div class="box-blue-header">
+ <div class="box-blue-title">
+ <img src="how-to-use-ipojo-annotations_files/information.gif"> <b>Field and Method</b>
+ </div>
+ </div>
+ <div class="box-blue-content">
+If another property with the same name is defined the method or the field is added to the existing property.
+ </div>
+ <div class="box-blue-footer"></div>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Updated\Newsinthe1.3.0SNAPSHOTversion\"></a>@Updated <b>[ News in the 1.3.0-SNAPSHOT version ]</b></h3>
+<p><b>Goal:</b> Defines method called when a reconfiguration is completed.<br>
+<b>Target:</b> a method (receiving a dictionary in argument)</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-@Bind"></a>@Bind</h3>
+
+<p><b>Goal:</b> Defines a bind method<br>
+<b>Target:</b> Method<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>Id: Dependency Id, if the id is already defines in a
+"@requires " or "@unbind" annotation, it adds this method as a bind
+method of the already created dependency. (optional, default= no id,
+compute an id if the method name begin by "bind" (for instance
+"bindFoo" will have the "Foo" id))</li>
+ <li>Specification : required dependency (optional)</li>
+ <li>Aggregate : is the dependency an aggregate dependency (optional, default= "false")</li>
+ <li>Optional: is the dependency an optional dependency (optional, default= "false")</li>
+ <li>Filter: dependency LDAP filter (optional)</li>
+ <li>Policy: defines the binding policy (accepted value : dynamic, static, dynamic-priority) (optional, default = "dynamic")</li>
+ <li>Comparator: defines the comparator to use to sort service references (optional, default = OSGi Service Reference Comparator)</li>
+ <li>From : defines the specific provider to use</li>
+</ul>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Unbind"></a>@Unbind</h3>
+
+<p><b>Goal:</b> Defines an unbind method<br>
+<b>Target:</b> Method<br>
+<b>Attributes:</b></p>
+<ul>
+ <li>Id: Dependency Id, if the id is already defines in a
+"@requires" or "@bind" annotation, it adds this method as an unbind
+method of the already created dependency. (optional, default= no id,
+compute an id if the method name begin by "unbind" (for instance
+"unbindFoo" will have the "Foo" id))</li>
+ <li>Specification : required dependency (optional)</li>
+ <li>Aggregate : is the dependency an aggregate dependency (optional, default= "false")</li>
+ <li>Optional: is the dependency an optional dependency (optional, default= "false")</li>
+ <li>Filter: dependency LDAP filter (optional)</li>
+ <li>Policy: defines the binding policy (accepted value : dynamic, static, dynamic-priority) (optional, default = "dynamic")</li>
+ <li>Comparator: defines the comparator to use to sort service references (optional, default = OSGi Service Reference Comparator)</li>
+ <li>From : defines the specific provider to use</li>
+</ul>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-@Validate"></a>@Validate</h3>
+
+<p><b>Goal:</b> defines a validate lifecycle callback<br>
+<b>Target:</b> method</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-@Invalidate"></a>@Invalidate</h3>
+
+<p><b>Goal:</b> defines a validate lifecycle callback<br>
+<b>Target:</b> method</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-TemporalDependencies(externalhandler)"></a>Temporal Dependencies (external handler)</h3>
+
+<p>The temporal dependency handler is an external handler. However, it
+can be used with an annotation defined in the iPOJO annotations jar
+file. <br>
+The annotation is org.apache.felix.ipojo.handler.temporal.Requires and targets a field. <br>
+<b>Attributes:</b></p>
+<ul>
+ <li>filter : specify the dependency filter</li>
+ <li>timeout : specify the dependency timeout (optional)</li>
+ <li>onTimeout
+: specify the onTimeout action (null, nullable, empty-array,
+default-implementation (specify the class name in this case) (optional).</li>
+ <li>specification
+: the required service specification. This attribute is required for
+Collection field. (optional, default = annotated field type).</li>
+ <li>Proxy : Inject a proxy instead of the real object. This allows passing this reference to collaborators. (Default = false)</li>
+</ul>
+
+
+
+
+<h3><a name="HowtouseiPOJOAnnotations-ExposinginstancesasaJMXMBean(externalhandler)"></a>Exposing instances as a JMX MBean (external handler)</h3>
+
+<p>The JMX Handler allows exposing an instance as a JMX MBean. To
+configure the JMX handler directly from your code, three annotations
+are provided. They are in the <tt>org.apache.felix.ipojo.handlers.jmx</tt> package</p>
+
+<p>The <tt>@org.apache.felix.ipojo.handlers.jmx.Config</tt> (<tt>@Config</tt> if the package it correctly imported) annotation is a type annotation (so placed on the <tt>class</tt> element. This annotation indicates that the instance will be exposed as an MBean. This annotation supports:</p>
+<ul>
+ <li>usesMOSGi: set to <tt>true</tt> to use MOSGi. Otherwise, the MBean will be exposed in the MBean Platform Server (default: <tt>false</tt>).</li>
+ <li>objectname: set the MBean objectname. The objectname must follow JMX specification. (default: <tt>package-name:factory-name:instance-name</tt>)</li>
+ <li>domain: set the MBean domain. (default: <tt>package-name</tt>)</li>
+ <li>name: set the MBean name. (default: <tt>instance-name</tt>).</li>
+</ul>
+
+
+<p>The <tt>@org.apache.felix.ipojo.handlers.jmx.Property</tt> (<tt>@Property</tt>) annotation is a field annotation indicating that the field is exposed in the MBean. The supported attributes are:</p>
+<ul>
+ <li>name: set the property name</li>
+ <li>rights: set the access permission. Possible values are <tt>r</tt> (read only) and <tt>w</tt> (read and write). By default, properties are in read-only mode.</li>
+ <li>notification: enables notification on this property. By default notifications are disabled.</li>
+</ul>
+
+
+<p>The <tt>@org.apache.felix.ipojo.handlers.jmx.Method</tt> (<tt>@Method</tt>) annotation is a method annotation indicating that the method is exposed in the MBean. Only one attribute can be customized:</p>
+<ul>
+ <li>description: set the method description.</li>
+</ul>
+
+
+
+<h2><a name="HowtouseiPOJOAnnotations-AdvancedtopicsandFAQ"></a>Advanced topics and FAQ</h2>
+
+<h3><a name="HowtouseiPOJOAnnotations-Metadatafileandannotationmerge"></a>Metadata file and annotation merge</h3>
+
+<p>It is possible to defines component type both in the metadata file
+(in XML) and by using annotation. However, if a component type defined
+by using annotations has the same name than a type define in the XML
+file, the XML descriptor override the annotation defined type. However,
+a warning message is launched during the manipulation.</p>
+
+<h3><a name="HowtouseiPOJOAnnotations-Instancecreation"></a>Instance creation</h3>
+
+<p>Annotation can only be used to define component type. To define
+instances, you need to use the XML descriptor. Instance can refer to
+annotated types by referring to their names.</p>
+<div class="code">
+<div class="dp-highlighter"><table class="dp-xml" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="line1"><span></span><span class="tag"><</span><span>instance </span><span class="attribute">component</span><span>=</span><span class="attribute-value">"ipojo.example.hello.impl.HelloImpl"</span><span>/> </span></td></tr><tr><td class="line2"><span class="tag"><</span><span>instance </span><span class="attribute">component</span><span>=</span><span class="attribute-value">"AnnotedHelloClient"</span><span>/> </span></td></tr></tbody></table></div><textarea style="display: none;" name="newcodemacro" class="xml:nocontrols:nogutter" rows="10" readonly="readonly"><instance component="ipojo.example.hello.impl.HelloImpl"/>
+<instance component="AnnotedHelloClient"/></textarea>
+<script class="javascript">
+ if(!window.newcodemacro_initialised)
+ {
+ window.newcodemacro_initialised = true;
+ window.oldonloadmethod = window.onload;
+ window.onload = function(){
+ dp.SyntaxHighlighter.HighlightAll('newcodemacro');
+ if(window.oldonloadmethod)
+ {
+ window.oldonloadmethod();
+ }
+ }
+ }
+
+</script>
+</div>
+
+
+<h3><a name="HowtouseiPOJOAnnotations-UsingCustomAnnotations"></a>Using Custom Annotations</h3>
+
+<p>External handlers can provides their own annotations. Using these
+annotations just requires to add them to your build path. To external
+handlers annotations, please refer to the external handler
+documentation.
+<br clear="all">
+<br clear="all"></p>
+
+ </div>
+ <img src="how-to-use-ipojo-annotations_files/footer.png" class="footer">
+</div>
+
+<script type="text/javascript">
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script src="how-to-use-ipojo-annotations_files/ga.js" type="text/javascript"></script>
+<script type="text/javascript">
+try{
+var pageTracker = _gat._getTracker("UA-1518442-4");
+pageTracker._trackPageview();
+} catch(err) {}
+</script>
+
+ </div>
+ </body><div FirebugVersion="1.4.0" style="display: none;" id="_firebugConsole"></div></html>
\ No newline at end of file
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/apache.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/apache.png
new file mode 100644
index 0000000..5132f65
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/apache.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button.html b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button.html
new file mode 100644
index 0000000..1721083
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button.html
@@ -0,0 +1,2 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"></head><body></body></html>
\ No newline at end of file
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button_data/2009-europe-125x125.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button_data/2009-europe-125x125.png
new file mode 100644
index 0000000..654a99c
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/button_data/2009-europe-125x125.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/feed-icon-32x32.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/feed-icon-32x32.png
new file mode 100644
index 0000000..ea50b84
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/feed-icon-32x32.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/footer.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/footer.png
new file mode 100644
index 0000000..15be425
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/footer.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ga.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ga.js
new file mode 100644
index 0000000..0501070
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ga.js
@@ -0,0 +1,41 @@
+var _gat=new Object({c:"length",lb:"4.3.1",m:"cookie",b:undefined,cb:function(d,a){this.zb=d;this.Nb=a},r:"__utma=",W:"__utmb=",ma:"__utmc=",Ta:"__utmk=",na:"__utmv=",oa:"__utmx=",Sa:"GASO=",X:"__utmz=",lc:"http://www.google-analytics.com/__utm.gif",mc:"https://ssl.google-analytics.com/__utm.gif",Wa:"utmcid=",Ya:"utmcsr=",$a:"utmgclid=",Ua:"utmccn=",Xa:"utmcmd=",Za:"utmctr=",Va:"utmcct=",Hb:false,_gasoDomain:undefined,_gasoCPath:undefined,e:window,a:document,k:navigator,t:function(d){var a=1,c=0,h,
+o;if(!_gat.q(d)){a=0;for(h=d[_gat.c]-1;h>=0;h--){o=d.charCodeAt(h);a=(a<<6&268435455)+o+(o<<14);c=a&266338304;a=c!=0?a^c>>21:a}}return a},C:function(d,a,c){var h=_gat,o="-",k,l,s=h.q;if(!s(d)&&!s(a)&&!s(c)){k=h.w(d,a);if(k>-1){l=d.indexOf(c,k);if(l<0)l=d[h.c];o=h.F(d,k+h.w(a,"=")+1,l)}}return o},Ea:function(d){var a=false,c=0,h,o;if(!_gat.q(d)){a=true;for(h=0;h<d[_gat.c];h++){o=d.charAt(h);c+="."==o?1:0;a=a&&c<=1&&(0==h&&"-"==o||_gat.P(".0123456789",o))}}return a},d:function(d,a){var c=encodeURIComponent;
+return c instanceof Function?(a?encodeURI(d):c(d)):escape(d)},J:function(d,a){var c=decodeURIComponent,h;d=d.split("+").join(" ");if(c instanceof Function)try{h=a?decodeURI(d):c(d)}catch(o){h=unescape(d)}else h=unescape(d);return h},Db:function(d){return d&&d.hash?_gat.F(d.href,_gat.w(d.href,"#")):""},q:function(d){return _gat.b==d||"-"==d||""==d},Lb:function(d){return d[_gat.c]>0&&_gat.P(" \n\r\t",d)},P:function(d,a){return _gat.w(d,a)>-1},h:function(d,a){d[d[_gat.c]]=a},T:function(d){return d.toLowerCase()},
+z:function(d,a){return d.split(a)},w:function(d,a){return d.indexOf(a)},F:function(d,a,c){c=_gat.b==c?d[_gat.c]:c;return d.substring(a,c)},uc:function(){var d=_gat.b,a=window;if(a&&a.gaGlobal&&a.gaGlobal.hid)d=a.gaGlobal.hid;else{d=Math.round(Math.random()*2147483647);a.gaGlobal=a.gaGlobal?a.gaGlobal:{};a.gaGlobal.hid=d}return d},wa:function(){return Math.round(Math.random()*2147483647)},Gc:function(){return(_gat.wa()^_gat.vc())*2147483647},vc:function(){var d=_gat.k,a=_gat.a,c=_gat.e,h=a[_gat.m]?
+a[_gat.m]:"",o=c.history[_gat.c],k,l,s=[d.appName,d.version,d.language?d.language:d.browserLanguage,d.platform,d.userAgent,d.javaEnabled()?1:0].join("");if(c.screen)s+=c.screen.width+"x"+c.screen.height+c.screen.colorDepth;else if(c.java){l=java.awt.Toolkit.getDefaultToolkit().getScreenSize();s+=l.screen.width+"x"+l.screen.height}s+=h;s+=a.referrer?a.referrer:"";k=s[_gat.c];while(o>0)s+=o--^k++;return _gat.t(s)}});_gat.hc=function(){var d=this,a=_gat.cb;function c(h,o){return new a(h,o)}d.db="utm_campaign";d.eb="utm_content";d.fb="utm_id";d.gb="utm_medium";d.hb="utm_nooverride";d.ib="utm_source";d.jb="utm_term";d.kb="gclid";d.pa=0;d.I=0;d.wb="15768000";d.Tb="1800";d.ea=[];d.ga=[];d.Ic="cse";d.Gb="q";d.ab="google";d.fa=[c(d.ab,d.Gb),c("yahoo","p"),c("msn","q"),c("bing","q"),c("aol","query"),c("aol","encquery"),c("lycos","query"),c("ask","q"),c("altavista","q"),c("netscape","query"),c("cnn","query"),c("looksmart","qt"),c("about",
+"terms"),c("mamma","query"),c("alltheweb","q"),c("gigablast","q"),c("voila","rdata"),c("virgilio","qs"),c("live","q"),c("baidu","wd"),c("alice","qs"),c("yandex","text"),c("najdi","q"),c("aol","q"),c("club-internet","query"),c("mama","query"),c("seznam","q"),c("search","q"),c("wp","szukaj"),c("onet","qt"),c("netsprint","q"),c("google.interia","q"),c("szukacz","q"),c("yam","k"),c("pchome","q"),c("kvasir","searchExpr"),c("sesam","q"),c("ozu","q"),c("terra","query"),c("nostrum","query"),c("mynet","q"),
+c("ekolay","q"),c("search.ilse","search_for")];d.B=undefined;d.Kb=false;d.p="/";d.ha=100;d.Da="/__utm.gif";d.ta=1;d.ua=1;d.G="|";d.sa=1;d.qa=1;d.pb=1;d.g="auto";d.D=1;d.Ga=1000;d.Yc=10;d.nc=10;d.Zc=0.2};_gat.Y=function(d,a){var c,h,o,k,l,s,q,f=this,n=_gat,w=n.q,x=n.c,g,z=a;f.a=d;function B(i){var b=i instanceof Array?i.join("."):"";return w(b)?"-":b}function A(i,b){var e=[],j;if(!w(i)){e=n.z(i,".");if(b)for(j=0;j<e[x];j++)if(!n.Ea(e[j]))e[j]="-"}return e}function p(){return u(63072000000)}function u(i){var b=new Date,e=new Date(b.getTime()+i);return"expires="+e.toGMTString()+"; "}function m(i,b){f.a[n.m]=i+"; path="+z.p+"; "+b+f.Cc()}function r(i,b,e){var j=f.V,t,v;for(t=0;t<j[x];t++){v=j[t][0];
+v+=w(b)?b:b+j[t][4];j[t][2](n.C(i,v,e))}}f.Jb=function(){return n.b==g||g==f.t()};f.Ba=function(){return l?l:"-"};f.Wb=function(i){l=i};f.Ma=function(i){g=n.Ea(i)?i*1:"-"};f.Aa=function(){return B(s)};f.Na=function(i){s=A(i)};f.Hc=function(){return g?g:"-"};f.Cc=function(){return w(z.g)?"":"domain="+z.g+";"};f.ya=function(){return B(c)};f.Ub=function(i){c=A(i,1)};f.K=function(){return B(h)};f.La=function(i){h=A(i,1)};f.za=function(){return B(o)};f.Vb=function(i){o=A(i,1)};f.Ca=function(){return B(k)};
+f.Xb=function(i){k=A(i);for(var b=0;b<k[x];b++)if(b<4&&!n.Ea(k[b]))k[b]="-"};f.Dc=function(){return q};f.Uc=function(i){q=i};f.pc=function(){c=[];h=[];o=[];k=[];l=n.b;s=[];g=n.b};f.t=function(){var i="",b;for(b=0;b<f.V[x];b++)i+=f.V[b][1]();return n.t(i)};f.Ha=function(i){var b=f.a[n.m],e=false;if(b){r(b,i,";");f.Ma(f.t());e=true}return e};f.Rc=function(i){r(i,"","&");f.Ma(n.C(i,n.Ta,"&"))};f.Wc=function(){var i=f.V,b=[],e;for(e=0;e<i[x];e++)n.h(b,i[e][0]+i[e][1]());n.h(b,n.Ta+f.t());return b.join("&")};
+f.bd=function(i,b){var e=f.V,j=z.p,t;f.Ha(i);z.p=b;for(t=0;t<e[x];t++)if(!w(e[t][1]()))e[t][3]();z.p=j};f.dc=function(){m(n.r+f.ya(),p())};f.Pa=function(){m(n.W+f.K(),u(z.Tb*1000))};f.ec=function(){m(n.ma+f.za(),"")};f.Ra=function(){m(n.X+f.Ca(),u(z.wb*1000))};f.fc=function(){m(n.oa+f.Ba(),p())};f.Qa=function(){m(n.na+f.Aa(),p())};f.cd=function(){m(n.Sa+f.Dc(),"")};f.V=[[n.r,f.ya,f.Ub,f.dc,"."],[n.W,f.K,f.La,f.Pa,""],[n.ma,f.za,f.Vb,f.ec,""],[n.oa,f.Ba,f.Wb,f.fc,""],[n.X,f.Ca,f.Xb,f.Ra,"."],[n.na,
+f.Aa,f.Na,f.Qa,"."]]};_gat.jc=function(d){var a=this,c=_gat,h=d,o,k=function(l){var s=(new Date).getTime(),q;q=(s-l[3])*(h.Zc/1000);if(q>=1){l[2]=Math.min(Math.floor(l[2]*1+q),h.nc);l[3]=s}return l};a.O=function(l,s,q,f,n,w,x){var g,z=h.D,B=q.location;if(!o)o=new c.Y(q,h);o.Ha(f);g=c.z(o.K(),".");if(g[1]<500||n){if(w)g=k(g);if(n||!w||g[2]>=1){if(!n&&w)g[2]=g[2]*1-1;g[1]=g[1]*1+1;l="?utmwv="+_gat.lb+"&utmn="+c.wa()+(c.q(B.hostname)?"":"&utmhn="+c.d(B.hostname))+(h.ha==100?"":"&utmsp="+c.d(h.ha))+l;if(0==z||2==z){var A=
+new Image(1,1);A.src=h.Da+l;var p=2==z?function(){}:x||function(){};A.onload=p}if(1==z||2==z){var u=new Image(1,1);u.src=("https:"==B.protocol?c.mc:c.lc)+l+"&utmac="+s+"&utmcc="+a.wc(q,f);u.onload=x||function(){}}}}o.La(g.join("."));o.Pa()};a.wc=function(l,s){var q=[],f=[c.r,c.X,c.na,c.oa],n,w=l[c.m],x;for(n=0;n<f[c.c];n++){x=c.C(w,f[n]+s,";");if(!c.q(x))c.h(q,f[n]+x+";")}return c.d(q.join("+"))}};_gat.i=function(){this.la=[]};_gat.i.bb=function(d,a,c,h,o,k){var l=this;l.cc=d;l.Oa=a;l.L=c;l.sb=h;l.Pb=o;l.Qb=k};_gat.i.bb.prototype.S=function(){var d=this,a=_gat.d;return"&"+["utmt=item","utmtid="+a(d.cc),"utmipc="+a(d.Oa),"utmipn="+a(d.L),"utmiva="+a(d.sb),"utmipr="+a(d.Pb),"utmiqt="+a(d.Qb)].join("&")};_gat.i.$=function(d,a,c,h,o,k,l,s){var q=this;q.v=d;q.ob=a;q.bc=c;q.ac=h;q.Yb=o;q.ub=k;q.$b=l;q.xb=s;q.ca=[]};_gat.i.$.prototype.mb=function(d,a,c,h,o){var k=this,l=k.Eb(d),s=k.v,q=_gat;if(q.b==
+l)q.h(k.ca,new q.i.bb(s,d,a,c,h,o));else{l.cc=s;l.Oa=d;l.L=a;l.sb=c;l.Pb=h;l.Qb=o}};_gat.i.$.prototype.Eb=function(d){var a,c=this.ca,h;for(h=0;h<c[_gat.c];h++)a=d==c[h].Oa?c[h]:a;return a};_gat.i.$.prototype.S=function(){var d=this,a=_gat.d;return"&"+["utmt=tran","utmtid="+a(d.v),"utmtst="+a(d.ob),"utmtto="+a(d.bc),"utmttx="+a(d.ac),"utmtsp="+a(d.Yb),"utmtci="+a(d.ub),"utmtrg="+a(d.$b),"utmtco="+a(d.xb)].join("&")};_gat.i.prototype.nb=function(d,a,c,h,o,k,l,s){var q=this,f=_gat,n=q.xa(d);if(f.b==
+n){n=new f.i.$(d,a,c,h,o,k,l,s);f.h(q.la,n)}else{n.ob=a;n.bc=c;n.ac=h;n.Yb=o;n.ub=k;n.$b=l;n.xb=s}return n};_gat.i.prototype.xa=function(d){var a,c=this.la,h;for(h=0;h<c[_gat.c];h++)a=d==c[h].v?c[h]:a;return a};_gat.gc=function(d){var a=this,c="-",h=_gat,o=d;a.Ja=screen;a.qb=!self.screen&&self.java?java.awt.Toolkit.getDefaultToolkit():h.b;a.a=document;a.e=window;a.k=navigator;a.Ka=c;a.Sb=c;a.tb=c;a.Ob=c;a.Mb=1;a.Bb=c;function k(){var l,s,q,f,n="ShockwaveFlash",w="$version",x=a.k?a.k.plugins:h.b;if(x&&x[h.c]>0)for(l=0;l<x[h.c]&&!q;l++){s=x[l];if(h.P(s.name,"Shockwave Flash"))q=h.z(s.description,"Shockwave Flash ")[1]}else{n=n+"."+n;try{f=new ActiveXObject(n+".7");q=f.GetVariable(w)}catch(g){}if(!q)try{f=
+new ActiveXObject(n+".6");q="WIN 6,0,21,0";f.AllowScriptAccess="always";q=f.GetVariable(w)}catch(z){}if(!q)try{f=new ActiveXObject(n);q=f.GetVariable(w)}catch(z){}if(q){q=h.z(h.z(q," ")[1],",");q=q[0]+"."+q[1]+" r"+q[2]}}return q?q:c}a.xc=function(){var l;if(self.screen){a.Ka=a.Ja.width+"x"+a.Ja.height;a.Sb=a.Ja.colorDepth+"-bit"}else if(a.qb)try{l=a.qb.getScreenSize();a.Ka=l.width+"x"+l.height}catch(s){}a.Ob=h.T(a.k&&a.k.language?a.k.language:(a.k&&a.k.browserLanguage?a.k.browserLanguage:c));a.Mb=
+a.k&&a.k.javaEnabled()?1:0;a.Bb=o?k():c;a.tb=h.d(a.a.characterSet?a.a.characterSet:(a.a.charset?a.a.charset:c))};a.Xc=function(){return"&"+["utmcs="+h.d(a.tb),"utmsr="+a.Ka,"utmsc="+a.Sb,"utmul="+a.Ob,"utmje="+a.Mb,"utmfl="+h.d(a.Bb)].join("&")}};_gat.n=function(d,a,c,h,o){var k=this,l=_gat,s=l.q,q=l.b,f=l.P,n=l.C,w=l.T,x=l.z,g=l.c;k.a=a;k.f=d;k.Rb=c;k.ja=h;k.o=o;function z(p){return s(p)||"0"==p||!f(p,"://")}function B(p){var u="";p=w(x(p,"://")[1]);if(f(p,"/")){p=x(p,"/")[1];if(f(p,"?"))u=x(p,"?")[0]}return u}function A(p){var u="";u=w(x(p,"://")[1]);if(f(u,"/"))u=x(u,"/")[0];return u}k.Fc=function(p){var u=k.Fb(),m=k.o;return new l.n.s(n(p,m.fb+"=","&"),n(p,m.ib+"=","&"),n(p,m.kb+"=","&"),k.ba(p,m.db,"(not set)"),k.ba(p,m.gb,"(not set)"),
+k.ba(p,m.jb,u&&!s(u.R)?l.J(u.R):q),k.ba(p,m.eb,q))};k.Ib=function(p){var u=A(p),m=B(p);if(f(u,k.o.ab)){p=x(p,"?").join("&");if(f(p,"&"+k.o.Gb+"="))if(m==k.o.Ic)return true}return false};k.Fb=function(){var p,u,m=k.Rb,r,i,b=k.o.fa;if(z(m)||k.Ib(m))return;p=A(m);for(r=0;r<b[g];r++){i=b[r];if(f(p,w(i.zb))){m=x(m,"?").join("&");if(f(m,"&"+i.Nb+"=")){u=x(m,"&"+i.Nb+"=")[1];if(f(u,"&"))u=x(u,"&")[0];return new l.n.s(q,i.zb,q,"(organic)","organic",u,q)}}}};k.ba=function(p,u,m){var r=n(p,u+"=","&"),i=!s(r)?
+l.J(r):(!s(m)?m:"-");return i};k.Nc=function(p){var u=k.o.ea,m=false,r,i;if(p&&"organic"==p.da){r=w(l.J(p.R));for(i=0;i<u[g];i++)m=m||w(u[i])==r}return m};k.Ec=function(){var p="",u="",m=k.Rb;if(z(m)||k.Ib(m))return;p=w(x(m,"://")[1]);if(f(p,"/")){u=l.F(p,l.w(p,"/"));if(f(u,"?"))u=x(u,"?")[0];p=x(p,"/")[0]}if(0==l.w(p,"www."))p=l.F(p,4);return new l.n.s(q,p,q,"(referral)","referral",q,u)};k.sc=function(p){var u="";if(k.o.pa){u=l.Db(p);u=""!=u?u+"&":u}u+=p.search;return u};k.zc=function(){return new l.n.s(q,
+"(direct)",q,"(direct)","(none)",q,q)};k.Oc=function(p){var u=false,m,r,i=k.o.ga;if(p&&"referral"==p.da){m=w(l.d(p.ia));for(r=0;r<i[g];r++)u=u||f(m,w(i[r]))}return u};k.U=function(p){return q!=p&&p.Fa()};k.yc=function(p,u){var m="",r="-",i,b,e=0,j,t,v=k.f;if(!p)return"";t=k.a[l.m]?k.a[l.m]:"";m=k.sc(k.a.location);if(k.o.I&&p.Jb()){r=p.Ca();if(!s(r)&&!f(r,";")){p.Ra();return""}}r=n(t,l.X+v+".",";");i=k.Fc(m);if(k.U(i)){b=n(m,k.o.hb+"=","&");if("1"==b&&!s(r))return""}if(!k.U(i)){i=k.Fb();if(!s(r)&&
+k.Nc(i))return""}if(!k.U(i)&&u){i=k.Ec();if(!s(r)&&k.Oc(i))return""}if(!k.U(i))if(s(r)&&u)i=k.zc();if(!k.U(i))return"";if(!s(r)){var y=x(r,"."),E=new l.n.s;E.Cb(y.slice(4).join("."));j=w(E.ka())==w(i.ka());e=y[3]*1}if(!j||u){var F=n(t,l.r+v+".",";"),I=F.lastIndexOf("."),G=I>9?l.F(F,I+1)*1:0;e++;G=0==G?1:G;p.Xb([v,k.ja,G,e,i.ka()].join("."));p.Ra();return"&utmcn=1"}else return"&utmcr=1"}};_gat.n.s=function(d,a,c,h,o,k,l){var s=this;s.v=d;s.ia=a;s.ra=c;s.L=h;s.da=o;s.R=k;s.vb=l};_gat.n.s.prototype.ka=
+function(){var d=this,a=_gat,c=[],h=[[a.Wa,d.v],[a.Ya,d.ia],[a.$a,d.ra],[a.Ua,d.L],[a.Xa,d.da],[a.Za,d.R],[a.Va,d.vb]],o,k;if(d.Fa())for(o=0;o<h[a.c];o++)if(!a.q(h[o][1])){k=h[o][1].split("+").join("%20");k=k.split(" ").join("%20");a.h(c,h[o][0]+k)}return c.join("|")};_gat.n.s.prototype.Fa=function(){var d=this,a=_gat.q;return!(a(d.v)&&a(d.ia)&&a(d.ra))};_gat.n.s.prototype.Cb=function(d){var a=this,c=_gat,h=function(o){return c.J(c.C(d,o,"|"))};a.v=h(c.Wa);a.ia=h(c.Ya);a.ra=h(c.$a);a.L=h(c.Ua);a.da=
+h(c.Xa);a.R=h(c.Za);a.vb=h(c.Va)};_gat.Z=function(){var d=this,a=_gat,c={},h="k",o="v",k=[h,o],l="(",s=")",q="*",f="!",n="'",w={};w[n]="'0";w[s]="'1";w[q]="'2";w[f]="'3";var x=1;function g(m,r,i,b){if(a.b==c[m])c[m]={};if(a.b==c[m][r])c[m][r]=[];c[m][r][i]=b}function z(m,r,i){return a.b!=c[m]&&a.b!=c[m][r]?c[m][r][i]:a.b}function B(m,r){if(a.b!=c[m]&&a.b!=c[m][r]){c[m][r]=a.b;var i=true,b;for(b=0;b<k[a.c];b++)if(a.b!=c[m][k[b]]){i=false;break}if(i)c[m]=a.b}}function A(m){var r="",i=false,b,e;for(b=0;b<k[a.c];b++){e=m[k[b]];if(a.b!=
+e){if(i)r+=k[b];r+=p(e);i=false}else i=true}return r}function p(m){var r=[],i,b;for(b=0;b<m[a.c];b++)if(a.b!=m[b]){i="";if(b!=x&&a.b==m[b-1]){i+=b.toString();i+=f}i+=u(m[b]);a.h(r,i)}return l+r.join(q)+s}function u(m){var r="",i,b,e;for(i=0;i<m[a.c];i++){b=m.charAt(i);e=w[b];r+=a.b!=e?e:b}return r}d.Kc=function(m){return a.b!=c[m]};d.N=function(){var m=[],r;for(r in c)if(a.b!=c[r])a.h(m,r.toString()+A(c[r]));return m.join("")};d.Sc=function(m){if(m==a.b)return d.N();var r=[m.N()],i;for(i in c)if(a.b!=
+c[i]&&!m.Kc(i))a.h(r,i.toString()+A(c[i]));return r.join("")};d._setKey=function(m,r,i){if(typeof i!="string")return false;g(m,h,r,i);return true};d._setValue=function(m,r,i){if(typeof i!="number"&&(a.b==Number||!(i instanceof Number)))return false;if(Math.round(i)!=i||i==NaN||i==Infinity)return false;g(m,o,r,i.toString());return true};d._getKey=function(m,r){return z(m,h,r)};d._getValue=function(m,r){return z(m,o,r)};d._clearKey=function(m){B(m,h)};d._clearValue=function(m){B(m,o)}};_gat.ic=function(d,a){var c=this;c.jd=a;c.Pc=d;c._trackEvent=function(h,o,k){return a._trackEvent(c.Pc,h,o,k)}};_gat.kc=function(d){var a=this,c=_gat,h=c.b,o=c.q,k=c.w,l=c.F,s=c.C,q=c.P,f=c.z,n="location",w=c.c,x=h,g=new c.hc,z=false;a.a=document;a.e=window;a.ja=Math.round((new Date).getTime()/1000);a.H=d;a.yb=a.a.referrer;a.va=h;a.j=h;a.A=h;a.M=false;a.aa=h;a.rb="";a.l=h;a.Ab=h;a.f=h;a.u=h;function B(){if("auto"==g.g){var b=a.a.domain;if("www."==l(b,0,4))b=l(b,4);g.g=b}g.g=c.T(g.g)}function A(){var b=g.g,e=k(b,"www.google.")*k(b,".google.")*k(b,"google.");return e||"/"!=g.p||k(b,"google.org")>-1}function p(b,
+e,j){if(o(b)||o(e)||o(j))return"-";var t=s(b,c.r+a.f+".",e),v;if(!o(t)){v=f(t,".");v[5]=v[5]?v[5]*1+1:1;v[3]=v[4];v[4]=j;t=v.join(".")}return t}function u(){return"file:"!=a.a[n].protocol&&A()}function m(b){if(!b||""==b)return"";while(c.Lb(b.charAt(0)))b=l(b,1);while(c.Lb(b.charAt(b[w]-1)))b=l(b,0,b[w]-1);return b}function r(b,e,j){if(!o(b())){e(c.J(b()));if(!q(b(),";"))j()}}function i(b){var e,j=""!=b&&a.a[n].host!=b;if(j)for(e=0;e<g.B[w];e++)j=j&&k(c.T(b),c.T(g.B[e]))==-1;return j}a.Bc=function(){if(!g.g||
+""==g.g||"none"==g.g){g.g="";return 1}B();return g.pb?c.t(g.g):1};a.tc=function(b,e){if(o(b))b="-";else{e+=g.p&&"/"!=g.p?g.p:"";var j=k(b,e);b=j>=0&&j<=8?"0":("["==b.charAt(0)&&"]"==b.charAt(b[w]-1)?"-":b)}return b};a.Ia=function(b){var e="",j=a.a;e+=a.aa?a.aa.Xc():"";e+=g.qa?a.rb:"";e+=g.ta&&!o(j.title)?"&utmdt="+c.d(j.title):"";e+="&utmhid="+c.uc()+"&utmr="+a.va+"&utmp="+a.Tc(b);return e};a.Tc=function(b){var e=a.a[n];b=h!=b&&""!=b?c.d(b,true):c.d(e.pathname+unescape(e.search),true);return b};a.$c=
+function(b){if(a.Q()){var e="";if(a.l!=h&&a.l.N().length>0)e+="&utme="+c.d(a.l.N());e+=a.Ia(b);x.O(e,a.H,a.a,a.f)}};a.qc=function(){var b=new c.Y(a.a,g);return b.Ha(a.f)?b.Wc():h};a._getLinkerUrl=function(b,e){var j=f(b,"#"),t=b,v=a.qc();if(v)if(e&&1>=j[w])t+="#"+v;else if(!e||1>=j[w])if(1>=j[w])t+=(q(b,"?")?"&":"?")+v;else t=j[0]+(q(b,"?")?"&":"?")+v+"#"+j[1];return t};a.Zb=function(){var b;if(a.A&&a.A[w]>=10&&!q(a.A,"=")){a.u.Uc(a.A);a.u.cd();c._gasoDomain=g.g;c._gasoCPath=g.p;b=a.a.createElement("script");
+b.type="text/javascript";b.id="_gasojs";b.src="https://www.google.com/analytics/reporting/overlay_js?gaso="+a.A+"&"+c.wa();a.a.getElementsByTagName("head")[0].appendChild(b)}};a.Jc=function(){var b=a.a[c.m],e=a.ja,j=a.u,t=a.f+"",v=a.e,y=v?v.gaGlobal:h,E,F=q(b,c.r+t+"."),I=q(b,c.W+t),G=q(b,c.ma+t),C,D=[],H="",K=false,J;b=o(b)?"":b;if(g.I){E=c.Db(a.a[n]);if(g.pa&&!o(E))H=E+"&";H+=a.a[n].search;if(!o(H)&&q(H,c.r)){j.Rc(H);if(!j.Jb())j.pc();C=j.ya()}r(j.Ba,j.Wb,j.fc);r(j.Aa,j.Na,j.Qa)}if(!o(C))if(o(j.K())||
+o(j.za())){C=p(H,"&",e);a.M=true}else{D=f(j.K(),".");t=D[0]}else if(F)if(!I||!G){C=p(b,";",e);a.M=true}else{C=s(b,c.r+t+".",";");D=f(s(b,c.W+t,";"),".")}else{C=[t,c.Gc(),e,e,e,1].join(".");a.M=true;K=true}C=f(C,".");if(v&&y&&y.dh==t){C[4]=y.sid?y.sid:C[4];if(K){C[3]=y.sid?y.sid:C[4];if(y.vid){J=f(y.vid,".");C[1]=J[0];C[2]=J[1]}}}j.Ub(C.join("."));D[0]=t;D[1]=D[1]?D[1]:0;D[2]=undefined!=D[2]?D[2]:g.Yc;D[3]=D[3]?D[3]:C[4];j.La(D.join("."));j.Vb(t);if(!o(j.Hc()))j.Ma(j.t());j.dc();j.Pa();j.ec()};a.Lc=
+function(){x=new c.jc(g)};a._initData=function(){var b;if(!z){a.Lc();a.f=a.Bc();a.u=new c.Y(a.a,g)}if(u())a.Jc();if(!z){if(u()){a.va=a.tc(a.Ac(),a.a.domain);if(g.sa){a.aa=new c.gc(g.ua);a.aa.xc()}if(g.qa){b=new c.n(a.f,a.a,a.va,a.ja,g);a.rb=b.yc(a.u,a.M)}}a.l=new c.Z;a.Ab=new c.Z;z=true}if(!c.Hb)a.Mc()};a._visitCode=function(){a._initData();var b=s(a.a[c.m],c.r+a.f+".",";"),e=f(b,".");return e[w]<4?"":e[1]};a._cookiePathCopy=function(b){a._initData();if(a.u)a.u.bd(a.f,b)};a.Mc=function(){var b=a.a[n].hash,
+e;e=b&&""!=b&&0==k(b,"#gaso=")?s(b,"gaso=","&"):s(a.a[c.m],c.Sa,";");if(e[w]>=10){a.A=e;if(a.e.addEventListener)a.e.addEventListener("load",a.Zb,false);else a.e.attachEvent("onload",a.Zb)}c.Hb=true};a.Q=function(){return a._visitCode()%10000<g.ha*100};a.Vc=function(){var b,e,j=a.a.links;if(!g.Kb){var t=a.a.domain;if("www."==l(t,0,4))t=l(t,4);g.B.push("."+t)}for(b=0;b<j[w]&&(g.Ga==-1||b<g.Ga);b++){e=j[b];if(i(e.host))if(!e.gatcOnclick){e.gatcOnclick=e.onclick?e.onclick:a.Qc;e.onclick=function(v){var y=
+!this.target||this.target=="_self"||this.target=="_top"||this.target=="_parent";y=y&&!a.oc(v);a.ad(v,this,y);return y?false:(this.gatcOnclick?this.gatcOnclick(v):true)}}}};a.Qc=function(){};a._trackPageview=function(b){if(u()){a._initData();if(g.B)a.Vc();a.$c(b);a.M=false}};a._trackTrans=function(){var b=a.f,e=[],j,t,v,y;a._initData();if(a.j&&a.Q()){for(j=0;j<a.j.la[w];j++){t=a.j.la[j];c.h(e,t.S());for(v=0;v<t.ca[w];v++)c.h(e,t.ca[v].S())}for(y=0;y<e[w];y++)x.O(e[y],a.H,a.a,b,true)}};a._setTrans=
+function(){var b=a.a,e,j,t,v,y=b.getElementById?b.getElementById("utmtrans"):(b.utmform&&b.utmform.utmtrans?b.utmform.utmtrans:h);a._initData();if(y&&y.value){a.j=new c.i;v=f(y.value,"UTM:");g.G=!g.G||""==g.G?"|":g.G;for(e=0;e<v[w];e++){v[e]=m(v[e]);j=f(v[e],g.G);for(t=0;t<j[w];t++)j[t]=m(j[t]);if("T"==j[0])a._addTrans(j[1],j[2],j[3],j[4],j[5],j[6],j[7],j[8]);else if("I"==j[0])a._addItem(j[1],j[2],j[3],j[4],j[5],j[6])}}};a._addTrans=function(b,e,j,t,v,y,E,F){a.j=a.j?a.j:new c.i;return a.j.nb(b,e,
+j,t,v,y,E,F)};a._addItem=function(b,e,j,t,v,y){var E;a.j=a.j?a.j:new c.i;E=a.j.xa(b);if(!E)E=a._addTrans(b,"","","","","","","");E.mb(e,j,t,v,y)};a._setVar=function(b){if(b&&""!=b&&A()){a._initData();var e=new c.Y(a.a,g),j=a.f;e.Na(j+"."+c.d(b));e.Qa();if(a.Q())x.O("&utmt=var",a.H,a.a,a.f)}};a._link=function(b,e){if(g.I&&b){a._initData();a.a[n].href=a._getLinkerUrl(b,e)}};a._linkByPost=function(b,e){if(g.I&&b&&b.action){a._initData();b.action=a._getLinkerUrl(b.action,e)}};a._setXKey=function(b,e,
+j){a.l._setKey(b,e,j)};a._setXValue=function(b,e,j){a.l._setValue(b,e,j)};a._getXKey=function(b,e){return a.l._getKey(b,e)};a._getXValue=function(b,e){return a.l.getValue(b,e)};a._clearXKey=function(b){a.l._clearKey(b)};a._clearXValue=function(b){a.l._clearValue(b)};a._createXObj=function(){a._initData();return new c.Z};a._sendXEvent=function(b){var e="";a._initData();if(a.Q()){e+="&utmt=event&utme="+c.d(a.l.Sc(b))+a.Ia();x.O(e,a.H,a.a,a.f,false,true)}};a._createEventTracker=function(b){a._initData();
+return new c.ic(b,a)};a._trackEvent=function(b,e,j,t){var v=true,y=a.Ab;if(h!=b&&h!=e&&""!=b&&""!=e){y._clearKey(5);y._clearValue(5);v=y._setKey(5,1,b)?v:false;v=y._setKey(5,2,e)?v:false;v=h==j||y._setKey(5,3,j)?v:false;v=h==t||y._setValue(5,1,t)?v:false;if(v)a._sendXEvent(y)}else v=false;return v};a.ad=function(b,e,j){a._initData();if(a.Q()){var t=new c.Z;t._setKey(6,1,e.href);var v=j?function(){a.rc(b,e)}:undefined;x.O("&utmt=event&utme="+c.d(t.N())+a.Ia(),a.H,a.a,a.f,false,true,v)}};a.rc=function(b,
+e){if(!b)b=a.e.event;var j=true;if(e.gatcOnclick)j=e.gatcOnclick(b);if(j||typeof j=="undefined")if(!e.target||e.target=="_self")a.e.location=e.href;else if(e.target=="_top")a.e.top.document.location=e.href;else if(e.target=="_parent")a.e.parent.document.location=e.href};a.oc=function(b){if(!b)b=a.e.event;var e=b.shiftKey||b.ctrlKey||b.altKey;if(!e)if(b.modifiers&&a.e.Event)e=b.modifiers&a.e.Event.CONTROL_MASK||b.modifiers&a.e.Event.SHIFT_MASK||b.modifiers&a.e.Event.ALT_MASK;return e};a._setDomainName=
+function(b){g.g=b};a.dd=function(){return g.g};a._addOrganic=function(b,e){c.h(g.fa,new c.cb(b,e))};a._clearOrganic=function(){g.fa=[]};a.hd=function(){return g.fa};a._addIgnoredOrganic=function(b){c.h(g.ea,b)};a._clearIgnoredOrganic=function(){g.ea=[]};a.ed=function(){return g.ea};a._addIgnoredRef=function(b){c.h(g.ga,b)};a._clearIgnoredRef=function(){g.ga=[]};a.fd=function(){return g.ga};a._setAllowHash=function(b){g.pb=b?1:0};a._setCampaignTrack=function(b){g.qa=b?1:0};a._setClientInfo=function(b){g.sa=
+b?1:0};a._getClientInfo=function(){return g.sa};a._setCookiePath=function(b){g.p=b};a._setTransactionDelim=function(b){g.G=b};a._setCookieTimeout=function(b){g.wb=b};a._setDetectFlash=function(b){g.ua=b?1:0};a._getDetectFlash=function(){return g.ua};a._setDetectTitle=function(b){g.ta=b?1:0};a._getDetectTitle=function(){return g.ta};a._setLocalGifPath=function(b){g.Da=b};a._getLocalGifPath=function(){return g.Da};a._setLocalServerMode=function(){g.D=0};a._setRemoteServerMode=function(){g.D=1};a._setLocalRemoteServerMode=
+function(){g.D=2};a.gd=function(){return g.D};a._getServiceMode=function(){return g.D};a._setSampleRate=function(b){g.ha=b};a._setSessionTimeout=function(b){g.Tb=b};a._setAllowLinker=function(b){g.I=b?1:0};a._setAllowAnchor=function(b){g.pa=b?1:0};a._setCampNameKey=function(b){g.db=b};a._setCampContentKey=function(b){g.eb=b};a._setCampIdKey=function(b){g.fb=b};a._setCampMediumKey=function(b){g.gb=b};a._setCampNOKey=function(b){g.hb=b};a._setCampSourceKey=function(b){g.ib=b};a._setCampTermKey=function(b){g.jb=
+b};a._setCampCIdKey=function(b){g.kb=b};a._getAccount=function(){return a.H};a._getVersion=function(){return _gat.lb};a.kd=function(b){g.B=[];if(b)g.B=b};a.md=function(b){g.Kb=b};a.ld=function(b){g.Ga=b};a._setReferrerOverride=function(b){a.yb=b};a.Ac=function(){return a.yb}};_gat._getTracker=function(d){var a=new _gat.kc(d);return a};
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/header.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/header.png
new file mode 100644
index 0000000..977712b
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/header.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/hoverIntent.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/hoverIntent.js
new file mode 100644
index 0000000..91da57b
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/hoverIntent.js
@@ -0,0 +1,84 @@
+(function($){
+ /* hoverIntent by Brian Cherne */
+ $.fn.hoverIntent = function(f,g) {
+ // default configuration options
+ var cfg = {
+ sensitivity: 7,
+ interval: 100,
+ timeout: 0
+ };
+ // override configuration options with user supplied object
+ cfg = $.extend(cfg, g ? { over: f, out: g } : f );
+
+ // instantiate variables
+ // cX, cY = current X and Y position of mouse, updated by mousemove event
+ // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
+ var cX, cY, pX, pY;
+
+ // A private function for getting mouse position
+ var track = function(ev) {
+ cX = ev.pageX;
+ cY = ev.pageY;
+ };
+
+ // A private function for comparing current and previous mouse position
+ var compare = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ // compare mouse positions to see if they've crossed the threshold
+ if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
+ $(ob).unbind("mousemove",track);
+ // set hoverIntent state to true (so mouseOut can be called)
+ ob.hoverIntent_s = 1;
+ return cfg.over.apply(ob,[ev]);
+ } else {
+ // set previous coordinates for next time
+ pX = cX; pY = cY;
+ // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+ ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
+ }
+ };
+
+ // A private function for delaying the mouseOut function
+ var delay = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ ob.hoverIntent_s = 0;
+ return cfg.out.apply(ob,[ev]);
+ };
+
+ // A private function for handling mouse 'hovering'
+ var handleHover = function(e) {
+ // next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
+ var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+ while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
+ if ( p == this ) { return false; }
+
+ // copy objects to be passed into t (required for event object to be passed in IE)
+ var ev = jQuery.extend({},e);
+ var ob = this;
+
+ // cancel hoverIntent timer if it exists
+ if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
+
+ // else e.type == "onmouseover"
+ if (e.type == "mouseover") {
+ // set "previous" X and Y position based on initial entry point
+ pX = ev.pageX; pY = ev.pageY;
+ // update "current" X and Y position based on mousemove
+ $(ob).bind("mousemove",track);
+ // start polling interval (self-calling timeout) to compare mouse coordinates over time
+ if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
+
+ // else e.type == "onmouseout"
+ } else {
+ // unbind expensive mousemove event
+ $(ob).unbind("mousemove",track);
+ // if hoverIntent state is true, then call the mouseOut function after the specified delay
+ if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
+ }
+ };
+
+ // bind the function to the two event listeners
+ return this.mouseover(handleHover).mouseout(handleHover);
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/information.gif b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/information.gif
new file mode 100644
index 0000000..072ab66
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/information.gif
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ipojo.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ipojo.png
new file mode 100644
index 0000000..8f8d89e
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/ipojo.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/jquery-1.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/jquery-1.js
new file mode 100644
index 0000000..b1ae21d
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/jquery-1.js
@@ -0,0 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/linkext7.gif b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/linkext7.gif
new file mode 100644
index 0000000..f2dd2dc
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/linkext7.gif
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/logo.png b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/logo.png
new file mode 100644
index 0000000..dccbddc
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/logo.png
Binary files differ
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushCSharp.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushCSharp.js
new file mode 100644
index 0000000..f45540f
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushCSharp.js
@@ -0,0 +1,30 @@
+dp.sh.Brushes.CSharp = function()
+{
+ var keywords = 'abstract as base bool break byte case catch char checked class const ' +
+ 'continue decimal default delegate do double else enum event explicit ' +
+ 'extern false finally fixed float for foreach get goto if implicit in int ' +
+ 'interface internal is lock long namespace new null object operator out ' +
+ 'override params private protected public readonly ref return sbyte sealed set ' +
+ 'short sizeof stackalloc static string struct switch this throw true try ' +
+ 'typeof uint ulong unchecked unsafe ushort using virtual void while';
+
+ this.regexList = [
+ // There's a slight problem with matching single line comments and figuring out
+ // a difference between // and ///. Using lookahead and lookbehind solves the
+ // problem, unfortunately JavaScript doesn't support lookbehind. So I'm at a
+ // loss how to translate that regular expression to JavaScript compatible one.
+// { regex: new RegExp('(?<!/)//(?!/).*$|(?<!/)////(?!/).*$|/\\*[^\\*]*(.)*?\\*/', 'gm'), css: 'comment' }, // one line comments starting with anything BUT '///' and multiline comments
+// { regex: new RegExp('(?<!/)///(?!/).*$', 'gm'), css: 'comments' }, // XML comments starting with ///
+
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // c# keyword
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.CSharp.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.CSharp.Aliases = ['c#', 'c-sharp', 'csharp'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushDelphi.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushDelphi.js
new file mode 100644
index 0000000..efeb2f7
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushDelphi.js
@@ -0,0 +1,31 @@
+/* Delphi brush is contributed by Eddie Shipman */
+dp.sh.Brushes.Delphi = function()
+{
+ var keywords = 'abs addr and ansichar ansistring array as asm begin boolean byte cardinal ' +
+ 'case char class comp const constructor currency destructor div do double ' +
+ 'downto else end except exports extended false file finalization finally ' +
+ 'for function goto if implementation in inherited int64 initialization ' +
+ 'integer interface is label library longint longword mod nil not object ' +
+ 'of on or packed pansichar pansistring pchar pcurrency pdatetime pextended ' +
+ 'pint64 pointer private procedure program property pshortstring pstring ' +
+ 'pvariant pwidechar pwidestring protected public published raise real real48 ' +
+ 'record repeat set shl shortint shortstring shr single smallint string then ' +
+ 'threadvar to true try type unit until uses val var varirnt while widechar ' +
+ 'widestring with word write writeln xor';
+
+ this.regexList = [
+ { regex: new RegExp('\\(\\*[\\s\\S]*?\\*\\)', 'gm'), css: 'comment' }, // multiline comments (* *)
+ { regex: new RegExp('{(?!\\$)[\\s\\S]*?}', 'gm'), css: 'comment' }, // multiline comments { }
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('\\{\\$[a-zA-Z]+ .+\\}', 'g'), css: 'directive' }, // Compiler Directives and Region tags
+ { regex: new RegExp('\\b[\\d\\.]+\\b', 'g'), css: 'number' }, // numbers 12345
+ { regex: new RegExp('\\$[a-zA-Z0-9]+\\b', 'g'), css: 'number' }, // numbers $F5D3
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-delphi';
+}
+
+dp.sh.Brushes.Delphi.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Delphi.Aliases = ['delphi', 'pascal'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJScript.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJScript.js
new file mode 100644
index 0000000..4ef8b9b
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJScript.js
@@ -0,0 +1,22 @@
+dp.sh.Brushes.JScript = function()
+{
+ var keywords = 'abstract boolean break byte case catch char class const continue debugger ' +
+ 'default delete do double else enum export extends false final finally float ' +
+ 'for function goto if implements import in instanceof int interface long native ' +
+ 'new null package private protected public return short static super switch ' +
+ 'synchronized this throw throws transient true try typeof var void volatile while with';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keywords
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.JScript.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.JScript.Aliases = ['js', 'jscript', 'javascript'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJava.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJava.js
new file mode 100644
index 0000000..b75d334
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushJava.js
@@ -0,0 +1,22 @@
+dp.sh.Brushes.Java = function()
+{
+ var keywords = 'abstract assert boolean break byte case catch char class const' +
+ 'continue default do double else enum extends false final finally float' +
+ 'for goto if implements import instanceof inst interface log native' +
+ 'new null package private protected public return short static strictfp super' +
+ 'switch synchronized this throw throws transient true try void volatile while';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('^\\s*@.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like @see
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keywords
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.Java.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Java.Aliases = ['java'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPhp.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPhp.js
new file mode 100644
index 0000000..57c85d4
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPhp.js
@@ -0,0 +1,23 @@
+dp.sh.Brushes.Php = function()
+{
+ var keywords = 'and or xor __FILE__ __LINE__ array as break case ' +
+ 'cfunction class const continue declare default die do echo else ' +
+ 'elseif empty enddeclare endfor endforeach endif endswitch endwhile eval exit ' +
+ 'extends for foreach function global if include include_once isset list ' +
+ 'new old_function print require require_once return static switch unset use ' +
+ 'var while __FUNCTION__ __CLASS__';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('\\$\\w+', 'g'), css: 'vars' }, // variables
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.Php.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Php.Aliases = ['php'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPython.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPython.js
new file mode 100644
index 0000000..1d61339
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushPython.js
@@ -0,0 +1,71 @@
+/* Python 2.3 syntax contributed by Gheorghe Milas */
+dp.sh.Brushes.Python = function()
+{
+ var keywords = 'and assert break class continue def del elif else except exec ' +
+ 'finally for from global if import in is lambda not or object pass print ' +
+ 'raise return try yield while';
+
+ var builtins = 'self __builtin__ __dict__ __future__ __methods__ __members__ __author__ __email__ __version__' +
+ '__class__ __bases__ __import__ __main__ __name__ __doc__ __self__ __debug__ __slots__ ' +
+ 'abs append apply basestring bool buffer callable chr classmethod clear close cmp coerce compile complex ' +
+ 'conjugate copy count delattr dict dir divmod enumerate Ellipsis eval execfile extend False file fileno filter float flush ' +
+ 'get getattr globals has_key hasarttr hash hex id index input insert int intern isatty isinstance isubclass ' +
+ 'items iter keys len list locals long map max min mode oct open ord pop pow property range ' +
+ 'raw_input read readline readlines reduce reload remove repr reverse round seek setattr slice sum ' +
+ 'staticmethod str super tell True truncate tuple type unichr unicode update values write writelines xrange zip';
+
+ var magicmethods = '__abs__ __add__ __and__ __call__ __cmp__ __coerce__ __complex__ __concat__ __contains__ __del__ __delattr__ __delitem__ ' +
+ '__delslice__ __div__ __divmod__ __float__ __getattr__ __getitem__ __getslice__ __hash__ __hex__ __eq__ __le__ __lt__ __gt__ __ge__ ' +
+ '__iadd__ __isub__ __imod__ __idiv__ __ipow__ __iand__ __ior__ __ixor__ __ilshift__ __irshift__ ' +
+ '__invert__ __init__ __int__ __inv__ __iter__ __len__ __long__ __lshift__ __mod__ __mul__ __new__ __neg__ __nonzero__ __oct__ __or__ ' +
+ '__pos__ __pow__ __radd__ __rand__ __rcmp__ __rdiv__ __rdivmod__ __repeat__ __repr__ __rlshift__ __rmod__ __rmul__ ' +
+ '__ror__ __rpow__ __rrshift__ __rshift__ __rsub__ __rxor__ __setattr__ __setitem__ __setslice__ __str__ __sub__ __xor__';
+
+ var exceptions = 'Exception StandardError ArithmeticError LookupError EnvironmentError AssertionError AttributeError EOFError ' +
+ 'FutureWarning IndentationError OverflowWarning PendingDeprecationWarning ReferenceError RuntimeWarning ' +
+ 'SyntaxWarning TabError UnicodeDecodeError UnicodeEncodeError UnicodeTranslateError UserWarning Warning ' +
+ 'IOError ImportError IndexError KeyError KeyboardInterrupt MemoryError NameError NotImplementedError OSError ' +
+ 'RuntimeError StopIteration SyntaxError SystemError SystemExit TypeError UnboundLocalError UnicodeError ValueError ' +
+ 'FloatingPointError OverflowError WindowsError ZeroDivisionError';
+
+ var types = 'NoneType TypeType IntType LongType FloatType ComplexType StringType UnicodeType BufferType TupleType ListType ' +
+ 'DictType FunctionType LambdaType CodeType ClassType UnboundMethodType InstanceType MethodType BuiltinFunctionType BuiltinMethodType ' +
+ 'ModuleType FileType XRangeType TracebackType FrameType SliceType EllipsisType';
+
+ var commonlibs = 'anydbm array asynchat asyncore AST base64 binascii binhex bisect bsddb buildtools bz2 ' +
+ 'BaseHTTPServer Bastion calendar cgi cmath cmd codecs codeop commands compiler copy copy_reg ' +
+ 'cPickle crypt cStringIO csv curses Carbon CGIHTTPServer ConfigParser Cookie datetime dbhash ' +
+ 'dbm difflib dircache distutils doctest DocXMLRPCServer email encodings errno exceptions fcntl ' +
+ 'filecmp fileinput ftplib gc gdbm getopt getpass glob gopherlib gzip heapq htmlentitydefs ' +
+ 'htmllib httplib HTMLParser imageop imaplib imgfile imghdr imp inspect itertools jpeg keyword ' +
+ 'linecache locale logging mailbox mailcap marshal math md5 mhlib mimetools mimetypes mimify mmap ' +
+ 'mpz multifile mutex MimeWriter netrc new nis nntplib nsremote operator optparse os parser pickle pipes ' +
+ 'popen2 poplib posix posixfile pprint preferences profile pstats pwd pydoc pythonprefs quietconsole ' +
+ 'quopri Queue random re readline resource rexec rfc822 rgbimg sched select sets sgmllib sha shelve shutil ' +
+ 'signal site smtplib socket stat statcache string struct symbol sys syslog SimpleHTTPServer ' +
+ 'SimpleXMLRPCServer SocketServer StringIO tabnanny tarfile telnetlib tempfile termios textwrap ' +
+ 'thread threading time timeit token tokenize traceback tty types Tkinter unicodedata unittest ' +
+ 'urllib urllib2 urlparse user UserDict UserList UserString warnings weakref webbrowser whichdb ' +
+ 'xml xmllib xmlrpclib xreadlines zipfile zlib';
+
+ this.regexList = [
+ { regex: new RegExp('#.*$', 'gm'), css: 'comment' }, // comments
+ { regex: new RegExp('^\\s*"""(.|\n)*?"""\\s*$', 'gm'), css: 'docstring' }, // documentation string "
+ { regex: new RegExp('^\\s*\'\'\'(.|\n)*?\'\'\'\\s*$', 'gm'), css: 'docstring' }, // documentation string '
+ { regex: new RegExp('"""(.|\n)*?"""', 'g'), css: 'string' }, // multi-line strings "
+ { regex: new RegExp('\'\'\'(.|\n)*?\'\'\'', 'g'), css: 'string' }, // multi-line strings '
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings "
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings '
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' }, // keywords
+ { regex: new RegExp(this.GetKeywords(builtins), 'gm'), css: 'builtins' }, // builtin objects, functions, methods, magic attributes
+ { regex: new RegExp(this.GetKeywords(magicmethods), 'gm'), css: 'magicmethods' }, // special methods
+ { regex: new RegExp(this.GetKeywords(exceptions), 'gm'), css: 'exceptions' }, // standard exception classes
+ { regex: new RegExp(this.GetKeywords(types), 'gm'), css: 'types' }, // types from types.py
+ { regex: new RegExp(this.GetKeywords(commonlibs), 'gm'), css: 'commonlibs' } // common standard library modules
+ ];
+
+ this.CssClass = 'dp-py';
+}
+
+dp.sh.Brushes.Python.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Python.Aliases = ['py', 'python'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushShell.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushShell.js
new file mode 100644
index 0000000..5e7c2ef
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushShell.js
@@ -0,0 +1,27 @@
+dp.sh.Brushes.Shell = function()
+{
+ var keywords = 'alias array autor base basename break ' +
+ 'cat catv cd chdir cmpv continue conv copy ' +
+ 'crc ctime cut dirname echo env eval exec else if elif then ' +
+ 'export expr extern false fmode fork fprint ' +
+ 'fsize fstat fullname global goend goto grep ifdef ' +
+ 'ifset ifenv inv kill line link list ' +
+ 'local localset mkdirs mktemp move mtime nop print ' +
+ 'prints pwd read readc readl readonly rel ' +
+ 'remove return seek set shift sleep sortl ' +
+ 'static stime sum system systime tee test times ' +
+ 'tr trap true type typeset tz umask unalias ' +
+ 'unexport unset unsetenv ver wait wc whence ' +
+ 'sane exit prompt let';
+
+
+ this.regexList = [
+ { regex: new RegExp('#.*$', 'gm'), css: 'comment' }, // one line
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-shell';
+}
+
+dp.sh.Brushes.Shell.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Shell.Aliases = ['shell'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushSql.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushSql.js
new file mode 100644
index 0000000..4855d0c
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushSql.js
@@ -0,0 +1,40 @@
+dp.sh.Brushes.Sql = function()
+{
+ var funcs = 'abs avg case cast coalesce convert count current_timestamp ' +
+ 'current_user day isnull left lower month nullif replace right ' +
+ 'session_user space substring sum system_user upper user year';
+
+ var keywords = 'absolute action add after alter as asc at authorization begin bigint ' +
+ 'binary bit by cascade char character check checkpoint close collate ' +
+ 'column commit committed connect connection constraint contains continue ' +
+ 'create cube current current_date current_time cursor database date ' +
+ 'deallocate dec decimal declare default delete desc distinct double drop ' +
+ 'dynamic else end end-exec escape except exec execute false fetch first ' +
+ 'float for force foreign forward free from full function global goto grant ' +
+ 'group grouping having hour ignore index inner insensitive insert instead ' +
+ 'int integer intersect into is isolation key last level load local max min ' +
+ 'minute modify move name national nchar next no numeric of off on only ' +
+ 'open option order out output partial password precision prepare primary ' +
+ 'prior privileges procedure public read real references relative repeatable ' +
+ 'restrict return returns revoke rollback rollup rows rule schema scroll ' +
+ 'second section select sequence serializable set size smallint static ' +
+ 'statistics table temp temporary then time timestamp to top transaction ' +
+ 'translation trigger true truncate uncommitted union unique update values ' +
+ 'varchar varying view when where with work';
+
+ var operators = 'all and any between cross in join like not null or outer some';
+
+ this.regexList = [
+ { regex: new RegExp('--(.*)$', 'gm'), css: 'comment' }, // one line and multiline comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings
+ { regex: new RegExp(this.GetKeywords(funcs), 'gmi'), css: 'func' }, // functions
+ { regex: new RegExp(this.GetKeywords(operators), 'gmi'), css: 'op' }, // operators and such
+ { regex: new RegExp(this.GetKeywords(keywords), 'gmi'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-sql';
+}
+
+dp.sh.Brushes.Sql.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Sql.Aliases = ['sql'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushVb.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushVb.js
new file mode 100644
index 0000000..431e16f
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushVb.js
@@ -0,0 +1,29 @@
+dp.sh.Brushes.Vb = function()
+{
+ var keywords = 'AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto ' +
+ 'Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate ' +
+ 'CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType ' +
+ 'Date Decimal Declare Default Delegate Dim DirectCast Do Double Each ' +
+ 'Else ElseIf End Enum Erase Error Event Exit False Finally For Friend ' +
+ 'Function Get GetType GoSub GoTo Handles If Implements Imports In ' +
+ 'Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module ' +
+ 'MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing ' +
+ 'NotInheritable NotOverridable Object On Option Optional Or OrElse ' +
+ 'Overloads Overridable Overrides ParamArray Preserve Private Property ' +
+ 'Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume ' +
+ 'Return Select Set Shadows Shared Short Single Static Step Stop String ' +
+ 'Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until ' +
+ 'Variant When While With WithEvents WriteOnly Xor';
+
+ this.regexList = [
+ { regex: new RegExp('\'.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // c# keyword
+ ];
+
+ this.CssClass = 'dp-vb';
+}
+
+dp.sh.Brushes.Vb.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Vb.Aliases = ['vb', 'vb.net'];
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushXml.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushXml.js
new file mode 100644
index 0000000..941ad57
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shBrushXml.js
@@ -0,0 +1,61 @@
+dp.sh.Brushes.Xml = function()
+{
+ this.CssClass = 'dp-xml';
+}
+
+dp.sh.Brushes.Xml.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Xml.Aliases = ['xml', 'xhtml', 'xslt', 'html', 'xhtml'];
+
+dp.sh.Brushes.Xml.prototype.ProcessRegexList = function()
+{
+ function push(array, value)
+ {
+ array[array.length] = value;
+ }
+
+ /* If only there was a way to get index of a group within a match, the whole XML
+ could be matched with the expression looking something like that:
+
+ (<!\[CDATA\[\s*.*\s*\]\]>)
+ | (<!--\s*.*\s*?-->)
+ | (<)*(\w+)*\s*(\w+)\s*=\s*(".*?"|'.*?'|\w+)(/*>)*
+ | (</?)(.*?)(/?>)
+ */
+ var index = 0;
+ var match = null;
+ var regex = null;
+
+ // Match CDATA in the following format <![ ... [ ... ]]>
+ // <\!\[[\w\s]*?\[(.|\s)*?\]\]>
+ this.GetMatches(new RegExp('<\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\]>', 'gm'), 'cdata');
+
+ // Match comments
+ // <!--\s*.*\s*?-->
+ this.GetMatches(new RegExp('<!--\\s*.*\\s*?-->', 'gm'), 'comments');
+
+ // Match attributes and their values
+ // (\w+)\s*=\s*(".*?"|\'.*?\'|\w+)*
+ regex = new RegExp('([\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\'|\\w+)*', 'gm');
+ while((match = regex.exec(this.code)) != null)
+ {
+ push(this.matches, new dp.sh.Match(match[1], match.index, 'attribute'));
+
+ // if xml is invalid and attribute has no property value, ignore it
+ if(match[2] != undefined)
+ {
+ push(this.matches, new dp.sh.Match(match[2], match.index + match[0].indexOf(match[2]), 'attribute-value'));
+ }
+ }
+
+ // Match opening and closing tag brackets
+ // </*\?*(?!\!)|/*\?*>
+ this.GetMatches(new RegExp('</*\\?*(?!\\!)|/*\\?*>', 'gm'), 'tag');
+
+ // Match tag names
+ // </*\?*\s*(\w+)
+ regex = new RegExp('</*\\?*\\s*([\\w-\.]+)', 'gm');
+ while((match = regex.exec(this.code)) != null)
+ {
+ push(this.matches, new dp.sh.Match(match[1], match.index + match[0].indexOf(match[1]), 'tag-name'));
+ }
+}
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shCore.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shCore.js
new file mode 100644
index 0000000..b27395e
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/shCore.js
@@ -0,0 +1,622 @@
+/**
+ * Code Syntax Highlighter.
+ * Version 1.3.0
+ * Copyright (C) 2004 Alex Gorbatchev.
+ * http://www.dreamprojections.com/syntaxhighlighter/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//
+// create namespaces
+//
+var dp = {
+ sh : // dp.sh
+ {
+ Utils : {}, // dp.sh.Utils
+ Brushes : {}, // dp.sh.Brushes
+ Strings : {},
+ Version : '1.3.0'
+ }
+};
+
+dp.sh.Strings = {
+ AboutDialog : '<html><head><title>About...</title></head><body class="dp-about"><table cellspacing="0"><tr><td class="copy"><p class="title">dp.SyntaxHighlighter</div><div class="para">Version: {V}</p><p><a href="http://www.dreamprojections.com/syntaxhighlighter/?ref=about" target="_blank">http://www.dreamprojections.com/SyntaxHighlighter</a></p>©2004-2005 Alex Gorbatchev. All right reserved.</td></tr><tr><td class="footer"><input type="button" class="close" value="OK" onClick="window.close()"/></td></tr></table></body></html>',
+
+ // tools
+ ExpandCode : '+ expand code',
+ ViewPlain : 'view plain',
+ Print : 'print',
+ CopyToClipboard : 'copy to clipboard',
+ About : '?',
+
+ CopiedToClipboard : 'The code is in your clipboard now.'
+};
+
+dp.SyntaxHighlighter = dp.sh;
+
+//
+// Dialog and toolbar functions
+//
+
+dp.sh.Utils.Expand = function(sender)
+{
+ var table = sender;
+ var span = sender;
+
+ // find the span in which the text label and pipe contained so we can hide it
+ while(span != null && span.tagName != 'SPAN')
+ span = span.parentNode;
+
+ // find the table
+ while(table != null && table.tagName != 'TABLE')
+ table = table.parentNode;
+
+ // remove the 'expand code' button
+ span.parentNode.removeChild(span);
+
+ table.tBodies[0].className = 'show';
+ table.parentNode.style.height = '100%'; // containing div isn't getting updated properly when the TBODY is shown
+}
+
+// opens a new windows and puts the original unformatted source code inside.
+dp.sh.Utils.ViewSource = function(sender)
+{
+ var code = sender.parentNode.originalCode;
+ var wnd = window.open('', '_blank', 'width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=1');
+
+ code = code.replace(/</g, '<');
+
+ wnd.document.write('<pre>' + code + '</pre>');
+ wnd.document.close();
+}
+
+// copies the original source code in to the clipboard (IE only)
+dp.sh.Utils.ToClipboard = function(sender)
+{
+ var code = sender.parentNode.originalCode;
+
+ // This works only for IE. There's a way to make it work with Mozilla as well,
+ // but it requires security settings changed on the client, which isn't by
+ // default, so 99% of users won't have it working anyways.
+ if(window.clipboardData)
+ {
+ window.clipboardData.setData('text', code);
+
+ alert(dp.sh.Strings.CopiedToClipboard);
+ }
+}
+
+// creates an invisible iframe, puts the original source code inside and prints it
+dp.sh.Utils.PrintSource = function(sender)
+{
+ var td = sender.parentNode;
+ var code = td.processedCode;
+ var iframe = document.createElement('IFRAME');
+ var doc = null;
+ var wnd =
+
+ // this hides the iframe
+ iframe.style.cssText = 'position:absolute; width:0px; height:0px; left:-5px; top:-5px;';
+
+ td.appendChild(iframe);
+
+ doc = iframe.contentWindow.document;
+ code = code.replace(/</g, '<');
+
+ doc.open();
+ doc.write('<pre>' + code + '</pre>');
+ doc.close();
+
+ iframe.contentWindow.focus();
+ iframe.contentWindow.print();
+
+ td.removeChild(iframe);
+}
+
+dp.sh.Utils.About = function()
+{
+ var wnd = window.open('', '_blank', 'dialog,width=320,height=150,scrollbars=0');
+ var doc = wnd.document;
+
+ var styles = document.getElementsByTagName('style');
+ var links = document.getElementsByTagName('link');
+
+ doc.write(dp.sh.Strings.AboutDialog.replace('{V}', dp.sh.Version));
+
+ // copy over ALL the styles from the parent page
+ for(var i = 0; i < styles.length; i++)
+ doc.write('<style>' + styles[i].innerHTML + '</style>');
+
+ for(var i = 0; i < links.length; i++)
+ if(links[i].rel.toLowerCase() == 'stylesheet')
+ doc.write('<link type="text/css" rel="stylesheet" href="' + links[i].href + '"></link>');
+
+ doc.close();
+ wnd.focus();
+}
+
+//
+// Match object
+//
+dp.sh.Match = function(value, index, css)
+{
+ this.value = value;
+ this.index = index;
+ this.length = value.length;
+ this.css = css;
+}
+
+//
+// Highlighter object
+//
+dp.sh.Highlighter = function()
+{
+ this.addGutter = true;
+ this.addControls = true;
+ this.collapse = false;
+ this.tabsToSpaces = true;
+}
+
+// static callback for the match sorting
+dp.sh.Highlighter.SortCallback = function(m1, m2)
+{
+ // sort matches by index first
+ if(m1.index < m2.index)
+ return -1;
+ else if(m1.index > m2.index)
+ return 1;
+ else
+ {
+ // if index is the same, sort by length
+ if(m1.length < m2.length)
+ return -1;
+ else if(m1.length > m2.length)
+ return 1;
+ }
+ return 0;
+}
+
+// gets a list of all matches for a given regular expression
+dp.sh.Highlighter.prototype.GetMatches = function(regex, css)
+{
+ var index = 0;
+ var match = null;
+
+ while((match = regex.exec(this.code)) != null)
+ {
+ this.matches[this.matches.length] = new dp.sh.Match(match[0], match.index, css);
+ }
+}
+
+dp.sh.Highlighter.prototype.AddBit = function(str, css)
+{
+ var span = document.createElement('span');
+
+ str = str.replace(/&/g, '&');
+ str = str.replace(/ /g, ' ');
+ str = str.replace(/</g, '<');
+ str = str.replace(/\n/gm, ' <br>');
+
+ // when adding a piece of code, check to see if it has line breaks in it
+ // and if it does, wrap individual line breaks with span tags
+ if(css != null)
+ {
+ var regex = new RegExp('<br>', 'gi');
+
+ if(regex.test(str))
+ {
+ var lines = str.split(' <br>');
+
+ str = '';
+
+ for(var i = 0; i < lines.length; i++)
+ {
+ span = document.createElement('SPAN');
+ span.className = css;
+ span.innerHTML = lines[i];
+
+ this.div.appendChild(span);
+
+ // don't add a <BR> for the last line
+ if(i + 1 < lines.length)
+ this.div.appendChild(document.createElement('BR'));
+ }
+ }
+ else
+ {
+ span.className = css;
+ span.innerHTML = str;
+ this.div.appendChild(span);
+ }
+ }
+ else
+ {
+ span.innerHTML = str;
+ this.div.appendChild(span);
+ }
+}
+
+// checks if one match is inside any other match
+dp.sh.Highlighter.prototype.IsInside = function(match)
+{
+ if(match == null || match.length == 0)
+ return;
+
+ for(var i = 0; i < this.matches.length; i++)
+ {
+ var c = this.matches[i];
+
+ if(c == null)
+ continue;
+
+ if((match.index > c.index) && (match.index <= c.index + c.length))
+ return true;
+ }
+
+ return false;
+}
+
+dp.sh.Highlighter.prototype.ProcessRegexList = function()
+{
+ for(var i = 0; i < this.regexList.length; i++)
+ this.GetMatches(this.regexList[i].regex, this.regexList[i].css);
+}
+
+dp.sh.Highlighter.prototype.ProcessSmartTabs = function(code)
+{
+ var lines = code.split('\n');
+ var result = '';
+ var tabSize = 4;
+ var tab = '\t';
+
+ // This function inserts specified amount of spaces in the string
+ // where a tab is while removing that given tab.
+ function InsertSpaces(line, pos, count)
+ {
+ var left = line.substr(0, pos);
+ var right = line.substr(pos + 1, line.length); // pos + 1 will get rid of the tab
+ var spaces = '';
+
+ for(var i = 0; i < count; i++)
+ spaces += ' ';
+
+ return left + spaces + right;
+ }
+
+ // This function process one line for 'smart tabs'
+ function ProcessLine(line, tabSize)
+ {
+ if(line.indexOf(tab) == -1)
+ return line;
+
+ var pos = 0;
+
+ while((pos = line.indexOf(tab)) != -1)
+ {
+ // This is pretty much all there is to the 'smart tabs' logic.
+ // Based on the position within the line and size of a tab,
+ // calculate the amount of spaces we need to insert.
+ var spaces = tabSize - pos % tabSize;
+
+ line = InsertSpaces(line, pos, spaces);
+ }
+
+ return line;
+ }
+
+ // Go through all the lines and do the 'smart tabs' magic.
+ for(var i = 0; i < lines.length; i++)
+ result += ProcessLine(lines[i], tabSize) + '\n';
+
+ return result;
+}
+
+dp.sh.Highlighter.prototype.SwitchToTable = function()
+{
+ // thanks to Lachlan Donald from SitePoint.com for this <br/> tag fix.
+ var html = this.div.innerHTML.replace(/<(br)\/?>/gi, '\n');
+ var lines = html.split('\n');
+ var row = null;
+ var cell = null;
+ var tBody = null;
+ var html = '';
+ var pipe = ' | ';
+
+ // creates an anchor to a utility
+ function UtilHref(util, text)
+ {
+ return '<a href="#" onclick="dp.sh.Utils.' + util + '(this); return false;">' + text + '</a>';
+ }
+
+ tBody = document.createElement('TBODY'); // can be created and all others go to tBodies collection.
+
+ this.table.appendChild(tBody);
+
+ if(this.addGutter == true)
+ {
+ row = tBody.insertRow(-1);
+ cell = row.insertCell(-1);
+ cell.className = 'tools-corner';
+ }
+
+ if(this.addControls == true)
+ {
+ var tHead = document.createElement('THEAD'); // controls will be placed in here
+ this.table.appendChild(tHead);
+
+ row = tHead.insertRow(-1);
+
+ // add corner if there's a gutter
+ if(this.addGutter == true)
+ {
+ cell = row.insertCell(-1);
+ cell.className = 'tools-corner';
+ }
+
+ cell = row.insertCell(-1);
+
+ // preserve some variables for the controls
+ cell.originalCode = this.originalCode;
+ cell.processedCode = this.code;
+ cell.className = 'tools';
+
+ if(this.collapse == true)
+ {
+ tBody.className = 'hide';
+ cell.innerHTML += '<span><b>' + UtilHref('Expand', dp.sh.Strings.ExpandCode) + '</b>' + pipe + '</span>';
+ }
+
+ cell.innerHTML += UtilHref('ViewSource', dp.sh.Strings.ViewPlain) + pipe + UtilHref('PrintSource', dp.sh.Strings.Print);
+
+ // IE has this clipboard object which is easy enough to use
+ if(window.clipboardData)
+ cell.innerHTML += pipe + UtilHref('ToClipboard', dp.sh.Strings.CopyToClipboard);
+
+ cell.innerHTML += pipe + UtilHref('About', dp.sh.Strings.About);
+ }
+
+ for(var i = 0, lineIndex = this.firstLine; i < lines.length - 1; i++, lineIndex++)
+ {
+ row = tBody.insertRow(-1);
+
+ if(this.addGutter == true)
+ {
+ cell = row.insertCell(-1);
+ cell.className = 'gutter';
+ cell.innerHTML = lineIndex;
+ }
+
+ cell = row.insertCell(-1);
+ cell.className = 'line' + (i % 2 + 1); // uses .line1 and .line2 css styles for alternating lines
+ cell.innerHTML = lines[i];
+ }
+
+ this.div.innerHTML = '';
+}
+
+dp.sh.Highlighter.prototype.Highlight = function(code)
+{
+ function Trim(str)
+ {
+ return str.replace(/^\s*(.*?)[\s\n]*$/g, '$1');
+ }
+
+ function Chop(str)
+ {
+ return str.replace(/\n*$/, '').replace(/^\n*/, '');
+ }
+
+ function Unindent(str)
+ {
+ var lines = str.split('\n');
+ var indents = new Array();
+ var regex = new RegExp('^\\s*', 'g');
+ var min = 1000;
+
+ // go through every line and check for common number of indents
+ for(var i = 0; i < lines.length && min > 0; i++)
+ {
+ if(Trim(lines[i]).length == 0)
+ continue;
+
+ var matches = regex.exec(lines[i]);
+
+ if(matches != null && matches.length > 0)
+ min = Math.min(matches[0].length, min);
+ }
+
+ // trim minimum common number of white space from the begining of every line
+ if(min > 0)
+ for(var i = 0; i < lines.length; i++)
+ lines[i] = lines[i].substr(min);
+
+ return lines.join('\n');
+ }
+
+ // This function returns a portions of the string from pos1 to pos2 inclusive
+ function Copy(string, pos1, pos2)
+ {
+ return string.substr(pos1, pos2 - pos1);
+ }
+
+ var pos = 0;
+
+ this.originalCode = code;
+ this.code = Chop(Unindent(code));
+ this.div = document.createElement('DIV');
+ this.table = document.createElement('TABLE');
+ this.matches = new Array();
+
+ if(this.CssClass != null)
+ this.table.className = this.CssClass;
+
+ // replace tabs with spaces
+ if(this.tabsToSpaces == true)
+ this.code = this.ProcessSmartTabs(this.code);
+
+ this.table.border = 0;
+ this.table.cellSpacing = 0;
+ this.table.cellPadding = 0;
+
+ this.ProcessRegexList();
+
+ // if no matches found, add entire code as plain text
+ if(this.matches.length == 0)
+ {
+ this.AddBit(this.code, null);
+ this.SwitchToTable();
+ return;
+ }
+
+ // sort the matches
+ this.matches = this.matches.sort(dp.sh.Highlighter.SortCallback);
+
+ // The following loop checks to see if any of the matches are inside
+ // of other matches. This process would get rid of highligting strings
+ // inside comments, keywords inside strings and so on.
+ for(var i = 0; i < this.matches.length; i++)
+ if(this.IsInside(this.matches[i]))
+ this.matches[i] = null;
+
+ // Finally, go through the final list of matches and pull the all
+ // together adding everything in between that isn't a match.
+ for(var i = 0; i < this.matches.length; i++)
+ {
+ var match = this.matches[i];
+
+ if(match == null || match.length == 0)
+ continue;
+
+ this.AddBit(Copy(this.code, pos, match.index), null);
+ this.AddBit(match.value, match.css);
+
+ pos = match.index + match.length;
+ }
+
+ this.AddBit(this.code.substr(pos), null);
+
+ this.SwitchToTable();
+}
+
+dp.sh.Highlighter.prototype.GetKeywords = function(str)
+{
+ return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
+}
+
+// highlightes all elements identified by name and gets source code from specified property
+dp.sh.HighlightAll = function(name, showGutter /* optional */, showControls /* optional */, collapseAll /* optional */, firstLine /* optional */)
+{
+ function FindValue()
+ {
+ var a = arguments;
+
+ for(var i = 0; i < a.length; i++)
+ {
+ if(a[i] == null)
+ continue;
+
+ if(typeof(a[i]) == 'string' && a[i] != '')
+ return a[i] + '';
+
+ if(typeof(a[i]) == 'object' && a[i].value != '')
+ return a[i].value + '';
+ }
+
+ return null;
+ }
+
+ function IsOptionSet(value, list)
+ {
+ for(var i = 0; i < list.length; i++)
+ if(list[i] == value)
+ return true;
+
+ return false;
+ }
+
+ function GetOptionValue(name, list, defaultValue)
+ {
+ var regex = new RegExp('^' + name + '\\[(\\w+)\\]$', 'gi');
+ var matches = null;
+
+ for(var i = 0; i < list.length; i++)
+ if((matches = regex.exec(list[i])) != null)
+ return matches[1];
+
+ return defaultValue;
+ }
+
+ var elements = document.getElementsByName(name);
+ var highlighter = null;
+ var registered = new Object();
+ var propertyName = 'value';
+
+ // if no code blocks found, leave
+ if(elements == null)
+ return;
+
+ // register all brushes
+ for(var brush in dp.sh.Brushes)
+ {
+ var aliases = dp.sh.Brushes[brush].Aliases;
+
+ if(aliases == null)
+ continue;
+
+ for(var i = 0; i < aliases.length; i++)
+ registered[aliases[i]] = brush;
+ }
+
+ for(var i = 0; i < elements.length; i++)
+ {
+ var element = elements[i];
+ var options = FindValue(
+ element.attributes['class'], element.className,
+ element.attributes['language'], element.language
+ );
+ var language = '';
+
+ if(options == null)
+ continue;
+
+ options = options.split(':');
+
+ language = options[0].toLowerCase();
+
+ if(registered[language] == null)
+ continue;
+
+ // instantiate a brush
+ highlighter = new dp.sh.Brushes[registered[language]]();
+
+ // hide the original element
+ element.style.display = 'none';
+
+ highlighter.addGutter = (showGutter == null) ? !IsOptionSet('nogutter', options) : showGutter;
+ highlighter.addControls = (showControls == null) ? !IsOptionSet('nocontrols', options) : showControls;
+ highlighter.collapse = (collapseAll == null) ? IsOptionSet('collapse', options) : collapseAll;
+
+ // first line idea comes from Andrew Collington, thanks!
+ highlighter.firstLine = (firstLine == null) ? parseInt(GetOptionValue('firstline', options, 1)) : firstLine;
+
+ highlighter.Highlight(element[propertyName]);
+
+ // place the result table inside a div
+ var div = document.createElement('DIV');
+
+ div.className = 'dp-highlighter';
+ div.appendChild(highlighter.table);
+
+ element.parentNode.insertBefore(div, element);
+ }
+}
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/site.css b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/site.css
new file mode 100644
index 0000000..959ab0a
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/site.css
@@ -0,0 +1,25 @@
+/* @override http://felix.apache.org/site/media.data/site.css */
+
+body { background-color: #ffffff; color: #3b3b3b; font-family: Tahoma, Arial, sans-serif; font-size: 10pt; line-height: 140% }
+h1, h2, h3, h4, h5, h6 { font-weight: normal; color: #000000; line-height: 100%; margin-top: 0px}
+h1 { font-size: 200% }
+h2 { font-size: 175% }
+h3 { font-size: 150% }
+h4 { font-size: 140% }
+h5 { font-size: 130% }
+h6 { font-size: 120% }
+a { color: #1980af }
+a:visited { color: #1980af }
+a:hover { color: #1faae9 }
+.title { position: absolute; left: 1px; right: 1px; top:25px; height: 81px; background: url(http://felix.apache.org/site/media.data/gradient.png) repeat-x; background-position: bottom; }
+.logo { position: absolute; width: 15em; height: 81px; text-align: center; }
+.header { text-align: right; margin-right: 20pt; margin-top: 30pt;}
+.menu { border-top: 10px solid #f9bb00; position: absolute; top: 107px; left: 1px; width: 15em; bottom: 0px; padding: 0px; background-color: #fcfcfc }
+.menu ul { background-color: #fdf5d9; list-style: none; padding-left: 4em; margin-top: 0px; padding-top: 2em; padding-bottom: 2em; margin-left: 0px; color: #4a4a43}
+.menu a { text-decoration: none; color: #4a4a43 }
+.main { position: absolute; border-top: 10px solid #cde0ea; top: 107px; left: 15em; right: 1px; margin-left: 2px; padding-right: 4em; padding-left: 1em; padding-top: 1em;}
+.code { background-color: #eeeeee; border: solid 1px black; padding: 0.5em }
+.code-keyword { color: #880000 }
+.code-quote { color: #008800 }
+.code-object { color: #0000dd }
+.code-java { margin: 0em }
\ No newline at end of file
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/superfish.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/superfish.js
new file mode 100644
index 0000000..c6a9c7d
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/superfish.js
@@ -0,0 +1,121 @@
+
+/*
+ * Superfish v1.4.8 - jQuery menu widget
+ * Copyright (c) 2008 Joel Birch
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
+ */
+
+;(function($){
+ $.fn.superfish = function(op){
+
+ var sf = $.fn.superfish,
+ c = sf.c,
+ $arrow = $(['<span class="',c.arrowClass,'"> »</span>'].join('')),
+ over = function(){
+ var $$ = $(this), menu = getMenu($$);
+ clearTimeout(menu.sfTimer);
+ $$.showSuperfishUl().siblings().hideSuperfishUl();
+ },
+ out = function(){
+ var $$ = $(this), menu = getMenu($$), o = sf.op;
+ clearTimeout(menu.sfTimer);
+ menu.sfTimer=setTimeout(function(){
+ o.retainPath=($.inArray($$[0],o.$path)>-1);
+ $$.hideSuperfishUl();
+ if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);}
+ },o.delay);
+ },
+ getMenu = function($menu){
+ var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
+ sf.op = sf.o[menu.serial];
+ return menu;
+ },
+ addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };
+
+ return this.each(function() {
+ var s = this.serial = sf.o.length;
+ var o = $.extend({},sf.defaults,op);
+ o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
+ $(this).addClass([o.hoverClass,c.bcClass].join(' '))
+ .filter('li:has(ul)').removeClass(o.pathClass);
+ });
+ sf.o[s] = sf.op = o;
+
+ $('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() {
+ if (o.autoArrows) addArrow( $('>a:first-child',this) );
+ })
+ .not('.'+c.bcClass)
+ .hideSuperfishUl();
+
+ var $a = $('a',this);
+ $a.each(function(i){
+ var $li = $a.eq(i).parents('li');
+ $a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);});
+ });
+ o.onInit.call(this);
+
+ }).each(function() {
+ var menuClasses = [c.menuClass];
+ if (sf.op.dropShadows && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass);
+ $(this).addClass(menuClasses.join(' '));
+ });
+ };
+
+ var sf = $.fn.superfish;
+ sf.o = [];
+ sf.op = {};
+ sf.IE7fix = function(){
+ var o = sf.op;
+ if ($.browser.msie && $.browser.version > 6 && o.dropShadows && o.animation.opacity!=undefined)
+ this.toggleClass(sf.c.shadowClass+'-off');
+ };
+ sf.c = {
+ bcClass : 'sf-breadcrumb',
+ menuClass : 'sf-js-enabled',
+ anchorClass : 'sf-with-ul',
+ arrowClass : 'sf-sub-indicator',
+ shadowClass : 'sf-shadow'
+ };
+ sf.defaults = {
+ hoverClass : 'sfHover',
+ pathClass : 'overideThisToUse',
+ pathLevels : 1,
+ delay : 800,
+ animation : {opacity:'show'},
+ speed : 'normal',
+ autoArrows : true,
+ dropShadows : true,
+ disableHI : false, // true disables hoverIntent detection
+ onInit : function(){}, // callback functions
+ onBeforeShow: function(){},
+ onShow : function(){},
+ onHide : function(){}
+ };
+ $.fn.extend({
+ hideSuperfishUl : function(){
+ var o = sf.op,
+ not = (o.retainPath===true) ? o.$path : '';
+ o.retainPath = false;
+ var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass)
+ .find('>ul').hide().css('visibility','hidden');
+ o.onHide.call($ul);
+ return this;
+ },
+ showSuperfishUl : function(){
+ var o = sf.op,
+ sh = sf.c.shadowClass+'-off',
+ $ul = this.addClass(o.hoverClass)
+ .find('>ul:hidden').css('visibility','visible');
+ sf.IE7fix.call($ul);
+ o.onBeforeShow.call($ul);
+ $ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); });
+ return this;
+ }
+ });
+
+})(jQuery);
diff --git a/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/supersubs.js b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/supersubs.js
new file mode 100644
index 0000000..4522151
--- /dev/null
+++ b/ipojo/runtime/annotations/doc/how-to-use-ipojo-annotations_files/supersubs.js
@@ -0,0 +1,90 @@
+
+/*
+ * Supersubs v0.2b - jQuery plugin
+ * Copyright (c) 2008 Joel Birch
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * This plugin automatically adjusts submenu widths of suckerfish-style menus to that of
+ * their longest list item children. If you use this, please expect bugs and report them
+ * to the jQuery Google Group with the word 'Superfish' in the subject line.
+ *
+ */
+
+;(function($){ // $ will refer to jQuery within this closure
+
+ $.fn.supersubs = function(options){
+ var opts = $.extend({}, $.fn.supersubs.defaults, options);
+ // return original object to support chaining
+ return this.each(function() {
+ // cache selections
+ var $$ = $(this);
+ // support metadata
+ var o = $.meta ? $.extend({}, opts, $$.data()) : opts;
+ // get the font size of menu.
+ // .css('fontSize') returns various results cross-browser, so measure an em dash instead
+ var fontsize = $('<li id="menu-fontsize">—</li>').css({
+ 'padding' : 0,
+ 'position' : 'absolute',
+ 'top' : '-999em',
+ 'width' : 'auto'
+ }).appendTo($$).width(); //clientWidth is faster, but was incorrect here
+ // remove em dash
+ $('#menu-fontsize').remove();
+ // cache all ul elements
+ $ULs = $$.find('ul');
+ // loop through each ul in menu
+ $ULs.each(function(i) {
+ // cache this ul
+ var $ul = $ULs.eq(i);
+ // get all (li) children of this ul
+ var $LIs = $ul.children();
+ // get all anchor grand-children
+ var $As = $LIs.children('a');
+ // force content to one line and save current float property
+ var liFloat = $LIs.css('white-space','nowrap').css('float');
+ // remove width restrictions and floats so elements remain vertically stacked
+ var emWidth = $ul.add($LIs).add($As).css({
+ 'float' : 'none',
+ 'width' : 'auto'
+ })
+ // this ul will now be shrink-wrapped to longest li due to position:absolute
+ // so save its width as ems. Clientwidth is 2 times faster than .width() - thanks Dan Switzer
+ .end().end()[0].clientWidth / fontsize;
+ // add more width to ensure lines don't turn over at certain sizes in various browsers
+ emWidth += o.extraWidth;
+ // restrict to at least minWidth and at most maxWidth
+ if (emWidth > o.maxWidth) { emWidth = o.maxWidth; }
+ else if (emWidth < o.minWidth) { emWidth = o.minWidth; }
+ emWidth += 'em';
+ // set ul to width in ems
+ $ul.css('width',emWidth);
+ // restore li floats to avoid IE bugs
+ // set li width to full width of this ul
+ // revert white-space to normal
+ $LIs.css({
+ 'float' : liFloat,
+ 'width' : '100%',
+ 'white-space' : 'normal'
+ })
+ // update offset position of descendant ul to reflect new width of parent
+ .each(function(){
+ var $childUl = $('>ul',this);
+ var offsetDirection = $childUl.css('left')!==undefined ? 'left' : 'right';
+ $childUl.css(offsetDirection,emWidth);
+ });
+ });
+
+ });
+ };
+ // expose defaults
+ $.fn.supersubs.defaults = {
+ minWidth : 9, // requires em unit.
+ maxWidth : 25, // requires em unit.
+ extraWidth : 0 // extra width can ensure lines don't sometimes turn over due to slight browser differences in how they round-off values
+ };
+
+})(jQuery); // plugin code ends
diff --git a/ipojo/runtime/annotations/pom.xml b/ipojo/runtime/annotations/pom.xml
new file mode 100644
index 0000000..9dbc71e
--- /dev/null
+++ b/ipojo/runtime/annotations/pom.xml
@@ -0,0 +1,122 @@
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.1</version>
+ <relativePath>../../../pom/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>org.apache.felix.ipojo.annotations</artifactId>
+ <groupId>org.apache.felix</groupId>
+ <version>1.9.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+ <name>Apache Felix iPOJO Annotations</name>
+
+ <description>
+ iPOJO Annotation pack. contained annotations are used to define iPOJO component type.
+ </description>
+ <url>
+ http://felix.apache.org/site/how-to-use-ipojo-annotations.html
+ </url>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.3</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>Apache Felix iPOJO Annotations</Bundle-Name>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor> The Apache Software Foundation </Bundle-Vendor>
+ <Bundle-Description> iPOJO Annotations </Bundle-Description>
+ <Bundle-DocURL>http://felix.apache.org/site/how-to-use-ipojo-annotations.html</Bundle-DocURL>
+ <Export-Package> org.apache.felix.ipojo.annotations,
+ org.apache.felix.ipojo.handler.temporal,
+ org.apache.felix.ipojo.handlers.jmx,
+ org.apache.felix.ipojo.extender,
+ org.apache.felix.ipojo.whiteboard,
+ org.apache.felix.ipojo.handlers.event,
+ org.apache.felix.ipojo.transaction
+ </Export-Package>
+ <Include-Resource>
+ META-INF/LICENSE=LICENSE,
+ META-INF/NOTICE=NOTICE,
+ META-INF/DEPENDENCIES=DEPENDENCIES
+ </Include-Resource>
+ </instructions>
+ <obrRepository>NONE</obrRepository>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+ <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+ <excludes>
+ <param>doc/**/*</param>
+ <param>maven-eclipse.xml</param>
+ <param>.checkstyle</param>
+ <param>.externalToolBuilders/*</param>
+ <param>LICENSE.asm</param>
+ <param>DEPENDENCIES</param>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ <resource>
+ <directory>.</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ <include>DEPENDENCIES*</include>
+ </includes>
+ </resource>
+ </resources>
+
+ </build>
+</project>
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Bind.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Bind.java
new file mode 100644
index 0000000..d84c831
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Bind.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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Comparator;
+
+/**
+ * This annotation declares a bind method.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Bind {
+
+ /**
+ * Set the dependency filter.
+ * Default : no filter
+ */
+ String filter() default "";
+
+ /**
+ * Set if the dependency is an aggregate dependency.
+ * Default : false
+ */
+ boolean aggregate() default false;
+
+ /**
+ * Set if the dependency is optional.
+ * Default : false
+ */
+ boolean optional() default false;
+
+ /**
+ * Set the required specification.
+ * Default : empty (try to discover)
+ */
+ String specification() default "";
+
+ /**
+ * Set the dependency id.
+ * Default : empty
+ */
+ String id() default "";
+
+ /**
+ * Set the binding policy.
+ * Acceptable policy are dynamic, static and dynamic-priority.
+ * Default: dynamic.
+ */
+ String policy() default "dynamic";
+
+ /**
+ * Set the comparator.
+ * The indicated class must implement {@link Comparator}
+ */
+ Class comparator() default Comparator.class;
+
+ /**
+ * Set the from attribute.
+ */
+ String from() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Component.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Component.java
new file mode 100644
index 0000000..28fbb2d
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Component.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a component.
+ * This annotation is mandatory to declares an iPOJO component.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Component {
+
+ /**
+ * Set if the component type is public.
+ * Default: true
+ * @deprecated renamed to publicFactory.
+ */
+ boolean public_factory() default true;
+
+ /**
+ * Set if the component type is public.
+ * Default: true
+ */
+ boolean publicFactory() default true;
+
+ /**
+ * Set the component type name.
+ * Default : implementation class name.
+ */
+ String name() default "";
+
+ /**
+ * Enable / Disable the architecture exposition.
+ * Default : false
+ */
+ boolean architecture() default false;
+
+ /**
+ * Set if the component is immediate.
+ * Default : false
+ */
+ boolean immediate() default false;
+
+ /**
+ * Set if the component must propagate received configuration to provided services.
+ * default: false
+ */
+ boolean propagation() default false;
+
+ /**
+ * Set the Managed Service PID.
+ * default no PID (i.e. the managed service will not be exposed).
+ */
+ String managedservice() default "";
+
+ /**
+ * Set the factory-method, if the pojo has to be created
+ * from a static method. The specified method must be a static
+ * method and return a pojo object.
+ * By default, iPOJO uses the 'regular' constructor.
+ * @deprecated now is called <tt>factoryMethod</tt>.
+ */
+ String factory_method() default "";
+
+ /**
+ * Set the factory-method, if the pojo has to be created
+ * from a static method. The specified method must be a static
+ * method and return a pojo object.
+ * By default, iPOJO uses the 'regular' constructor.
+ */
+ String factoryMethod() default "";
+
+ /**
+ * Set the version of the component type.
+ */
+ String version() default "";
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java
new file mode 100644
index 0000000..e4bfaeb
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java
@@ -0,0 +1,32 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a lifecycle controller.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface Controller {
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Handler.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Handler.java
new file mode 100644
index 0000000..7b8e4a4
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Handler.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a handler.
+ * This annotation is mandatory to declares an iPOJO handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Handler {
+
+ /**
+ * Name of the handler (required).
+ */
+ String name();
+
+ /**
+ * Namespace of the handler (required).
+ */
+ String namespace();
+
+ /**
+ * Start-level of the handler.
+ * Default: 1.
+ */
+ int level() default 1;
+
+ /**
+ * Enable / Disable the architecture exposition.
+ * Default : false
+ */
+ boolean architecture() default false;
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/HandlerDeclaration.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/HandlerDeclaration.java
new file mode 100644
index 0000000..c4eaeaf
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/HandlerDeclaration.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to configure a handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface HandlerDeclaration {
+
+ /**
+ * The content of this attribute represents the XML
+ * that would be used in the metadata.xml.
+ */
+ String value();
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Instantiate.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Instantiate.java
new file mode 100644
index 0000000..dba9d43
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Instantiate.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to create an 'empty' instance of the
+ * current component type.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Instantiate {
+
+ /**
+ * Optional attribute to set the instance name.
+ * Default: no name
+ */
+ String name() default "";
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Invalidate.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Invalidate.java
new file mode 100644
index 0000000..72a7c6c
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Invalidate.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares an invalidate callback.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Invalidate {
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Modified.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Modified.java
new file mode 100644
index 0000000..ea18919
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Modified.java
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Comparator;
+
+/**
+ * This annotation declares a modify method.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Modified {
+
+ /**
+ * Set the dependency filter.
+ * Default : no filter
+ */
+ String filter() default "";
+
+ /**
+ * Set if the dependency is an aggregate dependency.
+ * Default : false
+ */
+ boolean aggregate() default false;
+
+
+ /**
+ * Set if the dependency is optional.
+ * Default : false
+ */
+ boolean optional() default false;
+
+ /**
+ * Set the required specification.
+ * Default : empty (try to discover).
+ */
+ String specification() default "";
+
+ /**
+ * Set the dependency id.
+ * Default : empty.
+ */
+ String id() default "";
+
+ /**
+ * Set the binding policy.
+ * Acceptable policy are dynamic, static and dynamic-priority.
+ * Default: dynamic.
+ */
+ String policy() default "dynamic";
+
+ /**
+ * Set the comparator.
+ * The indicated class must implement {@link Comparator}
+ */
+ Class comparator() default Comparator.class;
+
+ /**
+ * Set the from attribute.
+ */
+ String from() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostRegistration.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostRegistration.java
new file mode 100644
index 0000000..2e6bac8
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostRegistration.java
@@ -0,0 +1,32 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+
+/**
+ * This annotation declares a post-service-registration method.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface PostRegistration {
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostUnregistration.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostUnregistration.java
new file mode 100644
index 0000000..428dff5
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/PostUnregistration.java
@@ -0,0 +1,32 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+
+/**
+ * This annotation declares a post-service-unregistration method.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface PostUnregistration {
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Property.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Property.java
new file mode 100644
index 0000000..2c80de5
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Property.java
@@ -0,0 +1,50 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a component property.
+ * It can target both fields and methods.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
+public @interface Property {
+
+ /**
+ * Set property name.
+ * Default : empty
+ */
+ String name() default "";
+
+ /**
+ * Set property value.
+ * Default : empty
+ */
+ String value() default "";
+
+ /**
+ * Is the property mandatory?
+ * Default: false
+ */
+ boolean mandatory() default false;
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
new file mode 100644
index 0000000..c58c3ef
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+* This annotation declares that the component instances will provide a service.
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+@Target(ElementType.TYPE)
+public @interface Provides {
+
+ /**
+ * Set the provided specifications.
+ * Default : all implemented interfaces
+ */
+ Class[] specifications() default { };
+
+ /**
+ * Set the service object creation strategy.
+ * Two value are possible: SINGLETON, SERVICE, METHOD, INSTANCE or the strategy class name.
+ * SERVICE means OSGi Service Factory.
+ * METHOD delegates the creation to the factory-method of the component
+ * INSTANCE creates one service object per requiring instance
+ * for other strategies, specify the qualified name of the CreationStrategy class.
+ * Default : SINGLETON
+ */
+ String strategy() default "SINGLETON";
+
+ /**
+ * Allows adding static properties to the service.
+ * Nested properties are static service properties,
+ * so <b>must</b> contain the name and the value as
+ * they are not attached to a field.
+ * The array contains {@link StaticServiceProperty} elements.
+ * Default : No service properties
+ */
+ StaticServiceProperty[] properties() default {};
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Requires.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Requires.java
new file mode 100644
index 0000000..4ea2408
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Requires.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Target;
+import java.util.Comparator;
+
+/**
+ * This annotation declares a service requirement.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target({ ElementType.FIELD, ElementType.PARAMETER })
+@Inherited
+public @interface Requires {
+
+ /**
+ * Set the LDAP filter of the dependency.
+ * Default : no filter
+ */
+ String filter() default "";
+
+ /**
+ * Set if the dependency is optional.
+ * Default : false
+ */
+ boolean optional() default false;
+
+ /**
+ * Set the dependency id.
+ * Default : empty
+ */
+ String id() default "";
+
+ /**
+ * Enable / Disable nullable pattern.
+ * Default : true
+ */
+ boolean nullable() default true;
+
+ /**
+ * Set the default-implementation to use if the dependency is optional,
+ * and no providers are available.
+ * The class must implement the required service interface.
+ * Default : no default-implementation
+ */
+ Class defaultimplementation() default Class.class;
+
+ /**
+ * Set the binding policy.
+ * Acceptable policy are dynamic, static and dynamic-priority.
+ * Default: dynamic.
+ */
+ String policy() default "dynamic";
+
+ /**
+ * Set the comparator.
+ * The indicated class must implement {@link Comparator}
+ */
+ Class comparator() default Comparator.class;
+
+ /**
+ * Set the from attribute.
+ */
+ String from() default "";
+
+ /**
+ * Set the required service specification.
+ * This attribute is required for Collection field.
+ */
+ String specification() default "";
+
+ /**
+ * Set to true if the service dependency is injected
+ * as a proxy.
+ * Default: true
+ */
+ boolean proxy() default true;
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java
new file mode 100644
index 0000000..bd0dd5f
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a service controller.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface ServiceController {
+
+ /**
+ * Sets the initial value of the controller.
+ */
+ boolean value() default true;
+
+
+ /**
+ * Sets the targeted specification.
+ * If not set, target all specifications.
+ */
+ Class specification() default Object.class;
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceProperty.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceProperty.java
new file mode 100644
index 0000000..ceba75f
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceProperty.java
@@ -0,0 +1,50 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a service property.
+ * It can target both fields and methods.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface ServiceProperty {
+
+ /**
+ * Set the property name.
+ * Default : empty
+ */
+ String name() default "";
+
+ /**
+ * Set the property value.
+ * Default : empty
+ */
+ String value() default "";
+
+ /**
+ * Is the property mandatory?
+ * Default: false
+ */
+ boolean mandatory() default false;
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java
new file mode 100644
index 0000000..f9ac981
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java
@@ -0,0 +1,51 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+
+/**
+ * This annotation declares a static service property.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public @interface StaticServiceProperty {
+
+ /**
+ * Set the property name.
+ * This name is mandatory for static properties.
+ */
+ String name();
+
+ /**
+ * Set the property value.
+ * Default : empty
+ */
+ String value() default "";
+
+ /**
+ * Is the property mandatory?
+ * Default: false
+ */
+ boolean mandatory() default false;
+
+ /**
+ * Set the type.
+ * This value is required only for static properties.
+ */
+ String type();
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Unbind.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Unbind.java
new file mode 100644
index 0000000..b44ca3b
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Unbind.java
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Comparator;
+
+/**
+ * This annotation declares an unbind method.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Unbind {
+
+ /**
+ * Set the dependency filter.
+ * Default : no filter
+ */
+ String filter() default "";
+
+ /**
+ * Set if the dependency is an aggregate dependency.
+ * Default : false
+ */
+ boolean aggregate() default false;
+
+
+ /**
+ * Set if the dependency is optional.
+ * Default : false
+ */
+ boolean optional() default false;
+
+ /**
+ * Set the required specification.
+ * Default : empty (try to discover).
+ */
+ String specification() default "";
+
+ /**
+ * Set the dependency id.
+ * Default : empty.
+ */
+ String id() default "";
+
+ /**
+ * Set the binding policy.
+ * Acceptable policy are dynamic, static and dynamic-priority.
+ * Default: dynamic.
+ */
+ String policy() default "dynamic";
+
+ /**
+ * Set the comparator.
+ * The indicated class must implement {@link Comparator}
+ */
+ Class comparator() default Comparator.class;
+
+ /**
+ * Set the from attribute.
+ */
+ String from() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Updated.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Updated.java
new file mode 100644
index 0000000..285d236
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Updated.java
@@ -0,0 +1,32 @@
+/*
+ * 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.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares an updated callback.
+ * Updated callback are called after a reconfiguration.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Updated {
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Validate.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Validate.java
new file mode 100644
index 0000000..50e239f
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/annotations/Validate.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 org.apache.felix.ipojo.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation declares a validate callback.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Validate {
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/extender/Extender.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/extender/Extender.java
new file mode 100644
index 0000000..6c1af00
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/extender/Extender.java
@@ -0,0 +1,49 @@
+/*
+ * 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.felix.ipojo.extender;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Extender pattern Handler annotation.
+ * Allows configuring an extender pattern.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Extender {
+
+ /**
+ * Sets the looked Manifest entry.
+ */
+ String extension();
+
+ /**
+ * Sets the on service arrival callback.
+ */
+ String onArrival();
+
+ /**
+ * Sets the on service departure callback.
+ */
+ String onDeparture();
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Requires.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Requires.java
new file mode 100644
index 0000000..3e9e03a
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Requires.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.felix.ipojo.handler.temporal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Temporal dependency annotation.
+ * Allows specifying a temporal dependency.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @deprecated replaced by {@link Temporal}
+ */
+@Target(ElementType.FIELD)
+@Deprecated
+public @interface Requires {
+
+ /**
+ * Set the LDAP filter of the dependency.
+ * Default : no filter
+ */
+ String filter() default "";
+
+ /**
+ * Timeout of the dependency.
+ * Default : true
+ */
+ long timeout() default 3000;
+
+ /**
+ * Set the on timeout action.
+ * Supports null, nullable, empty, and default-implementation.
+ * In this latter case, you must specify the qualified class name
+ * of the default-implementation (instead of default-implementation).
+ * Default: no action (i.e throws a runtime exception)
+ */
+ String onTimeout() default "";
+
+ /**
+ * Set the service specification (for Collection fields).
+ * This attribute is mandatory for Collections.
+ */
+ String specification() default "";
+
+ /**
+ * Inject a proxy instead of the real object.
+ * This allows passing this reference to collaborators.
+ * Default: false
+ */
+ boolean proxy() default false;
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Temporal.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Temporal.java
new file mode 100644
index 0000000..c123cda
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handler/temporal/Temporal.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 org.apache.felix.ipojo.handler.temporal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Temporal dependency annotation.
+ * Allows specifying a temporal dependency.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface Temporal {
+
+ /**
+ * Set the dependency id.
+ * Default: empty, will use the field name.
+ */
+ String id() default "";
+
+ /**
+ * Set the LDAP filter of the dependency.
+ * Default: no filter
+ */
+ String filter() default "";
+
+ /**
+ * Timeout of the dependency.
+ * Default: 3000 ms.
+ */
+ long timeout() default 3000;
+
+ /**
+ * Set the on timeout action.
+ * Supports null, nullable, empty, and default-implementation.
+ * In this latter case, you must specify the qualified class name
+ * of the default-implementation (instead of default-implementation).
+ * Default: no action (i.e throws a runtime exception)
+ */
+ String onTimeout() default "";
+
+ /**
+ * Set the service specification (for Collection fields).
+ * This attribute is mandatory for Collections.
+ */
+ String specification() default "";
+
+ /**
+ * Inject a proxy instead of the real object.
+ * This allows passing this reference to collaborators.
+ * Default: false
+ */
+ boolean proxy() default false;
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publisher.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publisher.java
new file mode 100644
index 0000000..466d6f0
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publisher.java
@@ -0,0 +1,69 @@
+/*
+ * 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.felix.ipojo.handlers.event;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+
+/**
+ * Event Admin Publisher handler.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @deprecated replaced by Publishes
+ */
+@Target(ElementType.FIELD)
+public @interface Publisher {
+
+ /**
+ * Sets the publisher name.
+ */
+ String name();
+
+ /**
+ * Sets topics on which event are sent.
+ * The topics are separated by a ',' such as in
+ * "foo, bar".
+ * Default : no topics (configured in the instance configuration)
+ */
+ String topics() default "";
+
+ /**
+ * Enables/Disables synchronous sending.
+ * Default : false (asynchronous)
+ */
+ boolean synchronous() default false;
+
+ /**
+ * Sets the data key in which the data is
+ * put.
+ * Default : user.data
+ * @deprecated replaced by dataKey
+ */
+ String data_key() default "user.data";
+
+ /**
+ * Sets the data key in which the data is
+ * put.
+ * Default : user.data
+ */
+ String dataKey() default "user.data";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publishes.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publishes.java
new file mode 100644
index 0000000..5102df4
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Publishes.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo.handlers.event;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+
+/**
+ * Event Admin Publisher handler.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface Publishes {
+
+ /**
+ * Sets the publisher name.
+ */
+ String name();
+
+ /**
+ * Sets topics on which event are sent.
+ * The topics are separated by a ',' such as in
+ * "foo, bar".
+ * Default : no topics (configured in the instance configuration)
+ */
+ String topics() default "";
+
+ /**
+ * Enables/Disables synchronous sending.
+ * Default : false (asynchronous)
+ */
+ boolean synchronous() default false;
+
+ /**
+ * Sets the data key in which the data is
+ * put.
+ * Default : user.data
+ */
+ String dataKey() default "user.data";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Subscriber.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Subscriber.java
new file mode 100644
index 0000000..70cb7a8
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/event/Subscriber.java
@@ -0,0 +1,83 @@
+/*
+ * 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.felix.ipojo.handlers.event;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+
+/**
+ * Event Admin Subscriber handler.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Subscriber {
+
+ /**
+ * Sets the subscriber name.
+ */
+ String name();
+
+ /**
+ * Sets topics on which event are received.
+ * The topics are separated by a ',' such as in
+ * "foo, bar".
+ * Default : no topics (configured in the instance configuration)
+ */
+ String topics() default "";
+
+ /**
+ * Sets the data key in which the data is
+ * stored.
+ * Default : no key
+ * @deprecated replaced by dataKey
+ */
+ String data_key() default "";
+
+ /**
+ * Sets the data type (type of the received data).
+ * Default : no type.
+ * @deprecated replaced by dataType
+ */
+ String data_type() default "";
+
+ /**
+ * Sets the data key in which the data is
+ * stored.
+ * Default : no key
+ */
+ String dataKey() default "";
+
+ /**
+ * Sets the data type (type of the received data).
+ * Default : no type.
+ */
+ String dataType() default "";
+
+
+ /**
+ * Sets the event filter. Only event matching with the
+ * specified LDAP filter are received.
+ * default : no filter;
+ */
+ String filter() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Config.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Config.java
new file mode 100644
index 0000000..9a0d5e9
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Config.java
@@ -0,0 +1,62 @@
+/*
+ * 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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Handler annotation.
+ * Allows exposing the instances as a JMX MBean.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @deprecated replaced by {@link JMXBean}
+ */
+@Target(ElementType.TYPE)
+public @interface Config {
+
+ /**
+ * Enables or Disables MOSGi usage.
+ * If MOSGi is used, MBeans are exposed with the MOSGi mechanism.
+ * Otherwise the MBean Platform Server is used.
+ * Default : false
+ */
+ boolean usesMOSGi() default false;
+
+ /**
+ * Sets the MBean object name.
+ * Default : 'package-name:factory-name:instance-name'.
+ */
+ String objectname() default "";
+
+ /**
+ * Sets the MBean domain.
+ * Default : 'package-name'
+ */
+ String domain() default "";
+
+ /**
+ * Sets the MBean name.
+ * Default : 'instance-name'
+ */
+ String name() default "";
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXBean.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXBean.java
new file mode 100644
index 0000000..ca31185
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXBean.java
@@ -0,0 +1,61 @@
+/*
+ * 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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Handler annotation.
+ * Allows exposing the instances as a JMX MBean.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface JMXBean {
+
+ /**
+ * Enables or Disables MOSGi usage.
+ * If MOSGi is used, MBeans are exposed with the MOSGi mechanism.
+ * Otherwise the MBean Platform Server is used.
+ * Default : false
+ */
+ boolean usesMOSGi() default false;
+
+ /**
+ * Sets the MBean object name.
+ * Default : 'package-name:factory-name:instance-name'.
+ */
+ String objectname() default "";
+
+ /**
+ * Sets the MBean domain.
+ * Default : 'package-name'
+ */
+ String domain() default "";
+
+ /**
+ * Sets the MBean name.
+ * Default : 'instance-name'
+ */
+ String name() default "";
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXMethod.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXMethod.java
new file mode 100644
index 0000000..27ca89a
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXMethod.java
@@ -0,0 +1,42 @@
+/*
+ * 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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Method annotation.
+ * Allows exposing methods in the MBean.
+ * This annotation must be used only if the {@link Config} annotation
+ * is used.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface JMXMethod {
+
+ /**
+ * Gets the method description.
+ * Default : no description
+ */
+ String description() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXProperty.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXProperty.java
new file mode 100644
index 0000000..1b31c7c
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/JMXProperty.java
@@ -0,0 +1,55 @@
+/*
+ * 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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Property annotation.
+ * Allows exposing properties in the MBean.
+ * This annotation must be used only if the {@link Config} annotation
+ * is used.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.FIELD)
+public @interface JMXProperty {
+
+ /**
+ * Sets the property name.
+ */
+ String name();
+
+ /**
+ * Sets the access permission.
+ * Possible values are 'r' (default) or 'w'.
+ * 'w' implies read and write access.
+ */
+ String rights() default "r";
+
+ /**
+ * Enables notification on the current property.
+ * Notifications are disable by default.
+ */
+ boolean notification() default false;
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Method.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Method.java
new file mode 100644
index 0000000..4fb0cd1
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Method.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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Method annotation.
+ * Allows exposing methods in the MBean.
+ * This annotation must be used only if the {@link Config} annotation
+ * is used.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @deprecated replaced by {@link JMXMethod}
+ */
+@Target(ElementType.METHOD)
+public @interface Method {
+
+ /**
+ * Gets the method description.
+ * Default : no description
+ */
+ String description() default "";
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Property.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Property.java
new file mode 100644
index 0000000..6de9e83
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/handlers/jmx/Property.java
@@ -0,0 +1,56 @@
+/*
+ * 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.felix.ipojo.handlers.jmx;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * JMX Property annotation.
+ * Allows exposing properties in the MBean.
+ * This annotation must be used only if the {@link Config} annotation
+ * is used.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @deprecated replaced by {@link JMXProperty}
+ */
+@Target(ElementType.FIELD)
+public @interface Property {
+
+ /**
+ * Sets the property name.
+ */
+ String name();
+
+ /**
+ * Sets the access permission.
+ * Possible values are 'r' (default) or 'w'.
+ * 'w' implies read and write access.
+ */
+ String rights() default "r";
+
+ /**
+ * Enables notification on the current property.
+ * Notifications are disable by default.
+ */
+ boolean notification() default false;
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transaction.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transaction.java
new file mode 100644
index 0000000..c70762c
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transaction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.felix.ipojo.transaction;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Transaction Handler annotation.
+ * Allows the declaration of transactionnal methods
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Transaction {
+
+ /**
+ * Sets the Transaction field. The specified field must be of the type
+ * Transaction.
+ */
+ String field() default "";
+
+ /**
+ * Sets the method called when a transaction is committed.
+ */
+ String oncommit() default "";
+
+ /**
+ * Sets the method called when a transaction is rolled back.
+ */
+ String onrollback() default "";
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transactional.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transactional.java
new file mode 100644
index 0000000..ba41f5e
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/transaction/Transactional.java
@@ -0,0 +1,56 @@
+/*
+ * 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.felix.ipojo.transaction;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Transactionnal Method annotation.
+ * Allows the declaration of transactionnal method.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.METHOD)
+public @interface Transactional {
+
+ /**
+ * Sets the transaction timeout.
+ */
+ long timeout() default 0;;
+
+ /**
+ * Sets the transaction propagation policy.
+ * Supported values are : requires mandatory, notsupported, never, requiresnew.
+ */
+ String propagation() default "requires";
+
+ /**
+ * Sets the exceptions that do not rollback the current transaction.
+ */
+ String[] norollbackfor() default { };
+
+ /**
+ * Sets if the transaction rollback throws an exception.
+ */
+ boolean exceptiononrollback() default false;
+
+
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Wbp.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Wbp.java
new file mode 100644
index 0000000..570ade1
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Wbp.java
@@ -0,0 +1,54 @@
+/*
+ * 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.felix.ipojo.whiteboard;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Whiteboard pattern Handler annotation.
+ * Allows configuring a whiteboard pattern.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Wbp {
+
+ /**
+ * Sets the whiteboard pattern filter.
+ */
+ String filter();
+
+ /**
+ * Sets the on service arrival callback.
+ */
+ String onArrival();
+
+ /**
+ * Sets the on service departure callback.
+ */
+ String onDeparture();
+
+ /**
+ * Sets the on service modification callback.
+ * Default : no callback.
+ */
+ String onModification() default "";
+}
diff --git a/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Whiteboards.java b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Whiteboards.java
new file mode 100644
index 0000000..d00f1b5
--- /dev/null
+++ b/ipojo/runtime/annotations/src/main/java/org/apache/felix/ipojo/whiteboard/Whiteboards.java
@@ -0,0 +1,38 @@
+/*
+ * 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.felix.ipojo.whiteboard;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Whiteboard pattern Handler annotation.
+ * Allows configuring several whiteboard patterns.
+ * Be aware that despite is it provided in the annotations jar,
+ * it refers to an external handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Target(ElementType.TYPE)
+public @interface Whiteboards {
+
+ /**
+ * Whiteboards list.
+ */
+ Wbp[] whiteboards() default {};
+}
diff --git a/ipojo/runtime/composite/DEPENDENCIES b/ipojo/runtime/composite/DEPENDENCIES
new file mode 100644
index 0000000..d66cccd
--- /dev/null
+++ b/ipojo/runtime/composite/DEPENDENCIES
@@ -0,0 +1,23 @@
+Apache Felix iPOJO Composite
+Copyright 2008-2011 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software developed at
+Copyright (c) 2000-2005 INRIA, France Telecom
+Licensed under BSD License.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+- Apache License 2.0
+- BSD License
diff --git a/ipojo/runtime/composite/LICENSE b/ipojo/runtime/composite/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ipojo/runtime/composite/LICENSE
@@ -0,0 +1,202 @@
+
+ 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/ipojo/runtime/composite/LICENSE.asm b/ipojo/runtime/composite/LICENSE.asm
new file mode 100644
index 0000000..cc529ed
--- /dev/null
+++ b/ipojo/runtime/composite/LICENSE.asm
@@ -0,0 +1,29 @@
+Copyright (c) 2000-2005 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ipojo/runtime/composite/NOTICE b/ipojo/runtime/composite/NOTICE
new file mode 100644
index 0000000..4c77df6
--- /dev/null
+++ b/ipojo/runtime/composite/NOTICE
@@ -0,0 +1,11 @@
+Apache Felix iPOJO Composite
+Copyright 2008-2011 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Copyright (c) 2000-2005 INRIA, France Telecom
+Licensed under BSD License.
+
diff --git a/ipojo/runtime/composite/doc/changelog.txt b/ipojo/runtime/composite/doc/changelog.txt
new file mode 100644
index 0000000..36548c7
--- /dev/null
+++ b/ipojo/runtime/composite/doc/changelog.txt
@@ -0,0 +1,43 @@
+Changes from the 1.6.0 to 1.8.0
+-------------------------------
+** Improvement
+ * [FELIX-2746] - Composite should support instance configuration
+
+Changes from the 1.4.0 to 1.6.0
+-------------------------------
+** Improvement
+ * [FELIX-1427] - Service injection with Smart Proxies
+ * [FELIX-1906] - Allow calling a method when service properties of an already injected service are modified
+
+
+Changes from 1.2.0 to 1.4.0
+---------------------------
+** Bug
+ * [FELIX-994] - ClassCastException while Architecture on a composite with a provided service
+
+** Improvement
+ * Update parent pom
+
+
+Changes from 1.0.0 to 1.2.0
+---------------------------
+** Bug
+ * [FELIX-797] - Composite Architecture contains duplicate instances
+ * [FELIX-817] - iPOJO Service Exported throw an error when stopping
+
+Changes from 0.8.1 to 1.0.0
+---------------------------
+** Bug
+ * [FELIX-622] - iPOJO Composite Service Instance can use factories twice to create service instance
+ * [FELIX-628] - Architecture service should not publish the instance.name property
+ * [FELIX-637] - Composite service implementation does not support long argument
+
+** Improvement
+ * [FELIX-673] - Provide OBR description to iPOJO bundles
+ * [FELIX-688] - Better error reporting when an instance creation failed
+ * [FELIX-689] - Instance 'name' property should become 'instance.name'
+ * [FELIX-716] - Provide XML schemas for iPOJO descriptors
+
+Version 0.8.1
+-------------
+ * Initial release
diff --git a/ipojo/runtime/composite/obr.xml b/ipojo/runtime/composite/obr.xml
new file mode 100644
index 0000000..a39ed25
--- /dev/null
+++ b/ipojo/runtime/composite/obr.xml
@@ -0,0 +1,40 @@
+<!--
+ 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.
+-->
+<obr>
+ <capability name="ipojo.handler">
+ <p n="name" v="instance"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ <p n="type" v="composite"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="subservice"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ <p n="type" v="composite"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="provides"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ <p n="type" v="composite"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="architecture"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ <p n="type" v="composite"/>
+ </capability>
+</obr>
\ No newline at end of file
diff --git a/ipojo/runtime/composite/pom.xml b/ipojo/runtime/composite/pom.xml
new file mode 100644
index 0000000..b4b7573
--- /dev/null
+++ b/ipojo/runtime/composite/pom.xml
@@ -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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.1</version>
+ <relativePath>../../../pom/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix iPOJO Composite</name>
+ <artifactId>org.apache.felix.ipojo.composite</artifactId>
+ <groupId>org.apache.felix</groupId>
+ <version>1.9.0-SNAPSHOT</version>
+
+ <properties>
+ <ipojo.package.version>1.8.0</ipojo.package.version>
+ </properties>
+
+ <description>
+ iPOJO Composition Model. This is an iPOJO extension to execute service composition.
+ </description>
+ <url>http://felix.apache.org/site/ipojo-composition-tutorial.html</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
+ <version>1.8.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ <version>1.8.0</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.3</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>Apache Felix iPOJO Composite</Bundle-Name>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor> The Apache Software Foundation </Bundle-Vendor>
+ <Bundle-Description> iPOJO Composititon Framework
+ </Bundle-Description>
+ <Bundle-DocURL>
+ http://felix.apache.org/site/ipojo-composition-tutorial.html
+ </Bundle-DocURL>
+ <Import-Package>
+ org.apache.felix.ipojo,
+ org.apache.felix.ipojo.architecture,
+ org.apache.felix.ipojo.context,
+ org.apache.felix.ipojo.metadata,
+ org.apache.felix.ipojo.parser,
+ org.apache.felix.ipojo.util,
+ org.apache.felix.ipojo.manipulation,
+ org.osgi.framework;version=1.3,
+ !org.objectweb.asm.tree,
+ </Import-Package>
+ <Private-Package>
+ org.apache.felix.ipojo.composite.architecture,
+ org.apache.felix.ipojo.composite.service*,
+ org.apache.felix.ipojo.composite.instance,
+ org.apache.felix.ipojo.composite.util,
+ <!-- ASM (Manipulator dependencies) -->
+ org.objectweb.asm.commons,
+ org.objectweb.asm.signature,
+ org.objectweb.asm
+ </Private-Package>
+ <Export-Package>
+ org.apache.felix.ipojo.composite; version="${ipojo.package.version}"
+ </Export-Package>
+ <Include-Resource>
+ META-INF/LICENSE=LICENSE,
+ META-INF/NOTICE=NOTICE,
+ META-INF/LICENSE.asm=LICENSE.asm,
+ META-INF/DEPENDENCIES=DEPENDENCIES
+ </Include-Resource>
+ <IPOJO-Extension>
+ composite:org.apache.felix.ipojo.composite.CompositeFactory
+ </IPOJO-Extension>
+ <_donotcopy> (CVS|.svn|.+.bak|~.+|metadata.xml) </_donotcopy>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-ipojo-plugin</artifactId>
+ <version>1.8.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>ipojo-bundle</goal>
+ </goals>
+ <configuration>
+ <metadata>src/main/resources/metadata.xml</metadata>
+ <ignoreAnnotations>true</ignoreAnnotations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+ <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+ <excludes>
+ <param>doc/**/*</param>
+ <param>maven-eclipse.xml</param>
+ <param>.checkstyle</param>
+ <param>.externalToolBuilders/*</param>
+ <param>LICENSE.asm</param>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <enableRulesSummary>false</enableRulesSummary>
+ <violationSeverity>warning</violationSeverity>
+ <configLocation>http://felix.apache.org/ipojo/dev/checkstyle_ipojo.xml</configLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java
new file mode 100644
index 0000000..d739de5
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java
@@ -0,0 +1,181 @@
+/*
+ * 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.felix.ipojo.composite;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Logger;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * The component factory manages component instance objects. This management
+ * consist in creating and managing component instance build with the component
+ * factory. This class could export Factory and ManagedServiceFactory services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeFactory extends ComponentFactory implements TrackerCustomizer {
+
+ /**
+ * Tracker used to track required handler factories.
+ */
+ protected Tracker m_tracker;
+
+ /**
+ * Create a composite factory.
+ * @param context : bundle context
+ * @param metadata : metadata of the component to create
+ * @throws ConfigurationException occurs when the element describing the factory is malformed.
+ */
+ public CompositeFactory(BundleContext context, Element metadata) throws ConfigurationException {
+ super(context, metadata);
+ }
+
+ /**
+ * Check if the metadata are well formed.
+ * @param metadata : metadata
+ * @throws ConfigurationException occurs when the element describing the factory is malformed.
+ * @see org.apache.felix.ipojo.ComponentFactory#check(org.apache.felix.ipojo.metadata.Element)
+ */
+ public void check(Element metadata) throws ConfigurationException {
+ String name = metadata.getAttribute("name");
+ if (name == null) {
+ throw new ConfigurationException("A composite needs a name : " + metadata);
+ }
+ }
+
+ public String getClassName() { return "composite"; }
+
+
+ /**
+ * Compute required handlers.
+ * @return the list of required handler.
+ */
+ public List getRequiredHandlerList() {
+ List list = new ArrayList();
+ Element[] elems = m_componentMetadata.getElements();
+ for (int i = 0; i < elems.length; i++) {
+ Element current = elems[i];
+ RequiredHandler req = new RequiredHandler(current.getName(), current.getNameSpace());
+ if (! list.contains(req)) { list.add(req); }
+ }
+
+ // Add architecture if architecture != 'false'
+ String arch = m_componentMetadata.getAttribute("architecture");
+ if (arch == null || arch.equalsIgnoreCase("true")) {
+ RequiredHandler req = new RequiredHandler("architecture", null);
+ if (! list.contains(req)) { list.add(req); }
+ }
+
+ return list;
+ }
+
+ /**
+ * Stop all the instance managers.
+ */
+ public synchronized void stopping() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ }
+ m_tracker = null;
+ }
+
+ /**
+ * Start all the instance managers.
+ */
+ public synchronized void starting() {
+ if (m_requiredHandlers.size() != 0) {
+ try {
+ String filter = "(&(" + Constants.OBJECTCLASS + "=" + HandlerFactory.class.getName() + ")"
+ + "(" + Handler.HANDLER_TYPE_PROPERTY + "=" + CompositeHandler.HANDLER_TYPE + ")"
+ + "(factory.state=1)"
+ + ")";
+ m_tracker = new Tracker(m_context, m_context.createFilter(filter), this);
+ m_tracker.open();
+ } catch (InvalidSyntaxException e) {
+ m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage());
+ stop();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Create an instance from the current factory.
+ * @param configuration : instance configuration
+ * @param context : bundle context to inject in the instance manager
+ * @param handlers : array of handler object to attached on the instance
+ * @return the created instance
+ * @throws ConfigurationException either the instance configuration or the instance starting has failed
+ * @see org.apache.felix.ipojo.ComponentFactory#createInstance(java.util.Dictionary, org.apache.felix.ipojo.IPojoContext, org.apache.felix.ipojo.HandlerManager[])
+ */
+ public ComponentInstance createInstance(Dictionary configuration, IPojoContext context, HandlerManager[] handlers) throws ConfigurationException {
+ CompositeManager inst = new CompositeManager(this, context, handlers);
+ inst.configure(m_componentMetadata, configuration);
+ inst.start();
+ return inst;
+ }
+
+ /**
+ * Reconfigure an existing instance.
+ * @param properties : the new configuration to push.
+ * @throws UnacceptableConfiguration : occurs if the new configuration is
+ * not consistent with the component type.
+ * @throws MissingHandlerException : occurs when an handler is unavailable when creating the instance.
+ * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
+ */
+ public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException {
+ if (properties == null || (properties.get("instance.name") == null && properties.get("name") == null)) { // Support both instance.name and name
+ throw new UnacceptableConfiguration("The configuration does not contains the \"instance.name\" property");
+ }
+
+ String name = (String) properties.get("instance.name");
+ if (name == null) {
+ name = (String) properties.get("name");
+ }
+
+ ComponentInstance instance = (ComponentInstance) m_componentInstances.get(name);
+ if (instance == null) { // The instance does not exists.
+ return;
+ }
+
+ instance.reconfigure(properties); // re-configure the component
+ }
+
+ public String getFactoryName() {
+ return m_componentMetadata.getAttribute("name"); // Mandatory attribute.
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java
new file mode 100644
index 0000000..4194fed
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.felix.ipojo.composite;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.util.Logger;
+
+
+/**
+ * Composite Handler Abstract Class. An composite handler need implements these
+ * method to be notified of lifecycle change...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class CompositeHandler extends Handler {
+
+ /**
+ * Composite Handler type.
+ */
+ public static final String HANDLER_TYPE = "composite";
+
+ /**
+ * Reference on the composite manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Composite Factory.
+ */
+ private CompositeFactory m_factory;
+
+ /**
+ * Set the manager.
+ * This method me be called only once time.
+ * @param instance : the composite manager.
+ */
+ protected final void attach(ComponentInstance instance) {
+ m_manager = (CompositeManager) instance;
+ }
+
+ public final void setFactory(Factory factory) {
+ m_factory = (CompositeFactory) factory;
+ }
+
+ public final Logger getLogger() {
+ return m_factory.getLogger();
+ }
+
+ public final CompositeManager getCompositeManager() {
+ return m_manager;
+ }
+
+ /**
+ * Get a plugged handler of the same container.
+ * This method must be call only in the start method (or after).
+ * In the configure method, this method can not return a consistent
+ * result as all handlers are not plugged.
+ * @param name : name of the handler to find (class name).
+ * @return the composite handler object or null if the handler is not found.
+ */
+ public final Handler getHandler(String name) {
+ return m_manager.getCompositeHandler(name);
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeInstanceDescription.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeInstanceDescription.java
new file mode 100644
index 0000000..f451686
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeInstanceDescription.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.felix.ipojo.composite;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+
+/**
+ * Composite Instance Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeInstanceDescription extends InstanceDescription {
+
+ /**
+ * Creates a Primitive Instance Description.
+ * @param type the component type description
+ * @param instance the instance description
+ */
+ public CompositeInstanceDescription(ComponentTypeDescription type, CompositeManager instance) {
+ super(type, instance);
+ }
+
+
+ /**
+ * Gets the list of contained instance in the describe instance.
+ * This list contains only instances who exposed their architecture.
+ * @return the list of contained instances.
+ */
+ public InstanceDescription[] getContainedInstances() {
+ // Get instances description of internal instance
+ ServiceContext internal = ((CompositeManager) m_instance).getServiceContext();
+ try {
+ ServiceReference[]refs = internal.getServiceReferences(Architecture.class.getName(), null);
+ if (refs != null) {
+ InstanceDescription[] descs = new InstanceDescription[refs.length];
+ for (int i = 0; i < refs.length; i++) {
+ Architecture arch = (Architecture) internal.getService(refs[i]);
+ descs[i] = arch.getInstanceDescription();
+ internal.ungetService(refs[i]);
+ }
+ return descs;
+ }
+ } catch (InvalidSyntaxException e) {
+ // Cannot happen
+ }
+ return new InstanceDescription[0];
+ }
+
+ /**
+ * Gets the list of internally published services.
+ * @return the list of published services.
+ */
+ public Element getInternalServices() {
+ Element services = new Element("services", "");
+ ServiceContext internal = ((CompositeManager) m_instance)
+ .getServiceContext();
+ try {
+ ServiceReference[] refs = internal.getServiceReferences(null, "(!(objectclass=" + Factory.class.getName() + "))");
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ Element svc = new Element("service", "");
+ String[] keys = refs[i].getPropertyKeys();
+ for (int j = 0; j < keys.length; j++) {
+ Object v = refs[i].getProperty(keys[j]);
+ if (v instanceof String[]) {
+ List l = Arrays.asList((String[]) v);
+ svc.addAttribute(new Attribute(keys[j], l.toString()));
+ } else {
+ svc.addAttribute(new Attribute(keys[j], v.toString()));
+ }
+ }
+ services.addElement(svc);
+ }
+
+ } catch (InvalidSyntaxException e) {
+ // Cannot happen
+ }
+ return services;
+ }
+
+ /**
+ * Gets the instance description.
+ * Overridden to add created objects.
+ * @return the instance description
+ */
+ public Element getDescription() {
+ Element elem = super.getDescription();
+ elem.addElement(getInternalServices());
+
+ InstanceDescription[] descs = getContainedInstances();
+ if (descs.length > 0) {
+ Element inst = new Element("ContainedInstances", "");
+ for (int i = 0; i < descs.length; i++) {
+ inst.addElement(descs[i].getDescription());
+ }
+ elem.addElement(inst);
+ }
+
+ return elem;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java
new file mode 100644
index 0000000..1fe3a10
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java
@@ -0,0 +1,442 @@
+/*
+ * 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.felix.ipojo.composite;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.InstanceStateListener;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+
+/**
+ * iPOJO Composite manager. The composite manager class manages one instance of
+ * a component type which is a composition. It manages component lifecycle, and
+ * handlers...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeManager implements ComponentInstance, InstanceStateListener {
+
+ /**
+ * The context of the component.
+ */
+ private final BundleContext m_context;
+
+ /**
+ * Parent factory (ComponentFactory).
+ */
+ private final CompositeFactory m_factory;
+
+ /**
+ * Composite Handler list.
+ */
+ private HandlerManager[] m_handlers;
+
+ /**
+ * Instance State Listener List.
+ */
+ private List m_listeners = new ArrayList();
+
+ /**
+ * Internal service context of the composition.
+ */
+ private CompositeServiceContext m_internalContext;
+
+ /**
+ * The instance description.
+ */
+ private final CompositeInstanceDescription m_description;
+
+ /**
+ * Name of the component instance.
+ */
+ private String m_name;
+
+ /**
+ * Component state (STOPPED at the beginning).
+ */
+ private int m_state = STOPPED;
+
+ /**
+ * Logger.
+ */
+ private Logger m_logger;
+
+ /**
+ * Construct a new Component Manager.
+ * @param factory : the factory managing the instance manager
+ * @param context : the bundle context to give to the instance
+ * @param handlers : the handlers to plug
+ */
+ public CompositeManager(CompositeFactory factory, BundleContext context, HandlerManager[] handlers) {
+ m_factory = factory;
+ m_context = context;
+ // Initialize the service context.
+ m_internalContext = new CompositeServiceContext(m_context, this);
+ m_handlers = handlers;
+ m_description = new CompositeInstanceDescription(m_factory.getComponentDescription(), this);
+ m_logger = new Logger(m_context, this);
+
+ }
+
+ /**
+ * Plug the given handler to the current container.
+ * @param handler : the handler to plug.
+ */
+ public synchronized void addCompositeHandler(HandlerManager handler) {
+ if (m_handlers.length > 0) {
+ HandlerManager[] newInstances = new HandlerManager[m_handlers.length + 1];
+ System.arraycopy(m_handlers, 0, newInstances, 0, m_handlers.length);
+ newInstances[m_handlers.length] = handler;
+ m_handlers = newInstances;
+ } else {
+ m_handlers = new HandlerManager[] { handler };
+ }
+ }
+
+ /**
+ * Add an instance to the created instance list.
+ * @param listener : the instance state listener to add.
+ * @see org.apache.felix.ipojo.ComponentInstance#addInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public void addInstanceStateListener(InstanceStateListener listener) {
+ synchronized (m_listeners) {
+ m_listeners.add(listener);
+ }
+ }
+
+ /**
+ * Configure the instance manager. Stop the existing handler, clear the
+ * handler list, change the metadata, recreate the handler
+ *
+ * @param metadata : the component type metadata
+ * @param configuration : the configuration of the instance
+ * @throws ConfigurationException : occurs when the component type are incorrect.
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ // Add the name
+ m_name = (String) configuration.get("instance.name");
+
+ // Create the standard handlers and add these handlers to the list
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].init(this, metadata, configuration);
+ }
+ }
+
+ /**
+ * Dispose the instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#dispose()
+ */
+ public void dispose() {
+ if (m_state > STOPPED) { stop(); }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, DISPOSED);
+ }
+
+ m_factory.disposed(this);
+
+ // Cleaning
+ m_state = DISPOSED;
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].dispose();
+ }
+ m_handlers = new HandlerManager[0];
+ m_listeners.clear();
+ }
+
+ /**
+ * Return a specified handler.
+ * @param name : class name of the handler to find
+ * @return : the handler, or null if not found
+ */
+ public CompositeHandler getCompositeHandler(String name) {
+ for (int i = 0; i < m_handlers.length; i++) {
+ HandlerFactory fact = (HandlerFactory) m_handlers[i].getFactory();
+ if (fact.getHandlerName().equals(name) || fact.getComponentDescription().getClassName().equals(name)) {
+ return (CompositeHandler) m_handlers[i].getHandler();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the bundle context used by this instance.
+ * @return the parent context of the instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#getContext()
+ */
+ public BundleContext getContext() {
+ return m_context;
+ }
+
+ /**
+ * Get the factory which create this instance.
+ * @return the factory of the component
+ * @see org.apache.felix.ipojo.ComponentInstance#getFactory()
+ */
+ public ComponentFactory getFactory() {
+ return m_factory;
+ }
+
+ /**
+ * Get the global bundle context.
+ * @return the global bundle context.
+ */
+ public BundleContext getGlobalContext() {
+ IPojoContext context = (IPojoContext) m_context;
+ return context.getGlobalContext();
+ }
+
+ /**
+ * Return the instance description of this instance.
+ * @return the instance description.
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ return m_description;
+ }
+
+ /**
+ * Get the instance name.
+ * @return the instance name
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceName()
+ */
+ public String getInstanceName() {
+ return m_name;
+ }
+
+ /**
+ * Get the parent service context.
+ * @return the parent service context.
+ */
+ public ServiceContext getParentServiceContext() {
+ IPojoContext context = (IPojoContext) m_context;
+ return context.getServiceContext();
+ }
+
+ /**
+ * REturn the list of handlers plugged on this instance.
+ * @return the list of the registered handlers.
+ */
+ public CompositeHandler[] getRegistredCompositeHandlers() {
+ CompositeHandler[] handler = new CompositeHandler[m_handlers.length];
+ for (int i = 0; i < m_handlers.length; i++) {
+ handler[i] = (CompositeHandler) m_handlers[i].getHandler();
+ }
+ return handler;
+ }
+
+ /**
+ * Get the internal service context of this instance.
+ * @return the internal service context.
+ */
+ public ServiceContext getServiceContext() {
+ return m_internalContext;
+ }
+
+ /**
+ * Get the actual state of the instance.
+ * @return the actual state of the instance
+ * @see org.apache.felix.ipojo.ComponentInstance#getState()
+ */
+ public int getState() {
+ return m_state;
+ }
+
+ /**
+ * Check if the instance is started.
+ * @return true if the instance is started.
+ * @see org.apache.felix.ipojo.ComponentInstance#isStarted()
+ */
+ public boolean isStarted() {
+ return m_state > STOPPED;
+ }
+
+ /**
+ * Reconfigure the current instance.
+ * @param configuration : the new instance configuration.
+ * @see org.apache.felix.ipojo.ComponentInstance#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary configuration) {
+ m_logger.log(Logger.INFO, "Reconfiguring composite with " + configuration);
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_logger.log(Logger.INFO, "Delegating reconfiguration to " + m_handlers[i].getClassName());
+ m_handlers[i].getHandler().reconfigure(configuration);
+ }
+ }
+
+ /**
+ * Remove an instance state listener.
+ * @param listener : the listener to remove
+ * @see org.apache.felix.ipojo.ComponentInstance#removeInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public void removeInstanceStateListener(InstanceStateListener listener) {
+ synchronized (m_listeners) {
+ m_listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Set the state of the component.
+ * if the state changed call the stateChanged(int) method on the handlers.
+ * @param state : new state
+ */
+ public void setState(int state) {
+ if (m_state != state) {
+ if (state > m_state) {
+ // The state increases (Stopped = > IV, IV => V) => invoke handlers from the higher priority to the lower
+ m_state = state;
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ } else {
+ // The state decreases (V => IV, IV = > Stopped, Stopped => Disposed)
+ m_state = state;
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, state);
+ }
+ }
+ }
+
+ /**
+ * Start the instance manager.
+ */
+ public synchronized void start() {
+ if (m_state > STOPPED) {
+ return;
+ } // Instance already started
+
+
+ // The new state of the component is UNRESOLVED
+ m_state = INVALID;
+
+ m_internalContext.start(); // Turn on the factory tracking
+
+ // Plug handler descriptions
+ Handler[] handlers = getRegistredCompositeHandlers();
+ for (int i = 0; i < handlers.length; i++) {
+ m_description.addHandler(handlers[i].getDescription());
+ }
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].start();
+ m_handlers[i].addInstanceStateListener(this);
+ }
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) {
+ setState(INVALID);
+ return;
+ }
+ }
+ setState(VALID);
+
+ }
+
+ /**
+ * State Change listener callback.
+ * This method is notified at each time a plugged handler becomes invalid.
+ * @param instance : changing instance
+ * @param newState : new state
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public synchronized void stateChanged(ComponentInstance instance, int newState) {
+ if (m_state <= STOPPED) { return; }
+
+ // Update the component state if necessary
+ if (newState == INVALID && m_state == VALID) {
+ // Need to update the state to UNRESOLVED
+ setState(INVALID);
+ return;
+ }
+ if (newState == VALID && m_state == INVALID) {
+ // An handler becomes valid => check if all handlers are valid
+ boolean isValid = true;
+ for (int i = 0; i < m_handlers.length; i++) {
+ isValid = isValid && m_handlers[i].getState() == VALID;
+ }
+
+ if (isValid) { setState(VALID); }
+ }
+ if (newState == DISPOSED) {
+ kill();
+ }
+ }
+
+ /**
+ * Stop the instance manager.
+ */
+ public synchronized void stop() {
+ if (m_state <= STOPPED) {
+ return;
+ } // Instance already stopped
+
+ setState(INVALID);
+ // Stop all the handlers
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].removeInstanceStateListener(this);
+ m_handlers[i].stop();
+ }
+
+ m_internalContext.stop(); // Turn off the factory tracking
+ m_state = STOPPED;
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, STOPPED);
+ }
+ }
+
+ /**
+ * Kill the current instance.
+ * Only the factory of this instance can call this method.
+ */
+ protected synchronized void kill() {
+ if (m_state > STOPPED) { stop(); }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, DISPOSED);
+ }
+
+ // Cleaning
+ m_state = DISPOSED;
+
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].dispose();
+ }
+ m_handlers = new HandlerManager[0];
+ m_listeners.clear();
+ }
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java
new file mode 100644
index 0000000..5bdc75e
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java
@@ -0,0 +1,487 @@
+/*
+ * 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.felix.ipojo.composite;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.context.ServiceReferenceImpl;
+import org.apache.felix.ipojo.context.ServiceRegistry;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * CompositeServiceContext Class. This class provides an implementation of the
+ * service context for composite.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeServiceContext implements ServiceContext, TrackerCustomizer {
+
+ /**
+ * Structure storing the reference, the factory and the registration.
+ */
+ private class Record {
+ /**
+ * Reference of the represented factory from the external context.
+ */
+ private ServiceReference m_ref;
+ /**
+ * Registration of the factory in the internal context.
+ */
+ private ServiceRegistration m_reg;
+ /**
+ * Represented Factory.
+ */
+ private FactoryProxy m_fact;
+ }
+
+ /**
+ * List of imported factories.
+ */
+ private List m_factories = new ArrayList();
+ /**
+ * Internal service registry.
+ */
+ private ServiceRegistry m_registry;
+
+ /**
+ * Component Instance who creates this registry.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Global service context.
+ */
+ private BundleContext m_global;
+
+ /**
+ * Tracker tracking Factories to import.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * Constructor. This constructor instantiate a service registry with the
+ * given bundle context.
+ *
+ * @param context : the bundle context
+ */
+ public CompositeServiceContext(BundleContext context) {
+ m_registry = new ServiceRegistry(context);
+ if (context instanceof IPojoContext) {
+ m_global = ((IPojoContext) context).getGlobalContext();
+ } else {
+ m_global = context; // the parent context is the global context
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context : the bundle context
+ * @param instance : the component instance owning this context
+ */
+ public CompositeServiceContext(BundleContext context, ComponentInstance instance) {
+ this(context);
+ m_instance = instance;
+ }
+
+ /**
+ * Add a service listener.
+ * @param arg0 : The service listener to add
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void addServiceListener(ServiceListener arg0) {
+ m_registry.addServiceListener(arg0);
+ }
+
+ /**
+ * Add a filtered service listener.
+ * @param arg0 : the service listener object to add
+ * @param arg1 : the LDAP filter for this listener
+ * @throws InvalidSyntaxException : occurs if the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener,
+ * java.lang.String)
+ */
+ public void addServiceListener(ServiceListener arg0, String arg1) throws InvalidSyntaxException {
+ m_registry.addServiceListener(arg0, arg1);
+ }
+
+ /**
+ * Get all service references.
+ * @param arg0 : The required service interface.
+ * @param arg1 : LDAP filter
+ * @return the list of all service reference matching with the query
+ * @throws InvalidSyntaxException : occurs when the given filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getAllServiceReferences(java.lang.String,
+ * java.lang.String)
+ */
+ public ServiceReference[] getAllServiceReferences(String arg0, String arg1) throws InvalidSyntaxException {
+ return m_registry.getAllServiceReferences(arg0, arg1);
+ }
+
+ /**
+ * Get a service object for the given service reference.
+ * @param arg0 : the service reference
+ * @return the service object or null if the reference is no more valid or if the object is not accessible
+ * @see org.apache.felix.ipojo.ServiceContext#getService(org.osgi.framework.ServiceReference)
+ */
+ public Object getService(ServiceReference arg0) {
+ if (arg0 instanceof ServiceReferenceImpl) {
+ return m_registry.getService(m_instance, arg0);
+ } else {
+ throw new RuntimeException("Cannot get a global service from the local registry");
+ }
+ }
+
+
+ /**
+ * Get a service reference for the required interface.
+ * @param arg0 : the required interface name
+ * @return the service reference or null if no available provider
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference getServiceReference(String arg0) {
+ return m_registry.getServiceReference(arg0);
+ }
+
+ /**
+ * Get all accessible service reference for the given query.
+ * @param clazz : required interface
+ * @param filter : LDAP filter
+ * @return the list (array) of service reference matching with the query.
+ * @throws InvalidSyntaxException : occurs when the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ return m_registry.getServiceReferences(clazz, filter);
+ }
+
+
+ /**
+ * Register a service inside the composite context.
+ * @param arg0 : list of interfaces to register.
+ * @param arg1 : service object
+ * @param arg2 : properties list
+ * @return the service registration
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String[] arg0, Object arg1, Dictionary arg2) {
+ return m_registry.registerService(m_instance, arg0, arg1, arg2);
+ }
+
+ /**
+ * Register a service inside the composite context.
+ * @param arg0 : interface to register.
+ * @param arg1 : service object
+ * @param arg2 : properties list
+ * @return the service registration
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String arg0, Object arg1, Dictionary arg2) {
+ return m_registry.registerService(m_instance, arg0, arg1, arg2);
+ }
+
+ /**
+ * Remove a service listener.
+ * @param arg0 : the service listener to remove
+ * @see org.apache.felix.ipojo.ServiceContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void removeServiceListener(ServiceListener arg0) {
+ m_registry.removeServiceListener(arg0);
+ }
+
+ /**
+ * Unget a service.
+ * @param arg0 the service reference to unget
+ * @return true
+ * @see org.apache.felix.ipojo.ServiceContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ public boolean ungetService(ServiceReference arg0) {
+ return m_registry.ungetService(m_instance, arg0);
+ }
+
+ /**
+ * Import a factory form the parent to the internal registry.
+ *
+ * @param ref : the reference of the factory to import.
+ */
+ private void importFactory(ServiceReference ref) {
+ Record rec = new Record();
+ m_factories.add(rec);
+ Dictionary dict = new Properties();
+ for (int j = 0; j < ref.getPropertyKeys().length; j++) {
+ dict.put(ref.getPropertyKeys()[j], ref.getProperty(ref.getPropertyKeys()[j]));
+ }
+ rec.m_fact = new FactoryProxy((Factory) m_tracker.getService(ref), this);
+ rec.m_reg = registerService(Factory.class.getName(), rec.m_fact, dict);
+ rec.m_ref = ref;
+ }
+
+ /**
+ * Remove a factory of the available factory list.
+ *
+ * @param ref : the reference on the factory to remove.
+ */
+ private void removeFactory(ServiceReference ref) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == ref) {
+ if (rec.m_reg != null) {
+ rec.m_reg.unregister();
+ rec.m_fact = null;
+ }
+ m_tracker.ungetService(rec.m_ref);
+ m_factories.remove(rec);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Start the registry management.
+ */
+ public void start() {
+ m_tracker = new Tracker(m_global, Factory.class.getName(), this);
+ m_tracker.open();
+ }
+
+ /**
+ * Stop the registry management.
+ */
+ public synchronized void stop() {
+ m_tracker.close();
+ m_registry.reset();
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ removeFactory(rec.m_ref);
+ }
+ m_tracker = null;
+ }
+
+ /**
+ * Check if the factory list contain the given reference.
+ *
+ * @param ref : the reference to find.
+ * @return true if the list contains the given reference.
+ */
+ private boolean containsRef(ServiceReference ref) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == ref) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a bundle listener.
+ * Delegate on the global bundle context.
+ * @param arg0 : bundle listener to add
+ * @see org.osgi.framework.BundleContext#addBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void addBundleListener(BundleListener arg0) {
+ m_global.addBundleListener(arg0);
+ }
+
+ /**
+ * Add a framework listener.
+ * Delegate on the global bundle context.
+ * @param arg0 : framework listener to add.
+ * @see org.osgi.framework.BundleContext#addFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void addFrameworkListener(FrameworkListener arg0) {
+ m_global.addFrameworkListener(arg0);
+ }
+
+ /**
+ * Create a LDAP filter.
+ * @param arg0 : String-form of the filter
+ * @return the created filter object
+ * @throws InvalidSyntaxException : if the given argument is not a valid against the LDAP grammar.
+ * @see org.osgi.framework.BundleContext#createFilter(java.lang.String)
+ */
+ public Filter createFilter(String arg0) throws InvalidSyntaxException {
+ return m_global.createFilter(arg0);
+ }
+
+ /**
+ * Get the current bundle.
+ * @return the current bundle
+ * @see org.osgi.framework.BundleContext#getBundle()
+ */
+ public Bundle getBundle() {
+ return m_global.getBundle();
+ }
+
+ /**
+ * Get the bundle object with the given id.
+ * @param bundleId : bundle id
+ * @return the bundle object
+ * @see org.osgi.framework.BundleContext#getBundle(long)
+ */
+ public Bundle getBundle(long bundleId) {
+ return m_global.getBundle(bundleId);
+ }
+
+ /**
+ * Get installed bundles.
+ * @return the list of installed bundles
+ * @see org.osgi.framework.BundleContext#getBundles()
+ */
+ public Bundle[] getBundles() {
+ return m_global.getBundles();
+ }
+
+
+ /**
+ * Get a data file.
+ * @param filename : File name.
+ * @return the File object
+ * @see org.osgi.framework.BundleContext#getDataFile(java.lang.String)
+ */
+ public File getDataFile(String filename) {
+ return m_global.getDataFile(filename);
+ }
+
+ /**
+ * Get a property value.
+ * @param key : key of the asked property
+ * @return the property value (object) or null if no property are associated with the given key
+ * @see org.osgi.framework.BundleContext#getProperty(java.lang.String)
+ */
+ public String getProperty(String key) {
+ return m_global.getProperty(key);
+ }
+
+ /**
+ * Install a bundle.
+ * @param location : URL of the bundle to install
+ * @return the installed bundle
+ * @throws BundleException : if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String)
+ */
+ public Bundle installBundle(String location) throws BundleException {
+ return m_global.installBundle(location);
+ }
+
+ /**
+ * Install a bundle.
+ * @param location : URL of the bundle to install
+ * @param input :
+ * @return the installed bundle
+ * @throws BundleException : if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String, java.io.InputStream)
+ */
+ public Bundle installBundle(String location, InputStream input) throws BundleException {
+ return m_global.installBundle(location, input);
+ }
+
+ /**
+ * Remove a bundle listener.
+ * @param listener : the listener to remove
+ * @see org.osgi.framework.BundleContext#removeBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void removeBundleListener(BundleListener listener) {
+ m_global.removeBundleListener(listener);
+ }
+
+ /**
+ * Remove a framework listener.
+ * @param listener : the listener to remove
+ * @see org.osgi.framework.BundleContext#removeFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void removeFrameworkListener(FrameworkListener listener) {
+ m_global.removeFrameworkListener(listener);
+ }
+
+ /**
+ * A new factory is detected.
+ * @param reference : service reference
+ * @return true if not already imported.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference reference) {
+ if (!containsRef(reference)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A matching reference has been added. The import factory can now be imported.
+ * @param reference : the added reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ importFactory(reference);
+ }
+
+ /**
+ * An imported factory is modified.
+ * @param reference : modified reference
+ * @param service : factory object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == reference) {
+ Dictionary dict = new Properties();
+ for (int j = 0; j < reference.getPropertyKeys().length; j++) {
+ dict.put(reference.getPropertyKeys()[j], reference.getProperty(reference.getPropertyKeys()[j]));
+ }
+ rec.m_reg.setProperties(dict);
+ return;
+ }
+ }
+ }
+
+ /**
+ * An imported factory disappears.
+ * @param reference : reference
+ * @param service : factory object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ if (containsRef(reference)) {
+ removeFactory(reference);
+ }
+
+ }
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
new file mode 100644
index 0000000..db3a053
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
@@ -0,0 +1,177 @@
+/*
+ * 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.felix.ipojo.composite;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.FactoryStateListener;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Bridge representing a Factory inside a composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryProxy implements Factory {
+
+ /**
+ * Delegated factory.
+ */
+ private Factory m_delegate;
+
+ /**
+ * Destination context.
+ */
+ private ServiceContext m_context;
+
+ /**
+ * Constructor.
+ * @param fact : the targeted factory.
+ * @param svcContext : the service context to target.
+ */
+ public FactoryProxy(Factory fact, ServiceContext svcContext) {
+ m_delegate = fact;
+ m_context = svcContext;
+ }
+
+ /**
+ * Create an instance manager (i.e. component type instance).
+ * @param configuration : the configuration properties for this component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration : when a given configuration is not valid.
+ * @throws MissingHandlerException : occurs when the creation failed due to a missing handler (the factory should be invalid)
+ * @throws ConfigurationException : occurs when the creation failed due to a configuration issue
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary)
+ */
+ public ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException,
+ ConfigurationException {
+ return m_delegate.createComponentInstance(configuration, m_context);
+ }
+
+ /**
+ * Create an instance manager (i.e. component type instance). This has these service interaction in the scope given in argument.
+ * @param configuration : the configuration properties for this component.
+ * @param serviceContext : the service context of the component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration : when the given configuration is not valid.
+ * @throws MissingHandlerException : when at least one handler is missing.
+ * @throws ConfigurationException : when an issue occurs during the oconfiguration of the instance.
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary, org.apache.felix.ipojo.ServiceContext)
+ */
+ public ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration,
+ MissingHandlerException, ConfigurationException {
+ return m_delegate.createComponentInstance(configuration, serviceContext);
+ }
+
+ /**
+ * Get the component type information containing provided service, configuration properties ...
+ * @return the component type information.
+ * @see org.apache.felix.ipojo.Factory#getDescription()
+ */
+ public Element getDescription() {
+ return m_delegate.getDescription();
+ }
+
+ /**
+ * Return the factory name.
+ * @return the name of the factory.
+ * @see org.apache.felix.ipojo.Factory#getName()
+ */
+ public String getName() {
+ return m_delegate.getName();
+ }
+
+ /**
+ * Check if the given configuration is acceptable as a configuration of a component instance.
+ * @param conf : the configuration to test
+ * @return true if the configuration is acceptable
+ * @see org.apache.felix.ipojo.Factory#isAcceptable(java.util.Dictionary)
+ */
+ public boolean isAcceptable(Dictionary conf) {
+ return m_delegate.isAcceptable(conf);
+ }
+
+ /**
+ * Reconfigure an instance already created. This configuration need to have the name property to identify the instance.
+ * @param conf : the configuration to reconfigure the instance.
+ * @throws UnacceptableConfiguration : if the given configuration is not consistent for the targeted instance.
+ * @throws MissingHandlerException : when at least one handler is missing
+ * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException {
+ m_delegate.reconfigure(conf);
+ }
+
+ /**
+ * Add a factory listener.
+ * @param listener : the listener to add.
+ * @see org.apache.felix.ipojo.Factory#addFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void addFactoryStateListener(FactoryStateListener listener) {
+ m_delegate.addFactoryStateListener(listener);
+
+ }
+
+ public List getMissingHandlers() {
+ return m_delegate.getMissingHandlers();
+ }
+
+ public List getRequiredHandlers() {
+ return m_delegate.getRequiredHandlers();
+ }
+
+ /**
+ * Remove a service listener.
+ * @param listener : the listener to remove
+ * @see org.apache.felix.ipojo.Factory#removeFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void removeFactoryStateListener(FactoryStateListener listener) {
+ m_delegate.removeFactoryStateListener(listener);
+
+ }
+
+ public ComponentTypeDescription getComponentDescription() {
+ return m_delegate.getComponentDescription();
+ }
+
+ public String getClassName() {
+ return m_delegate.getClassName();
+ }
+
+ public int getState() {
+ return m_delegate.getState();
+ }
+
+ public BundleContext getBundleContext() {
+ return m_delegate.getBundleContext();
+ }
+
+ public String getVersion() {
+ return m_delegate.getVersion();
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java
new file mode 100644
index 0000000..817ec08
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.felix.ipojo.composite.architecture;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Composite Architecture Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ArchitectureHandler extends CompositeHandler implements Architecture {
+
+ /**
+ * Name of the component.
+ */
+ private String m_name;
+
+ /**
+ * Configure the handler.
+ *
+ * @param metadata : the metadata of the component
+ * @param configuration : the instance configuration
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager,
+ * org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) {
+ m_name = (String) configuration.get("instance.name");
+ }
+
+ /**
+ * Stop the handler.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ // Nothing to do.
+ }
+
+ /**
+ * Start the handler.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ info("Start composite architecture handler with " + m_name + " name");
+ }
+
+ /**
+ * Get the instance description.
+ * @return the instance description
+ * @see org.apache.felix.ipojo.architecture.Architecture#getDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ return getCompositeManager().getInstanceDescription();
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
new file mode 100644
index 0000000..696217c
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
@@ -0,0 +1,438 @@
+/*
+ * 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.felix.ipojo.composite.instance;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.InstanceStateListener;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseException;
+
+/**
+ * Composite Instance Handler.
+ * This handler allows creating an instance inside a composite.
+ * This instance is determine by its type and a configuration.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceHandler extends CompositeHandler implements InstanceStateListener {
+
+ /**
+ * Internal context.
+ */
+ private ServiceContext m_scope;
+
+ /**
+ * Available factories.
+ */
+ private Factory[] m_factories;
+
+ /**
+ * Handler description.
+ */
+ private InstanceHandlerDescription m_description;
+
+
+ /**
+ * This structure aims to manage a configuration. It stores all necessary
+ * information to create an instance and to track the factory.
+ */
+ class ManagedConfiguration {
+ /**
+ * Configuration of the instance to create.
+ */
+ private Dictionary m_configuration;
+
+ /**
+ * Factory name.
+ */
+ private String m_factoryName;
+
+ /**
+ * Created instance.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Desired Factory (can be the classname).
+ */
+ private String m_desiredFactory;
+
+ /**
+ * Constructor.
+ *
+ * @param conf : the configuration to create.
+ */
+ ManagedConfiguration(Dictionary conf) {
+ m_configuration = conf;
+ m_desiredFactory = (String) conf.get("component");
+ }
+
+ /**
+ * Return the managed configuration.
+ * @return the configuration.
+ */
+ protected Dictionary getConfiguration() {
+ return m_configuration;
+ }
+
+ /**
+ * Return the used factory name.
+ * @return the factory name
+ */
+ protected String getFactory() {
+ return m_factoryName;
+ }
+
+ protected String getNeededFactoryName() {
+ return m_desiredFactory;
+ }
+
+ /**
+ * Return the created instance.
+ * @return the instance (or null if no instance are created).
+ */
+ protected ComponentInstance getInstance() {
+ return m_instance;
+ }
+
+ /**
+ * Set the factory name.
+ *
+ * @param name : the factory name.
+ */
+ protected void setFactory(String name) {
+ m_factoryName = name;
+ }
+
+ /**
+ * Set the instance object.
+ *
+ * @param instance : the instance
+ */
+ protected void setInstance(ComponentInstance instance) {
+ m_instance = instance;
+ }
+ }
+
+ /**
+ * Configurations to create and maintains.
+ */
+ private ManagedConfiguration[] m_configurations = new ManagedConfiguration[0];
+
+ /**
+ * Create an instance using the given factory and the given configuration.
+ *
+ * @param fact : the factory name to used.
+ * @param config : the configuration.
+ */
+ private void createInstance(Factory fact, ManagedConfiguration config) {
+ Dictionary conf = config.getConfiguration();
+ try {
+ config.setInstance(fact.createComponentInstance(conf, m_scope));
+ config.setFactory(fact.getName());
+ config.getInstance().addInstanceStateListener(this);
+ } catch (UnacceptableConfiguration e) {
+ error("A factory is available for the configuration but the configuration is not acceptable", e);
+ } catch (MissingHandlerException e) {
+ error("The instance creation has failed, at least one handler is missing", e);
+ } catch (ConfigurationException e) {
+ error("The instance creation has failed, an error during the configuration has occured", e);
+ }
+ }
+
+ /**
+ * A new valid factory appears.
+ * @param factory : factory.
+ */
+ public void bindFactory(Factory factory) {
+ boolean implicated = false;
+ String factName = factory.getName();
+ String className = factory.getComponentDescription().getClassName();
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null
+ && (m_configurations[i].getNeededFactoryName().equals(factName)
+ || m_configurations[i].getNeededFactoryName().equals(className))) {
+ createInstance(factory, m_configurations[i]);
+ implicated = true;
+ }
+ }
+ if (implicated && ! getValidity()) {
+ checkValidity();
+ }
+ }
+
+ /**
+ * An existing factory disappears or becomes invalid.
+ * @param factory : factory
+ */
+ public void unbindFactory(Factory factory) {
+ boolean implicated = false;
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null && m_configurations[i].getFactory().equals(factory.getName())) {
+ m_configurations[i].setInstance(null);
+ m_configurations[i].setFactory(null);
+ implicated = true;
+ }
+ }
+ if (implicated && getValidity()) {
+ checkValidity();
+ }
+ }
+
+ /**
+ * Stop all created instances.
+ */
+ public synchronized void stop() {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null) {
+ m_configurations[i].getInstance().removeInstanceStateListener(this);
+ if (m_configurations[i].getInstance().getState() != ComponentInstance.DISPOSED) {
+ m_configurations[i].getInstance().dispose();
+ }
+ }
+ m_configurations[i].setInstance(null);
+ m_configurations[i].setFactory(null);
+ }
+ m_configurations = new ManagedConfiguration[0];
+ }
+
+ /**
+ * Configure method.
+ * @param metadata : component type metadata.
+ * @param configuration : instance configuration.
+ * @throws ConfigurationException : occurs an instance cannot be parsed correctly.
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_scope = getCompositeManager().getServiceContext();
+
+ // Prepare the configuration to append.
+ Properties toAppend = new Properties();
+ Enumeration keys = configuration.keys();
+ while(keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (! (key.equals("instance.name")
+ || key.equals("component"))) { // Remove instance.name and component
+ toAppend.put(key, configuration.get(key));
+ }
+ }
+
+ Element[] instances = metadata.getElements("instance");
+ m_configurations = new ManagedConfiguration[instances.length];
+ for (int i = 0; i < instances.length; i++) {
+ Properties conf = null;
+ try {
+ conf = parseInstance(instances[i]);
+ } catch (ParseException e) {
+ error("An instance cannot be parsed correctly", e);
+ throw new ConfigurationException("An instance cannot be parsed correctly : " + e.getMessage());
+ }
+
+ Properties instanceConfiguration = new Properties();
+ instanceConfiguration.putAll(conf);
+ instanceConfiguration.putAll(toAppend);
+ m_configurations[i] = new ManagedConfiguration(instanceConfiguration);
+ }
+
+ m_description = new InstanceHandlerDescription(this, m_configurations);
+ }
+
+ /**
+ * Parse an Element to get a dictionary.
+ * @param instance : the Element describing an instance.
+ * @return : the resulting dictionary
+ * @throws ParseException : occurs when a configuration cannot be parse correctly.
+ */
+ public static Properties parseInstance(Element instance) throws ParseException {
+ Properties dict = new Properties();
+ String name = instance.getAttribute("name");
+ if (name != null) {
+ dict.put("instance.name", name);
+ }
+
+ String comp = instance.getAttribute("component");
+ if (comp == null) {
+ throw new ParseException("An instance does not have the 'component' attribute");
+ } else {
+ dict.put("component", comp);
+ }
+
+ Element[] props = instance.getElements("property");
+ for (int i = 0; props != null && i < props.length; i++) {
+ parseProperty(props[i], dict);
+ }
+
+ return dict;
+ }
+
+ /**
+ * Parse a property.
+ * @param prop : the current element to parse
+ * @param dict : the dictionary to populate
+ * @throws ParseException : occurs if the property cannot be parsed correctly
+ */
+ public static void parseProperty(Element prop, Dictionary dict) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name == null) { throw new ParseException("A property does not have the 'name' attribute"); }
+ // Final case : the property element has a 'value' attribute
+ if (value == null) {
+ // Recursive case
+ // Check if there is 'property' element
+ Element[] subProps = prop.getElements("property");
+ if (subProps == null) { throw new ParseException("A complex property must have at least one 'property' sub-element"); }
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ dict.put(prop.getAttribute("name"), dict2);
+ }
+ } else {
+ dict.put(name, value);
+ }
+ }
+
+ /**
+ * Start method.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ for (int j = 0; j < m_factories.length; j++) {
+ String factName = m_factories[j].getName();
+ String className = m_factories[j].getClassName();
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) {
+ createInstance(m_factories[j], m_configurations[i]);
+ }
+ }
+ }
+ checkValidity();
+ }
+
+ /**
+ * Check handler validity.
+ * The method update the validaity of the handler.
+ */
+ private void checkValidity() {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null || m_configurations[i].getInstance().getState() != ComponentInstance.VALID) {
+ setValidity(false);
+ return;
+ }
+ }
+ setValidity(true);
+ }
+
+ /**
+ * Instance state listener.
+ * This method listens when managed instance states change.
+ * @param instance : instance
+ * @param newState : the now state of the given instance
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public void stateChanged(ComponentInstance instance, int newState) {
+ switch (newState) {
+ case ComponentInstance.DISPOSED:
+ case ComponentInstance.STOPPED:
+ break; // Should not happen
+ case ComponentInstance.VALID:
+ if (!getValidity()) {
+ checkValidity();
+ }
+ break;
+ case ComponentInstance.INVALID:
+ if (getValidity()) {
+ checkValidity();
+ }
+ break;
+ default:
+ break;
+
+ }
+ }
+
+ /**
+ * Method returning an instance object of the given component type.
+ * This method must be called only on 'primitive' type.
+ * @param type : type.
+ * @return an instance object or null if not found.
+ */
+ public Object getObjectFromInstance(String type) {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null && type.equals(m_configurations[i].getFactory())) {
+ if (m_configurations[i].getInstance().getState() == ComponentInstance.VALID) {
+ return ((InstanceManager) m_configurations[i].getInstance()).getPojoObject();
+ } else {
+ error("An object cannot be get from the instance of the type " + type + ": invalid instance" + m_configurations[i].getInstance().getInstanceDescription().getDescription());
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the handler description, i.e. the state of created instances.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.Handler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+ /**
+ * Get the list of used component type.
+ * @return the list containing the used component type
+ */
+ public List getUsedType() {
+ List result = new ArrayList();
+ for (int i = 0; i < m_configurations.length; i++) {
+ result.add(m_configurations[i].getConfiguration().get("component"));
+ }
+ return result;
+ }
+
+ /**
+ * The composite is reconfigured, we check if we have a property to change.
+ * We reconfigure all contained instances.
+ * @param configuration the new instance configuration
+ */
+ public void reconfigure(Dictionary configuration) {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null) {
+ info("Reconfiguring " + m_configurations[i].getInstance().getInstanceName());
+ m_configurations[i].getInstance().reconfigure(configuration);
+ }
+ }
+
+ }
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java
new file mode 100644
index 0000000..a7fc53e
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java
@@ -0,0 +1,90 @@
+/*
+ * 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.felix.ipojo.composite.instance;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler.ManagedConfiguration;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Description of the Instance Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceHandlerDescription extends HandlerDescription {
+
+
+ /**
+ * List of managed instances.
+ */
+ private ManagedConfiguration[] m_configurations;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : handler
+ * @param insts : list of component instances
+ */
+ public InstanceHandlerDescription(CompositeHandler handler, ManagedConfiguration[] insts) {
+ super(handler);
+ m_configurations = insts;
+ }
+
+ /**
+ * Build handler description.
+ * @return the handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element instances = super.getHandlerInfo();
+ for (int i = 0; i < m_configurations.length; i++) {
+ ManagedConfiguration inst = m_configurations[i];
+ Element instance = new Element("Instance", "");
+ if (inst.getInstance() == null) {
+ instance.addAttribute(new Attribute("Factory", inst.getConfiguration().get("component").toString()));
+ instance.addAttribute(new Attribute("State", "Not Available"));
+ } else {
+ instance.addAttribute(new Attribute("Factory", inst.getFactory()));
+ instance.addAttribute(new Attribute("Name", inst.getInstance().getInstanceName()));
+ String state = null;
+ switch(inst.getInstance().getState()) {
+ case ComponentInstance.DISPOSED :
+ state = "disposed"; break;
+ case ComponentInstance.STOPPED :
+ state = "stopped"; break;
+ case ComponentInstance.VALID :
+ state = "valid"; break;
+ case ComponentInstance.INVALID :
+ state = "invalid"; break;
+ default :
+ break;
+ }
+ instance.addAttribute(new Attribute("State", state));
+ // The instance description is already contained inside parent instance description.
+ //instance.addElement(inst.getInstance().getInstanceDescription().getDescription());
+ }
+ instances.addElement(instance);
+ }
+ return instances;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
new file mode 100644
index 0000000..5c1d8e0
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
@@ -0,0 +1,392 @@
+/*
+ * 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.felix.ipojo.composite.service.instantiator;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.composite.util.SourceManager;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Service Instantiator Class. This handler allows to instantiate service
+ * instance inside the composition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyHandler extends CompositeHandler implements DependencyStateListener {
+
+ /**
+ * List of instances to manage.
+ */
+ private List/* <SvcInstance> */m_instances = new ArrayList();
+
+ /**
+ * List of importers.
+ */
+ private List/* <ServiceImporter> */ m_importers = new ArrayList();
+
+ /**
+ * Flag indicating if the handler has already finished the start method.
+ */
+ private boolean m_isStarted;
+
+ /**
+ * The handler description.
+ */
+ private ServiceInstantiatorDescription m_description;
+
+ /**
+ * Source Managers.
+ */
+ private List m_sources;
+
+
+ /**
+ * Create a Service instance object form the given Element.
+ * This method parse the given element and configure the service instance object.
+ * @param service : the Element describing the service instance
+ * @param conf : the configuration from the composite instance
+ * @throws ConfigurationException : the service instance cannot be created correctly
+ */
+ private void createServiceInstance(Element service, Dictionary conf) throws ConfigurationException {
+ // Prepare the configuration to append.
+ Properties toAppend = new Properties();
+ Enumeration keys = conf.keys();
+ while(keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (! key.equals("instance.name")
+ || key.equals("component")) { // Remove instance.name and component
+ toAppend.put(key, conf.get(key));
+ }
+ }
+
+ String spec = service.getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed service : the specification attribute is mandatory");
+ }
+ String filter = "(&(!(factory.name=" + getCompositeManager().getFactory().getComponentDescription().getName() + "))(factory.state=1))"; // Cannot reinstantiate yourself
+ String givenFilter = service.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ Filter fil;
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("Malformed filter " + filter + " : " + e.getMessage());
+ }
+
+ Properties prop = new Properties();
+ Element[] props = service.getElements("property");
+ for (int k = 0; props != null && k < props.length; k++) {
+ try {
+ InstanceHandler.parseProperty(props[k], prop);
+ } catch (ParseException e) {
+ throw new ConfigurationException("An instance configuration is invalid : " + e.getMessage());
+ }
+ }
+
+ Properties instanceConfiguration = new Properties();
+ instanceConfiguration.putAll(prop);
+ instanceConfiguration.putAll(toAppend);
+
+ String aggregate = service.getAttribute("aggregate");
+ boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
+
+ String optional = service.getAttribute("optional");
+ boolean opt = optional != null && optional.equalsIgnoreCase("true");
+
+ int policy = DependencyModel.getPolicy(service);
+
+ Comparator cmp = DependencyModel.getComparator(service, getCompositeManager().getGlobalContext());
+
+ SvcInstance inst = new SvcInstance(this, spec, instanceConfiguration, agg, opt, fil, cmp, policy);
+ m_instances.add(inst);
+
+ String sources = service.getAttribute("context-source");
+ if (sources != null) {
+ SourceManager source = new SourceManager(sources, filter, inst, getCompositeManager());
+ if (m_sources == null) {
+ m_sources = new ArrayList(1);
+ }
+ m_sources.add(source);
+ }
+ }
+
+ /**
+ * Create a Service importer object from the given Element.
+ * This method parse the given element and configure the service importer object.
+ * @param imp : Element describing the import
+ * @param confFilter : instance filter customization
+ * @throws ConfigurationException : the service importer cannot be created correctly
+ */
+ private void createServiceImport(Element imp, Dictionary confFilter) throws ConfigurationException {
+ boolean optional = false;
+ boolean aggregate = false;
+ String specification = imp.getAttribute("specification");
+
+ if (specification == null) {
+ // Malformed import
+ error("Malformed import: the specification attribute is mandatory");
+ throw new ConfigurationException("Malformed import : the specification attribute is mandatory");
+ } else {
+ String opt = imp.getAttribute("optional");
+ optional = opt != null && opt.equalsIgnoreCase("true");
+
+ String agg = imp.getAttribute("aggregate");
+ aggregate = agg != null && agg.equalsIgnoreCase("true");
+
+ String original = "(&(objectClass=" + specification + ")(!(instance.name=" + getCompositeManager().getInstanceName() + ")))"; // Cannot import yourself
+ String filter = original;
+ String givenFilter = imp.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ String identitity = imp.getAttribute("id");
+
+ String scope = imp.getAttribute("scope");
+ BundleContext context = getCompositeManager().getGlobalContext(); // Get the default bundle context.
+ if (scope != null) {
+ if (scope.equalsIgnoreCase("global")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.GLOBAL);
+ } else if (scope.equalsIgnoreCase("composite")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL);
+ } else if (scope.equalsIgnoreCase("composite+global")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL_AND_GLOBAL);
+ }
+ }
+
+ // Configure instance filter if available
+ if (confFilter != null && identitity != null && confFilter.get(identitity) != null) {
+ filter = "(&" + original + (String) confFilter.get(identitity) + ")";
+ }
+
+ Filter fil = null;
+ if (filter != null) {
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A required filter " + filter + " is malformed : " + e.getMessage());
+ }
+ }
+
+ Comparator cmp = DependencyModel.getComparator(imp, getCompositeManager().getGlobalContext());
+ Class spec = DependencyModel.loadSpecification(specification, getCompositeManager().getGlobalContext());
+ int policy = DependencyModel.getPolicy(imp);
+
+ ServiceImporter importer = new ServiceImporter(spec, fil, aggregate, optional, cmp, policy, context, identitity, this);
+ m_importers.add(importer);
+
+ String sources = imp.getAttribute("context-source");
+ if (sources != null) {
+ SourceManager source = new SourceManager(sources, filter, importer, getCompositeManager());
+ if (m_sources == null) {
+ m_sources = new ArrayList(1);
+ }
+ m_sources.add(source);
+ }
+
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param metadata : the metadata of the component
+ * @param conf : the instance configuration
+ * @throws ConfigurationException : the specification attribute is missing
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary conf) throws ConfigurationException {
+ Element[] services = metadata.getElements("subservice");
+ // Get instance filters
+ Dictionary confFilter = null;
+ if (conf.get("requires.filters") != null) {
+ confFilter = (Dictionary) conf.get("requires.filters");
+ }
+
+ for (int i = 0; i < services.length; i++) {
+ String action = services[i].getAttribute("action");
+ if (action == null) {
+ throw new ConfigurationException("The action attribute must be set to 'instantiate' or 'import'");
+ } else if ("instantiate".equalsIgnoreCase(action)) {
+ createServiceInstance(services[i], conf);
+ } else if ("import".equalsIgnoreCase(action)) {
+ createServiceImport(services[i], confFilter);
+ } else {
+ throw new ConfigurationException("Unknown action : " + action);
+ }
+ }
+
+ m_description = new ServiceInstantiatorDescription(this, m_instances, m_importers);
+ }
+
+ /**
+ * Start the service instantiator handler.
+ * Start all created service instance.
+ * @see org.apache.felix.ipojo.CompositeHandler#start()
+ */
+ public void start() {
+ for (int i = 0; m_sources != null && i < m_sources.size(); i++) {
+ SourceManager source = (SourceManager) m_sources.get(i);
+ source.start();
+ }
+
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ imp.start();
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ inst.start();
+ }
+
+ isHandlerValid();
+ m_isStarted = true;
+ }
+
+ /**
+ * Check the handler validity.
+ * @see org.apache.felix.ipojo.CompositeHandler#isValid()
+ */
+ private void isHandlerValid() {
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ if (imp.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ if (inst.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ setValidity(true);
+ }
+
+ /**
+ * Handler stop method.
+ * Stop all created service instance.
+ * @see org.apache.felix.ipojo.CompositeHandler#stop()
+ */
+ public void stop() {
+ for (int i = 0; m_sources != null && i < m_sources.size(); i++) {
+ SourceManager source = (SourceManager) m_sources.get(i);
+ source.stop();
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ inst.stop();
+ }
+
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ imp.stop();
+ }
+
+ m_isStarted = false;
+ }
+
+ /**
+ * State change callback.
+ * This method is used to freeze the set of used provider if the static binding policy is used.
+ * @param newState : the new state of the underlying instance
+ * @see org.apache.felix.ipojo.Handler#stateChanged(int)
+ */
+ public void stateChanged(int newState) {
+ // If we are becoming valid and started, check if we need to freeze importers.
+ if (m_isStarted && newState == ComponentInstance.VALID) {
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
+ imp.freeze();
+ }
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance imp = (SvcInstance) m_instances.get(i);
+ if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
+ imp.freeze();
+ }
+ }
+ }
+ }
+
+ /**
+ * An service instance becomes valid.
+ * @param dep : dependency becoming valid.
+ */
+ public void validate(DependencyModel dep) {
+ if (!getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * A service instance becomes invalid.
+ * @param dep : dependency becoming valid.
+ */
+ public void invalidate(DependencyModel dep) {
+ if (getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * Get the service instantiator handler description.
+ * @return the description
+ * @see org.apache.felix.ipojo.CompositeHandler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+ public List getInstances() {
+ return m_instances;
+ }
+
+ public List getRequirements() {
+ return m_importers;
+ }
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java
new file mode 100644
index 0000000..ed63da7
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java
@@ -0,0 +1,327 @@
+/*
+ * 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.felix.ipojo.composite.service.instantiator;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Import a service form the parent to the internal service registry.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceImporter extends DependencyModel {
+
+ /**
+ * Reference on the handler.
+ */
+ private ServiceDependencyHandler m_handler;
+
+ private final class Record {
+ /**
+ * External Reference.
+ */
+ private ServiceReference m_ref;
+
+ /**
+ * Internal Registration.
+ */
+ private ServiceRegistration m_reg;
+
+ /**
+ * Exposed Object.
+ */
+ private Object m_svcObject;
+
+ /**
+ * Constructor.
+ * @param ref : service reference.
+ */
+ protected Record(ServiceReference ref) {
+ m_ref = ref;
+ }
+
+ /**
+ * Register the current import.
+ */
+ private void register() {
+ if (m_reg != null) {
+ m_reg.unregister();
+ }
+ m_svcObject = getService(m_ref);
+ m_reg = m_handler.getCompositeManager().getServiceContext()
+ .registerService(getSpecification().getName(), m_svcObject, getProps(m_ref));
+ }
+
+ /**
+ * Update the current import.
+ */
+ private void update() {
+ if (m_reg != null) {
+ m_reg.setProperties(getProps(m_ref));
+ }
+ }
+
+ /**
+ * Unregister and release the current import.
+ */
+ private void dispose() {
+ if (m_reg != null) {
+ m_reg.unregister();
+ m_svcObject = null;
+ m_reg = null;
+ }
+ m_ref = null;
+ }
+
+ /**
+ * Test object equality.
+ * @param object : object to confront against the current object.
+ * @return true if the two objects are equals (same service reference).
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object object) {
+ if (object instanceof Record) {
+ Record rec = (Record) object;
+ return rec.m_ref == m_ref;
+ }
+ return false;
+ }
+
+ /**
+ * Hash code method.
+ * @return the hash code by calling the parent method.
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+
+ /**
+ * List of managed records.
+ */
+ private List/*<Record>*/m_records = new ArrayList()/* <Record> */;
+
+ /**
+ * Requirement Id.
+ */
+ private String m_id;
+
+ /**
+ * Is this requirement attached to a service-level requirement.
+ */
+ private boolean m_specLevelReq;
+
+ /**
+ * Is the set of used provider frozen ?
+ */
+ private boolean m_isFrozen;
+
+ /**
+ * Constructor.
+ *
+ * @param specification : targeted specification
+ * @param filter : LDAP filter
+ * @param multiple : should the importer imports several services ?
+ * @param optional : is the import optional ?
+ * @param cmp : comparator to use for the tracking
+ * @param policy : resolving policy
+ * @param context : bundle context to use for the tracking (can be a servie context)
+ * @param identitity : requirement id (may be null)
+ * @param handler : handler
+ */
+ public ServiceImporter(Class specification, Filter filter, boolean multiple, boolean optional, Comparator cmp, int policy, BundleContext context, String identitity
+ , ServiceDependencyHandler handler) {
+ super(specification, multiple, optional, filter, cmp, policy, context, handler, handler.getCompositeManager());
+
+ this.m_handler = handler;
+
+ if (m_id == null) {
+ m_id = super.getSpecification().getName();
+ } else {
+ m_id = identitity;
+ }
+
+ }
+
+ /**
+ * Get the properties for the exposed service from the given reference.
+ *
+ * @param ref : the reference.
+ * @return the property dictionary
+ */
+ private static Dictionary getProps(ServiceReference ref) {
+ Properties prop = new Properties();
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ prop.put(keys[i], ref.getProperty(keys[i]));
+ }
+ return prop;
+ }
+
+ /**
+ * Freeze the set of used provider.
+ * This method allow to fix the set of provider when the static binding policy is used.
+ */
+ public void freeze() {
+ m_isFrozen = true;
+ }
+
+ /**
+ * Unfreezes.
+ */
+ public void unfreeze() {
+ m_isFrozen = false;
+ }
+
+ public boolean isFrozen() {
+ return m_isFrozen;
+ }
+
+ /**
+ * Stop the management of the import.
+ */
+ public void stop() {
+
+ super.stop();
+
+ for (int i = 0; i < m_records.size(); i++) {
+ Record rec = (Record) m_records.get(i);
+ rec.dispose();
+ }
+
+ m_records.clear();
+
+ }
+
+ /**
+ * Get the record list using the given reference.
+ *
+ * @param ref : the reference
+ * @return the list containing all record using the given reference
+ */
+ private List/* <Record> */getRecordsByRef(ServiceReference ref) {
+ List list = new ArrayList();
+ for (int i = 0; i < m_records.size(); i++) {
+ Record rec = (Record) m_records.get(i);
+ if (rec.m_ref == ref) {
+ list.add(rec);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Build the list of imported service provider.
+ * @return the list of all imported services.
+ */
+ public List getProviders() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_records.size(); i++) {
+ list.add((((Record) m_records.get(i)).m_ref).getProperty("instance.name"));
+ }
+ return list;
+ }
+
+ /**
+ * Set that this dependency is a service level dependency.
+ * This forces the scoping policy to be STRICT.
+ * @param b
+ */
+ public void setServiceLevelDependency() {
+ m_specLevelReq = true;
+ PolicyServiceContext context = new PolicyServiceContext(m_handler.getCompositeManager().getGlobalContext(), m_handler.getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL);
+ setBundleContext(context);
+ }
+
+ public String getId() {
+ return m_id;
+ }
+
+ public boolean isServiceLevelRequirement() {
+ return m_specLevelReq;
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ for (int i = 0; departs != null && i < departs.length; i++) {
+ onServiceDeparture(departs[i]);
+ }
+
+ for (int i = 0; arrivals != null && i < arrivals.length; i++) {
+ onServiceArrival(arrivals[i]);
+ }
+ }
+
+ /**
+ * A new service is injected by the tracker.
+ * This method create a 'Record' and register it.
+ * @param ref : new service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference ref) {
+ Record rec = new Record(ref);
+ m_records.add(rec);
+ // Always register the reference, as the method is call only when needed.
+ rec.register();
+ }
+
+ /**
+ * A used service disappears.
+ * This method find the implicated 'Record', dispose it and remove it from the list.
+ * @param ref : leaving service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceDeparture(ServiceReference ref) {
+ List list = getRecordsByRef(ref);
+ for (int i = 0; i < list.size(); i++) { // Stop the implied record
+ Record rec = (Record) list.get(i);
+ rec.dispose();
+ }
+ m_records.removeAll(list);
+ }
+
+ /**
+ * A used service is modified.
+ * @param ref : modified service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceModification(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceModification(ServiceReference ref) {
+ List list = getRecordsByRef(ref);
+ for (int i = 0; i < list.size(); i++) { // Stop the implied record
+ Record rec = (Record) list.get(i);
+ rec.update();
+ }
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java
new file mode 100644
index 0000000..2125153
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java
@@ -0,0 +1,117 @@
+/*
+ * 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.felix.ipojo.composite.service.instantiator;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Description of the Service Creator Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceInstantiatorDescription extends HandlerDescription {
+
+ /**
+ * List of managed service instances.
+ */
+ private List m_instances;
+
+ /**
+ * List of exports.
+ */
+ private List m_imports;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : composite handler
+ * @param insts : list of service instances
+ * @param imps : list of service importers
+ */
+ public ServiceInstantiatorDescription(CompositeHandler handler, List insts, List imps) {
+ super(handler);
+ m_instances = insts;
+ m_imports = imps;
+ }
+
+ /**
+ * Build service instantiator handler description.
+ * @return the handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element services = super.getHandlerInfo();
+ for (int i = 0; i < m_imports.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_imports.get(i);
+ Element impo = new Element("Requires", "");
+ impo.addAttribute(new Attribute("Specification", imp.getSpecification().getName()));
+ if (imp.getFilter() != null) {
+ impo.addAttribute(new Attribute("Filter", imp.getFilter()));
+ }
+ if (imp.getState() == DependencyModel.RESOLVED) {
+ impo.addAttribute(new Attribute("State", "resolved"));
+ for (int j = 0; j < imp.getProviders().size(); j++) {
+ Element prov = new Element("Provider", "");
+ prov.addAttribute(new Attribute("name", (String) imp.getProviders().get(j)));
+ impo.addElement(prov);
+ }
+ } else {
+ impo.addAttribute(new Attribute("State", "unresolved"));
+ }
+ services.addElement(impo);
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ Element service = new Element("Service", "");
+ service.addAttribute(new Attribute("Specification", inst.getServiceSpecification()));
+ String state = "unresolved";
+ if (inst.getState() == DependencyModel.RESOLVED) {
+ state = "resolved";
+ }
+ service.addAttribute(new Attribute("State", state));
+ Map map = inst.getMatchingFactories();
+ Set keys = map.keySet();
+ Iterator iterator = keys.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Object object = map.get(ref);
+ if (object != null) {
+ Element fact = new Element("Factory", "");
+ fact.addAttribute(new Attribute("Name", ((ComponentInstance) object).getFactory().getName()));
+ service.addElement(fact);
+ }
+ }
+ services.addElement(service);
+ }
+ return services;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java
new file mode 100644
index 0000000..d61d347
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java
@@ -0,0 +1,295 @@
+/*
+ * 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.felix.ipojo.composite.service.instantiator;
+
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Manage a service instantiation. This service create component instance providing the required service specification.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SvcInstance extends DependencyModel {
+
+ /**
+ * Configuration to push to the instance.
+ */
+ private Dictionary m_configuration;
+
+ /**
+ * Handler creating the service instance.
+ */
+ private ServiceDependencyHandler m_handler;
+
+ /**
+ * Map of matching factories Service Reference => instance or null (null if the service reference is not actually used).
+ */
+ private Map /* <ServiceReference, Instance> */m_factories = new HashMap();
+
+ /**
+ * Required specification.
+ */
+ private String m_specification;
+
+ /**
+ * Is the service provider frozen ? (Is used for static biding policy)
+ */
+ private boolean m_isFrozen;
+
+ /**
+ * Constructor.
+ * @param handler : the handler.
+ * @param spec : required specification.
+ * @param conf : instance configuration.
+ * @param isAgg : is the service instance an aggregate service ?
+ * @param isOpt : is the service instance optional ?
+ * @param filt : LDAP filter
+ * @param cmp : comparator to use for the tracking
+ * @param policy : binding policy
+ * @throws ConfigurationException : an attribute cannot be parsed correctly, or is incorrect.
+ */
+ public SvcInstance(ServiceDependencyHandler handler, String spec, Dictionary conf, boolean isAgg, boolean isOpt, Filter filt, Comparator cmp, int policy) throws ConfigurationException {
+ super(Factory.class, isAgg, isOpt, filt, cmp, policy, null, handler, handler.getCompositeManager());
+
+ m_specification = spec;
+
+ m_handler = handler;
+ setBundleContext(m_handler.getCompositeManager().getServiceContext());
+
+ m_configuration = conf;
+ }
+
+ /**
+ * Stop the service instance.
+ */
+ public void stop() {
+ super.stop();
+
+ Set keys = m_factories.keySet();
+ Iterator iterator = keys.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Object object = m_factories.get(ref);
+ if (object != null) {
+ m_handler.info("Dispose a service instance when stopping the handler " + ((ComponentInstance) object).getInstanceName());
+ ((ComponentInstance) object).dispose();
+ }
+ }
+
+ m_factories.clear();
+
+ }
+
+ public boolean isFrozen() {
+ return m_isFrozen;
+ }
+
+ /**
+ * Freeze the set of used provider.
+ * This method is when the static binding policy is applied.
+ */
+ public void freeze() {
+ m_isFrozen = true;
+ }
+
+ /**
+ * Unfreezes.
+ */
+ public void unfreeze() {
+ m_isFrozen = false;
+ }
+
+
+ /**
+ * Create an instance for the given reference. The instance is not added inside the map.
+ * @param factory : the factory from which we need to create the instance.
+ * @return the created component instance.
+ * @throws ConfigurationException : the instance cannot be configured correctly.
+ * @throws MissingHandlerException : the factory is invalid.
+ * @throws UnacceptableConfiguration : the given configuration is invalid for the given factory.
+ */
+ private ComponentInstance createInstance(Factory factory) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException {
+ // Recreate the configuration to avoid sharing.
+ Properties props = new Properties();
+ Enumeration keys = m_configuration.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ props.put(key, m_configuration.get(key));
+ }
+ ComponentInstance instance = null;
+ instance = factory.createComponentInstance(props);
+ m_handler.info("Creation of a service instance " + instance.getInstanceName());
+ return instance;
+ }
+
+ /**
+ * Does the service instance match with the given factory ?
+ * @param fact : the factory to test.
+ * @return true if the factory match, false otherwise.
+ */
+ public boolean match(ServiceReference fact) {
+ // Check if the factory can provide the specification
+ ComponentTypeDescription desc = (ComponentTypeDescription) fact.getProperty("component.description");
+ if (desc == null) {
+ return false; // No component type description.
+ }
+
+ String[] provides = desc.getprovidedServiceSpecification();
+ for (int i = 0; provides != null && i < provides.length; i++) {
+ if (provides[i].equals(m_specification)) {
+ // Check that the factory needs every properties contained in
+ // the configuration
+ PropertyDescription[] props = desc.getProperties();
+ Properties conf = new Properties();
+ Enumeration keys = m_configuration.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (!containsProperty(key, props)) { return false; }
+ conf.put(key, m_configuration.get(key));
+ }
+
+ Factory factory = (Factory) getService(fact);
+ return factory.isAcceptable(conf);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Does the factory support the given property ? This method check if the property is contained in the given property description array.
+ * @param name : name of the property
+ * @param props : list of property description
+ * @return true if the factory support this property
+ */
+ private boolean containsProperty(String name, org.apache.felix.ipojo.architecture.PropertyDescription[] props) {
+ for (int i = 0; props != null && i < props.length; i++) {
+ if (props[i].getName().equalsIgnoreCase(name)) { return true; }
+ }
+ if (name.equalsIgnoreCase("name")) { return true; } // Skip the name property
+ return false;
+ }
+
+ /**
+ * Get the required specification.
+ * @return the required specification.
+ */
+ public String getServiceSpecification() {
+ return m_specification;
+ }
+
+ /**
+ * Get the map of used references [reference, component instance].
+ * @return the map of used references.
+ */
+ protected Map getMatchingFactories() {
+ return m_factories;
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ for (int i = 0; departs != null && i < departs.length; i++) {
+ onServiceDeparture(departs[i]);
+ }
+
+ for (int i = 0; arrivals != null && i < arrivals.length; i++) {
+ onServiceArrival(arrivals[i]);
+ }
+ }
+
+ /**
+ * A new service is injected.
+ * This method create the sub-service instance in the composite.
+ * @param ref : service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference ref) {
+ // The given factory matches.
+ try {
+ Factory fact = (Factory) getService(ref);
+ if (m_factories.get(ref) == null) {
+ ComponentInstance instance = createInstance(fact);
+ m_factories.put(ref, instance);
+ } else {
+ m_handler
+ .info("An arriving factory is already used, ignore the creation : "
+ + fact.getName());
+ }
+
+ } catch (UnacceptableConfiguration e) {
+ m_handler.error("A matching factory refuses the actual configuration : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ } catch (MissingHandlerException e) {
+ m_handler.error("A matching factory is no more valid : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ } catch (ConfigurationException e) {
+ m_handler.error("A matching configuration is refused by the instance : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ }
+
+ }
+
+
+ /**
+ * A used service is leaving.
+ * This method dispose the created instance.
+ * @param ref : leaving service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceDeparture(ServiceReference ref) {
+ // Remove the reference is contained
+ Object instance = m_factories.remove(ref);
+ if (instance != null) {
+ m_handler.info("Dispose the instance (departure of the factory) "
+ + ((ComponentInstance) instance).getInstanceName());
+ ((ComponentInstance) instance).dispose();
+ }
+ }
+
+ /**
+ * A factory is modified. This should not happen.
+ * @param arg0 the service reference
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceModification(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceModification(ServiceReference arg0) {
+ // Nothing to do.
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.java
new file mode 100644
index 0000000..9423d3c
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+/**
+ * Exception occurs when a composition error occurs.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionException extends Exception {
+ //TODO consider removing this class to use configuration exception.
+ /**
+ * serialVersionUID.
+ */
+ private static final long serialVersionUID = -3063353267573738105L;
+
+ /**
+ * Constructor.
+ * @param message : a message.
+ */
+ public CompositionException(String message) {
+ super(message);
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
new file mode 100644
index 0000000..69bc5bb
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
@@ -0,0 +1,360 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.manipulation.Manipulator;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Check and build a composition, i.e. a POJO containing the composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionMetadata {
+
+ /**
+ * Implemented composition.
+ */
+ private SpecificationMetadata m_specification;
+
+ /**
+ * Name of the composition.
+ */
+ private String m_name;
+
+ /**
+ * Bundle Context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Manipulation Metadata.
+ */
+ private Element m_manipulation;
+
+ /**
+ * Reference on the handler.
+ */
+ private ProvidedServiceHandler m_handler;
+
+ /**
+ * List of Mappings.
+ */
+ private List m_mappings = new ArrayList();
+
+ /**
+ * Constructor.
+ * @param context : bundle context
+ * @param description : 'provides' element
+ * @param psh : parent handler
+ * @param name : name of the composition.
+ */
+ public CompositionMetadata(BundleContext context, Element description, ProvidedServiceHandler psh, String name) {
+ m_context = context;
+ m_handler = psh;
+ // Get the composition name
+ m_name = description.getAttribute("specification") + name;
+
+ // Get implemented service specification
+ String spec = description.getAttribute("specification");
+ m_specification = new SpecificationMetadata(spec, m_context, false, false, m_handler);
+
+ Element[] mappings = description.getElements("delegation");
+ for (int i = 0; mappings != null && i < mappings.length; i++) {
+ String methodName = mappings[i].getAttribute("method");
+ MethodMetadata method = m_specification.getMethodByName(methodName);
+ if (method == null) {
+ m_handler.error("The method " + methodName + " does not exist in the specicifation " + spec);
+ return;
+ }
+
+ if (mappings[i].getAttribute("policy").equalsIgnoreCase("All")) {
+ method.setAllPolicy();
+ }
+ }
+ }
+
+ protected BundleContext getBundleContext() {
+ return m_context;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public SpecificationMetadata getSpecificationMetadata() {
+ return m_specification;
+ }
+
+ /**
+ * Build Available Mappings.
+ * @throws CompositionException : a factory is not available, the composition cannot be checked.
+ */
+ private void buildAvailableMappingList() throws CompositionException {
+ int index = 0;
+
+ for (int i = 0; i < m_handler.getInstanceType().size(); i++) {
+ String type = (String) m_handler.getInstanceType().get(i);
+ try {
+ ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), "(factory.name=" + type + ")");
+ if (refs == null) {
+ m_handler.error("The factory " + type + " is not available, cannot check the composition");
+ throw new CompositionException("The factory " + type + " needs to be available to check the composition");
+ } else {
+ String className = (String) refs[0].getProperty("component.class");
+ Class impl = m_context.getBundle().loadClass(className);
+ SpecificationMetadata spec = new SpecificationMetadata(impl, type, m_handler);
+ FieldMetadata field = new FieldMetadata(spec);
+ field.setName("_field" + index);
+ Mapping map = new Mapping(spec, field);
+ m_mappings.add(map);
+ index++;
+ }
+ } catch (InvalidSyntaxException e) {
+ m_handler.error("A LDAP filter is not valid : " + e.getMessage());
+ } catch (ClassNotFoundException e) {
+ m_handler.error("The implementation class of a component cannot be loaded : " + e.getMessage());
+ }
+ }
+
+ for (int i = 0; i < m_handler.getSpecifications().size(); i++) {
+ SpecificationMetadata spec = (SpecificationMetadata) m_handler.getSpecifications().get(i);
+ FieldMetadata field = new FieldMetadata(spec);
+ field.setName("_field" + index);
+ if (spec.isOptional()) {
+ field.setOptional(true);
+ }
+ if (spec.isAggregate()) {
+ field.setAggregate(true);
+ }
+ Mapping map = new Mapping(spec, field);
+ m_mappings.add(map);
+ index++;
+ }
+ }
+
+ /**
+ * Build the delegation mapping.
+ * @throws CompositionException : occurs when the mapping cannot be inferred correctly
+ */
+ protected void buildMapping() throws CompositionException {
+ buildAvailableMappingList();
+
+ // Dependency closure is OK, now look for method delegation
+ Map/* <MethodMetadata, Mapping> */svcMethods = new HashMap();
+ Map/* <MethodMetadata, Mapping> */instMethods = new HashMap();
+
+ for (int i = 0; i < m_mappings.size(); i++) {
+ Mapping map = (Mapping) m_mappings.get(i);
+ SpecificationMetadata spec = map.getSpecification();
+ for (int j = 0; j < spec.getMethods().size(); j++) {
+ MethodMetadata method = (MethodMetadata) spec.getMethods().get(j);
+ if (spec.isInterface()) {
+ svcMethods.put(method, map);
+ } else {
+ instMethods.put(method, map);
+ }
+ }
+ }
+
+ // For each needed method, search if available and store the mapping
+ for (int j = 0; j < m_specification.getMethods().size(); j++) {
+ MethodMetadata method = (MethodMetadata) m_specification.getMethods().get(j);
+ Set keys = instMethods.keySet(); // Look first in methods contained in the glue code.
+ Iterator iterator = keys.iterator();
+ boolean found = false;
+ while (iterator.hasNext() & !found) {
+ MethodMetadata met = (MethodMetadata) iterator.next();
+ if (met.equals(method)) {
+ found = true;
+ FieldMetadata field = ((Mapping) instMethods.get(met)).getField();
+ field.setUseful(true);
+ method.setDelegation(field);
+ }
+ }
+ if (!found) { // If not found looks inside method contained in services.
+ keys = svcMethods.keySet(); // Look first in methods contained in the glue code
+ iterator = keys.iterator();
+ while (!found && iterator.hasNext()) {
+ MethodMetadata met = (MethodMetadata) iterator.next();
+ if (met.equals(method)) {
+ found = true;
+ FieldMetadata field = ((Mapping) svcMethods.get(met)).getField();
+ field.setUseful(true);
+ method.setDelegation(field);
+ // Test optional
+ if (field.isOptional() && !method.throwsUnsupportedOperationException()) {
+ m_handler.warn("The method " + method.getMethod().getName() + " could not be provided correctly : the specification " + field.getSpecification().getName() + " is optional");
+ }
+ }
+ }
+ }
+ if (!found) { throw new CompositionException("Inconsistent composition - the method " + method.getMethod() + " could not be delegated"); }
+ }
+ }
+
+ /**
+ * Build a service implementation.
+ * @return the byte[] of the POJO.
+ */
+ protected byte[] buildPOJO() {
+ Class clazz = null;
+ try {
+ clazz = getBundleContext().getBundle().loadClass(m_specification.getName());
+ } catch (ClassNotFoundException e1) {
+ // The class has already be loaded.
+ return null;
+ }
+ byte[] pojo = POJOWriter.dump(clazz, m_name, getFieldList(), getMethodList(), m_handler);
+ Manipulator manipulator = new Manipulator();
+ try {
+ byte[] newclazz = manipulator.manipulate(pojo);
+ m_manipulation = manipulator.getManipulationMetadata();
+ return newclazz;
+ } catch (IOException e) {
+ m_handler.error("An error occurs during the composite implementation creation : " + e.getMessage(), e);
+ }
+ return null;
+ }
+
+ /**
+ * Build service implementation metadata.
+ * @param name : name of the future instance (used to avoid cycle)
+ * @return Component Type metadata.
+ */
+ protected Element buildMetadata(String name) {
+ Element elem = new Element("component", "");
+ Attribute className = new Attribute("classname", m_name);
+ Attribute factory = new Attribute("public", "false");
+ elem.addAttribute(className);
+ elem.addAttribute(factory);
+
+ // Add architecture for debug
+ elem.addAttribute(new Attribute("architecture", "true"));
+
+ // Provides
+ Element provides = new Element("provides", "");
+ provides.addAttribute(new Attribute("specification", m_specification.getName()));
+ elem.addElement(provides);
+
+ // Dependencies
+ List fields = getFieldList();
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && field.getSpecification().isInterface()) {
+ Element dep = new Element("requires", "");
+ dep.addAttribute(new Attribute("field", field.getName()));
+ dep.addAttribute(new Attribute("scope", "composite"));
+ dep.addAttribute(new Attribute("proxy", "false"));
+ if (field.getSpecification().isOptional()) {
+ dep.addAttribute(new Attribute("optional", "true"));
+ }
+ dep.addAttribute(new Attribute("filter", "(!(instance.name=" + name + "))"));
+ elem.addElement(dep);
+ }
+ }
+
+ Element properties = new Element("properties", "");
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && !field.getSpecification().isInterface()) {
+ Element prop = new Element("Property", "");
+ prop.addAttribute(new Attribute("field", field.getName()));
+ properties.addElement(prop);
+ }
+ }
+ if (properties.getElements().length != 0) {
+ elem.addElement(properties);
+ }
+
+ // Insert information to metadata
+ elem.addElement(m_manipulation);
+
+ return elem;
+ }
+
+ /**
+ * Get the field list to use for the delegation.
+ * @return the field list.
+ */
+ public List getFieldList() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_mappings.size(); i++) {
+ Mapping map = (Mapping) m_mappings.get(i);
+ list.add(map.getField());
+ }
+ return list;
+ }
+
+ /**
+ * Get the method list contained in the implemented specification.
+ * @return the List of implemented method.
+ */
+ private List getMethodList() {
+ return m_specification.getMethods();
+ }
+
+ /**
+ * Store links between Field and pointed Specification.
+ */
+ private class Mapping {
+
+ /**
+ * Specification.
+ */
+ private SpecificationMetadata m_spec;
+
+ /**
+ * Field.
+ */
+ private FieldMetadata m_field;
+
+ /**
+ * Constructor.
+ * @param spec : specification metadata.
+ * @param field : the field.
+ */
+ public Mapping(SpecificationMetadata spec, FieldMetadata field) {
+ m_spec = spec;
+ m_field = field;
+ }
+
+ public SpecificationMetadata getSpecification() {
+ return m_spec;
+ }
+
+ public FieldMetadata getField() {
+ return m_field;
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java
new file mode 100644
index 0000000..4df41a8
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java
@@ -0,0 +1,105 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+/**
+ * Field used inside a composition.
+ * This class contains all information useful for the generation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FieldMetadata {
+
+ /**
+ * Name of the field.
+ */
+ private String m_name;
+
+ /**
+ * Is the field an array?
+ */
+ private boolean m_isAggregate = false;
+
+ /**
+ * Interface of the field.
+ */
+ private SpecificationMetadata m_specification;
+
+ /**
+ * Is the field useful in this composition.
+ */
+ private boolean m_isUseful;
+
+ /**
+ * Is the dependency for this field optional.
+ */
+ private boolean m_isOptional = false;
+
+ /**
+ * Constructor.
+ * @param specification : interface of the field.
+ */
+ public FieldMetadata(SpecificationMetadata specification) {
+ super();
+ this.m_specification = specification;
+ if (m_specification.isAggregate()) {
+ m_isAggregate = true;
+ }
+ }
+
+ public boolean isAggregate() {
+ return m_isAggregate;
+ }
+
+ public void setAggregate(boolean aggregate) {
+ m_isAggregate = aggregate;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public void setName(String name) {
+ this.m_name = name;
+ }
+
+ public SpecificationMetadata getSpecification() {
+ return m_specification;
+ }
+
+ public void setSpecification(SpecificationMetadata specification) {
+ this.m_specification = specification;
+ }
+
+ public boolean isUseful() {
+ return m_isUseful;
+ }
+
+ public void setUseful(boolean useful) {
+ m_isUseful = useful;
+ }
+
+ public boolean isOptional() {
+ return m_isOptional;
+ }
+
+ public void setOptional(boolean opt) {
+ m_isOptional = opt;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java
new file mode 100644
index 0000000..510514e
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java
@@ -0,0 +1,138 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.lang.reflect.Method;
+
+/**
+ * Information on Method for the composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MethodMetadata {
+
+ /**
+ * ONE POLICY.
+ */
+ public static final int ONE_POLICY = 1;
+
+ /**
+ * ALL POLICY.
+ */
+ public static final int ALL_POLICY = 2;
+
+ /**
+ * Method Object.
+ */
+ private Method m_method;
+
+ /**
+ * Delegation field.
+ */
+ private FieldMetadata m_delegation;
+
+ /**
+ * Delegation policy (default = ONE).
+ */
+ private int m_policy = ONE_POLICY;
+
+ /**
+ * Constructor.
+ * @param method : method object.
+ */
+ public MethodMetadata(Method method) {
+ m_method = method;
+ }
+
+ public Method getMethod() {
+ return m_method;
+ }
+
+ public void setDelegation(FieldMetadata field) {
+ m_delegation = field;
+ }
+
+ public FieldMetadata getDelegation() {
+ return m_delegation;
+ }
+
+ /**
+ * Equals method.
+ * This method check if two MethodMetadata are equals or if the current MemethodMetadata is equals with a Method object.
+ * @param object : object.
+ * @return true if the current object and the given object are equals.
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object object) {
+ if (object instanceof MethodMetadata) {
+ Method met = ((MethodMetadata) object).getMethod();
+ return equals(met);
+ }
+
+ if (object instanceof Method) {
+ Method met = (Method) object;
+ if (! met.getName().equals(m_method.getName()) || met.getParameterTypes().length != m_method.getParameterTypes().length) {
+ return false;
+ }
+
+ for (int i = 0; i < m_method.getParameterTypes().length; i++) {
+ if (!m_method.getParameterTypes()[i].getName().equals(met.getParameterTypes()[i].getName())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Hash code method.
+ * @return the parent hash code.
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public int getPolicy() {
+ return m_policy;
+ }
+
+ /**
+ * Activate the all policy for this method.
+ */
+ public void setAllPolicy() {
+ m_policy = ALL_POLICY;
+ }
+
+ /**
+ * Check if the method can throw UnsupportedOperationException.
+ * @return true if the method has declared the UnsupportedOperationException.
+ */
+ protected boolean throwsUnsupportedOperationException() {
+ for (int i = 0; i < m_method.getExceptionTypes().length; i++) {
+ if (m_method.getExceptionTypes()[i].getName().equals(UnsupportedOperationException.class.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java
new file mode 100644
index 0000000..ffca9c8
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java
@@ -0,0 +1,323 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.felix.ipojo.Handler;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Create the Proxy class.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class POJOWriter implements Opcodes {
+
+ //TODO : merge this class with another class only static methods.
+
+ /**
+ * Create a class.
+ * @param cw : class writer
+ * @param className : class name
+ * @param spec : implemented specification
+ */
+ private static void createClass(ClassWriter cw, String className, String spec) {
+ // Create the class
+ cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", new String[] { spec.replace('.', '/') });
+ }
+
+ /**
+ * Inject field in the current class.
+ * @param cw : class writer.
+ * @param fields : list of field to inject.
+ */
+ private static void injectFields(ClassWriter cw, List fields) {
+ // Inject fields
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful()) {
+ SpecificationMetadata spec = field.getSpecification();
+ String fieldName = field.getName();
+ String desc = "";
+ if (field.isAggregate()) {
+ desc = "[L" + spec.getName().replace('.', '/') + ";";
+ } else {
+ desc = "L" + spec.getName().replace('.', '/') + ";";
+ }
+
+ cw.visitField(Opcodes.ACC_PRIVATE, fieldName, desc, null, null);
+ }
+ }
+ }
+
+ /**
+ * Generates an empty constructor.
+ * this constructor just call the java.lang.Object constructor.
+ * @param cw class writer
+ */
+ private static void generateConstructor(ClassWriter cw) {
+ // Inject a constructor <INIT>()V
+ MethodVisitor cst = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ cst.visitVarInsn(ALOAD, 0);
+ cst.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+ cst.visitInsn(RETURN);
+ cst.visitMaxs(0, 0);
+ cst.visitEnd();
+ }
+
+ /**
+ * Return the proxy 'classname' for the contract 'contractname' by delegating on available service.
+ * @param clazz : Specification class
+ * @param className : The class name to create
+ * @param fields : the list of fields on which delegate
+ * @param methods : the list of method on which delegate
+ * @param handler : handler object used to access the logger
+ * @return byte[] : the build class
+ */
+ public static byte[] dump(Class clazz, String className, List fields, List methods, Handler handler) {
+ Method[] itfmethods = clazz.getMethods();
+
+ // Create the class
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ className = className.replace('.', '/');
+ createClass(cw, className, clazz.getName());
+
+ // Inject fields inside the POJO
+ injectFields(cw, fields);
+
+ generateConstructor(cw);
+
+ for (int i = 0; i < itfmethods.length; ++i) {
+ Method method = itfmethods[i];
+
+ // Get the field for this method
+ // 1) find the MethodMetadata
+ FieldMetadata delegator = null; // field to delegate
+ MethodMetadata methodDelegator = null; // field to delegate
+ for (int j = 0; j < methods.size(); j++) {
+ MethodMetadata methodMeta = (MethodMetadata) methods.get(j);
+ if (methodMeta.equals(method)) {
+ delegator = methodMeta.getDelegation();
+ methodDelegator = methodMeta;
+ }
+ }
+
+ generateMethod(cw, className, methodDelegator, method, delegator, handler);
+
+ }
+
+ // End process
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ /**
+ * Generate on method.
+ * @param cw : class writer
+ * @param className : the current class name
+ * @param method : the method to generate
+ * @param sign : method signature to generate
+ * @param delegator : the field on which delegate
+ * @param handler : the handler (used to acess the logger)
+ */
+ private static void generateMethod(ClassWriter cw, String className, MethodMetadata method, Method sign, FieldMetadata delegator, Handler handler) {
+ String desc = Type.getMethodDescriptor(sign);
+ String name = sign.getName();
+ String[] exc = new String[sign.getExceptionTypes().length];
+ for (int i = 0; i < sign.getExceptionTypes().length; i++) {
+ exc[i] = Type.getType(sign.getExceptionTypes()[i]).getInternalName();
+ }
+
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, desc, null, exc);
+
+ if (delegator.isOptional()) {
+ if (!delegator.isAggregate()) {
+ generateOptionalCase(mv, delegator, className);
+ }
+ if (delegator.isAggregate() /*&& method.getPolicy() == MethodMetadata.ONE_POLICY*/) {
+ generateOptionalAggregateCase(mv, delegator, className);
+ }
+ }
+
+ if (delegator.isAggregate()) {
+ if (method.getPolicy() == MethodMetadata.ONE_POLICY) {
+ // Aggregate and One Policy
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ICONST_0); // Use the first one
+ mv.visitInsn(AALOAD);
+
+ loadArgs(mv, ACC_PUBLIC, Type.getArgumentTypes(desc));
+
+ // Invoke
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+
+ // Return
+ mv.visitInsn(Type.getReturnType(desc).getOpcode(Opcodes.IRETURN));
+
+ } else { // All policy
+ if (Type.getReturnType(desc).getSort() != Type.VOID) {
+ handler.error("All policy cannot be used on method which does not return void");
+ }
+
+ Type[] args = Type.getArgumentTypes(desc);
+ int index = args.length + 1;
+
+ // Init
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, index);
+ Label l1b = new Label();
+ mv.visitLabel(l1b);
+ Label l2b = new Label();
+ mv.visitJumpInsn(GOTO, l2b);
+
+ // Loop
+ Label l3b = new Label();
+ mv.visitLabel(l3b);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitVarInsn(ILOAD, index);
+ mv.visitInsn(AALOAD);
+
+ loadArgs(mv, ACC_PUBLIC, Type.getArgumentTypes(desc));
+
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+
+ Label l4b = new Label();
+ mv.visitLabel(l4b);
+ mv.visitIincInsn(index, 1); // i++;
+
+ // Condition
+ mv.visitLabel(l2b);
+ mv.visitVarInsn(ILOAD, index);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitJumpInsn(IF_ICMPLT, l3b);
+
+ Label l5b = new Label();
+ mv.visitLabel(l5b);
+ mv.visitInsn(RETURN);
+ }
+ } else {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+
+ loadArgs(mv, ACC_PUBLIC, Type.getArgumentTypes(desc));
+
+ // Invoke
+ if (delegator.getSpecification().isInterface()) {
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+ } else {
+ mv.visitMethodInsn(INVOKEVIRTUAL, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+ }
+
+ // Return
+ mv.visitInsn(Type.getReturnType(desc).getOpcode(IRETURN));
+ }
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Generate Optional Case for aggregate field.
+ * @param mv : method visitor
+ * @param delegator : Field on which delegate
+ * @param className : current class name
+ */
+ private static void generateOptionalAggregateCase(MethodVisitor mv, FieldMetadata delegator, String className) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ARRAYLENGTH);
+ Label l1a = new Label();
+ mv.visitJumpInsn(IFNE, l1a);
+ Label l2a = new Label();
+ mv.visitLabel(l2a);
+ mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn("Operation not supported");
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V");
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(l1a);
+ }
+
+ /**
+ * Generate Optional case for non aggregate fields.
+ *
+ * @param mv : the method visitor
+ * @param delegator : the field on which delegate.
+ * @param className : the name of the current class.
+ */
+ private static void generateOptionalCase(MethodVisitor mv, FieldMetadata delegator, String className) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitTypeInsn(INSTANCEOF, "org/apache/felix/ipojo/Nullable");
+ Label end = new Label();
+ mv.visitJumpInsn(IFEQ, end);
+ Label begin = new Label();
+ mv.visitLabel(begin);
+ mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn("Operation not supported");
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V");
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(end);
+ }
+
+ /**
+ * Load on stack the method arguments.
+ * @param mv method visitor
+ * @param access access level of the method
+ * @param args argument types array
+ */
+ private static void loadArgs(MethodVisitor mv, int access, Type[] args) {
+ int i = 0;
+ int j = args.length;
+ int k = getArgIndex(access, args, i);
+ for (int l = 0; l < j; l++) {
+ Type type = args[i + l];
+ mv.visitVarInsn(type.getOpcode(ILOAD), k);
+ k += type.getSize();
+ }
+ }
+
+ /**
+ * Gets the index of the argument 'i'.
+ * This method manages double-spaces.
+ * @param access method access (mostly public)
+ * @param args argument type array
+ * @param i wanted index
+ * @return the real index
+ */
+ private static int getArgIndex(int access, Type[] args, int i) {
+ int j = (access & 8) != 0 ? 0 : 1;
+ for (int k = 0; k < i; k++) {
+ j += args[k].getSize();
+ }
+ return j;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java
new file mode 100644
index 0000000..48ead7d
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java
@@ -0,0 +1,236 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Composite Provided Service.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedService implements DependencyStateListener {
+
+ /**
+ * Composite Manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Composition Model.
+ */
+ private CompositionMetadata m_composition;
+
+ /**
+ * Internal context.
+ */
+ private ServiceContext m_scope;
+
+ /**
+ * External context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Created Factory.
+ */
+ private ComponentFactory m_factory;
+
+ /**
+ * Created Instance.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Exporter.
+ */
+ private ServiceExporter m_exports;
+
+ /**
+ * Created instance name.
+ */
+ private String m_instanceName;
+
+ /**
+ * Constructor.
+ * The delegation mapping is infers in this method.
+ * @param handler : the handler.
+ * @param element : 'provides' element.
+ * @param name : name of this provided service.
+ */
+ public ProvidedService(ProvidedServiceHandler handler, Element element, String name) {
+ m_manager = handler.getCompositeManager();
+ m_scope = m_manager.getServiceContext();
+ m_context = m_manager.getContext();
+ m_composition = new CompositionMetadata(m_manager.getContext(), element, handler, name);
+ }
+
+ /**
+ * Start method.
+ * Build service implementation type, factory and instance.
+ * @throws CompositionException if a consistent mapping cannot be discovered.
+ */
+ public void start() throws CompositionException {
+ m_composition.buildMapping();
+
+ m_instanceName = m_composition.getSpecificationMetadata().getName() + "Provider-Gen";
+ byte[] clazz = m_composition.buildPOJO();
+ Element metadata = m_composition.buildMetadata(m_instanceName);
+
+ // Create the factory
+ try {
+ m_factory = new ComponentFactory(m_context, clazz, metadata);
+ m_factory.start();
+ } catch (ConfigurationException e) {
+ // Should not happen.
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "A factory cannot be created", e);
+ }
+
+ try {
+ Class spec = DependencyModel.loadSpecification(m_composition.getSpecificationMetadata().getName(), m_context);
+ Filter filter = m_context.createFilter("(instance.name=" + m_instanceName + ")");
+ // Create the exports
+ m_exports = new ServiceExporter(spec, filter, false, false, null, DependencyModel.DYNAMIC_BINDING_POLICY, m_scope, m_context, this, m_manager);
+ } catch (InvalidSyntaxException e) {
+ throw new CompositionException("A provided service filter is invalid : " + e.getMessage());
+ } catch (ConfigurationException e) {
+ throw new CompositionException("The class " + m_composition.getSpecificationMetadata().getName() + " cannot be loaded : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Stop the provided service.
+ * Kill the exporter, the instance and the factory.
+ */
+ public void stop() {
+ if (m_exports != null) {
+ m_exports.stop();
+ m_exports = null;
+ }
+ if (m_instance != null) {
+ m_instance.dispose();
+ m_instance = null;
+ }
+ if (m_factory != null) {
+ m_factory.stop();
+ m_factory = null;
+ }
+ }
+
+ protected CompositeManager getManager() {
+ return m_manager;
+ }
+
+ /**
+ * The exporter becomes valid.
+ * @param exporter : the exporter
+ */
+ public void validate(DependencyModel exporter) {
+ // Nothing to do.
+ }
+
+ /**
+ * The exporter becomes invalid.
+ * @param exporter : the exporter
+ */
+ public void invalidate(DependencyModel exporter) {
+ // Nothing to do.
+ }
+
+ /**
+ * Get an object from the given type.
+ * @param type : type
+ * @return an object from an instance of this type or null
+ */
+ private Object getObjectByType(String type) {
+ InstanceHandler handler = (InstanceHandler) m_manager.getCompositeHandler("org.apache.felix.ipojo.composite.instance.InstanceHandler");
+ Object pojo = handler.getObjectFromInstance(type);
+ if (pojo == null) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "An instance object cannot be found for the type : " + type);
+ }
+ return pojo;
+ }
+
+ public String getSpecification() {
+ return m_composition.getSpecificationMetadata().getName();
+ }
+
+ /**
+ * Unregister the exposed service.
+ */
+ public void unregister() {
+ m_exports.stop();
+ }
+
+ /**
+ * Register the exposed service.
+ */
+ public void register() {
+ Properties props = new Properties();
+ props.put("instance.name", m_instanceName);
+ List fields = m_composition.getFieldList();
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && !field.getSpecification().isInterface()) {
+ String type = field.getSpecification().getComponentType();
+ Object pojo = getObjectByType(type);
+ props.put(field.getName(), pojo);
+ }
+ }
+
+ if (m_instance == null) {
+ // Else we have to create the instance
+ try {
+ m_instance = m_factory.createComponentInstance(props, m_manager.getServiceContext());
+ } catch (UnacceptableConfiguration e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ } catch (MissingHandlerException e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ } catch (ConfigurationException e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ }
+ } else {
+ // We have to reconfigure the instance in order to inject up to date glue component instance.
+ m_instance.reconfigure(props);
+ }
+
+ m_exports.start();
+ }
+
+ public boolean isRegistered() {
+ return m_exports.getState() == DependencyModel.RESOLVED;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
new file mode 100644
index 0000000..2c82005
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
@@ -0,0 +1,503 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.composite.service.instantiator.ServiceDependencyHandler;
+import org.apache.felix.ipojo.composite.service.instantiator.ServiceImporter;
+import org.apache.felix.ipojo.composite.service.instantiator.SvcInstance;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Composite Provided Service Handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandler extends CompositeHandler implements DependencyStateListener {
+
+ /**
+ * External context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * List of "available" services in the internal context.
+ */
+ private List m_services = new ArrayList();
+
+ /**
+ * List of exporters.
+ */
+ private List m_exporters = new ArrayList();
+
+ /**
+ * List of managed services.
+ */
+ private List m_managedServices = new ArrayList();
+
+ /**
+ * List of component type.
+ */
+ private List m_types;
+
+ /**
+ * Handler description.
+ */
+ private ProvidedServiceHandlerDescription m_description;
+
+ /**
+ * Initialize the component type.
+ * @param desc : component type description to populate.
+ * @param metadata : component type metadata.
+ * @throws ConfigurationException : metadata are incorrect.
+ * @see org.apache.felix.ipojo.Handler#initializeComponentFactory(org.apache.felix.ipojo.architecture.ComponentTypeDescription, org.apache.felix.ipojo.metadata.Element)
+ */
+ public void initializeComponentFactory(ComponentTypeDescription desc, Element metadata) throws ConfigurationException {
+ Element[] provides = metadata.getElements("provides");
+ for (int i = 0; i < provides.length; i++) {
+ String action = provides[i].getAttribute("action");
+ if (action == null) {
+ throw new ConfigurationException("Invalid composition service providing : no specified action");
+ } else if (action.equalsIgnoreCase("implement")) {
+ String spec = provides[i].getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed provides : the specification attribute is mandatory");
+ } else {
+ desc.addProvidedServiceSpecification(spec);
+ }
+ } else if (action.equalsIgnoreCase("export")) {
+ String spec = provides[i].getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed exports - Missing the specification attribute");
+ } else {
+ desc.addProvidedServiceSpecification(spec);
+ }
+ } else {
+ throw new ConfigurationException("Invalid composition service providing : unknown action " + action);
+ }
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param metadata : the metadata of the component
+ * @param configuration : the instance configuration
+ * @throws ConfigurationException : the exporter cannot be created
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_context = getCompositeManager().getContext();
+
+ Element[] provides = metadata.getElements("provides", "");
+ for (int i = 0; i < provides.length; i++) {
+ String action = provides[i].getAttribute("action");
+ if (action.equalsIgnoreCase("implement")) {
+ ProvidedService svc = new ProvidedService(this, provides[i], Integer.toString(i));
+ m_managedServices.add(svc);
+ } else if (action.equalsIgnoreCase("export")) {
+ boolean optional = false;
+ boolean aggregate = false;
+ String specification = provides[i].getAttribute("specification");
+
+ String filter = "(objectClass=" + specification + ")";
+
+ String opt = provides[i].getAttribute("optional");
+ optional = opt != null && opt.equalsIgnoreCase("true");
+
+ String agg = provides[i].getAttribute("aggregate");
+ aggregate = agg != null && agg.equalsIgnoreCase("true");
+
+ String givenFilter = provides[i].getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ Filter fil = null;
+ try {
+ fil = m_context.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("An exporter filter is invalid " + filter + " : " + e.getMessage());
+ }
+
+ Comparator cmp = DependencyModel.getComparator(provides[i], m_context);
+ int policy = DependencyModel.getPolicy(provides[i]);
+ Class spec = DependencyModel.loadSpecification(specification, m_context);
+
+ ServiceExporter imp = new ServiceExporter(spec, fil, aggregate, optional, cmp, policy, getCompositeManager().getServiceContext(), m_context, this, getCompositeManager());
+ m_exporters.add(imp);
+ } // Others case cannot happen. The test was already made during the factory initialization.
+ }
+
+ m_description = new ProvidedServiceHandlerDescription(this, m_managedServices, m_exporters);
+
+ }
+
+ /**
+ * Start method.
+ * Start all managed provided service.
+ * @see org.apache.felix.ipojo.CompositeHandler#start()
+ */
+ public void start() {
+ // Compute imports and instances
+ computeAvailableServices();
+ computeAvailableTypes();
+
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ try {
+ checkServiceSpecification(svc);
+ svc.start();
+ } catch (CompositionException e) {
+ error("Cannot start the provided service handler", e);
+ setValidity(false);
+ return;
+ }
+ }
+
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ exp.start();
+ }
+
+ isHandlerValid();
+ }
+
+ /**
+ * Stop method.
+ * Stop all managed provided service.
+ * @see org.apache.felix.ipojo.CompositeHandler#stop()
+ */
+ public void stop() {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.stop();
+ }
+
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ exp.stop();
+ }
+ }
+
+ /**
+ * Check the handler validity.
+ * @see org.apache.felix.ipojo.CompositeHandler#isValid()
+ */
+ private void isHandlerValid() {
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ if (exp.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ setValidity(true);
+ }
+
+ /**
+ * Handler state changed.
+ * @param state : the new instance state.
+ * @see org.apache.felix.ipojo.CompositeHandler#stateChanged(int)
+ */
+ public void stateChanged(int state) {
+ if (state == ComponentInstance.INVALID) {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.unregister();
+ }
+ return;
+ }
+
+ // If the new state is VALID => register all the services
+ if (state == ComponentInstance.VALID) {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.register();
+ }
+ return;
+ }
+ }
+
+ /**
+ * Notify the handler that an exporter becomes invalid.
+ *
+ * @param exporter : the implicated exporter.
+ */
+ public void invalidate(DependencyModel exporter) {
+ // An export is no more valid
+ if (getValidity()) {
+ setValidity(false);
+ }
+
+ }
+
+ /**
+ * Notify the handler that an exporter becomes valid.
+ *
+ * @param exporter : the implicated exporter.
+ */
+ public void validate(DependencyModel exporter) {
+ // An import becomes valid
+ if (!getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * Build the list of available specification.
+ * @return the list of available specification.
+ */
+ protected List getSpecifications() {
+ return m_services;
+ }
+
+ /**
+ * Build the list of available specifications.
+ */
+ private void computeAvailableServices() {
+ // Get instantiated services :
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+
+ for (int i = 0; handler != null && i < handler.getInstances().size(); i++) {
+ SvcInstance svc = (SvcInstance) handler.getInstances().get(i);
+ String itf = svc.getServiceSpecification();
+ boolean agg = svc.isAggregate();
+ boolean opt = svc.isOptional();
+
+ SpecificationMetadata specMeta = new SpecificationMetadata(itf, m_context, agg, opt, this);
+ m_services.add(specMeta);
+ }
+
+ for (int i = 0; handler != null && i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ String itf = imp.getSpecification().getName();
+ boolean agg = imp.isAggregate();
+ boolean opt = imp.isOptional();
+
+ SpecificationMetadata specMeta = new SpecificationMetadata(itf, m_context, agg, opt, this);
+ m_services.add(specMeta);
+ }
+ }
+
+ /**
+ * Check composite requirement against service specification requirement is available.
+ * @param svc : the provided service to check
+ * @throws CompositionException : occurs if the specification field of the service specification cannot be analyzed correctly.
+ */
+ private void checkServiceSpecification(ProvidedService svc) throws CompositionException {
+ try {
+ Class spec = m_context.getBundle().loadClass(svc.getSpecification());
+ Field specField = spec.getField("specification");
+ Object object = specField.get(null);
+ if (object instanceof String) {
+ Element specification = ManifestMetadataParser.parse((String) object);
+ Element[] reqs = specification.getElements("requires");
+ for (int j = 0; reqs != null && j < reqs.length; j++) {
+ ServiceImporter imp = getAttachedRequirement(reqs[j]);
+ if (imp != null) {
+ // Fix service-level dependency flag
+ imp.setServiceLevelDependency();
+ }
+ checkRequirement(imp, reqs[j]);
+ }
+ } else {
+ error("[" + getCompositeManager().getInstanceName() + "] The specification field of the service specification " + svc.getSpecification() + " needs to be a String");
+ throw new CompositionException("Service Specification checking failed : The specification field of the service specification " + svc.getSpecification() + " needs to be a String");
+ }
+ } catch (NoSuchFieldException e) {
+ return; // No specification field
+ } catch (ClassNotFoundException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The service specification " + svc.getSpecification() + " cannot be load");
+ throw new CompositionException("The service specification " + svc.getSpecification() + " cannot be load : " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ } catch (ParseException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " does not contain a valid String : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " does not contain a valid String : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Look for the implementation (i.e. composite) requirement for the given service-level requirement metadata.
+ * @param element : the service-level requirement metadata
+ * @return the ServiceImporter object, null if not found or if the DependencyHandler is not plugged to the instance
+ */
+ private ServiceImporter getAttachedRequirement(Element element) {
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+ if (handler == null) { return null; }
+
+ String identity = element.getAttribute("id");
+ if (identity != null) {
+ // Look for dependency Id
+ for (int i = 0; i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ if (imp.getId().equals(identity)) { return imp; }
+ }
+ }
+
+ // If not found or no id, look for a dependency with the same specification
+ String requirement = element.getAttribute("specification");
+ for (int i = 0; i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ if (imp.getId().equals(requirement) || imp.getSpecification().getName().equals(requirement)) { return imp; }
+ }
+ return null;
+ }
+
+ /**
+ * Check the correctness of the composite requirement against the service level dependency.
+ * @param imp : requirement to check
+ * @param elem : service-level dependency metadata
+ * @throws CompositionException : occurs if the requirement does not match with service-level specification requirement
+ */
+ private void checkRequirement(ServiceImporter imp, Element elem) throws CompositionException {
+ String optional = elem.getAttribute("optional");
+ boolean opt = optional != null && optional.equalsIgnoreCase("true");
+
+ String aggregate = elem.getAttribute("aggregate");
+ boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
+
+ if (imp == null) {
+ // Add the missing requirement
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+ if (handler == null) {
+ // Look for the ServiceDependencyHandler factory
+ HandlerManager handlerManager = null;
+ try {
+ ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), "(&(handler.name=subservice)(handler.namespace=" + HandlerFactory.IPOJO_NAMESPACE + ")(handler.type=composite))");
+ Factory factory = (Factory) m_context.getService(refs[0]);
+ handlerManager = (HandlerManager) factory.createComponentInstance(null, getCompositeManager().getServiceContext());
+ } catch (InvalidSyntaxException e) {
+ // Should not happen
+ } catch (UnacceptableConfiguration e) {
+ // Should not happen
+ } catch (MissingHandlerException e) {
+ // Should not happen
+ } catch (ConfigurationException e) {
+ // Should not happen
+ }
+
+ // Add the required handler
+ try {
+ handlerManager.init(getCompositeManager(), new Element("composite", ""), new Properties());
+ } catch (ConfigurationException e) {
+ error("Internal error : cannot configure the Import Handler : " + e.getMessage());
+ throw new CompositionException("Internal error : cannot configure the Import Handler : " + e.getMessage());
+ }
+ handler = (ServiceDependencyHandler) handlerManager.getHandler();
+ getCompositeManager().addCompositeHandler(handlerManager);
+ }
+
+ String spec = elem.getAttribute("specification");
+ String filter = "(&(objectClass=" + spec + ")(!(instance.name=" + getCompositeManager().getInstanceName() + ")))"; // Cannot import yourself
+ String givenFilter = elem.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ BundleContext context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.GLOBAL);
+
+ Filter fil = null;
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new CompositionException("A required filter " + filter + " is malformed : " + e.getMessage());
+ }
+
+ Class specToImport = null;
+ try {
+ specToImport = getCompositeManager().getGlobalContext().getBundle().loadClass(spec);
+ } catch (ClassNotFoundException e) {
+ throw new CompositionException("A required specification cannot be loaded : " + spec);
+ }
+
+ ServiceImporter importer = new ServiceImporter(specToImport, fil, agg, opt, null, DependencyModel.DYNAMIC_BINDING_POLICY, context, null, handler);
+
+ handler.getRequirements().add(importer);
+ SpecificationMetadata specMeta = new SpecificationMetadata(spec, m_context, agg, opt, this);
+ m_services.add(specMeta); // Update the available types
+ return;
+ }
+
+ if (imp.isAggregate() && !agg) {
+ error("[" + getCompositeManager().getInstanceName() + "] The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ throw new CompositionException("The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ }
+
+ String filter = elem.getAttribute("filter");
+ if (filter != null) {
+ String filter2 = imp.getFilter();
+ if (filter2 == null || !filter2.equalsIgnoreCase(filter)) {
+ error("[" + getCompositeManager().getInstanceName() + "] The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ throw new CompositionException("The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ }
+ }
+ }
+
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+ /**
+ * Build available instance types.
+ */
+ private void computeAvailableTypes() {
+ InstanceHandler handler = (InstanceHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":instance");
+ if (handler == null) {
+ m_types = new ArrayList();
+ } else {
+ m_types = handler.getUsedType();
+ }
+ }
+
+ public List getInstanceType() {
+ return m_types;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.java
new file mode 100644
index 0000000..5c22246
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.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 org.apache.felix.ipojo.composite.service.provides;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+
+/**
+ * Provided Service Handler Description for composite.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandlerDescription extends HandlerDescription {
+
+ /**
+ * Provided Service Description list.
+ */
+ private List m_services = new ArrayList();
+
+ /**
+ * List of exports.
+ */
+ private List m_exports;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : composite handler.
+ * @param services : The list of Provided Service.
+ * @param exporters : list of managed exports
+ */
+ public ProvidedServiceHandlerDescription(CompositeHandler handler, List services, List exporters) {
+ super(handler);
+ m_services = services;
+ m_exports = exporters;
+ }
+
+ /**
+ * Get the handler description.
+ * @return the provided service handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element services = super.getHandlerInfo();
+ for (int i = 0; i < m_services.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_services.get(i);
+ Element service = new Element("service", "");
+ String state = "unregistered";
+ if (svc.isRegistered()) {
+ state = "registered";
+ }
+ String spec = "[" + svc.getSpecification() + "]";
+ service.addAttribute(new Attribute("Specification", spec));
+ service.addAttribute(new Attribute("State", state));
+ services.addElement(service);
+ }
+
+ for (int i = 0; i < m_exports.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exports.get(i);
+ Element expo = new Element("Exports", "");
+ expo.addAttribute(new Attribute("Specification", exp.getSpecification().getName()));
+ expo.addAttribute(new Attribute("Filter", exp.getFilter()));
+ if (exp.getState() == DependencyModel.RESOLVED) {
+ expo.addAttribute(new Attribute("State", "resolved"));
+ } else {
+ expo.addAttribute(new Attribute("State", "unresolved"));
+ }
+ services.addElement(expo);
+ }
+
+ return services;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java
new file mode 100644
index 0000000..bb0b14f
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java
@@ -0,0 +1,176 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.apache.felix.ipojo.util.SecurityHelper;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Export an service from the scope to the parent context.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceExporter extends DependencyModel {
+
+ /**
+ * Destination context.
+ */
+ private BundleContext m_destination;
+
+ /**
+ * Composite Manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Map of service reference - service registration storing exported services.
+ */
+ private Map/*<ServiceReference, ServiceRegistration>*/m_registrations = new HashMap();
+
+ /**
+ * Constructor.
+ *
+ * @param specification : exported service specification.
+ * @param filter : LDAP filter
+ * @param multiple : is the export an aggregate export?
+ * @param optional : is the export optional?
+ * @param cmp : comparator to use in the dependency
+ * @param policy : binding policy.
+ * @param from : internal service context
+ * @param dest : parent bundle context
+ * @param listener : dependency lifecycle listener to notify when the dependency state change.
+ * @param manager : composite manager
+ */
+ public ServiceExporter(Class specification, Filter filter, boolean multiple, boolean optional, Comparator cmp, int policy, ServiceContext from, BundleContext dest, DependencyStateListener listener, CompositeManager manager) {
+ super(specification, multiple, optional, filter, cmp, policy, from, listener, manager);
+
+ m_destination = dest;
+
+ m_manager = manager;
+
+ }
+
+ /**
+ * Transform service reference property in a dictionary.
+ * instance.name and factory.name are injected too.
+ * @param ref : the service reference.
+ * @return the dictionary containing all property of the given service reference.
+ */
+ private Dictionary getProps(ServiceReference ref) {
+ Properties prop = new Properties();
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ prop.put(keys[i], ref.getProperty(keys[i]));
+ }
+
+ prop.put("instance.name", m_manager.getInstanceName());
+ prop.put("factory.name", m_manager.getFactory().getName());
+
+ return prop;
+ }
+
+ /**
+ * Stop an exporter.
+ * Remove the service listener
+ * Unregister all exported services.
+ */
+ public void stop() {
+ super.stop();
+ Set refs = m_registrations.keySet();
+ Iterator iterator = refs.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(ref);
+ try {
+ reg.unregister();
+ } catch (IllegalStateException e) {
+ // The service is already unregistered
+ // Do nothing silently
+ }
+ }
+ m_registrations.clear();
+ }
+
+ /**
+ * A service has been injected. Register it.
+ * @param reference : the new reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference reference) {
+ Object svc = getService(reference);
+ // Security Check
+ if (SecurityHelper.hasPermissionToRegisterService(getSpecification().getName(), m_destination)) {
+ ServiceRegistration reg = m_destination
+ .registerService(getSpecification().getName(), svc, getProps(reference));
+ m_registrations.put(reference, reg);
+ } else {
+ throw new SecurityException("The bundle " + m_destination.getBundle().getBundleId() + " does not have the "
+ + "permission to register the service " + getSpecification().getName());
+ }
+ }
+
+ /**
+ * An exported service was modified.
+ * @param reference : modified reference
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void onServiceModification(ServiceReference reference) {
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(reference);
+ if (reg != null) {
+ reg.setProperties(getProps(reference));
+ }
+ }
+
+ /**
+ * An exported service disappears.
+ * @param reference : service reference
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void onServiceDeparture(ServiceReference reference) {
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(reference);
+ if (reg != null) {
+ reg.unregister();
+ }
+ m_registrations.remove(reference);
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ throw new UnsupportedOperationException("Dynamic dependency reconfiguration is not supported by service exporter");
+ }
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java
new file mode 100644
index 0000000..392de87
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java
@@ -0,0 +1,158 @@
+/*
+ * 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.felix.ipojo.composite.service.provides;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Represent a service specification.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SpecificationMetadata {
+
+ /**
+ * Name of the specification, i.e. name of the interface.
+ */
+ private String m_name;
+
+ /**
+ * List of the method contained in the specification.
+ */
+ private List/* <MethodMetadata> */m_methods = new ArrayList/* <MethodMetadata> */();
+
+ /**
+ * Is the specification an aggregate?
+ */
+ private boolean m_isAggregate;
+
+ /**
+ * Is the specification optional?
+ */
+ private boolean m_isOptional = false;
+
+ /**
+ * Is the specification an interface?
+ */
+ private boolean m_isInterface = true;
+
+ /**
+ * Component Type.
+ */
+ private String m_componentType = null;
+
+ /**
+ * Reference on the handler.
+ */
+ private ProvidedServiceHandler m_handler;
+
+ /**
+ * Constructor.
+ * @param name : specification name.
+ * @param context : bundle context.
+ * @param isAggregate : is the specification aggregate.
+ * @param isOptional : is the specification optional.
+ * @param psd : the handler.
+ */
+ public SpecificationMetadata(String name, BundleContext context, boolean isAggregate, boolean isOptional, ProvidedServiceHandler psd) {
+ m_name = name;
+ m_handler = psd;
+
+ // Populate methods :
+ try {
+ Class clazz = context.getBundle().loadClass(name);
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ MethodMetadata method = new MethodMetadata(methods[i]);
+ m_methods.add(method);
+ }
+ } catch (ClassNotFoundException e) {
+ m_handler.error("Cannot open " + name + " : " + e.getMessage());
+ return;
+ }
+
+ m_isAggregate = isAggregate;
+ m_isOptional = isOptional;
+ }
+
+ /**
+ * Constructor.
+ * @param clazz : class
+ * @param type : component type
+ * @param psd : the parent handler
+ */
+ public SpecificationMetadata(Class clazz, String type, ProvidedServiceHandler psd) {
+ m_handler = psd;
+ m_isAggregate = false;
+ m_isOptional = false;
+ m_componentType = type;
+ m_name = clazz.getName();
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ MethodMetadata method = new MethodMetadata(methods[i]);
+ m_methods.add(method);
+ }
+ m_isInterface = false;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public List/* <MethodMetadata> */getMethods() {
+ return m_methods;
+ }
+
+ /**
+ * Get a method by its name.
+ * @param name : method name
+ * @return the method metadata contained in the current specification with the given name. Null if the method is not found.
+ */
+ public MethodMetadata getMethodByName(String name) {
+ for (int i = 0; i < m_methods.size(); i++) {
+ MethodMetadata met = (MethodMetadata) m_methods.get(i);
+ if (met.getMethod().getName().equals(name)) { return met; }
+ }
+ return null;
+ }
+
+ public boolean isAggregate() {
+ return m_isAggregate;
+ }
+
+ public boolean isOptional() {
+ return m_isOptional;
+ }
+
+ public boolean isInterface() {
+ return m_isInterface;
+ }
+
+ public void setIsOptional(boolean optional) {
+ m_isOptional = optional;
+ }
+
+ public String getComponentType() {
+ return m_componentType;
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java
new file mode 100644
index 0000000..d7f227a
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java
@@ -0,0 +1,376 @@
+/*
+ * 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.felix.ipojo.composite.util;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+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;
+
+/**
+ * This class manages context-source management.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SourceManager implements ContextListener {
+
+ /**
+ * Source Name service property.
+ */
+ public static final String SOURCE_NAME = "source.name";
+
+ /**
+ * Managed dependency.
+ */
+ private DependencyModel m_dependency;
+
+ /**
+ * List of monitored context sources.
+ */
+ private List/* <ContextSource> */m_sources = new ArrayList(1);
+
+ /**
+ * PRoperties contained in the original filter.
+ */
+ private String[] m_properties;
+
+ /**
+ * Original filter (containing variables).
+ */
+ private String m_filter;
+
+ /**
+ * Bundle context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Service Tracker List.
+ */
+ private List/*<SourceTracker>*/m_trackers = new ArrayList(1);
+
+ /**
+ * Constructor.
+ * @param sources : context-source attribute from the dependency metadata
+ * @param depfilter : original dependency filter
+ * @param dependency : dependency object
+ * @param manager : composite manager
+ * @throws ConfigurationException : the sources are incorrect.
+ */
+ public SourceManager(String sources, String depfilter, DependencyModel dependency, CompositeManager manager) throws ConfigurationException {
+ m_filter = depfilter;
+ m_properties = getProperties(depfilter);
+ m_dependency = dependency;
+ m_context = manager.getGlobalContext();
+ if (manager.getParentServiceContext() == null) {
+ // The parent is the global context
+ parseSources(sources, manager.getGlobalContext(), manager.getGlobalContext(), manager.getServiceContext());
+ } else {
+ parseSources(sources, manager.getGlobalContext(), manager.getParentServiceContext(), manager.getServiceContext());
+ }
+ }
+
+ /**
+ * Start the context management.
+ */
+ public void start() {
+ for (int i = 0; i < m_trackers.size(); i++) {
+ ((SourceTracker) m_trackers.get(i)).open();
+ }
+ computeFilter();
+ }
+
+ /**
+ * Stop the context management.
+ */
+ public void stop() {
+ for (int i = 0; i < m_trackers.size(); i++) {
+ ((SourceTracker) m_trackers.get(i)).close();
+ }
+ setFilter(m_filter);
+ m_sources.clear();
+ }
+
+ /**
+ * Get the state of this source manager.
+ * @return the state of this source manager.
+ */
+ public int getState() {
+ if (m_sources.isEmpty()) {
+ return DependencyModel.UNRESOLVED;
+ } else {
+ return DependencyModel.RESOLVED;
+ }
+ }
+
+ /**
+ * Set the filter of the managed dependency.
+ * @param filter : the new filter to apply
+ */
+ private void setFilter(String filter) {
+ if (!filter.equals(m_dependency.getFilter())) {
+ // Reconfigure
+ try {
+ m_dependency.setFilter(m_context.createFilter(filter));
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("A context filter is invalid : " + filter);
+ }
+ }
+ }
+
+ /**
+ * Compute the new filter.
+ */
+ private void computeFilter() {
+ String fil = m_filter;
+ synchronized (this) {
+ for (int i = 0; i < m_sources.size(); i++) {
+ Dictionary props = ((ContextSource) m_sources.get(i)).getContext();
+ fil = substitute(fil, props); //NOPMD
+ }
+ }
+ if (!fil.equals(m_dependency.getFilter())) {
+ setFilter(fil);
+ }
+ }
+
+ /**
+ * This method substitute ${var} substring by values stored in a map.
+ * @param str : string with variables
+ * @param values : dictionary containing the variable name and the value.
+ * @return resulted string
+ */
+ public static String substitute(String str, Dictionary values) {
+ int len = str.length();
+ StringBuffer buffer = new StringBuffer(len);
+
+ int prev = 0;
+ int start = str.indexOf("${");
+ int end = str.indexOf('}', start);
+ while (start != -1 && end != -1) {
+ String key = str.substring(start + 2, end);
+ Object value = values.get(key);
+ if (value == null) {
+ buffer.append(str.substring(prev, end + 1));
+ } else {
+ buffer.append(str.substring(prev, start));
+ buffer.append(value);
+ }
+ prev = end + 1;
+ if (prev >= str.length()) {
+ break;
+ }
+
+ start = str.indexOf("${", prev);
+ if (start != -1) {
+ end = str.indexOf('}', start);
+ }
+ }
+
+ buffer.append(str.substring(prev));
+
+ return buffer.toString();
+ }
+
+ /**
+ * Compute the properties (${name}) from the given filter.
+ * @param str : string form of the filter.
+ * @return the list of found properties.
+ */
+ public static String[] getProperties(String str) {
+ List list = new ArrayList();
+ int prev = 0;
+ int start = str.indexOf("${");
+ int end = str.indexOf('}', start);
+ while (start != -1 && end != -1) {
+ String key = str.substring(start + 2, end);
+ list.add(key);
+ prev = end + 1;
+ if (prev >= str.length()) {
+ break;
+ }
+
+ start = str.indexOf("${", prev);
+ if (start != -1) {
+ end = str.indexOf('}', start);
+ }
+ }
+
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * A context source has modified a monitored property.
+ * @param source : source
+ * @param property : modified property
+ * @param value : new value.
+ * @see org.apache.felix.ipojo.ContextListener#update(org.apache.felix.ipojo.ContextSource, java.lang.String, java.lang.Object)
+ */
+ public synchronized void update(ContextSource source, String property, Object value) {
+ computeFilter();
+ }
+
+ /**
+ * Parse the context-source attribute in order to create source tracker object.
+ * @param sourceAtt : context-source attribute.
+ * @param global : global bundle context.
+ * @param parent : parent bundle context.
+ * @param local : local bundle context.
+ * @throws ConfigurationException : the context-source attribute is invalid.
+ */
+ private void parseSources(String sourceAtt, BundleContext global, BundleContext parent, BundleContext local) throws ConfigurationException {
+ String[] sources = ParseUtils.split(sourceAtt, ",");
+ for (int i = 0; i < sources.length; i++) {
+ String[] srcs = ParseUtils.split(sources[i], ":");
+ if (srcs.length == 1) {
+ // No prefix use local. //TODO choose default case.
+ SourceTracker tracker = new SourceTracker(srcs[0], local);
+ m_trackers.add(tracker);
+ } else if (srcs.length == 2) {
+ // According to prefix add the source in the good list.
+ if (srcs[0].equalsIgnoreCase("parent")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], parent);
+ m_trackers.add(tracker);
+ } else if (srcs[0].equalsIgnoreCase("local")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], local);
+ m_trackers.add(tracker);
+ } else if (srcs[0].equalsIgnoreCase("global")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], global);
+ m_trackers.add(tracker);
+ } else {
+ throw new ConfigurationException("Unknowns context scope : " + srcs[0]);
+ }
+ } else {
+ throw new ConfigurationException("Malformed context source : " + sources[i]);
+ }
+ }
+ }
+
+ /**
+ * A context source appears.
+ * @param source : new context source.
+ */
+ private void addContextSource(ContextSource source) {
+ m_sources.add(source);
+ computeFilter();
+ source.registerContextListener(this, m_properties);
+ }
+
+ /**
+ * A context source disappears.
+ * @param source : leaving context source.
+ */
+ private void removeContextSource(ContextSource source) {
+ m_sources.remove(source);
+ computeFilter();
+ }
+
+ private class SourceTracker implements TrackerCustomizer {
+
+ /**
+ * Service tracker.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * Constructor.
+ * @param name : name of the required context-source.
+ * @param countext : bundle context to use.
+ * @throws ConfigurationException : the context-source name is invalid.
+ */
+ public SourceTracker(String name, BundleContext countext) throws ConfigurationException {
+ String fil = "(&(" + Constants.OBJECTCLASS + "=" + ContextSource.class.getName() + ")(" + SOURCE_NAME + "=" + name + "))";
+ try {
+ Filter filter = countext.createFilter(fil);
+ m_tracker = new Tracker(countext, filter, this);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A Context source filter is invalid " + fil + " : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Open the tracker.
+ */
+ public void open() {
+ m_tracker.open();
+ }
+
+ /**
+ * Close the tracker.
+ */
+ public void close() {
+ m_tracker.close();
+ }
+
+ /**
+ * A new context-source was added.
+ * This method inject the context-source object in the source manager.
+ * @param reference : service reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ addContextSource((ContextSource) m_tracker.getService(reference));
+ }
+
+ /**
+ * A new context-source is adding in the tracker..
+ * @param reference : service reference
+ * @return true.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference reference) {
+ return true;
+ }
+
+ /**
+ * A used context-source is modified.
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Nothing to do.
+ }
+
+ /**
+ * A used context-source disappears.
+ * This method notify the Source Manager in order to manage this departure.
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ removeContextSource((ContextSource) service);
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/composite/src/main/resources/composite.xsd b/ipojo/runtime/composite/src/main/resources/composite.xsd
new file mode 100644
index 0000000..6a95fb8
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/resources/composite.xsd
@@ -0,0 +1,147 @@
+<!--
+ 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.
+-->
+<xs:schema elementFormDefault="qualified"
+ targetNamespace="org.apache.felix.ipojo.composite"
+ xmlns="org.apache.felix.ipojo.composite"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ipojo="org.apache.felix.ipojo">
+
+ <xs:import namespace="org.apache.felix.ipojo" schemaLocation="http://people.apache.org/~clement/ipojo/schemas/core.xsd"></xs:import>
+ <xs:complexType name="CompositeType">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="subservice" minOccurs="0"
+ maxOccurs="unbounded">
+ </xs:element>
+ <xs:element ref="provides" minOccurs="0"
+ maxOccurs="unbounded">
+ </xs:element>
+ <xs:element ref="instance" minOccurs="0"
+ maxOccurs="unbounded">
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax"
+ minOccurs="0" maxOccurs="unbounded">
+ </xs:any>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="optional"></xs:attribute>
+ <xs:attribute name="public" type="xs:boolean" use="optional"></xs:attribute>
+ <xs:attribute name="architecture" type="xs:boolean"
+ use="optional">
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:element name="subservice" type="SubserviceType" />
+ <xs:element name="provides" type="CompositeProvidesType" />
+
+ <xs:complexType name="CompositeProvidesType">
+ <xs:complexContent>
+ <xs:extension base="ipojo:ServiceDependencyType">
+ <xs:sequence>
+ <xs:element name="delegation" type="DelegationType"></xs:element>
+ </xs:sequence>
+ <xs:attribute name="specification" type="xs:string"
+ use="required">
+ </xs:attribute>
+
+ <xs:attribute name="action">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="implement"></xs:enumeration>
+ <xs:enumeration value="export"></xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+
+
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="SubserviceType">
+ <xs:complexContent>
+ <xs:extension base="ipojo:ServiceDependencyType">
+
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="CompositePropertyType"></xs:element>
+ </xs:sequence>
+ <xs:attribute name="action" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="import"></xs:enumeration>
+ <xs:enumeration value="instantiate"></xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="specification" type="xs:string"
+ use="required">
+ </xs:attribute>
+ <xs:attribute name="context-source" type="xs:string"
+ use="optional">
+ </xs:attribute>
+ <xs:attribute name="scope">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="global"></xs:enumeration>
+ <xs:enumeration value="composite"></xs:enumeration>
+ <xs:enumeration value="composite+global"></xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:simpleType name="actionType">
+ <xs:restriction base="xs:string"></xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="CompositePropertyType">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="CompositePropertyType" minOccurs="0" maxOccurs="unbounded"></xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"></xs:attribute>
+ <xs:attribute name="value" type="xs:string" use="optional"></xs:attribute>
+ </xs:complexType>
+
+ <xs:complexType name="CompositeInstanceType">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="CompositePropertyType"></xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="optional"></xs:attribute>
+ <xs:attribute name="component" type="xs:string"
+ use="required">
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:element name="instance" type="CompositeInstanceType"></xs:element>
+
+ <xs:element name="composite" type="CompositeType"></xs:element>
+
+ <xs:complexType name="DelegationType">
+ <xs:attribute name="method" type="xs:string" use="required"></xs:attribute>
+ <xs:attribute name="policy" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="all"></xs:enumeration>
+ <xs:enumeration value="one"></xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+</xs:schema>
\ No newline at end of file
diff --git a/ipojo/runtime/composite/src/main/resources/metadata.xml b/ipojo/runtime/composite/src/main/resources/metadata.xml
new file mode 100644
index 0000000..0f6262b
--- /dev/null
+++ b/ipojo/runtime/composite/src/main/resources/metadata.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<ipojo>
+ <!-- Composite Handler -->
+ <handler
+ classname="org.apache.felix.ipojo.composite.instance.InstanceHandler"
+ name="instance" type="composite" architecture="false" level="1">
+ <requires filter="(factory.state=1)" field="m_factories"
+ optional="true">
+ <callback type="bind" method="bindFactory" />
+ <callback type="unbind" method="unbindFactory" />
+ </requires>
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.service.instantiator.ServiceDependencyHandler"
+ name="subservice" type="composite" architecture="false">
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.service.provides.ProvidedServiceHandler"
+ name="provides" type="composite" architecture="false" level="3">
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.architecture.ArchitectureHandler"
+ name="architecture" type="composite" architecture="false">
+ <provides>
+ <property field="m_name" name="architecture.instance"/>
+ </provides>
+ </handler>
+</ipojo>
\ No newline at end of file
diff --git a/ipojo/runtime/core/DEPENDENCIES b/ipojo/runtime/core/DEPENDENCIES
new file mode 100644
index 0000000..6cfe273
--- /dev/null
+++ b/ipojo/runtime/core/DEPENDENCIES
@@ -0,0 +1,28 @@
+Apache Felix iPOJO Runtime
+Copyright 2008-2011 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software developed at
+Copyright (c) 2000-2005 INRIA, France Telecom
+Licensed under BSD License.
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2007).
+Licensed under the Apache License 2.0.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+- Apache License 2.0
+- BSD License
diff --git a/ipojo/runtime/core/LICENSE b/ipojo/runtime/core/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ipojo/runtime/core/LICENSE
@@ -0,0 +1,202 @@
+
+ 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/ipojo/runtime/core/LICENSE.asm b/ipojo/runtime/core/LICENSE.asm
new file mode 100644
index 0000000..cc529ed
--- /dev/null
+++ b/ipojo/runtime/core/LICENSE.asm
@@ -0,0 +1,29 @@
+Copyright (c) 2000-2005 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ipojo/runtime/core/NOTICE b/ipojo/runtime/core/NOTICE
new file mode 100644
index 0000000..65496d1
--- /dev/null
+++ b/ipojo/runtime/core/NOTICE
@@ -0,0 +1,15 @@
+Apache Felix iPOJO Runtime
+Copyright 2008-2011 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Copyright (c) 2000-2005 INRIA, France Telecom
+Licensed under BSD License.
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo.html b/ipojo/runtime/core/doc/apache-felix-ipojo.html
new file mode 100644
index 0000000..80c96b1
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo.html
@@ -0,0 +1,307 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head>
+
+
+
+ <title>Apache Felix - Apache Felix iPOJO</title>
+ <link rel="stylesheet" href="apache-felix-ipojo_files/site.css" type="text/css" media="all">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head><body>
+ <div class="title"><div class="logo"><a href="http://felix.apache.org/site/index.html"><img alt="Apache Felix" src="apache-felix-ipojo_files/logo.png" border="0"></a></div><div class="header"><a href="http://www.apache.org/"><img alt="Apache" src="apache-felix-ipojo_files/apache.png" border="0"></a></div></div>
+ <div class="menu">
+<ul>
+ <li><a href="http://felix.apache.org/site/news.html" title="news">news</a></li>
+ <li><a href="http://felix.apache.org/site/license.html" title="license">license</a></li>
+ <li><a href="http://felix.apache.org/site/downloads.cgi" rel="nofollow">downloads</a></li>
+ <li><a href="http://felix.apache.org/site/documentation.html" title="documentation">documentation</a></li>
+ <li><a href="http://felix.apache.org/site/mailinglists.html" title="mailinglists">mailing lists</a></li>
+ <li><a href="http://felix.apache.org/site/contributing.html" title="Contributing">contributing</a></li>
+ <li><a href="http://www.apache.org/" rel="nofollow">asf</a></li>
+ <li><a href="http://www.apache.org/foundation/sponsorship.html" rel="nofollow">sponsorship</a></li>
+ <li><a href="http://www.apache.org/foundation/thanks.html" rel="nofollow">sponsors</a>
+<!-- ApacheCon Ad -->
+<iframe src="apache-felix-ipojo_files/button.html" style="border-width: 0pt; float: left;" scrolling="no" frameborder="0" height="135" width="135"></iframe>
+<p style="height: 100px;">
+<!-- ApacheCon Ad -->
+</p></li></ul> </div>
+ <div class="main">
+<style type="text/css">
+ @import url(http://people.apache.org/~clement/ipojo/site/superfish.css);
+</style>
+
+<style type="text/css">
+ @import url(http://people.apache.org/~clement/ipojo/site/style.css);
+</style>
+
+<p>
+<script class="javascript" src="apache-felix-ipojo_files/shCore.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushCSharp.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushPhp.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushJScript.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushVb.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushSql.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushXml.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushShell.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushDelphi.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushPython.js"></script>
+<script class="javascript" src="apache-felix-ipojo_files/shBrushJava.js"></script>
+
+<script type="text/javascript" src="apache-felix-ipojo_files/jquery-1.js"></script>
+<script type="text/javascript" src="apache-felix-ipojo_files/hoverIntent.js"></script>
+<script type="text/javascript" src="apache-felix-ipojo_files/superfish.js"></script>
+<script type="text/javascript" src="apache-felix-ipojo_files/supersubs.js"></script>
+
+<script type="text/javascript">
+
+ $(document).ready(function(){
+ $("ul.sf-menu").supersubs({
+ minWidth: 14, // minimum width of sub-menus in em units
+ maxWidth: 30, // maximum width of sub-menus in em units
+ extraWidth: 1 // extra width can ensure lines don't sometimes turn over
+ // due to slight rounding differences and font-family
+ }).superfish(); // call supersubs first, then superfish, so that subs are
+ // not display:none when measuring. Call before initialising
+ // containing tabs for same reason.
+ });
+
+</script>
+</p><div class="main">
+<div class="page-header">
+<img src="apache-felix-ipojo_files/header.png" class="header">
+<a href="http://ipojo.org/"><img src="apache-felix-ipojo_files/ipojo.png" class="header-logo" width="225"></a>
+<ul class="sf-menu sf-js-enabled sf-shadow" id="ipojo-menu">
+<li class="current">
+<!-- Menu Overview -->
+<a href="" class="sf-with-ul">Overview<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="" title="Apache Felix iPOJO">Home</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-why-choose-ipojo.html" title="apache-felix-ipojo-why-choose-ipojo">Why choose iPOJO</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-successstories.html" title="apache-felix-ipojo-successstories">Success stories</a>
+ </li>
+ <li style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-feature-overview.html" title="Apache Felix iPOJO Feature Overview">Features</a>
+ </li>
+</ul>
+</li>
+
+<li class="">
+<!-- Menu download -->
+</li><li>
+<a href="http://felix.apache.org/site/download.html" title="Download">Download </a>
+</li>
+
+<li class="">
+<!-- Menu Documentation -->
+<a href="" class="sf-with-ul">Documentation<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <!-- sub- menu : getting started -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">Getting Started<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-in-10-minutes.html" title="iPOJO in 10 minutes">iPOJO in 10 minutes</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/how-to-use-ipojo-annotations.html" title="How to use iPOJO Annotations">Using Annotations</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-hello-word-maven-based-tutorial.html" title="iPOJO Hello Word (Maven-Based) tutorial">Maven tutorial</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-advanced-tutorial.html" title="iPOJO Advanced Tutorial">Advanced tutorial</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-dosgi.html" title="apache-felix-ipojo-dosgi">Using Distributed OSGi</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-composition-tutorial.html" title="iPOJO Composition Tutorial">Application Composition</a></li>
+ </ul>
+ </li> <!-- end of getting started -->
+ <!-- sub menu : Describing Components -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="http://felix.apache.org/site/describing-components.html" class="sf-with-ul">Describing components<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/service-requirement-handler.html" title="Service Requirement Handler">Requiring a service</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/providing-osgi-services.html" title="Providing OSGi services">Providing a service</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/lifecycle-callback-handler.html" title="Lifecycle Callback Handler">Lifecycle management</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/configuration-handler.html" title="Configuration Handler">Configuration</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/architecture-handler.html" title="Architecture Handler">Introspection</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/controller-lifecycle-handler.html" title="Controller Lifecycle Handler">Impacting the lifecycle</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/event-admin-handlers.html" title="Event Admin Handlers">Asynchronous communication</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-jmx-handler.html" title="iPOJO JMX Handler">JMX management</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/extender-pattern-handler.html" title="Extender Pattern Handler">Extender pattern</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/white-board-pattern-handler.html" title="White Board Pattern Handler">Whiteboard pattern</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/temporal-service-dependency.html" title="Temporal Service Dependency">Temporal dependencies</a></li>
+ </ul>
+ </li> <!-- End of describing components -->
+ <!-- sub- menu : User Guide -->
+ <li style="white-space: normal; float: left; width: 100%;" class="">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">User Guide<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/using-xml-schemas.html" title="Using XML Schemas">XML Schemas</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-api.html" title="apache-felix-ipojo-api">API</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-testing-components.html" title="apache-felix-ipojo-testing-components">Testing components</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-eclipse-integration.html" title="apache-felix-ipojo-eclipse-integration">Eclipse Integration</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-faq.html" title="iPOJO FAQ">FAQ</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-reference-card.html" title="iPOJO-Reference-Card">Reference Card</a></li>
+ </ul>
+ </li> <!-- end of user guide -->
+ <!-- sub- menu : Dev Guide -->
+ <li class="" style="white-space: normal; float: left; width: 100%;">
+ <a style="float: none; width: auto;" href="" class="sf-with-ul">Advanced Topics<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+ <ul style="left: 14em; float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/ipojo/api/1.2.0" rel="nofollow">Javadoc</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/how-to-write-your-own-handler.html" title="How to write your own handler">Handler development</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/how-to-use-ipojo-manipulation-metadata.html" title="How to use iPOJO Manipulation Metadata">Manipulation Metadata </a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/dive-into-the-ipojo-manipulation-depths.html" title="Dive into the iPOJO Manipulation depths">Dive into the iPOJO Manipulation depths</a></li>
+ </ul>
+ </li> <!-- End of Dev guide -->
+</ul>
+</li> <!-- End of doc -->
+<!-- Menu 4 : Tools -->
+<li class="">
+<a href="" class="sf-with-ul">Tools<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-ant-task.html" title="iPOJO Ant Task">Ant Task</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-eclipse-plug-in.html" title="iPOJO Eclipse Plug-in">Eclipse Plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-maven-plug-in.html" title="iPOJO Maven Plug-in">Maven Plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-arch-command.html" title="iPOJO-Arch-Command"><tt>arch</tt> shell command</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-online-manipulator.html" title="apache-felix-ipojo-online-manipulator">Online Manipulator</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/ipojo-webconsole-plugin.html" title="iPOJO Webconsole Plugin">Webconsole plugin</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-junit4osgi.html" title="apache-felix-ipojo-junit4osgi">Junit4OSGi</a></li>
+</ul>
+</li><!-- End of tools -->
+<!-- Menu 5 : Misc -->
+<li class="">
+<a href="" class="sf-with-ul">Misc<span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span><span class="sf-sub-indicator"> »</span></a>
+<ul style="float: none; width: 14em; display: none; visibility: hidden;">
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-issuestracker.html" title="apache-felix-ipojo-issuestracker">Issues Tracker</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-supportedvms.html" title="apache-felix-ipojo-supportedVMs">Supported JVMs</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/apache-felix-ipojo-supportedosgi.html" title="apache-felix-ipojo-supportedOSGi">Supported OSGi Implementations</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://ipojo-dark-side.blogspot.com/" rel="nofollow">iPOJO's Dark Side Blog</a></li>
+ <li style="white-space: normal; float: left; width: 100%;"><a style="float: none; width: auto;" href="http://felix.apache.org/site/article-presentations.html" title="Article & Presentations">Article & Presentations</a></li>
+</ul>
+</li><!-- End of misc -->
+</ul> <!-- End of the menu -->
+</div> <!-- Page header -->
+
+
+<div class="content">
+
+<p><b>iPOJO</b> is a service component runtime aiming to simplify OSGi
+application development. It natively supports ALL the dynamism of OSGi.
+Based on the concept of POJO, application logic is developed easily.
+Non functional properties are just <em>injected</em> in the component at runtime.
+<br clear="all">
+<br clear="all">
+iPOJO strength points are :</p>
+<ul>
+ <li>components are developed as POJO, nothing else is required !</li>
+ <li>the component model is extensible, so feel free to adapt it to your needs</li>
+ <li>the standard component model manages service providing and service dependencies, and so can require any other OSGi services</li>
+ <li>iPOJO manages the component instance lifecycle and the environment dynamics as it has never been possible</li>
+ <li>iPOJO provides a powerful composition system to create highly dynamic applications</li>
+</ul>
+
+
+<p><br clear="all"></p>
+
+<p>
+</p><div class="smallboxes" style="margin-left: auto; margin-right: auto; width: 824px;">
+<table>
+<tbody><tr>
+<td>
+<div class="smallbox" style="margin-left: 10px; margin-right: 10px;">
+ <div class="smallbox-title">
+ <img src="apache-felix-ipojo_files/status_online.png"> <b>Why choose iPOJO ?</b>
+ </div>
+ <div class="smallbox-content">
+ <ul>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-successstories.html#apache-felix-ipojo-successstories-schneider">iPOJO at Schneider Electric</a></li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-successstories.html#apache-felix-ipojo-successstories-ugasp">iPOJO at Ubidreams</a> </li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-successstories.html" title="apache-felix-ipojo-successstories">others success stories</a></li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-keypoints.html" title="apache-felix-ipojo-keypoints">iPOJO Key points</a></li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-why-choose-ipojo.html" title="apache-felix-ipojo-why-choose-ipojo">Why choose iPOJO </a></li>
+ </ul>
+ </div>
+</div>
+</td>
+<td>
+<div class="smallbox" style="margin-left: 10px; margin-right: 10px;">
+ <div class="smallbox-title">
+ <img src="apache-felix-ipojo_files/cog.png"> <b>Getting started</b>
+ </div>
+ <div class="smallbox-content">
+ <ul>
+ <li><a href="http://felix.apache.org/site/download.html" title="Download">Downloads</a></li>
+ <li><a href="http://felix.apache.org/site/ipojo-in-10-minutes.html" title="iPOJO in 10 minutes">iPOJO in 10 minutes</a></li>
+ <li><a href="http://felix.apache.org/site/how-to-use-ipojo-annotations.html" title="How to use iPOJO Annotations">Using Annotations</a></li>
+ <li><a href="http://felix.apache.org/site/ipojo-hello-word-maven-based-tutorial.html" title="iPOJO Hello Word (Maven-Based) tutorial">Maven tutorial</a></li>
+ <li><a href="http://felix.apache.org/site/ipojo-advanced-tutorial.html" title="iPOJO Advanced Tutorial">Advanced tutorial</a></li>
+ </ul>
+ </div>
+</div>
+</td>
+<td>
+<div class="smallbox" style="margin-left: 10px; margin-right: 10px;">
+ <div class="smallbox-title">
+ <img src="apache-felix-ipojo_files/user_edit.png"> <b>The developer corner</b>
+ </div>
+ <div class="smallbox-content">
+ <ul>
+ <li><a href="http://felix.apache.org/site/describing-components.html" title="Describing components">Describing components</a></li>
+ <li><a href="http://felix.apache.org/site/ipojo-reference-card.html" title="iPOJO-Reference-Card">iPOJO Reference Card</a></li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-eclipse-integration.html" title="apache-felix-ipojo-eclipse-integration">Eclipse Integration</a></li>
+ <li><a href="http://felix.apache.org/site/apache-felix-ipojo-online-manipulator.html" title="apache-felix-ipojo-online-manipulator">Online Manipulator</a></li>
+ <li><a href="http://felix.apache.org/site/ipojo-faq.html" title="iPOJO FAQ">FAQ</a></li>
+ </ul>
+ </div>
+</div>
+</td>
+</tr>
+</tbody></table>
+</div>
+
+
+
+<p>The easiest way to get involved in iPOJO is to try it out. See the
+Quick Start Guide for how to get up and running with the iPOJO
+framework and the downloads page for more information on acquiring
+iPOJO & related tools.</p>
+
+<p>You can also have a look to <a href="http://felix.apache.org/site/presentations.data/ipojo-berlin-20080611.pdf" rel="nofollow">this</a> presentation describing iPOJO main features.
+<br clear="all"></p>
+
+<h2><a name="ApacheFelixiPOJO-What'snews"></a>What's news</h2>
+
+<ul>
+ <li><b>New web site... and some issues</b> : the iPOJO web site
+has changed. Enjoy! However, we're still setting everything correctly,
+it should be done shortly. (2009-07-08)</li>
+ <li><b>iPOJO And Distributed Services</b> : New tutorial about Distributed OSGi and iPOJO available. (2009-07-05)</li>
+ <li><b>iPOJO web console plugin is now available</b> : Manages your iPOJO system with the Apache Felix Web Console. More info <a href="http://felix.apache.org/site/ipojo-webconsole-plugin.html" title="iPOJO Webconsole Plugin">here</a> (2009-06-30)</li>
+ <li><b>iPOJO online manipulator is now available</b> : It allows to avoid offline manipulation! More info <a href="http://felix.apache.org/site/apache-felix-ipojo-online-manipulator.html" title="apache-felix-ipojo-online-manipulator">here</a> (2009-04-22)</li>
+ <li><b>iPOJO API is now in the iPOJO Trunk</b> : The iPOJO API is now available from the iPOJO Trunk (2009-02-09)</li>
+ <li><b>Integrating iPOJO inside Eclipse</b> : <a href="http://felix.apache.org/site/apache-felix-ipojo-eclipse-integration.html" title="apache-felix-ipojo-eclipse-integration">How to don't break your compile & run cycle</a> (2009-02-05)</li>
+ <li><b>Towards iPOJO 1.2.0</b> : The release process has began (2009-01-15)</li>
+ <li><b>Release of the Apache Felix iPOJO 1.0.0</b> : iPOJO 1.0.0 is available in the <a href="http://felix.apache.org/site/download.html" title="Download">download section</a> (2008-10-22)</li>
+</ul>
+
+<ul>
+ <li><a href="http://felix.apache.org/site/ipojo-news.html" title="iPOJO-news">all news...</a></li>
+</ul>
+
+
+
+
+ </div>
+ <img src="apache-felix-ipojo_files/footer.png" class="footer">
+</div>
+
+<script type="text/javascript">
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script src="apache-felix-ipojo_files/ga.js" type="text/javascript"></script>
+<script type="text/javascript">
+try{
+var pageTracker = _gat._getTracker("UA-1518442-4");
+pageTracker._trackPageview();
+} catch(err) {}
+</script>
+
+ </div>
+ </body></html>
\ No newline at end of file
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/apache.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/apache.png
new file mode 100644
index 0000000..5132f65
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/apache.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/button.html b/ipojo/runtime/core/doc/apache-felix-ipojo_files/button.html
new file mode 100644
index 0000000..1721083
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/button.html
@@ -0,0 +1,2 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"></head><body></body></html>
\ No newline at end of file
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/cog.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/cog.png
new file mode 100644
index 0000000..67de2c6
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/cog.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/footer.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/footer.png
new file mode 100644
index 0000000..15be425
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/footer.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/ga.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/ga.js
new file mode 100644
index 0000000..0501070
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/ga.js
@@ -0,0 +1,41 @@
+var _gat=new Object({c:"length",lb:"4.3.1",m:"cookie",b:undefined,cb:function(d,a){this.zb=d;this.Nb=a},r:"__utma=",W:"__utmb=",ma:"__utmc=",Ta:"__utmk=",na:"__utmv=",oa:"__utmx=",Sa:"GASO=",X:"__utmz=",lc:"http://www.google-analytics.com/__utm.gif",mc:"https://ssl.google-analytics.com/__utm.gif",Wa:"utmcid=",Ya:"utmcsr=",$a:"utmgclid=",Ua:"utmccn=",Xa:"utmcmd=",Za:"utmctr=",Va:"utmcct=",Hb:false,_gasoDomain:undefined,_gasoCPath:undefined,e:window,a:document,k:navigator,t:function(d){var a=1,c=0,h,
+o;if(!_gat.q(d)){a=0;for(h=d[_gat.c]-1;h>=0;h--){o=d.charCodeAt(h);a=(a<<6&268435455)+o+(o<<14);c=a&266338304;a=c!=0?a^c>>21:a}}return a},C:function(d,a,c){var h=_gat,o="-",k,l,s=h.q;if(!s(d)&&!s(a)&&!s(c)){k=h.w(d,a);if(k>-1){l=d.indexOf(c,k);if(l<0)l=d[h.c];o=h.F(d,k+h.w(a,"=")+1,l)}}return o},Ea:function(d){var a=false,c=0,h,o;if(!_gat.q(d)){a=true;for(h=0;h<d[_gat.c];h++){o=d.charAt(h);c+="."==o?1:0;a=a&&c<=1&&(0==h&&"-"==o||_gat.P(".0123456789",o))}}return a},d:function(d,a){var c=encodeURIComponent;
+return c instanceof Function?(a?encodeURI(d):c(d)):escape(d)},J:function(d,a){var c=decodeURIComponent,h;d=d.split("+").join(" ");if(c instanceof Function)try{h=a?decodeURI(d):c(d)}catch(o){h=unescape(d)}else h=unescape(d);return h},Db:function(d){return d&&d.hash?_gat.F(d.href,_gat.w(d.href,"#")):""},q:function(d){return _gat.b==d||"-"==d||""==d},Lb:function(d){return d[_gat.c]>0&&_gat.P(" \n\r\t",d)},P:function(d,a){return _gat.w(d,a)>-1},h:function(d,a){d[d[_gat.c]]=a},T:function(d){return d.toLowerCase()},
+z:function(d,a){return d.split(a)},w:function(d,a){return d.indexOf(a)},F:function(d,a,c){c=_gat.b==c?d[_gat.c]:c;return d.substring(a,c)},uc:function(){var d=_gat.b,a=window;if(a&&a.gaGlobal&&a.gaGlobal.hid)d=a.gaGlobal.hid;else{d=Math.round(Math.random()*2147483647);a.gaGlobal=a.gaGlobal?a.gaGlobal:{};a.gaGlobal.hid=d}return d},wa:function(){return Math.round(Math.random()*2147483647)},Gc:function(){return(_gat.wa()^_gat.vc())*2147483647},vc:function(){var d=_gat.k,a=_gat.a,c=_gat.e,h=a[_gat.m]?
+a[_gat.m]:"",o=c.history[_gat.c],k,l,s=[d.appName,d.version,d.language?d.language:d.browserLanguage,d.platform,d.userAgent,d.javaEnabled()?1:0].join("");if(c.screen)s+=c.screen.width+"x"+c.screen.height+c.screen.colorDepth;else if(c.java){l=java.awt.Toolkit.getDefaultToolkit().getScreenSize();s+=l.screen.width+"x"+l.screen.height}s+=h;s+=a.referrer?a.referrer:"";k=s[_gat.c];while(o>0)s+=o--^k++;return _gat.t(s)}});_gat.hc=function(){var d=this,a=_gat.cb;function c(h,o){return new a(h,o)}d.db="utm_campaign";d.eb="utm_content";d.fb="utm_id";d.gb="utm_medium";d.hb="utm_nooverride";d.ib="utm_source";d.jb="utm_term";d.kb="gclid";d.pa=0;d.I=0;d.wb="15768000";d.Tb="1800";d.ea=[];d.ga=[];d.Ic="cse";d.Gb="q";d.ab="google";d.fa=[c(d.ab,d.Gb),c("yahoo","p"),c("msn","q"),c("bing","q"),c("aol","query"),c("aol","encquery"),c("lycos","query"),c("ask","q"),c("altavista","q"),c("netscape","query"),c("cnn","query"),c("looksmart","qt"),c("about",
+"terms"),c("mamma","query"),c("alltheweb","q"),c("gigablast","q"),c("voila","rdata"),c("virgilio","qs"),c("live","q"),c("baidu","wd"),c("alice","qs"),c("yandex","text"),c("najdi","q"),c("aol","q"),c("club-internet","query"),c("mama","query"),c("seznam","q"),c("search","q"),c("wp","szukaj"),c("onet","qt"),c("netsprint","q"),c("google.interia","q"),c("szukacz","q"),c("yam","k"),c("pchome","q"),c("kvasir","searchExpr"),c("sesam","q"),c("ozu","q"),c("terra","query"),c("nostrum","query"),c("mynet","q"),
+c("ekolay","q"),c("search.ilse","search_for")];d.B=undefined;d.Kb=false;d.p="/";d.ha=100;d.Da="/__utm.gif";d.ta=1;d.ua=1;d.G="|";d.sa=1;d.qa=1;d.pb=1;d.g="auto";d.D=1;d.Ga=1000;d.Yc=10;d.nc=10;d.Zc=0.2};_gat.Y=function(d,a){var c,h,o,k,l,s,q,f=this,n=_gat,w=n.q,x=n.c,g,z=a;f.a=d;function B(i){var b=i instanceof Array?i.join("."):"";return w(b)?"-":b}function A(i,b){var e=[],j;if(!w(i)){e=n.z(i,".");if(b)for(j=0;j<e[x];j++)if(!n.Ea(e[j]))e[j]="-"}return e}function p(){return u(63072000000)}function u(i){var b=new Date,e=new Date(b.getTime()+i);return"expires="+e.toGMTString()+"; "}function m(i,b){f.a[n.m]=i+"; path="+z.p+"; "+b+f.Cc()}function r(i,b,e){var j=f.V,t,v;for(t=0;t<j[x];t++){v=j[t][0];
+v+=w(b)?b:b+j[t][4];j[t][2](n.C(i,v,e))}}f.Jb=function(){return n.b==g||g==f.t()};f.Ba=function(){return l?l:"-"};f.Wb=function(i){l=i};f.Ma=function(i){g=n.Ea(i)?i*1:"-"};f.Aa=function(){return B(s)};f.Na=function(i){s=A(i)};f.Hc=function(){return g?g:"-"};f.Cc=function(){return w(z.g)?"":"domain="+z.g+";"};f.ya=function(){return B(c)};f.Ub=function(i){c=A(i,1)};f.K=function(){return B(h)};f.La=function(i){h=A(i,1)};f.za=function(){return B(o)};f.Vb=function(i){o=A(i,1)};f.Ca=function(){return B(k)};
+f.Xb=function(i){k=A(i);for(var b=0;b<k[x];b++)if(b<4&&!n.Ea(k[b]))k[b]="-"};f.Dc=function(){return q};f.Uc=function(i){q=i};f.pc=function(){c=[];h=[];o=[];k=[];l=n.b;s=[];g=n.b};f.t=function(){var i="",b;for(b=0;b<f.V[x];b++)i+=f.V[b][1]();return n.t(i)};f.Ha=function(i){var b=f.a[n.m],e=false;if(b){r(b,i,";");f.Ma(f.t());e=true}return e};f.Rc=function(i){r(i,"","&");f.Ma(n.C(i,n.Ta,"&"))};f.Wc=function(){var i=f.V,b=[],e;for(e=0;e<i[x];e++)n.h(b,i[e][0]+i[e][1]());n.h(b,n.Ta+f.t());return b.join("&")};
+f.bd=function(i,b){var e=f.V,j=z.p,t;f.Ha(i);z.p=b;for(t=0;t<e[x];t++)if(!w(e[t][1]()))e[t][3]();z.p=j};f.dc=function(){m(n.r+f.ya(),p())};f.Pa=function(){m(n.W+f.K(),u(z.Tb*1000))};f.ec=function(){m(n.ma+f.za(),"")};f.Ra=function(){m(n.X+f.Ca(),u(z.wb*1000))};f.fc=function(){m(n.oa+f.Ba(),p())};f.Qa=function(){m(n.na+f.Aa(),p())};f.cd=function(){m(n.Sa+f.Dc(),"")};f.V=[[n.r,f.ya,f.Ub,f.dc,"."],[n.W,f.K,f.La,f.Pa,""],[n.ma,f.za,f.Vb,f.ec,""],[n.oa,f.Ba,f.Wb,f.fc,""],[n.X,f.Ca,f.Xb,f.Ra,"."],[n.na,
+f.Aa,f.Na,f.Qa,"."]]};_gat.jc=function(d){var a=this,c=_gat,h=d,o,k=function(l){var s=(new Date).getTime(),q;q=(s-l[3])*(h.Zc/1000);if(q>=1){l[2]=Math.min(Math.floor(l[2]*1+q),h.nc);l[3]=s}return l};a.O=function(l,s,q,f,n,w,x){var g,z=h.D,B=q.location;if(!o)o=new c.Y(q,h);o.Ha(f);g=c.z(o.K(),".");if(g[1]<500||n){if(w)g=k(g);if(n||!w||g[2]>=1){if(!n&&w)g[2]=g[2]*1-1;g[1]=g[1]*1+1;l="?utmwv="+_gat.lb+"&utmn="+c.wa()+(c.q(B.hostname)?"":"&utmhn="+c.d(B.hostname))+(h.ha==100?"":"&utmsp="+c.d(h.ha))+l;if(0==z||2==z){var A=
+new Image(1,1);A.src=h.Da+l;var p=2==z?function(){}:x||function(){};A.onload=p}if(1==z||2==z){var u=new Image(1,1);u.src=("https:"==B.protocol?c.mc:c.lc)+l+"&utmac="+s+"&utmcc="+a.wc(q,f);u.onload=x||function(){}}}}o.La(g.join("."));o.Pa()};a.wc=function(l,s){var q=[],f=[c.r,c.X,c.na,c.oa],n,w=l[c.m],x;for(n=0;n<f[c.c];n++){x=c.C(w,f[n]+s,";");if(!c.q(x))c.h(q,f[n]+x+";")}return c.d(q.join("+"))}};_gat.i=function(){this.la=[]};_gat.i.bb=function(d,a,c,h,o,k){var l=this;l.cc=d;l.Oa=a;l.L=c;l.sb=h;l.Pb=o;l.Qb=k};_gat.i.bb.prototype.S=function(){var d=this,a=_gat.d;return"&"+["utmt=item","utmtid="+a(d.cc),"utmipc="+a(d.Oa),"utmipn="+a(d.L),"utmiva="+a(d.sb),"utmipr="+a(d.Pb),"utmiqt="+a(d.Qb)].join("&")};_gat.i.$=function(d,a,c,h,o,k,l,s){var q=this;q.v=d;q.ob=a;q.bc=c;q.ac=h;q.Yb=o;q.ub=k;q.$b=l;q.xb=s;q.ca=[]};_gat.i.$.prototype.mb=function(d,a,c,h,o){var k=this,l=k.Eb(d),s=k.v,q=_gat;if(q.b==
+l)q.h(k.ca,new q.i.bb(s,d,a,c,h,o));else{l.cc=s;l.Oa=d;l.L=a;l.sb=c;l.Pb=h;l.Qb=o}};_gat.i.$.prototype.Eb=function(d){var a,c=this.ca,h;for(h=0;h<c[_gat.c];h++)a=d==c[h].Oa?c[h]:a;return a};_gat.i.$.prototype.S=function(){var d=this,a=_gat.d;return"&"+["utmt=tran","utmtid="+a(d.v),"utmtst="+a(d.ob),"utmtto="+a(d.bc),"utmttx="+a(d.ac),"utmtsp="+a(d.Yb),"utmtci="+a(d.ub),"utmtrg="+a(d.$b),"utmtco="+a(d.xb)].join("&")};_gat.i.prototype.nb=function(d,a,c,h,o,k,l,s){var q=this,f=_gat,n=q.xa(d);if(f.b==
+n){n=new f.i.$(d,a,c,h,o,k,l,s);f.h(q.la,n)}else{n.ob=a;n.bc=c;n.ac=h;n.Yb=o;n.ub=k;n.$b=l;n.xb=s}return n};_gat.i.prototype.xa=function(d){var a,c=this.la,h;for(h=0;h<c[_gat.c];h++)a=d==c[h].v?c[h]:a;return a};_gat.gc=function(d){var a=this,c="-",h=_gat,o=d;a.Ja=screen;a.qb=!self.screen&&self.java?java.awt.Toolkit.getDefaultToolkit():h.b;a.a=document;a.e=window;a.k=navigator;a.Ka=c;a.Sb=c;a.tb=c;a.Ob=c;a.Mb=1;a.Bb=c;function k(){var l,s,q,f,n="ShockwaveFlash",w="$version",x=a.k?a.k.plugins:h.b;if(x&&x[h.c]>0)for(l=0;l<x[h.c]&&!q;l++){s=x[l];if(h.P(s.name,"Shockwave Flash"))q=h.z(s.description,"Shockwave Flash ")[1]}else{n=n+"."+n;try{f=new ActiveXObject(n+".7");q=f.GetVariable(w)}catch(g){}if(!q)try{f=
+new ActiveXObject(n+".6");q="WIN 6,0,21,0";f.AllowScriptAccess="always";q=f.GetVariable(w)}catch(z){}if(!q)try{f=new ActiveXObject(n);q=f.GetVariable(w)}catch(z){}if(q){q=h.z(h.z(q," ")[1],",");q=q[0]+"."+q[1]+" r"+q[2]}}return q?q:c}a.xc=function(){var l;if(self.screen){a.Ka=a.Ja.width+"x"+a.Ja.height;a.Sb=a.Ja.colorDepth+"-bit"}else if(a.qb)try{l=a.qb.getScreenSize();a.Ka=l.width+"x"+l.height}catch(s){}a.Ob=h.T(a.k&&a.k.language?a.k.language:(a.k&&a.k.browserLanguage?a.k.browserLanguage:c));a.Mb=
+a.k&&a.k.javaEnabled()?1:0;a.Bb=o?k():c;a.tb=h.d(a.a.characterSet?a.a.characterSet:(a.a.charset?a.a.charset:c))};a.Xc=function(){return"&"+["utmcs="+h.d(a.tb),"utmsr="+a.Ka,"utmsc="+a.Sb,"utmul="+a.Ob,"utmje="+a.Mb,"utmfl="+h.d(a.Bb)].join("&")}};_gat.n=function(d,a,c,h,o){var k=this,l=_gat,s=l.q,q=l.b,f=l.P,n=l.C,w=l.T,x=l.z,g=l.c;k.a=a;k.f=d;k.Rb=c;k.ja=h;k.o=o;function z(p){return s(p)||"0"==p||!f(p,"://")}function B(p){var u="";p=w(x(p,"://")[1]);if(f(p,"/")){p=x(p,"/")[1];if(f(p,"?"))u=x(p,"?")[0]}return u}function A(p){var u="";u=w(x(p,"://")[1]);if(f(u,"/"))u=x(u,"/")[0];return u}k.Fc=function(p){var u=k.Fb(),m=k.o;return new l.n.s(n(p,m.fb+"=","&"),n(p,m.ib+"=","&"),n(p,m.kb+"=","&"),k.ba(p,m.db,"(not set)"),k.ba(p,m.gb,"(not set)"),
+k.ba(p,m.jb,u&&!s(u.R)?l.J(u.R):q),k.ba(p,m.eb,q))};k.Ib=function(p){var u=A(p),m=B(p);if(f(u,k.o.ab)){p=x(p,"?").join("&");if(f(p,"&"+k.o.Gb+"="))if(m==k.o.Ic)return true}return false};k.Fb=function(){var p,u,m=k.Rb,r,i,b=k.o.fa;if(z(m)||k.Ib(m))return;p=A(m);for(r=0;r<b[g];r++){i=b[r];if(f(p,w(i.zb))){m=x(m,"?").join("&");if(f(m,"&"+i.Nb+"=")){u=x(m,"&"+i.Nb+"=")[1];if(f(u,"&"))u=x(u,"&")[0];return new l.n.s(q,i.zb,q,"(organic)","organic",u,q)}}}};k.ba=function(p,u,m){var r=n(p,u+"=","&"),i=!s(r)?
+l.J(r):(!s(m)?m:"-");return i};k.Nc=function(p){var u=k.o.ea,m=false,r,i;if(p&&"organic"==p.da){r=w(l.J(p.R));for(i=0;i<u[g];i++)m=m||w(u[i])==r}return m};k.Ec=function(){var p="",u="",m=k.Rb;if(z(m)||k.Ib(m))return;p=w(x(m,"://")[1]);if(f(p,"/")){u=l.F(p,l.w(p,"/"));if(f(u,"?"))u=x(u,"?")[0];p=x(p,"/")[0]}if(0==l.w(p,"www."))p=l.F(p,4);return new l.n.s(q,p,q,"(referral)","referral",q,u)};k.sc=function(p){var u="";if(k.o.pa){u=l.Db(p);u=""!=u?u+"&":u}u+=p.search;return u};k.zc=function(){return new l.n.s(q,
+"(direct)",q,"(direct)","(none)",q,q)};k.Oc=function(p){var u=false,m,r,i=k.o.ga;if(p&&"referral"==p.da){m=w(l.d(p.ia));for(r=0;r<i[g];r++)u=u||f(m,w(i[r]))}return u};k.U=function(p){return q!=p&&p.Fa()};k.yc=function(p,u){var m="",r="-",i,b,e=0,j,t,v=k.f;if(!p)return"";t=k.a[l.m]?k.a[l.m]:"";m=k.sc(k.a.location);if(k.o.I&&p.Jb()){r=p.Ca();if(!s(r)&&!f(r,";")){p.Ra();return""}}r=n(t,l.X+v+".",";");i=k.Fc(m);if(k.U(i)){b=n(m,k.o.hb+"=","&");if("1"==b&&!s(r))return""}if(!k.U(i)){i=k.Fb();if(!s(r)&&
+k.Nc(i))return""}if(!k.U(i)&&u){i=k.Ec();if(!s(r)&&k.Oc(i))return""}if(!k.U(i))if(s(r)&&u)i=k.zc();if(!k.U(i))return"";if(!s(r)){var y=x(r,"."),E=new l.n.s;E.Cb(y.slice(4).join("."));j=w(E.ka())==w(i.ka());e=y[3]*1}if(!j||u){var F=n(t,l.r+v+".",";"),I=F.lastIndexOf("."),G=I>9?l.F(F,I+1)*1:0;e++;G=0==G?1:G;p.Xb([v,k.ja,G,e,i.ka()].join("."));p.Ra();return"&utmcn=1"}else return"&utmcr=1"}};_gat.n.s=function(d,a,c,h,o,k,l){var s=this;s.v=d;s.ia=a;s.ra=c;s.L=h;s.da=o;s.R=k;s.vb=l};_gat.n.s.prototype.ka=
+function(){var d=this,a=_gat,c=[],h=[[a.Wa,d.v],[a.Ya,d.ia],[a.$a,d.ra],[a.Ua,d.L],[a.Xa,d.da],[a.Za,d.R],[a.Va,d.vb]],o,k;if(d.Fa())for(o=0;o<h[a.c];o++)if(!a.q(h[o][1])){k=h[o][1].split("+").join("%20");k=k.split(" ").join("%20");a.h(c,h[o][0]+k)}return c.join("|")};_gat.n.s.prototype.Fa=function(){var d=this,a=_gat.q;return!(a(d.v)&&a(d.ia)&&a(d.ra))};_gat.n.s.prototype.Cb=function(d){var a=this,c=_gat,h=function(o){return c.J(c.C(d,o,"|"))};a.v=h(c.Wa);a.ia=h(c.Ya);a.ra=h(c.$a);a.L=h(c.Ua);a.da=
+h(c.Xa);a.R=h(c.Za);a.vb=h(c.Va)};_gat.Z=function(){var d=this,a=_gat,c={},h="k",o="v",k=[h,o],l="(",s=")",q="*",f="!",n="'",w={};w[n]="'0";w[s]="'1";w[q]="'2";w[f]="'3";var x=1;function g(m,r,i,b){if(a.b==c[m])c[m]={};if(a.b==c[m][r])c[m][r]=[];c[m][r][i]=b}function z(m,r,i){return a.b!=c[m]&&a.b!=c[m][r]?c[m][r][i]:a.b}function B(m,r){if(a.b!=c[m]&&a.b!=c[m][r]){c[m][r]=a.b;var i=true,b;for(b=0;b<k[a.c];b++)if(a.b!=c[m][k[b]]){i=false;break}if(i)c[m]=a.b}}function A(m){var r="",i=false,b,e;for(b=0;b<k[a.c];b++){e=m[k[b]];if(a.b!=
+e){if(i)r+=k[b];r+=p(e);i=false}else i=true}return r}function p(m){var r=[],i,b;for(b=0;b<m[a.c];b++)if(a.b!=m[b]){i="";if(b!=x&&a.b==m[b-1]){i+=b.toString();i+=f}i+=u(m[b]);a.h(r,i)}return l+r.join(q)+s}function u(m){var r="",i,b,e;for(i=0;i<m[a.c];i++){b=m.charAt(i);e=w[b];r+=a.b!=e?e:b}return r}d.Kc=function(m){return a.b!=c[m]};d.N=function(){var m=[],r;for(r in c)if(a.b!=c[r])a.h(m,r.toString()+A(c[r]));return m.join("")};d.Sc=function(m){if(m==a.b)return d.N();var r=[m.N()],i;for(i in c)if(a.b!=
+c[i]&&!m.Kc(i))a.h(r,i.toString()+A(c[i]));return r.join("")};d._setKey=function(m,r,i){if(typeof i!="string")return false;g(m,h,r,i);return true};d._setValue=function(m,r,i){if(typeof i!="number"&&(a.b==Number||!(i instanceof Number)))return false;if(Math.round(i)!=i||i==NaN||i==Infinity)return false;g(m,o,r,i.toString());return true};d._getKey=function(m,r){return z(m,h,r)};d._getValue=function(m,r){return z(m,o,r)};d._clearKey=function(m){B(m,h)};d._clearValue=function(m){B(m,o)}};_gat.ic=function(d,a){var c=this;c.jd=a;c.Pc=d;c._trackEvent=function(h,o,k){return a._trackEvent(c.Pc,h,o,k)}};_gat.kc=function(d){var a=this,c=_gat,h=c.b,o=c.q,k=c.w,l=c.F,s=c.C,q=c.P,f=c.z,n="location",w=c.c,x=h,g=new c.hc,z=false;a.a=document;a.e=window;a.ja=Math.round((new Date).getTime()/1000);a.H=d;a.yb=a.a.referrer;a.va=h;a.j=h;a.A=h;a.M=false;a.aa=h;a.rb="";a.l=h;a.Ab=h;a.f=h;a.u=h;function B(){if("auto"==g.g){var b=a.a.domain;if("www."==l(b,0,4))b=l(b,4);g.g=b}g.g=c.T(g.g)}function A(){var b=g.g,e=k(b,"www.google.")*k(b,".google.")*k(b,"google.");return e||"/"!=g.p||k(b,"google.org")>-1}function p(b,
+e,j){if(o(b)||o(e)||o(j))return"-";var t=s(b,c.r+a.f+".",e),v;if(!o(t)){v=f(t,".");v[5]=v[5]?v[5]*1+1:1;v[3]=v[4];v[4]=j;t=v.join(".")}return t}function u(){return"file:"!=a.a[n].protocol&&A()}function m(b){if(!b||""==b)return"";while(c.Lb(b.charAt(0)))b=l(b,1);while(c.Lb(b.charAt(b[w]-1)))b=l(b,0,b[w]-1);return b}function r(b,e,j){if(!o(b())){e(c.J(b()));if(!q(b(),";"))j()}}function i(b){var e,j=""!=b&&a.a[n].host!=b;if(j)for(e=0;e<g.B[w];e++)j=j&&k(c.T(b),c.T(g.B[e]))==-1;return j}a.Bc=function(){if(!g.g||
+""==g.g||"none"==g.g){g.g="";return 1}B();return g.pb?c.t(g.g):1};a.tc=function(b,e){if(o(b))b="-";else{e+=g.p&&"/"!=g.p?g.p:"";var j=k(b,e);b=j>=0&&j<=8?"0":("["==b.charAt(0)&&"]"==b.charAt(b[w]-1)?"-":b)}return b};a.Ia=function(b){var e="",j=a.a;e+=a.aa?a.aa.Xc():"";e+=g.qa?a.rb:"";e+=g.ta&&!o(j.title)?"&utmdt="+c.d(j.title):"";e+="&utmhid="+c.uc()+"&utmr="+a.va+"&utmp="+a.Tc(b);return e};a.Tc=function(b){var e=a.a[n];b=h!=b&&""!=b?c.d(b,true):c.d(e.pathname+unescape(e.search),true);return b};a.$c=
+function(b){if(a.Q()){var e="";if(a.l!=h&&a.l.N().length>0)e+="&utme="+c.d(a.l.N());e+=a.Ia(b);x.O(e,a.H,a.a,a.f)}};a.qc=function(){var b=new c.Y(a.a,g);return b.Ha(a.f)?b.Wc():h};a._getLinkerUrl=function(b,e){var j=f(b,"#"),t=b,v=a.qc();if(v)if(e&&1>=j[w])t+="#"+v;else if(!e||1>=j[w])if(1>=j[w])t+=(q(b,"?")?"&":"?")+v;else t=j[0]+(q(b,"?")?"&":"?")+v+"#"+j[1];return t};a.Zb=function(){var b;if(a.A&&a.A[w]>=10&&!q(a.A,"=")){a.u.Uc(a.A);a.u.cd();c._gasoDomain=g.g;c._gasoCPath=g.p;b=a.a.createElement("script");
+b.type="text/javascript";b.id="_gasojs";b.src="https://www.google.com/analytics/reporting/overlay_js?gaso="+a.A+"&"+c.wa();a.a.getElementsByTagName("head")[0].appendChild(b)}};a.Jc=function(){var b=a.a[c.m],e=a.ja,j=a.u,t=a.f+"",v=a.e,y=v?v.gaGlobal:h,E,F=q(b,c.r+t+"."),I=q(b,c.W+t),G=q(b,c.ma+t),C,D=[],H="",K=false,J;b=o(b)?"":b;if(g.I){E=c.Db(a.a[n]);if(g.pa&&!o(E))H=E+"&";H+=a.a[n].search;if(!o(H)&&q(H,c.r)){j.Rc(H);if(!j.Jb())j.pc();C=j.ya()}r(j.Ba,j.Wb,j.fc);r(j.Aa,j.Na,j.Qa)}if(!o(C))if(o(j.K())||
+o(j.za())){C=p(H,"&",e);a.M=true}else{D=f(j.K(),".");t=D[0]}else if(F)if(!I||!G){C=p(b,";",e);a.M=true}else{C=s(b,c.r+t+".",";");D=f(s(b,c.W+t,";"),".")}else{C=[t,c.Gc(),e,e,e,1].join(".");a.M=true;K=true}C=f(C,".");if(v&&y&&y.dh==t){C[4]=y.sid?y.sid:C[4];if(K){C[3]=y.sid?y.sid:C[4];if(y.vid){J=f(y.vid,".");C[1]=J[0];C[2]=J[1]}}}j.Ub(C.join("."));D[0]=t;D[1]=D[1]?D[1]:0;D[2]=undefined!=D[2]?D[2]:g.Yc;D[3]=D[3]?D[3]:C[4];j.La(D.join("."));j.Vb(t);if(!o(j.Hc()))j.Ma(j.t());j.dc();j.Pa();j.ec()};a.Lc=
+function(){x=new c.jc(g)};a._initData=function(){var b;if(!z){a.Lc();a.f=a.Bc();a.u=new c.Y(a.a,g)}if(u())a.Jc();if(!z){if(u()){a.va=a.tc(a.Ac(),a.a.domain);if(g.sa){a.aa=new c.gc(g.ua);a.aa.xc()}if(g.qa){b=new c.n(a.f,a.a,a.va,a.ja,g);a.rb=b.yc(a.u,a.M)}}a.l=new c.Z;a.Ab=new c.Z;z=true}if(!c.Hb)a.Mc()};a._visitCode=function(){a._initData();var b=s(a.a[c.m],c.r+a.f+".",";"),e=f(b,".");return e[w]<4?"":e[1]};a._cookiePathCopy=function(b){a._initData();if(a.u)a.u.bd(a.f,b)};a.Mc=function(){var b=a.a[n].hash,
+e;e=b&&""!=b&&0==k(b,"#gaso=")?s(b,"gaso=","&"):s(a.a[c.m],c.Sa,";");if(e[w]>=10){a.A=e;if(a.e.addEventListener)a.e.addEventListener("load",a.Zb,false);else a.e.attachEvent("onload",a.Zb)}c.Hb=true};a.Q=function(){return a._visitCode()%10000<g.ha*100};a.Vc=function(){var b,e,j=a.a.links;if(!g.Kb){var t=a.a.domain;if("www."==l(t,0,4))t=l(t,4);g.B.push("."+t)}for(b=0;b<j[w]&&(g.Ga==-1||b<g.Ga);b++){e=j[b];if(i(e.host))if(!e.gatcOnclick){e.gatcOnclick=e.onclick?e.onclick:a.Qc;e.onclick=function(v){var y=
+!this.target||this.target=="_self"||this.target=="_top"||this.target=="_parent";y=y&&!a.oc(v);a.ad(v,this,y);return y?false:(this.gatcOnclick?this.gatcOnclick(v):true)}}}};a.Qc=function(){};a._trackPageview=function(b){if(u()){a._initData();if(g.B)a.Vc();a.$c(b);a.M=false}};a._trackTrans=function(){var b=a.f,e=[],j,t,v,y;a._initData();if(a.j&&a.Q()){for(j=0;j<a.j.la[w];j++){t=a.j.la[j];c.h(e,t.S());for(v=0;v<t.ca[w];v++)c.h(e,t.ca[v].S())}for(y=0;y<e[w];y++)x.O(e[y],a.H,a.a,b,true)}};a._setTrans=
+function(){var b=a.a,e,j,t,v,y=b.getElementById?b.getElementById("utmtrans"):(b.utmform&&b.utmform.utmtrans?b.utmform.utmtrans:h);a._initData();if(y&&y.value){a.j=new c.i;v=f(y.value,"UTM:");g.G=!g.G||""==g.G?"|":g.G;for(e=0;e<v[w];e++){v[e]=m(v[e]);j=f(v[e],g.G);for(t=0;t<j[w];t++)j[t]=m(j[t]);if("T"==j[0])a._addTrans(j[1],j[2],j[3],j[4],j[5],j[6],j[7],j[8]);else if("I"==j[0])a._addItem(j[1],j[2],j[3],j[4],j[5],j[6])}}};a._addTrans=function(b,e,j,t,v,y,E,F){a.j=a.j?a.j:new c.i;return a.j.nb(b,e,
+j,t,v,y,E,F)};a._addItem=function(b,e,j,t,v,y){var E;a.j=a.j?a.j:new c.i;E=a.j.xa(b);if(!E)E=a._addTrans(b,"","","","","","","");E.mb(e,j,t,v,y)};a._setVar=function(b){if(b&&""!=b&&A()){a._initData();var e=new c.Y(a.a,g),j=a.f;e.Na(j+"."+c.d(b));e.Qa();if(a.Q())x.O("&utmt=var",a.H,a.a,a.f)}};a._link=function(b,e){if(g.I&&b){a._initData();a.a[n].href=a._getLinkerUrl(b,e)}};a._linkByPost=function(b,e){if(g.I&&b&&b.action){a._initData();b.action=a._getLinkerUrl(b.action,e)}};a._setXKey=function(b,e,
+j){a.l._setKey(b,e,j)};a._setXValue=function(b,e,j){a.l._setValue(b,e,j)};a._getXKey=function(b,e){return a.l._getKey(b,e)};a._getXValue=function(b,e){return a.l.getValue(b,e)};a._clearXKey=function(b){a.l._clearKey(b)};a._clearXValue=function(b){a.l._clearValue(b)};a._createXObj=function(){a._initData();return new c.Z};a._sendXEvent=function(b){var e="";a._initData();if(a.Q()){e+="&utmt=event&utme="+c.d(a.l.Sc(b))+a.Ia();x.O(e,a.H,a.a,a.f,false,true)}};a._createEventTracker=function(b){a._initData();
+return new c.ic(b,a)};a._trackEvent=function(b,e,j,t){var v=true,y=a.Ab;if(h!=b&&h!=e&&""!=b&&""!=e){y._clearKey(5);y._clearValue(5);v=y._setKey(5,1,b)?v:false;v=y._setKey(5,2,e)?v:false;v=h==j||y._setKey(5,3,j)?v:false;v=h==t||y._setValue(5,1,t)?v:false;if(v)a._sendXEvent(y)}else v=false;return v};a.ad=function(b,e,j){a._initData();if(a.Q()){var t=new c.Z;t._setKey(6,1,e.href);var v=j?function(){a.rc(b,e)}:undefined;x.O("&utmt=event&utme="+c.d(t.N())+a.Ia(),a.H,a.a,a.f,false,true,v)}};a.rc=function(b,
+e){if(!b)b=a.e.event;var j=true;if(e.gatcOnclick)j=e.gatcOnclick(b);if(j||typeof j=="undefined")if(!e.target||e.target=="_self")a.e.location=e.href;else if(e.target=="_top")a.e.top.document.location=e.href;else if(e.target=="_parent")a.e.parent.document.location=e.href};a.oc=function(b){if(!b)b=a.e.event;var e=b.shiftKey||b.ctrlKey||b.altKey;if(!e)if(b.modifiers&&a.e.Event)e=b.modifiers&a.e.Event.CONTROL_MASK||b.modifiers&a.e.Event.SHIFT_MASK||b.modifiers&a.e.Event.ALT_MASK;return e};a._setDomainName=
+function(b){g.g=b};a.dd=function(){return g.g};a._addOrganic=function(b,e){c.h(g.fa,new c.cb(b,e))};a._clearOrganic=function(){g.fa=[]};a.hd=function(){return g.fa};a._addIgnoredOrganic=function(b){c.h(g.ea,b)};a._clearIgnoredOrganic=function(){g.ea=[]};a.ed=function(){return g.ea};a._addIgnoredRef=function(b){c.h(g.ga,b)};a._clearIgnoredRef=function(){g.ga=[]};a.fd=function(){return g.ga};a._setAllowHash=function(b){g.pb=b?1:0};a._setCampaignTrack=function(b){g.qa=b?1:0};a._setClientInfo=function(b){g.sa=
+b?1:0};a._getClientInfo=function(){return g.sa};a._setCookiePath=function(b){g.p=b};a._setTransactionDelim=function(b){g.G=b};a._setCookieTimeout=function(b){g.wb=b};a._setDetectFlash=function(b){g.ua=b?1:0};a._getDetectFlash=function(){return g.ua};a._setDetectTitle=function(b){g.ta=b?1:0};a._getDetectTitle=function(){return g.ta};a._setLocalGifPath=function(b){g.Da=b};a._getLocalGifPath=function(){return g.Da};a._setLocalServerMode=function(){g.D=0};a._setRemoteServerMode=function(){g.D=1};a._setLocalRemoteServerMode=
+function(){g.D=2};a.gd=function(){return g.D};a._getServiceMode=function(){return g.D};a._setSampleRate=function(b){g.ha=b};a._setSessionTimeout=function(b){g.Tb=b};a._setAllowLinker=function(b){g.I=b?1:0};a._setAllowAnchor=function(b){g.pa=b?1:0};a._setCampNameKey=function(b){g.db=b};a._setCampContentKey=function(b){g.eb=b};a._setCampIdKey=function(b){g.fb=b};a._setCampMediumKey=function(b){g.gb=b};a._setCampNOKey=function(b){g.hb=b};a._setCampSourceKey=function(b){g.ib=b};a._setCampTermKey=function(b){g.jb=
+b};a._setCampCIdKey=function(b){g.kb=b};a._getAccount=function(){return a.H};a._getVersion=function(){return _gat.lb};a.kd=function(b){g.B=[];if(b)g.B=b};a.md=function(b){g.Kb=b};a.ld=function(b){g.Ga=b};a._setReferrerOverride=function(b){a.yb=b};a.Ac=function(){return a.yb}};_gat._getTracker=function(d){var a=new _gat.kc(d);return a};
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/header.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/header.png
new file mode 100644
index 0000000..977712b
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/header.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/hoverIntent.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/hoverIntent.js
new file mode 100644
index 0000000..91da57b
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/hoverIntent.js
@@ -0,0 +1,84 @@
+(function($){
+ /* hoverIntent by Brian Cherne */
+ $.fn.hoverIntent = function(f,g) {
+ // default configuration options
+ var cfg = {
+ sensitivity: 7,
+ interval: 100,
+ timeout: 0
+ };
+ // override configuration options with user supplied object
+ cfg = $.extend(cfg, g ? { over: f, out: g } : f );
+
+ // instantiate variables
+ // cX, cY = current X and Y position of mouse, updated by mousemove event
+ // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
+ var cX, cY, pX, pY;
+
+ // A private function for getting mouse position
+ var track = function(ev) {
+ cX = ev.pageX;
+ cY = ev.pageY;
+ };
+
+ // A private function for comparing current and previous mouse position
+ var compare = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ // compare mouse positions to see if they've crossed the threshold
+ if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
+ $(ob).unbind("mousemove",track);
+ // set hoverIntent state to true (so mouseOut can be called)
+ ob.hoverIntent_s = 1;
+ return cfg.over.apply(ob,[ev]);
+ } else {
+ // set previous coordinates for next time
+ pX = cX; pY = cY;
+ // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+ ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
+ }
+ };
+
+ // A private function for delaying the mouseOut function
+ var delay = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ ob.hoverIntent_s = 0;
+ return cfg.out.apply(ob,[ev]);
+ };
+
+ // A private function for handling mouse 'hovering'
+ var handleHover = function(e) {
+ // next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
+ var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+ while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
+ if ( p == this ) { return false; }
+
+ // copy objects to be passed into t (required for event object to be passed in IE)
+ var ev = jQuery.extend({},e);
+ var ob = this;
+
+ // cancel hoverIntent timer if it exists
+ if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
+
+ // else e.type == "onmouseover"
+ if (e.type == "mouseover") {
+ // set "previous" X and Y position based on initial entry point
+ pX = ev.pageX; pY = ev.pageY;
+ // update "current" X and Y position based on mousemove
+ $(ob).bind("mousemove",track);
+ // start polling interval (self-calling timeout) to compare mouse coordinates over time
+ if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
+
+ // else e.type == "onmouseout"
+ } else {
+ // unbind expensive mousemove event
+ $(ob).unbind("mousemove",track);
+ // if hoverIntent state is true, then call the mouseOut function after the specified delay
+ if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
+ }
+ };
+
+ // bind the function to the two event listeners
+ return this.mouseover(handleHover).mouseout(handleHover);
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/ipojo.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/ipojo.png
new file mode 100644
index 0000000..8f8d89e
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/ipojo.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/jquery-1.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/jquery-1.js
new file mode 100644
index 0000000..b1ae21d
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/jquery-1.js
@@ -0,0 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/logo.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/logo.png
new file mode 100644
index 0000000..dccbddc
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/logo.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushCSharp.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushCSharp.js
new file mode 100644
index 0000000..f45540f
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushCSharp.js
@@ -0,0 +1,30 @@
+dp.sh.Brushes.CSharp = function()
+{
+ var keywords = 'abstract as base bool break byte case catch char checked class const ' +
+ 'continue decimal default delegate do double else enum event explicit ' +
+ 'extern false finally fixed float for foreach get goto if implicit in int ' +
+ 'interface internal is lock long namespace new null object operator out ' +
+ 'override params private protected public readonly ref return sbyte sealed set ' +
+ 'short sizeof stackalloc static string struct switch this throw true try ' +
+ 'typeof uint ulong unchecked unsafe ushort using virtual void while';
+
+ this.regexList = [
+ // There's a slight problem with matching single line comments and figuring out
+ // a difference between // and ///. Using lookahead and lookbehind solves the
+ // problem, unfortunately JavaScript doesn't support lookbehind. So I'm at a
+ // loss how to translate that regular expression to JavaScript compatible one.
+// { regex: new RegExp('(?<!/)//(?!/).*$|(?<!/)////(?!/).*$|/\\*[^\\*]*(.)*?\\*/', 'gm'), css: 'comment' }, // one line comments starting with anything BUT '///' and multiline comments
+// { regex: new RegExp('(?<!/)///(?!/).*$', 'gm'), css: 'comments' }, // XML comments starting with ///
+
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // c# keyword
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.CSharp.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.CSharp.Aliases = ['c#', 'c-sharp', 'csharp'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushDelphi.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushDelphi.js
new file mode 100644
index 0000000..efeb2f7
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushDelphi.js
@@ -0,0 +1,31 @@
+/* Delphi brush is contributed by Eddie Shipman */
+dp.sh.Brushes.Delphi = function()
+{
+ var keywords = 'abs addr and ansichar ansistring array as asm begin boolean byte cardinal ' +
+ 'case char class comp const constructor currency destructor div do double ' +
+ 'downto else end except exports extended false file finalization finally ' +
+ 'for function goto if implementation in inherited int64 initialization ' +
+ 'integer interface is label library longint longword mod nil not object ' +
+ 'of on or packed pansichar pansistring pchar pcurrency pdatetime pextended ' +
+ 'pint64 pointer private procedure program property pshortstring pstring ' +
+ 'pvariant pwidechar pwidestring protected public published raise real real48 ' +
+ 'record repeat set shl shortint shortstring shr single smallint string then ' +
+ 'threadvar to true try type unit until uses val var varirnt while widechar ' +
+ 'widestring with word write writeln xor';
+
+ this.regexList = [
+ { regex: new RegExp('\\(\\*[\\s\\S]*?\\*\\)', 'gm'), css: 'comment' }, // multiline comments (* *)
+ { regex: new RegExp('{(?!\\$)[\\s\\S]*?}', 'gm'), css: 'comment' }, // multiline comments { }
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('\\{\\$[a-zA-Z]+ .+\\}', 'g'), css: 'directive' }, // Compiler Directives and Region tags
+ { regex: new RegExp('\\b[\\d\\.]+\\b', 'g'), css: 'number' }, // numbers 12345
+ { regex: new RegExp('\\$[a-zA-Z0-9]+\\b', 'g'), css: 'number' }, // numbers $F5D3
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-delphi';
+}
+
+dp.sh.Brushes.Delphi.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Delphi.Aliases = ['delphi', 'pascal'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJScript.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJScript.js
new file mode 100644
index 0000000..4ef8b9b
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJScript.js
@@ -0,0 +1,22 @@
+dp.sh.Brushes.JScript = function()
+{
+ var keywords = 'abstract boolean break byte case catch char class const continue debugger ' +
+ 'default delete do double else enum export extends false final finally float ' +
+ 'for function goto if implements import in instanceof int interface long native ' +
+ 'new null package private protected public return short static super switch ' +
+ 'synchronized this throw throws transient true try typeof var void volatile while with';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keywords
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.JScript.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.JScript.Aliases = ['js', 'jscript', 'javascript'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJava.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJava.js
new file mode 100644
index 0000000..b75d334
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushJava.js
@@ -0,0 +1,22 @@
+dp.sh.Brushes.Java = function()
+{
+ var keywords = 'abstract assert boolean break byte case catch char class const' +
+ 'continue default do double else enum extends false final finally float' +
+ 'for goto if implements import instanceof inst interface log native' +
+ 'new null package private protected public return short static strictfp super' +
+ 'switch synchronized this throw throws transient true try void volatile while';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('^\\s*@.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like @see
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keywords
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.Java.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Java.Aliases = ['java'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPhp.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPhp.js
new file mode 100644
index 0000000..57c85d4
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPhp.js
@@ -0,0 +1,23 @@
+dp.sh.Brushes.Php = function()
+{
+ var keywords = 'and or xor __FILE__ __LINE__ array as break case ' +
+ 'cfunction class const continue declare default die do echo else ' +
+ 'elseif empty enddeclare endfor endforeach endif endswitch endwhile eval exit ' +
+ 'extends for foreach function global if include include_once isset list ' +
+ 'new old_function print require require_once return static switch unset use ' +
+ 'var while __FUNCTION__ __CLASS__';
+
+ this.regexList = [
+ { regex: new RegExp('//.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'), css: 'comment' }, // multiline comments
+ { regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'), css: 'string' }, // double quoted strings
+ { regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'), css: 'string' }, // single quoted strings
+ { regex: new RegExp('\\$\\w+', 'g'), css: 'vars' }, // variables
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.Php.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Php.Aliases = ['php'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPython.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPython.js
new file mode 100644
index 0000000..1d61339
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushPython.js
@@ -0,0 +1,71 @@
+/* Python 2.3 syntax contributed by Gheorghe Milas */
+dp.sh.Brushes.Python = function()
+{
+ var keywords = 'and assert break class continue def del elif else except exec ' +
+ 'finally for from global if import in is lambda not or object pass print ' +
+ 'raise return try yield while';
+
+ var builtins = 'self __builtin__ __dict__ __future__ __methods__ __members__ __author__ __email__ __version__' +
+ '__class__ __bases__ __import__ __main__ __name__ __doc__ __self__ __debug__ __slots__ ' +
+ 'abs append apply basestring bool buffer callable chr classmethod clear close cmp coerce compile complex ' +
+ 'conjugate copy count delattr dict dir divmod enumerate Ellipsis eval execfile extend False file fileno filter float flush ' +
+ 'get getattr globals has_key hasarttr hash hex id index input insert int intern isatty isinstance isubclass ' +
+ 'items iter keys len list locals long map max min mode oct open ord pop pow property range ' +
+ 'raw_input read readline readlines reduce reload remove repr reverse round seek setattr slice sum ' +
+ 'staticmethod str super tell True truncate tuple type unichr unicode update values write writelines xrange zip';
+
+ var magicmethods = '__abs__ __add__ __and__ __call__ __cmp__ __coerce__ __complex__ __concat__ __contains__ __del__ __delattr__ __delitem__ ' +
+ '__delslice__ __div__ __divmod__ __float__ __getattr__ __getitem__ __getslice__ __hash__ __hex__ __eq__ __le__ __lt__ __gt__ __ge__ ' +
+ '__iadd__ __isub__ __imod__ __idiv__ __ipow__ __iand__ __ior__ __ixor__ __ilshift__ __irshift__ ' +
+ '__invert__ __init__ __int__ __inv__ __iter__ __len__ __long__ __lshift__ __mod__ __mul__ __new__ __neg__ __nonzero__ __oct__ __or__ ' +
+ '__pos__ __pow__ __radd__ __rand__ __rcmp__ __rdiv__ __rdivmod__ __repeat__ __repr__ __rlshift__ __rmod__ __rmul__ ' +
+ '__ror__ __rpow__ __rrshift__ __rshift__ __rsub__ __rxor__ __setattr__ __setitem__ __setslice__ __str__ __sub__ __xor__';
+
+ var exceptions = 'Exception StandardError ArithmeticError LookupError EnvironmentError AssertionError AttributeError EOFError ' +
+ 'FutureWarning IndentationError OverflowWarning PendingDeprecationWarning ReferenceError RuntimeWarning ' +
+ 'SyntaxWarning TabError UnicodeDecodeError UnicodeEncodeError UnicodeTranslateError UserWarning Warning ' +
+ 'IOError ImportError IndexError KeyError KeyboardInterrupt MemoryError NameError NotImplementedError OSError ' +
+ 'RuntimeError StopIteration SyntaxError SystemError SystemExit TypeError UnboundLocalError UnicodeError ValueError ' +
+ 'FloatingPointError OverflowError WindowsError ZeroDivisionError';
+
+ var types = 'NoneType TypeType IntType LongType FloatType ComplexType StringType UnicodeType BufferType TupleType ListType ' +
+ 'DictType FunctionType LambdaType CodeType ClassType UnboundMethodType InstanceType MethodType BuiltinFunctionType BuiltinMethodType ' +
+ 'ModuleType FileType XRangeType TracebackType FrameType SliceType EllipsisType';
+
+ var commonlibs = 'anydbm array asynchat asyncore AST base64 binascii binhex bisect bsddb buildtools bz2 ' +
+ 'BaseHTTPServer Bastion calendar cgi cmath cmd codecs codeop commands compiler copy copy_reg ' +
+ 'cPickle crypt cStringIO csv curses Carbon CGIHTTPServer ConfigParser Cookie datetime dbhash ' +
+ 'dbm difflib dircache distutils doctest DocXMLRPCServer email encodings errno exceptions fcntl ' +
+ 'filecmp fileinput ftplib gc gdbm getopt getpass glob gopherlib gzip heapq htmlentitydefs ' +
+ 'htmllib httplib HTMLParser imageop imaplib imgfile imghdr imp inspect itertools jpeg keyword ' +
+ 'linecache locale logging mailbox mailcap marshal math md5 mhlib mimetools mimetypes mimify mmap ' +
+ 'mpz multifile mutex MimeWriter netrc new nis nntplib nsremote operator optparse os parser pickle pipes ' +
+ 'popen2 poplib posix posixfile pprint preferences profile pstats pwd pydoc pythonprefs quietconsole ' +
+ 'quopri Queue random re readline resource rexec rfc822 rgbimg sched select sets sgmllib sha shelve shutil ' +
+ 'signal site smtplib socket stat statcache string struct symbol sys syslog SimpleHTTPServer ' +
+ 'SimpleXMLRPCServer SocketServer StringIO tabnanny tarfile telnetlib tempfile termios textwrap ' +
+ 'thread threading time timeit token tokenize traceback tty types Tkinter unicodedata unittest ' +
+ 'urllib urllib2 urlparse user UserDict UserList UserString warnings weakref webbrowser whichdb ' +
+ 'xml xmllib xmlrpclib xreadlines zipfile zlib';
+
+ this.regexList = [
+ { regex: new RegExp('#.*$', 'gm'), css: 'comment' }, // comments
+ { regex: new RegExp('^\\s*"""(.|\n)*?"""\\s*$', 'gm'), css: 'docstring' }, // documentation string "
+ { regex: new RegExp('^\\s*\'\'\'(.|\n)*?\'\'\'\\s*$', 'gm'), css: 'docstring' }, // documentation string '
+ { regex: new RegExp('"""(.|\n)*?"""', 'g'), css: 'string' }, // multi-line strings "
+ { regex: new RegExp('\'\'\'(.|\n)*?\'\'\'', 'g'), css: 'string' }, // multi-line strings '
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings "
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings '
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' }, // keywords
+ { regex: new RegExp(this.GetKeywords(builtins), 'gm'), css: 'builtins' }, // builtin objects, functions, methods, magic attributes
+ { regex: new RegExp(this.GetKeywords(magicmethods), 'gm'), css: 'magicmethods' }, // special methods
+ { regex: new RegExp(this.GetKeywords(exceptions), 'gm'), css: 'exceptions' }, // standard exception classes
+ { regex: new RegExp(this.GetKeywords(types), 'gm'), css: 'types' }, // types from types.py
+ { regex: new RegExp(this.GetKeywords(commonlibs), 'gm'), css: 'commonlibs' } // common standard library modules
+ ];
+
+ this.CssClass = 'dp-py';
+}
+
+dp.sh.Brushes.Python.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Python.Aliases = ['py', 'python'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushShell.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushShell.js
new file mode 100644
index 0000000..5e7c2ef
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushShell.js
@@ -0,0 +1,27 @@
+dp.sh.Brushes.Shell = function()
+{
+ var keywords = 'alias array autor base basename break ' +
+ 'cat catv cd chdir cmpv continue conv copy ' +
+ 'crc ctime cut dirname echo env eval exec else if elif then ' +
+ 'export expr extern false fmode fork fprint ' +
+ 'fsize fstat fullname global goend goto grep ifdef ' +
+ 'ifset ifenv inv kill line link list ' +
+ 'local localset mkdirs mktemp move mtime nop print ' +
+ 'prints pwd read readc readl readonly rel ' +
+ 'remove return seek set shift sleep sortl ' +
+ 'static stime sum system systime tee test times ' +
+ 'tr trap true type typeset tz umask unalias ' +
+ 'unexport unset unsetenv ver wait wc whence ' +
+ 'sane exit prompt let';
+
+
+ this.regexList = [
+ { regex: new RegExp('#.*$', 'gm'), css: 'comment' }, // one line
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-shell';
+}
+
+dp.sh.Brushes.Shell.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Shell.Aliases = ['shell'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushSql.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushSql.js
new file mode 100644
index 0000000..4855d0c
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushSql.js
@@ -0,0 +1,40 @@
+dp.sh.Brushes.Sql = function()
+{
+ var funcs = 'abs avg case cast coalesce convert count current_timestamp ' +
+ 'current_user day isnull left lower month nullif replace right ' +
+ 'session_user space substring sum system_user upper user year';
+
+ var keywords = 'absolute action add after alter as asc at authorization begin bigint ' +
+ 'binary bit by cascade char character check checkpoint close collate ' +
+ 'column commit committed connect connection constraint contains continue ' +
+ 'create cube current current_date current_time cursor database date ' +
+ 'deallocate dec decimal declare default delete desc distinct double drop ' +
+ 'dynamic else end end-exec escape except exec execute false fetch first ' +
+ 'float for force foreign forward free from full function global goto grant ' +
+ 'group grouping having hour ignore index inner insensitive insert instead ' +
+ 'int integer intersect into is isolation key last level load local max min ' +
+ 'minute modify move name national nchar next no numeric of off on only ' +
+ 'open option order out output partial password precision prepare primary ' +
+ 'prior privileges procedure public read real references relative repeatable ' +
+ 'restrict return returns revoke rollback rollup rows rule schema scroll ' +
+ 'second section select sequence serializable set size smallint static ' +
+ 'statistics table temp temporary then time timestamp to top transaction ' +
+ 'translation trigger true truncate uncommitted union unique update values ' +
+ 'varchar varying view when where with work';
+
+ var operators = 'all and any between cross in join like not null or outer some';
+
+ this.regexList = [
+ { regex: new RegExp('--(.*)$', 'gm'), css: 'comment' }, // one line and multiline comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('\'(?:\\.|[^\\\'\'])*\'', 'g'), css: 'string' }, // strings
+ { regex: new RegExp(this.GetKeywords(funcs), 'gmi'), css: 'func' }, // functions
+ { regex: new RegExp(this.GetKeywords(operators), 'gmi'), css: 'op' }, // operators and such
+ { regex: new RegExp(this.GetKeywords(keywords), 'gmi'), css: 'keyword' } // keyword
+ ];
+
+ this.CssClass = 'dp-sql';
+}
+
+dp.sh.Brushes.Sql.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Sql.Aliases = ['sql'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushVb.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushVb.js
new file mode 100644
index 0000000..431e16f
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushVb.js
@@ -0,0 +1,29 @@
+dp.sh.Brushes.Vb = function()
+{
+ var keywords = 'AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto ' +
+ 'Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate ' +
+ 'CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType ' +
+ 'Date Decimal Declare Default Delegate Dim DirectCast Do Double Each ' +
+ 'Else ElseIf End Enum Erase Error Event Exit False Finally For Friend ' +
+ 'Function Get GetType GoSub GoTo Handles If Implements Imports In ' +
+ 'Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module ' +
+ 'MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing ' +
+ 'NotInheritable NotOverridable Object On Option Optional Or OrElse ' +
+ 'Overloads Overridable Overrides ParamArray Preserve Private Property ' +
+ 'Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume ' +
+ 'Return Select Set Shadows Shared Short Single Static Step Stop String ' +
+ 'Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until ' +
+ 'Variant When While With WithEvents WriteOnly Xor';
+
+ this.regexList = [
+ { regex: new RegExp('\'.*$', 'gm'), css: 'comment' }, // one line comments
+ { regex: new RegExp('"(?:\\.|[^\\""])*"', 'g'), css: 'string' }, // strings
+ { regex: new RegExp('^\\s*#.*', 'gm'), css: 'preprocessor' }, // preprocessor tags like #region and #endregion
+ { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' } // c# keyword
+ ];
+
+ this.CssClass = 'dp-vb';
+}
+
+dp.sh.Brushes.Vb.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Vb.Aliases = ['vb', 'vb.net'];
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushXml.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushXml.js
new file mode 100644
index 0000000..941ad57
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shBrushXml.js
@@ -0,0 +1,61 @@
+dp.sh.Brushes.Xml = function()
+{
+ this.CssClass = 'dp-xml';
+}
+
+dp.sh.Brushes.Xml.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Xml.Aliases = ['xml', 'xhtml', 'xslt', 'html', 'xhtml'];
+
+dp.sh.Brushes.Xml.prototype.ProcessRegexList = function()
+{
+ function push(array, value)
+ {
+ array[array.length] = value;
+ }
+
+ /* If only there was a way to get index of a group within a match, the whole XML
+ could be matched with the expression looking something like that:
+
+ (<!\[CDATA\[\s*.*\s*\]\]>)
+ | (<!--\s*.*\s*?-->)
+ | (<)*(\w+)*\s*(\w+)\s*=\s*(".*?"|'.*?'|\w+)(/*>)*
+ | (</?)(.*?)(/?>)
+ */
+ var index = 0;
+ var match = null;
+ var regex = null;
+
+ // Match CDATA in the following format <![ ... [ ... ]]>
+ // <\!\[[\w\s]*?\[(.|\s)*?\]\]>
+ this.GetMatches(new RegExp('<\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\]>', 'gm'), 'cdata');
+
+ // Match comments
+ // <!--\s*.*\s*?-->
+ this.GetMatches(new RegExp('<!--\\s*.*\\s*?-->', 'gm'), 'comments');
+
+ // Match attributes and their values
+ // (\w+)\s*=\s*(".*?"|\'.*?\'|\w+)*
+ regex = new RegExp('([\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\'|\\w+)*', 'gm');
+ while((match = regex.exec(this.code)) != null)
+ {
+ push(this.matches, new dp.sh.Match(match[1], match.index, 'attribute'));
+
+ // if xml is invalid and attribute has no property value, ignore it
+ if(match[2] != undefined)
+ {
+ push(this.matches, new dp.sh.Match(match[2], match.index + match[0].indexOf(match[2]), 'attribute-value'));
+ }
+ }
+
+ // Match opening and closing tag brackets
+ // </*\?*(?!\!)|/*\?*>
+ this.GetMatches(new RegExp('</*\\?*(?!\\!)|/*\\?*>', 'gm'), 'tag');
+
+ // Match tag names
+ // </*\?*\s*(\w+)
+ regex = new RegExp('</*\\?*\\s*([\\w-\.]+)', 'gm');
+ while((match = regex.exec(this.code)) != null)
+ {
+ push(this.matches, new dp.sh.Match(match[1], match.index + match[0].indexOf(match[1]), 'tag-name'));
+ }
+}
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/shCore.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shCore.js
new file mode 100644
index 0000000..b27395e
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/shCore.js
@@ -0,0 +1,622 @@
+/**
+ * Code Syntax Highlighter.
+ * Version 1.3.0
+ * Copyright (C) 2004 Alex Gorbatchev.
+ * http://www.dreamprojections.com/syntaxhighlighter/
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//
+// create namespaces
+//
+var dp = {
+ sh : // dp.sh
+ {
+ Utils : {}, // dp.sh.Utils
+ Brushes : {}, // dp.sh.Brushes
+ Strings : {},
+ Version : '1.3.0'
+ }
+};
+
+dp.sh.Strings = {
+ AboutDialog : '<html><head><title>About...</title></head><body class="dp-about"><table cellspacing="0"><tr><td class="copy"><p class="title">dp.SyntaxHighlighter</div><div class="para">Version: {V}</p><p><a href="http://www.dreamprojections.com/syntaxhighlighter/?ref=about" target="_blank">http://www.dreamprojections.com/SyntaxHighlighter</a></p>©2004-2005 Alex Gorbatchev. All right reserved.</td></tr><tr><td class="footer"><input type="button" class="close" value="OK" onClick="window.close()"/></td></tr></table></body></html>',
+
+ // tools
+ ExpandCode : '+ expand code',
+ ViewPlain : 'view plain',
+ Print : 'print',
+ CopyToClipboard : 'copy to clipboard',
+ About : '?',
+
+ CopiedToClipboard : 'The code is in your clipboard now.'
+};
+
+dp.SyntaxHighlighter = dp.sh;
+
+//
+// Dialog and toolbar functions
+//
+
+dp.sh.Utils.Expand = function(sender)
+{
+ var table = sender;
+ var span = sender;
+
+ // find the span in which the text label and pipe contained so we can hide it
+ while(span != null && span.tagName != 'SPAN')
+ span = span.parentNode;
+
+ // find the table
+ while(table != null && table.tagName != 'TABLE')
+ table = table.parentNode;
+
+ // remove the 'expand code' button
+ span.parentNode.removeChild(span);
+
+ table.tBodies[0].className = 'show';
+ table.parentNode.style.height = '100%'; // containing div isn't getting updated properly when the TBODY is shown
+}
+
+// opens a new windows and puts the original unformatted source code inside.
+dp.sh.Utils.ViewSource = function(sender)
+{
+ var code = sender.parentNode.originalCode;
+ var wnd = window.open('', '_blank', 'width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=1');
+
+ code = code.replace(/</g, '<');
+
+ wnd.document.write('<pre>' + code + '</pre>');
+ wnd.document.close();
+}
+
+// copies the original source code in to the clipboard (IE only)
+dp.sh.Utils.ToClipboard = function(sender)
+{
+ var code = sender.parentNode.originalCode;
+
+ // This works only for IE. There's a way to make it work with Mozilla as well,
+ // but it requires security settings changed on the client, which isn't by
+ // default, so 99% of users won't have it working anyways.
+ if(window.clipboardData)
+ {
+ window.clipboardData.setData('text', code);
+
+ alert(dp.sh.Strings.CopiedToClipboard);
+ }
+}
+
+// creates an invisible iframe, puts the original source code inside and prints it
+dp.sh.Utils.PrintSource = function(sender)
+{
+ var td = sender.parentNode;
+ var code = td.processedCode;
+ var iframe = document.createElement('IFRAME');
+ var doc = null;
+ var wnd =
+
+ // this hides the iframe
+ iframe.style.cssText = 'position:absolute; width:0px; height:0px; left:-5px; top:-5px;';
+
+ td.appendChild(iframe);
+
+ doc = iframe.contentWindow.document;
+ code = code.replace(/</g, '<');
+
+ doc.open();
+ doc.write('<pre>' + code + '</pre>');
+ doc.close();
+
+ iframe.contentWindow.focus();
+ iframe.contentWindow.print();
+
+ td.removeChild(iframe);
+}
+
+dp.sh.Utils.About = function()
+{
+ var wnd = window.open('', '_blank', 'dialog,width=320,height=150,scrollbars=0');
+ var doc = wnd.document;
+
+ var styles = document.getElementsByTagName('style');
+ var links = document.getElementsByTagName('link');
+
+ doc.write(dp.sh.Strings.AboutDialog.replace('{V}', dp.sh.Version));
+
+ // copy over ALL the styles from the parent page
+ for(var i = 0; i < styles.length; i++)
+ doc.write('<style>' + styles[i].innerHTML + '</style>');
+
+ for(var i = 0; i < links.length; i++)
+ if(links[i].rel.toLowerCase() == 'stylesheet')
+ doc.write('<link type="text/css" rel="stylesheet" href="' + links[i].href + '"></link>');
+
+ doc.close();
+ wnd.focus();
+}
+
+//
+// Match object
+//
+dp.sh.Match = function(value, index, css)
+{
+ this.value = value;
+ this.index = index;
+ this.length = value.length;
+ this.css = css;
+}
+
+//
+// Highlighter object
+//
+dp.sh.Highlighter = function()
+{
+ this.addGutter = true;
+ this.addControls = true;
+ this.collapse = false;
+ this.tabsToSpaces = true;
+}
+
+// static callback for the match sorting
+dp.sh.Highlighter.SortCallback = function(m1, m2)
+{
+ // sort matches by index first
+ if(m1.index < m2.index)
+ return -1;
+ else if(m1.index > m2.index)
+ return 1;
+ else
+ {
+ // if index is the same, sort by length
+ if(m1.length < m2.length)
+ return -1;
+ else if(m1.length > m2.length)
+ return 1;
+ }
+ return 0;
+}
+
+// gets a list of all matches for a given regular expression
+dp.sh.Highlighter.prototype.GetMatches = function(regex, css)
+{
+ var index = 0;
+ var match = null;
+
+ while((match = regex.exec(this.code)) != null)
+ {
+ this.matches[this.matches.length] = new dp.sh.Match(match[0], match.index, css);
+ }
+}
+
+dp.sh.Highlighter.prototype.AddBit = function(str, css)
+{
+ var span = document.createElement('span');
+
+ str = str.replace(/&/g, '&');
+ str = str.replace(/ /g, ' ');
+ str = str.replace(/</g, '<');
+ str = str.replace(/\n/gm, ' <br>');
+
+ // when adding a piece of code, check to see if it has line breaks in it
+ // and if it does, wrap individual line breaks with span tags
+ if(css != null)
+ {
+ var regex = new RegExp('<br>', 'gi');
+
+ if(regex.test(str))
+ {
+ var lines = str.split(' <br>');
+
+ str = '';
+
+ for(var i = 0; i < lines.length; i++)
+ {
+ span = document.createElement('SPAN');
+ span.className = css;
+ span.innerHTML = lines[i];
+
+ this.div.appendChild(span);
+
+ // don't add a <BR> for the last line
+ if(i + 1 < lines.length)
+ this.div.appendChild(document.createElement('BR'));
+ }
+ }
+ else
+ {
+ span.className = css;
+ span.innerHTML = str;
+ this.div.appendChild(span);
+ }
+ }
+ else
+ {
+ span.innerHTML = str;
+ this.div.appendChild(span);
+ }
+}
+
+// checks if one match is inside any other match
+dp.sh.Highlighter.prototype.IsInside = function(match)
+{
+ if(match == null || match.length == 0)
+ return;
+
+ for(var i = 0; i < this.matches.length; i++)
+ {
+ var c = this.matches[i];
+
+ if(c == null)
+ continue;
+
+ if((match.index > c.index) && (match.index <= c.index + c.length))
+ return true;
+ }
+
+ return false;
+}
+
+dp.sh.Highlighter.prototype.ProcessRegexList = function()
+{
+ for(var i = 0; i < this.regexList.length; i++)
+ this.GetMatches(this.regexList[i].regex, this.regexList[i].css);
+}
+
+dp.sh.Highlighter.prototype.ProcessSmartTabs = function(code)
+{
+ var lines = code.split('\n');
+ var result = '';
+ var tabSize = 4;
+ var tab = '\t';
+
+ // This function inserts specified amount of spaces in the string
+ // where a tab is while removing that given tab.
+ function InsertSpaces(line, pos, count)
+ {
+ var left = line.substr(0, pos);
+ var right = line.substr(pos + 1, line.length); // pos + 1 will get rid of the tab
+ var spaces = '';
+
+ for(var i = 0; i < count; i++)
+ spaces += ' ';
+
+ return left + spaces + right;
+ }
+
+ // This function process one line for 'smart tabs'
+ function ProcessLine(line, tabSize)
+ {
+ if(line.indexOf(tab) == -1)
+ return line;
+
+ var pos = 0;
+
+ while((pos = line.indexOf(tab)) != -1)
+ {
+ // This is pretty much all there is to the 'smart tabs' logic.
+ // Based on the position within the line and size of a tab,
+ // calculate the amount of spaces we need to insert.
+ var spaces = tabSize - pos % tabSize;
+
+ line = InsertSpaces(line, pos, spaces);
+ }
+
+ return line;
+ }
+
+ // Go through all the lines and do the 'smart tabs' magic.
+ for(var i = 0; i < lines.length; i++)
+ result += ProcessLine(lines[i], tabSize) + '\n';
+
+ return result;
+}
+
+dp.sh.Highlighter.prototype.SwitchToTable = function()
+{
+ // thanks to Lachlan Donald from SitePoint.com for this <br/> tag fix.
+ var html = this.div.innerHTML.replace(/<(br)\/?>/gi, '\n');
+ var lines = html.split('\n');
+ var row = null;
+ var cell = null;
+ var tBody = null;
+ var html = '';
+ var pipe = ' | ';
+
+ // creates an anchor to a utility
+ function UtilHref(util, text)
+ {
+ return '<a href="#" onclick="dp.sh.Utils.' + util + '(this); return false;">' + text + '</a>';
+ }
+
+ tBody = document.createElement('TBODY'); // can be created and all others go to tBodies collection.
+
+ this.table.appendChild(tBody);
+
+ if(this.addGutter == true)
+ {
+ row = tBody.insertRow(-1);
+ cell = row.insertCell(-1);
+ cell.className = 'tools-corner';
+ }
+
+ if(this.addControls == true)
+ {
+ var tHead = document.createElement('THEAD'); // controls will be placed in here
+ this.table.appendChild(tHead);
+
+ row = tHead.insertRow(-1);
+
+ // add corner if there's a gutter
+ if(this.addGutter == true)
+ {
+ cell = row.insertCell(-1);
+ cell.className = 'tools-corner';
+ }
+
+ cell = row.insertCell(-1);
+
+ // preserve some variables for the controls
+ cell.originalCode = this.originalCode;
+ cell.processedCode = this.code;
+ cell.className = 'tools';
+
+ if(this.collapse == true)
+ {
+ tBody.className = 'hide';
+ cell.innerHTML += '<span><b>' + UtilHref('Expand', dp.sh.Strings.ExpandCode) + '</b>' + pipe + '</span>';
+ }
+
+ cell.innerHTML += UtilHref('ViewSource', dp.sh.Strings.ViewPlain) + pipe + UtilHref('PrintSource', dp.sh.Strings.Print);
+
+ // IE has this clipboard object which is easy enough to use
+ if(window.clipboardData)
+ cell.innerHTML += pipe + UtilHref('ToClipboard', dp.sh.Strings.CopyToClipboard);
+
+ cell.innerHTML += pipe + UtilHref('About', dp.sh.Strings.About);
+ }
+
+ for(var i = 0, lineIndex = this.firstLine; i < lines.length - 1; i++, lineIndex++)
+ {
+ row = tBody.insertRow(-1);
+
+ if(this.addGutter == true)
+ {
+ cell = row.insertCell(-1);
+ cell.className = 'gutter';
+ cell.innerHTML = lineIndex;
+ }
+
+ cell = row.insertCell(-1);
+ cell.className = 'line' + (i % 2 + 1); // uses .line1 and .line2 css styles for alternating lines
+ cell.innerHTML = lines[i];
+ }
+
+ this.div.innerHTML = '';
+}
+
+dp.sh.Highlighter.prototype.Highlight = function(code)
+{
+ function Trim(str)
+ {
+ return str.replace(/^\s*(.*?)[\s\n]*$/g, '$1');
+ }
+
+ function Chop(str)
+ {
+ return str.replace(/\n*$/, '').replace(/^\n*/, '');
+ }
+
+ function Unindent(str)
+ {
+ var lines = str.split('\n');
+ var indents = new Array();
+ var regex = new RegExp('^\\s*', 'g');
+ var min = 1000;
+
+ // go through every line and check for common number of indents
+ for(var i = 0; i < lines.length && min > 0; i++)
+ {
+ if(Trim(lines[i]).length == 0)
+ continue;
+
+ var matches = regex.exec(lines[i]);
+
+ if(matches != null && matches.length > 0)
+ min = Math.min(matches[0].length, min);
+ }
+
+ // trim minimum common number of white space from the begining of every line
+ if(min > 0)
+ for(var i = 0; i < lines.length; i++)
+ lines[i] = lines[i].substr(min);
+
+ return lines.join('\n');
+ }
+
+ // This function returns a portions of the string from pos1 to pos2 inclusive
+ function Copy(string, pos1, pos2)
+ {
+ return string.substr(pos1, pos2 - pos1);
+ }
+
+ var pos = 0;
+
+ this.originalCode = code;
+ this.code = Chop(Unindent(code));
+ this.div = document.createElement('DIV');
+ this.table = document.createElement('TABLE');
+ this.matches = new Array();
+
+ if(this.CssClass != null)
+ this.table.className = this.CssClass;
+
+ // replace tabs with spaces
+ if(this.tabsToSpaces == true)
+ this.code = this.ProcessSmartTabs(this.code);
+
+ this.table.border = 0;
+ this.table.cellSpacing = 0;
+ this.table.cellPadding = 0;
+
+ this.ProcessRegexList();
+
+ // if no matches found, add entire code as plain text
+ if(this.matches.length == 0)
+ {
+ this.AddBit(this.code, null);
+ this.SwitchToTable();
+ return;
+ }
+
+ // sort the matches
+ this.matches = this.matches.sort(dp.sh.Highlighter.SortCallback);
+
+ // The following loop checks to see if any of the matches are inside
+ // of other matches. This process would get rid of highligting strings
+ // inside comments, keywords inside strings and so on.
+ for(var i = 0; i < this.matches.length; i++)
+ if(this.IsInside(this.matches[i]))
+ this.matches[i] = null;
+
+ // Finally, go through the final list of matches and pull the all
+ // together adding everything in between that isn't a match.
+ for(var i = 0; i < this.matches.length; i++)
+ {
+ var match = this.matches[i];
+
+ if(match == null || match.length == 0)
+ continue;
+
+ this.AddBit(Copy(this.code, pos, match.index), null);
+ this.AddBit(match.value, match.css);
+
+ pos = match.index + match.length;
+ }
+
+ this.AddBit(this.code.substr(pos), null);
+
+ this.SwitchToTable();
+}
+
+dp.sh.Highlighter.prototype.GetKeywords = function(str)
+{
+ return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
+}
+
+// highlightes all elements identified by name and gets source code from specified property
+dp.sh.HighlightAll = function(name, showGutter /* optional */, showControls /* optional */, collapseAll /* optional */, firstLine /* optional */)
+{
+ function FindValue()
+ {
+ var a = arguments;
+
+ for(var i = 0; i < a.length; i++)
+ {
+ if(a[i] == null)
+ continue;
+
+ if(typeof(a[i]) == 'string' && a[i] != '')
+ return a[i] + '';
+
+ if(typeof(a[i]) == 'object' && a[i].value != '')
+ return a[i].value + '';
+ }
+
+ return null;
+ }
+
+ function IsOptionSet(value, list)
+ {
+ for(var i = 0; i < list.length; i++)
+ if(list[i] == value)
+ return true;
+
+ return false;
+ }
+
+ function GetOptionValue(name, list, defaultValue)
+ {
+ var regex = new RegExp('^' + name + '\\[(\\w+)\\]$', 'gi');
+ var matches = null;
+
+ for(var i = 0; i < list.length; i++)
+ if((matches = regex.exec(list[i])) != null)
+ return matches[1];
+
+ return defaultValue;
+ }
+
+ var elements = document.getElementsByName(name);
+ var highlighter = null;
+ var registered = new Object();
+ var propertyName = 'value';
+
+ // if no code blocks found, leave
+ if(elements == null)
+ return;
+
+ // register all brushes
+ for(var brush in dp.sh.Brushes)
+ {
+ var aliases = dp.sh.Brushes[brush].Aliases;
+
+ if(aliases == null)
+ continue;
+
+ for(var i = 0; i < aliases.length; i++)
+ registered[aliases[i]] = brush;
+ }
+
+ for(var i = 0; i < elements.length; i++)
+ {
+ var element = elements[i];
+ var options = FindValue(
+ element.attributes['class'], element.className,
+ element.attributes['language'], element.language
+ );
+ var language = '';
+
+ if(options == null)
+ continue;
+
+ options = options.split(':');
+
+ language = options[0].toLowerCase();
+
+ if(registered[language] == null)
+ continue;
+
+ // instantiate a brush
+ highlighter = new dp.sh.Brushes[registered[language]]();
+
+ // hide the original element
+ element.style.display = 'none';
+
+ highlighter.addGutter = (showGutter == null) ? !IsOptionSet('nogutter', options) : showGutter;
+ highlighter.addControls = (showControls == null) ? !IsOptionSet('nocontrols', options) : showControls;
+ highlighter.collapse = (collapseAll == null) ? IsOptionSet('collapse', options) : collapseAll;
+
+ // first line idea comes from Andrew Collington, thanks!
+ highlighter.firstLine = (firstLine == null) ? parseInt(GetOptionValue('firstline', options, 1)) : firstLine;
+
+ highlighter.Highlight(element[propertyName]);
+
+ // place the result table inside a div
+ var div = document.createElement('DIV');
+
+ div.className = 'dp-highlighter';
+ div.appendChild(highlighter.table);
+
+ element.parentNode.insertBefore(div, element);
+ }
+}
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/site.css b/ipojo/runtime/core/doc/apache-felix-ipojo_files/site.css
new file mode 100644
index 0000000..959ab0a
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/site.css
@@ -0,0 +1,25 @@
+/* @override http://felix.apache.org/site/media.data/site.css */
+
+body { background-color: #ffffff; color: #3b3b3b; font-family: Tahoma, Arial, sans-serif; font-size: 10pt; line-height: 140% }
+h1, h2, h3, h4, h5, h6 { font-weight: normal; color: #000000; line-height: 100%; margin-top: 0px}
+h1 { font-size: 200% }
+h2 { font-size: 175% }
+h3 { font-size: 150% }
+h4 { font-size: 140% }
+h5 { font-size: 130% }
+h6 { font-size: 120% }
+a { color: #1980af }
+a:visited { color: #1980af }
+a:hover { color: #1faae9 }
+.title { position: absolute; left: 1px; right: 1px; top:25px; height: 81px; background: url(http://felix.apache.org/site/media.data/gradient.png) repeat-x; background-position: bottom; }
+.logo { position: absolute; width: 15em; height: 81px; text-align: center; }
+.header { text-align: right; margin-right: 20pt; margin-top: 30pt;}
+.menu { border-top: 10px solid #f9bb00; position: absolute; top: 107px; left: 1px; width: 15em; bottom: 0px; padding: 0px; background-color: #fcfcfc }
+.menu ul { background-color: #fdf5d9; list-style: none; padding-left: 4em; margin-top: 0px; padding-top: 2em; padding-bottom: 2em; margin-left: 0px; color: #4a4a43}
+.menu a { text-decoration: none; color: #4a4a43 }
+.main { position: absolute; border-top: 10px solid #cde0ea; top: 107px; left: 15em; right: 1px; margin-left: 2px; padding-right: 4em; padding-left: 1em; padding-top: 1em;}
+.code { background-color: #eeeeee; border: solid 1px black; padding: 0.5em }
+.code-keyword { color: #880000 }
+.code-quote { color: #008800 }
+.code-object { color: #0000dd }
+.code-java { margin: 0em }
\ No newline at end of file
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/status_online.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/status_online.png
new file mode 100644
index 0000000..947bd4b
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/status_online.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/superfish.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/superfish.js
new file mode 100644
index 0000000..c6a9c7d
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/superfish.js
@@ -0,0 +1,121 @@
+
+/*
+ * Superfish v1.4.8 - jQuery menu widget
+ * Copyright (c) 2008 Joel Birch
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
+ */
+
+;(function($){
+ $.fn.superfish = function(op){
+
+ var sf = $.fn.superfish,
+ c = sf.c,
+ $arrow = $(['<span class="',c.arrowClass,'"> »</span>'].join('')),
+ over = function(){
+ var $$ = $(this), menu = getMenu($$);
+ clearTimeout(menu.sfTimer);
+ $$.showSuperfishUl().siblings().hideSuperfishUl();
+ },
+ out = function(){
+ var $$ = $(this), menu = getMenu($$), o = sf.op;
+ clearTimeout(menu.sfTimer);
+ menu.sfTimer=setTimeout(function(){
+ o.retainPath=($.inArray($$[0],o.$path)>-1);
+ $$.hideSuperfishUl();
+ if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);}
+ },o.delay);
+ },
+ getMenu = function($menu){
+ var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
+ sf.op = sf.o[menu.serial];
+ return menu;
+ },
+ addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };
+
+ return this.each(function() {
+ var s = this.serial = sf.o.length;
+ var o = $.extend({},sf.defaults,op);
+ o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
+ $(this).addClass([o.hoverClass,c.bcClass].join(' '))
+ .filter('li:has(ul)').removeClass(o.pathClass);
+ });
+ sf.o[s] = sf.op = o;
+
+ $('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() {
+ if (o.autoArrows) addArrow( $('>a:first-child',this) );
+ })
+ .not('.'+c.bcClass)
+ .hideSuperfishUl();
+
+ var $a = $('a',this);
+ $a.each(function(i){
+ var $li = $a.eq(i).parents('li');
+ $a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);});
+ });
+ o.onInit.call(this);
+
+ }).each(function() {
+ var menuClasses = [c.menuClass];
+ if (sf.op.dropShadows && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass);
+ $(this).addClass(menuClasses.join(' '));
+ });
+ };
+
+ var sf = $.fn.superfish;
+ sf.o = [];
+ sf.op = {};
+ sf.IE7fix = function(){
+ var o = sf.op;
+ if ($.browser.msie && $.browser.version > 6 && o.dropShadows && o.animation.opacity!=undefined)
+ this.toggleClass(sf.c.shadowClass+'-off');
+ };
+ sf.c = {
+ bcClass : 'sf-breadcrumb',
+ menuClass : 'sf-js-enabled',
+ anchorClass : 'sf-with-ul',
+ arrowClass : 'sf-sub-indicator',
+ shadowClass : 'sf-shadow'
+ };
+ sf.defaults = {
+ hoverClass : 'sfHover',
+ pathClass : 'overideThisToUse',
+ pathLevels : 1,
+ delay : 800,
+ animation : {opacity:'show'},
+ speed : 'normal',
+ autoArrows : true,
+ dropShadows : true,
+ disableHI : false, // true disables hoverIntent detection
+ onInit : function(){}, // callback functions
+ onBeforeShow: function(){},
+ onShow : function(){},
+ onHide : function(){}
+ };
+ $.fn.extend({
+ hideSuperfishUl : function(){
+ var o = sf.op,
+ not = (o.retainPath===true) ? o.$path : '';
+ o.retainPath = false;
+ var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass)
+ .find('>ul').hide().css('visibility','hidden');
+ o.onHide.call($ul);
+ return this;
+ },
+ showSuperfishUl : function(){
+ var o = sf.op,
+ sh = sf.c.shadowClass+'-off',
+ $ul = this.addClass(o.hoverClass)
+ .find('>ul:hidden').css('visibility','visible');
+ sf.IE7fix.call($ul);
+ o.onBeforeShow.call($ul);
+ $ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); });
+ return this;
+ }
+ });
+
+})(jQuery);
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/supersubs.js b/ipojo/runtime/core/doc/apache-felix-ipojo_files/supersubs.js
new file mode 100644
index 0000000..4522151
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/supersubs.js
@@ -0,0 +1,90 @@
+
+/*
+ * Supersubs v0.2b - jQuery plugin
+ * Copyright (c) 2008 Joel Birch
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * This plugin automatically adjusts submenu widths of suckerfish-style menus to that of
+ * their longest list item children. If you use this, please expect bugs and report them
+ * to the jQuery Google Group with the word 'Superfish' in the subject line.
+ *
+ */
+
+;(function($){ // $ will refer to jQuery within this closure
+
+ $.fn.supersubs = function(options){
+ var opts = $.extend({}, $.fn.supersubs.defaults, options);
+ // return original object to support chaining
+ return this.each(function() {
+ // cache selections
+ var $$ = $(this);
+ // support metadata
+ var o = $.meta ? $.extend({}, opts, $$.data()) : opts;
+ // get the font size of menu.
+ // .css('fontSize') returns various results cross-browser, so measure an em dash instead
+ var fontsize = $('<li id="menu-fontsize">—</li>').css({
+ 'padding' : 0,
+ 'position' : 'absolute',
+ 'top' : '-999em',
+ 'width' : 'auto'
+ }).appendTo($$).width(); //clientWidth is faster, but was incorrect here
+ // remove em dash
+ $('#menu-fontsize').remove();
+ // cache all ul elements
+ $ULs = $$.find('ul');
+ // loop through each ul in menu
+ $ULs.each(function(i) {
+ // cache this ul
+ var $ul = $ULs.eq(i);
+ // get all (li) children of this ul
+ var $LIs = $ul.children();
+ // get all anchor grand-children
+ var $As = $LIs.children('a');
+ // force content to one line and save current float property
+ var liFloat = $LIs.css('white-space','nowrap').css('float');
+ // remove width restrictions and floats so elements remain vertically stacked
+ var emWidth = $ul.add($LIs).add($As).css({
+ 'float' : 'none',
+ 'width' : 'auto'
+ })
+ // this ul will now be shrink-wrapped to longest li due to position:absolute
+ // so save its width as ems. Clientwidth is 2 times faster than .width() - thanks Dan Switzer
+ .end().end()[0].clientWidth / fontsize;
+ // add more width to ensure lines don't turn over at certain sizes in various browsers
+ emWidth += o.extraWidth;
+ // restrict to at least minWidth and at most maxWidth
+ if (emWidth > o.maxWidth) { emWidth = o.maxWidth; }
+ else if (emWidth < o.minWidth) { emWidth = o.minWidth; }
+ emWidth += 'em';
+ // set ul to width in ems
+ $ul.css('width',emWidth);
+ // restore li floats to avoid IE bugs
+ // set li width to full width of this ul
+ // revert white-space to normal
+ $LIs.css({
+ 'float' : liFloat,
+ 'width' : '100%',
+ 'white-space' : 'normal'
+ })
+ // update offset position of descendant ul to reflect new width of parent
+ .each(function(){
+ var $childUl = $('>ul',this);
+ var offsetDirection = $childUl.css('left')!==undefined ? 'left' : 'right';
+ $childUl.css(offsetDirection,emWidth);
+ });
+ });
+
+ });
+ };
+ // expose defaults
+ $.fn.supersubs.defaults = {
+ minWidth : 9, // requires em unit.
+ maxWidth : 25, // requires em unit.
+ extraWidth : 0 // extra width can ensure lines don't sometimes turn over due to slight browser differences in how they round-off values
+ };
+
+})(jQuery); // plugin code ends
diff --git a/ipojo/runtime/core/doc/apache-felix-ipojo_files/user_edit.png b/ipojo/runtime/core/doc/apache-felix-ipojo_files/user_edit.png
new file mode 100644
index 0000000..c1974cd
--- /dev/null
+++ b/ipojo/runtime/core/doc/apache-felix-ipojo_files/user_edit.png
Binary files differ
diff --git a/ipojo/runtime/core/doc/changelog.txt b/ipojo/runtime/core/doc/changelog.txt
new file mode 100644
index 0000000..bb0f693
--- /dev/null
+++ b/ipojo/runtime/core/doc/changelog.txt
@@ -0,0 +1,150 @@
+Changes from the 1.6.8 to 1.8.0
+-------------------------------
+** Bug
+ * [FELIX-2694] - Instance state not recomputed after reconfiguration when the instance is stopped
+ * [FELIX-2716] - [iPOJO] Failure when creating proxies for classes in java.* packages
+
+** Improvement
+ * [FELIX-2781] - Expose the implementation class as service when no interfaces are found in the hierarchy
+ * [FELIX-1424] - Constructor Injection
+ * [FELIX-1428] - Constructor injection of Configuration properties
+ * [FELIX-2461] - Allow specifying the targeted service interface in the @ServiceController
+ * [FELIX-2620] - Change iPojo annotation parameters to follow java naming conventions
+ * [FELIX-2621] - Rename annotations to remove collisions
+ * [FELIX-2622] - Support static service properties that are not mirrored into fields
+ * [FELIX-2688] - iPojo "requires.filters" - Array object instead of Dictionary object
+ * [FELIX-2705] - Provide a way to extend the logger strategy
+ * [FELIX-2742] - Constructor injection of service dependencies
+ * [FELIX-2744] - Add annotations to the maven-ipojo-plugin archetype
+
+Changes from the 1.6.6 to 1.6.8
+-------------------------------
+** Improvement
+ * [FELIX-2688] - iPojo "requires.filters" - Array object instead of Dictionary object
+ * [FELIX-2705] - Provide a way to extend the logger strategy
+
+** Bug
+ * [FELIX-2685] - Wrong Element name when XML namespace contains ':'
+ * [FELIX-2694] - Instance state not recomputed after reconfiguration when the instance is stopped
+
+Changes from the 1.6.4 to 1.6.6
+-------------------------------
+** Improvement
+ * [FELIX-2594] - Have a way to create new custom iPojo handler without having to specify a handler usage
+ * [FELIX-2623] - @Update annotated methods should not require a Dictionary parameter
+
+** Bug
+ * [FELIX-2580] - iPOJO failed to create proxies on service which are not interface
+ * [FELIX-2596] - DependencyHandler.onObjectCreation throws NPE when bundle is refreshed
+ * [FELIX-2603] - wrong behavior of InstanceManager.onSet(..) method
+ * [FELIX-2636] - Cannot control the validity of an iPOJO instance using a configuration property
+
+Changes from the 1.6.2 to 1.6.4
+-------------------------------
+** Improvement
+ * [FELIX-2420] - Enum support for @Property annotation
+ * [FELIX-2461] - Allow specifying the targeted service interface in the @ServiceController
+ * [FELIX-2472] - Proxies should throw a runtime exception instead of a null pointer exception
+
+** Bug
+ * [FELIX-2561] - Properties set as required instead of optional in the component type descriptions
+
+Changes from the 1.6.0 to 1.6.2
+-------------------------------
+** Bug
+ * [FELIX-2308] - getService is called during the unregistration of a service.
+ * [FELIX-2309] - Potential NPE when a service has abruptly in the smart proxies.
+ * [FELIX-2323] - Unbind method should not be called during the invalidation process if the invalidation does not come from a service departure.
+
+** Improvement
+ * [FELIX-2296] - Access to ServiceReference in iPOJO service.
+
+Changes from the 1.4.0 to 1.6.0
+-------------------------------
+** Bug
+ * [FELIX-1533] - Potential deadlock when stopping the underlying OSGi framework
+ * [FELIX-1965] - iPojo component is made available regardless of exception during validate
+ * [FELIX-2014] - Potential ClassCastException when a service property does not receive a value and is used in the constructor
+ * [FELIX-2019] - The Property value is 'null' in the Architecture description, while the value is well assigned to the component's field.
+ * [FELIX-2052] - Handler require callback are not called if the service (required) is registered before the instance of the handler has been started.
+ * [FELIX-2093] - iPOJO doesn't always use the correct class loader
+
+** Improvement
+ * [FELIX-1425] - Service Proxy Mode
+ * [FELIX-1426] - Service injection with Dynamic Proxies
+ * [FELIX-1427] - Service injection with Smart Proxies
+ * [FELIX-1532] - Be able to set the iPOJO Log Level from BND
+ * [FELIX-1741] - Allows the configuration handler description to retrieve the managed service PID
+ * [FELIX-1854] - Allows instances to directly declares service.* properties (pid, ranking, vendor, description)
+ * [FELIX-1885] - Ease CreationStrategy & iPOJOServiceFactory usage
+ * [FELIX-1906] - Allow calling a method when service properties of an already injected service are modified
+
+** New Feature
+ * [FELIX-2132] - Provides a way to control service exposition from the implementation class
+
+
+Changes from 1.2.0 to 1.4.0
+---------------------------
+** Bug
+ * [FELIX-985] - iPOJO analyzes already installed bundle by holding a lock
+ * [FELIX-1002] - iPOJO Static binding policy is not compliant with the Declarative Service static binding policy.
+ * [FELIX-1318] - Case mismatch problem of iPOJO custom handler name
+** Improvement
+ * Update parent pom
+ * [FELIX-936] - Allowing publishing class as services
+ * [FELIX-966] - iPOJO: Better error reporting when getPojoObject return null
+ * [FELIX-982] - Declare iPOJO as a singleton bundle to avoid multiple version of the runtime at the same time
+ * [FELIX-1114] - callback after configuration change needed
+ * [FELIX-1163] - Improve error message when an array cannot be created due to a classloading issue
+ * [FELIX-1182] - iPOJO - reconfiguration : get all properties with the update callback
+
+
+Changes from 1.0.0 to 1.2.0
+---------------------------
+** Bug
+ * [FELIX-797] - Composite Architecture contains duplicate instances
+ * [FELIX-803] - iPOJO Core schema needs to be fixed
+ * [FELIX-805] - Instance not created if the factory becomes valid later
+ * [FELIX-866] - iPOJO Provides 'interface' attribute should be 'specifications'
+
+** Improvement
+ * [FELIX-787] - iPOJO logger log messages inside the log service and print them
+ * [FELIX-796] - Allows enabling/disabling the internal event dispatcher
+ * [FELIX-801] - Support service properties injection in bind/unbind callbacks
+ * [FELIX-815] - Support optional properties
+ * [FELIX-816] - Support comparator attribute with any service binding policy
+ * [FELIX-818] - Implement the ServiceReference compareTo method
+ * [FELIX-853] - Provide new service object creation strategies
+ * New introspection API
+
+Changes from 0.8.0 to 1.0.0
+---------------------------
+** Bug
+ * [FELIX-557] - Factories still living when a primitive component does not have its manipulation metadata
+ * [FELIX-632] - Component are set to immediate despite they are already immediate
+ * [FELIX-635] - Simplify factory name computation
+ * [FELIX-628] - Architecture service should not publish the instance.name property
+ * [FELIX-621] - Instances not disposed when instances creation failed
+
+** Improvement
+ * [FELIX-552] - ClassCastException when using services as dynamic proxies
+ * [FELIX-555] - Error message in the iPOJO Extender could be more accurate when failing to get the bundle context
+ * [FELIX-558] - Non caught NoClassDefFoundError when the instantiation of a Nullable object failed
+ * [FELIX-603] - Improve iPOJO Arch service dependency description
+ * [FELIX-626] - Allow specifying instance configuration containing empty dictionaries
+ * [FELIX-629] - Allows instance configuration to declares complex properties
+ * [FELIX-631] - Immediate Component Detection
+ * [FELIX-633] - Factory creation should be done in another thread
+ * [FELIX-634] - Improve error handling
+ * [FELIX-655] - Add a 'from' attribute in the service dependencies
+ * [FELIX-673] - Provide OBR description to iPOJO bundles
+ * [FELIX-683] - Supporting lists and vectors in the service dependency management
+ * [FELIX-686] - Supporting collections and set in the service dependency management
+ * [FELIX-688] - Better error reporting when an instance creation failed
+ * [FELIX-689] - Instance 'name' property should become 'instance.name'
+ * [FELIX-716] - Provide XML schemas for iPOJO descriptors
+ * [FELIX-732] - Duplicate instance created of a managed service
+
+Version 0.8.0
+-------------
+ * Initial release
diff --git a/ipojo/runtime/core/obr.xml b/ipojo/runtime/core/obr.xml
new file mode 100644
index 0000000..00a9cea
--- /dev/null
+++ b/ipojo/runtime/core/obr.xml
@@ -0,0 +1,44 @@
+<!--
+ 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.
+-->
+<obr>
+ <capability name="ipojo.handler">
+ <p n="name" v="controller"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="callback"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="requires"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="provides"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="properties"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+ <capability name="ipojo.handler">
+ <p n="name" v="architecture"/>
+ <p n="namespace" v="org.apache.felix.ipojo"/>
+ </capability>
+</obr>
\ No newline at end of file
diff --git a/ipojo/runtime/core/pom.xml b/ipojo/runtime/core/pom.xml
new file mode 100644
index 0000000..0169f41
--- /dev/null
+++ b/ipojo/runtime/core/pom.xml
@@ -0,0 +1,228 @@
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.1</version>
+ <relativePath>../../../pom/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix iPOJO</name>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ <version>1.9.0-SNAPSHOT</version>
+
+ <properties>
+ <!--
+ 1.8.1:
+ * change in the MethodInterceptor interface (FELIX-3144)
+ * change in the Factory interface (FELIX-3190)
+ -->
+ <ipojo.package.version>1.8.1</ipojo.package.version>
+
+ <!-- Embedded manipulator version -->
+ <ipojo.manipulator.version>1.8.0</ipojo.manipulator.version>
+ </properties>
+
+ <description>
+ iPOJO Core bundle
+ </description>
+ <url>
+ http://ipojo.org
+ </url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.metadata</artifactId>
+ <version>1.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
+ <version>${ipojo.manipulator.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>3.3.1</version>
+ <exclusions>
+ <exclusion>
+ <groupId>asm</groupId>
+ <artifactId>asm-tree</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <target>jsr14</target>
+ <source>1.5</source>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.3</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>Apache Felix iPOJO</Bundle-Name>
+ <Bundle-Vendor> The Apache Software Foundation </Bundle-Vendor>
+ <Bundle-SymbolicName>org.apache.felix.ipojo;singleton:=true</Bundle-SymbolicName>
+ <Bundle-Description> iPOJO Core Framework </Bundle-Description>
+ <Bundle-Activator> org.apache.felix.ipojo.Extender
+ </Bundle-Activator>
+ <Bundle-DocURL>
+ http://felix.apache.org/site/apache-felix-ipojo.html
+ </Bundle-DocURL>
+ <IPOJO-Extension>
+ component:org.apache.felix.ipojo.ComponentFactory,
+ handler:org.apache.felix.ipojo.HandlerManagerFactory
+ </IPOJO-Extension>
+ <Import-Package>
+ org.osgi.framework;version=1.3, <!-- To support KF 2 -->
+ org.osgi.service.cm,
+ org.osgi.service.log,
+ !org.objectweb.asm.tree,
+ !sun.io,
+ !net.sourceforge.cobertura.*, <!-- To support code coverage -->
+
+ <!--
+ Packages used for the online-manipulator.
+ They are optionals
+ -->
+ javax.xml.parsers;resolution:=optional,
+ org.osgi.service.url;resolution:=optional,
+ org.w3c.dom;resolution:=optional,
+ org.xml.sax;resolution:=optional,
+ org.xml.sax.helpers;resolution:=optional
+ </Import-Package>
+ <Private-Package>
+ org.apache.felix.ipojo.handlers.architecture,
+ org.apache.felix.ipojo.handlers.lifecycle.callback,
+ org.apache.felix.ipojo.handlers.lifecycle.controller,
+ <!-- ASM (Manipulator dependencies) -->
+ org.objectweb.asm.commons,
+ org.objectweb.asm.signature,
+ org.objectweb.asm
+ </Private-Package>
+ <Export-Package>
+ org.apache.felix.ipojo; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.metadata; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.architecture; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.parser; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.util; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.handlers.dependency; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.handlers.providedservice.*; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.handlers.configuration; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.context; version="${ipojo.package.version}",
+ <!-- Embedded manipulator -->
+ org.apache.felix.ipojo.manipulator; version="${ipojo.manipulator.version}",
+ org.apache.felix.ipojo.xml.parser; version="${ipojo.manipulator.version}",
+ org.apache.felix.ipojo.*; version="${ipojo.manipulator.version}",
+ <!-- Compendium packages -->
+ org.osgi.service.cm,
+ org.osgi.service.log,
+ </Export-Package>
+ <Include-Resource>
+ META-INF/LICENSE=LICENSE,
+ META-INF/NOTICE=NOTICE, META-INF/LICENSE.asm=LICENSE.asm,
+ META-INF/DEPENDENCIES=DEPENDENCIES
+ </Include-Resource>
+ <_donotcopy> (CVS|.svn|.+.bak|~.+|metadata.xml) </_donotcopy>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-ipojo-plugin</artifactId>
+ <version>1.8.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>ipojo-bundle</goal>
+ </goals>
+ <configuration>
+ <ignoreAnnotations>true</ignoreAnnotations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+ <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+ <excludes>
+ <param>doc/**/*</param>
+ <param>maven-eclipse.xml</param>
+ <param>.checkstyle</param>
+ <param>.externalToolBuilders/*</param>
+ <param>LICENSE.asm</param>
+ <param>.fbprefs</param>
+ <param>DEPENDENCIES</param>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <enableRulesSummary>false</enableRulesSummary>
+ <violationSeverity>warning</violationSeverity>
+ <configLocation>http://felix.apache.org/ipojo/dev/checkstyle_ipojo.xml</configLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ <resource>
+ <directory>.</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ <include>DEPENDENCIES*</include>
+ </includes>
+ </resource>
+ </resources>
+
+ </build>
+</project>
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
new file mode 100644
index 0000000..fdfc223
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
@@ -0,0 +1,723 @@
+/*
+ * 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.felix.ipojo;
+
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.util.Logger;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The component factory manages component instance objects. This management
+ * consists to create and manage component instances build with the current
+ * component factory. This class could export Factory and ManagedServiceFactory
+ * services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @see IPojoFactory
+ * @see TrackerCustomizer
+ */
+public class ComponentFactory extends IPojoFactory implements TrackerCustomizer {
+
+ /**
+ * System property set to automatically attach primitive handlers to primitive
+ * component types.
+ * The value is a String parsed as a list (comma separated). Each element is
+ * the fully qualified name of the handler <code>namespace:name</code>.
+ */
+ public static final String HANDLER_AUTO_PRIMITIVE = "org.apache.felix.ipojo.handler.auto.primitive";
+
+
+ /**
+ * The tracker used to track required handler factories.
+ * Immutable once set.
+ */
+ protected Tracker m_tracker;
+
+ /**
+ * The class loader to delegate classloading.
+ * Immutable once set.
+ */
+ private FactoryClassloader m_classLoader;
+
+ /**
+ * The component implementation class.
+ * (manipulated byte array)
+ */
+ private byte[] m_clazz;
+
+ /**
+ * The component implementation qualified class name.
+ * Immutable once set.
+ * This attribute is set during the creation of the factory.
+ */
+ private String m_classname;
+
+ /**
+ * The manipulation metadata of the implementation class.
+ * Immutable once set.
+ * This attribute is set during the creation of the factory.
+ */
+ private PojoMetadata m_manipulation;
+
+ /**
+ * Creates a instance manager factory.
+ * The class is given in parameter. The component type is not a composite.
+ *
+ * @param context the bundle context
+ * @param clazz the component class
+ * @param element the metadata of the component
+ * @throws ConfigurationException if the element describing the factory is malformed.
+ */
+ public ComponentFactory(BundleContext context, byte[] clazz, Element element) throws ConfigurationException {
+ this(context, element);
+ m_clazz = clazz;
+ }
+
+ /**
+ * Creates a instance manager factory.
+ *
+ * @param context the bundle context
+ * @param element the metadata of the component to create
+ * @throws ConfigurationException if element describing the factory is malformed.
+ */
+ public ComponentFactory(BundleContext context, Element element) throws ConfigurationException {
+ super(context, element);
+ check(element); // NOPMD. This invocation is normal.
+ }
+
+ /**
+ * Gets the component type description of the current factory.
+ *
+ * @return the description of the component type attached to this factory.
+ * @see org.apache.felix.ipojo.IPojoFactory#getComponentTypeDescription()
+ */
+ public ComponentTypeDescription getComponentTypeDescription() {
+ return new PrimitiveTypeDescription(this);
+ }
+
+ /**
+ * Allows a factory to check if the given element is well-formed.
+ * A component factory metadata is correct if they contain the 'classname' attribute.
+ * As this method is called from the (single-threaded) constructor, no synchronization is needed.
+ *
+ * @param element the metadata describing the component
+ * @throws ConfigurationException if the element describing the factory is malformed.
+ */
+ public void check(Element element) throws ConfigurationException {
+ m_classname = element.getAttribute("classname");
+ if (m_classname == null) {
+ throw new ConfigurationException("A component needs a class name : " + element);
+ }
+ m_manipulation = new PojoMetadata(m_componentMetadata);
+ }
+
+ /**
+ * Gets the class name.
+ * No synchronization needed, the classname is immutable.
+ *
+ * @return the class name.
+ * @see org.apache.felix.ipojo.IPojoFactory#getClassName()
+ */
+ public String getClassName() {
+ return m_classname;
+ }
+
+ /**
+ * Creates a primitive instance.
+ * This method is called when holding the lock.
+ *
+ * @param config the instance configuration
+ * @param context the service context (null if the instance has to be created in the global space).
+ * @param handlers the handlers to attach to the instance
+ * @return the created instance
+ * @throws org.apache.felix.ipojo.ConfigurationException
+ * if the configuration process failed.
+ * @see org.apache.felix.ipojo.IPojoFactory#createInstance(java.util.Dictionary, org.apache.felix.ipojo.IPojoContext, org.apache.felix.ipojo.HandlerManager[])
+ */
+ public ComponentInstance createInstance(Dictionary config, IPojoContext context, HandlerManager[] handlers) throws org.apache.felix.ipojo.ConfigurationException {
+ InstanceManager instance = new InstanceManager(this, context, handlers);
+
+ try {
+ instance.configure(m_componentMetadata, config);
+ instance.start();
+ return instance;
+ } catch (ConfigurationException e) {
+ // An exception occurs while executing the configure or start
+ // methods.
+ if (instance != null) {
+ instance.dispose();
+ instance = null;
+ }
+ throw e;
+ } catch (Throwable e) { // All others exception are handled here.
+ if (instance != null) {
+ instance.dispose();
+ instance = null;
+ }
+ m_logger.log(Logger.ERROR, e.getMessage(), e);
+ throw new ConfigurationException(e.getMessage());
+ }
+
+ }
+
+ /**
+ * Defines a class.
+ * This method needs to be synchronized to avoid that the classloader
+ * is created twice.
+ * This method delegate the <code>define</code> method invocation to the
+ * factory classloader.
+ *
+ * @param name the qualified name of the class
+ * @param clazz the byte array of the class
+ * @param domain the protection domain of the class
+ * @return the defined class object
+ */
+ public synchronized Class defineClass(String name, byte[] clazz, ProtectionDomain domain) {
+ if (m_classLoader == null) {
+ m_classLoader = new FactoryClassloader();
+ }
+ return m_classLoader.defineClass(name, clazz, domain);
+ }
+
+ /**
+ * Returns the URL of a resource.
+ * This methods delegates the invocation to the
+ * {@link Bundle#getResource(String)} method.
+ *
+ * @param resName the resource name
+ * @return the URL of the resource
+ */
+ public URL getResource(String resName) {
+ //No synchronization needed, the context is immutable and
+ //the call is managed by the underlying framework.
+ return m_context.getBundle().getResource(resName);
+ }
+
+ /**
+ * Loads a class. This method checks if the class
+ * to load is the implementation class or not.
+ * If it is, the factory classloader is used, else
+ * the {@link Bundle#loadClass(String)} is called.
+ *
+ * @param className the name of the class to load
+ * @return the resulting Class object
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public Class loadClass(String className) throws ClassNotFoundException {
+ if (m_clazz != null && m_classname.equals(className)) { // Immutable fields.
+ return defineClass(className, m_clazz, null);
+ }
+ return m_context.getBundle().loadClass(className);
+ }
+
+ /**
+ * Starts the factory.
+ * This method is called when holding the monitor lock.
+ */
+ public void starting() {
+ if (m_tracker != null) {
+ return; // Already started
+ } else {
+ if (m_requiredHandlers.size() != 0) {
+ try {
+ String filter = "(&(" + Handler.HANDLER_TYPE_PROPERTY + "=" + PrimitiveHandler.HANDLER_TYPE + ")" + "(factory.state=1)" + ")";
+ m_tracker = new Tracker(m_context, m_context.createFilter(filter), this);
+ m_tracker.open();
+ } catch (InvalidSyntaxException e) {
+ m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage()); //Holding the lock should not be an issue here.
+ stop();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops all the instance managers.
+ * This method is called when holding the lock.
+ */
+ public void stopping() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ m_tracker = null;
+ }
+ }
+
+ /**
+ * Computes the factory name. The factory name is computed from
+ * the 'name' and 'classname' attributes.
+ * This method does not manipulate any non-immutable fields,
+ * so does not need to be synchronized.
+ *
+ * @return the factory name.
+ */
+ public String getFactoryName() {
+ String name = m_componentMetadata.getAttribute("name");
+ if (name == null) {
+ // No factory name, use the classname (mandatory attribute)
+ name = m_componentMetadata.getAttribute("classname");
+ }
+ return name;
+ }
+
+ /**
+ * Computes required handlers.
+ * This method does not manipulate any non-immutable fields,
+ * so does not need to be synchronized.
+ * This method checks the {@link ComponentFactory#HANDLER_AUTO_PRIMITIVE}
+ * system property to add the listed handlers to the required handler set.
+ *
+ * @return the required handler list.
+ */
+ public List getRequiredHandlerList() {
+ List list = new ArrayList();
+ Element[] elems = m_componentMetadata.getElements();
+ for (int i = 0; i < elems.length; i++) {
+ Element current = elems[i];
+ if (!"manipulation".equals(current.getName())) { // Remove the manipulation element
+ RequiredHandler req = new RequiredHandler(current.getName(), current.getNameSpace());
+ if (!list.contains(req)) {
+ list.add(req);
+ }
+ }
+ }
+
+ // Add architecture if architecture != 'false'
+ String arch = m_componentMetadata.getAttribute("architecture");
+ if (arch == null || arch.equalsIgnoreCase("true")) {
+ list.add(new RequiredHandler("architecture", null));
+ }
+
+
+ // Determine if the component must be immediate.
+ // A component becomes immediate if it doesn't provide a service,
+ // and does not specified that the component is not immediate.
+ if (m_componentMetadata.getElements("provides") == null) {
+ String imm = m_componentMetadata.getAttribute("immediate");
+ if (imm == null) { // immediate not specified, set the immediate attribute to true
+ getLogger().log(
+ Logger.INFO,
+ "The component type " + getFactoryName()
+ + " becomes immediate");
+ m_componentMetadata.addAttribute(new Attribute("immediate",
+ "true"));
+ }
+ }
+
+ // Add lifecycle callback if immediate = true
+ RequiredHandler reqCallback = new RequiredHandler("callback", null);
+ String imm = m_componentMetadata.getAttribute("immediate");
+ if (!list.contains(reqCallback) && imm != null && imm.equalsIgnoreCase("true")) {
+ list.add(reqCallback);
+ }
+
+ // Manage auto attached handler.
+ String v = System.getProperty(HANDLER_AUTO_PRIMITIVE);
+ if (v != null && v.length() != 0) {
+ String[] hs = ParseUtils.split(v, ",");
+ for (int i = 0; i < hs.length; i++) {
+ String h = hs[i].trim();
+ String[] segments = ParseUtils.split(h, ":");
+ RequiredHandler rq = null;
+ if (segments.length == 2) { // External handler
+ rq = new RequiredHandler(segments[1], segments[0]);
+ } else if (segments.length == 1) { // Core handler
+ rq = new RequiredHandler(segments[1], null);
+ } // Others case are ignored.
+
+ if (rq != null) {
+ // Check it's not already contained
+ if (!list.contains(rq)) {
+ list.add(rq);
+ }
+ }
+ }
+ }
+
+
+ return list;
+ }
+
+ /**
+ * This method is called when a new handler factory is detected.
+ * Test if the factory can be used or not.
+ * This method need to be synchronized as it accesses to the content
+ * of required handlers.
+ *
+ * @param reference the new service reference.
+ * @return <code>true</code> if the given factory reference matches with a required handler.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public synchronized boolean addingService(ServiceReference reference) {
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ if (req.getReference() == null && match(req, reference)) {
+ int oldP = req.getLevel();
+ req.setReference(reference);
+ // If the priority has changed, sort the list.
+ if (oldP != req.getLevel()) {
+ // Manipulate the list.
+ Collections.sort(m_requiredHandlers);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method is called when a matching service has been added to the tracker,
+ * we can no compute the factory state. This method is synchronized to avoid
+ * concurrent calls to method modifying the factory state.
+ *
+ * @param reference the added service reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public synchronized void addedService(ServiceReference reference) {
+ if (m_state == INVALID) {
+ computeFactoryState();
+ }
+ }
+
+ /**
+ * This method is called when a used handler factory disappears.
+ * This method is synchronized to avoid concurrent calls to method modifying
+ * the factory state.
+ *
+ * @param reference the leaving service reference.
+ * @param service the handler factory object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public synchronized void removedService(ServiceReference reference, Object service) {
+ // Look for the implied reference and invalid the handler identifier
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ if (reference.equals(req.getReference())) {
+ req.unRef(); // This method will unget the service.
+ computeFactoryState();
+ return; // The factory can be used only once.
+ }
+ }
+ }
+
+ /**
+ * This method is called when a used handler factory is modified.
+ * However, handler factory modification is not possible, so this method
+ * is never called.
+ *
+ * @param reference the service reference
+ * @param service the Factory object (if already get)
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Noting to do
+ }
+
+ /**
+ * Returns manipulation metadata of this component type.
+ *
+ * @return manipulation metadata of this component type.
+ */
+ public PojoMetadata getPojoMetadata() {
+ return m_manipulation;
+ }
+
+ /**
+ * Gets the version of the component type.
+ *
+ * @return the version of <code>null</code> if not set.
+ * @see org.apache.felix.ipojo.Factory#getVersion()
+ */
+ public String getVersion() {
+ return m_version;
+ }
+
+ public ClassLoader getBundleClassLoader() {
+ return m_classLoader;
+ }
+
+ /**
+ * this class defines the classloader attached to a factory.
+ * This class loader is used to load the implementation (e.g. manipulated)
+ * class.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @see ClassLoader
+ */
+ private class FactoryClassloader extends ClassLoader {
+
+ /**
+ * The map of defined classes [Name, Class Object].
+ */
+ private final Map m_definedClasses = new HashMap();
+
+ /**
+ * The defineClass method.
+ *
+ * @param name name of the class
+ * @param clazz the byte array of the class
+ * @param domain the protection domain
+ * @return the defined class.
+ */
+ public Class defineClass(String name, byte[] clazz, ProtectionDomain domain) {
+ if (m_definedClasses.containsKey(name)) {
+ return (Class) m_definedClasses.get(name);
+ }
+ Class clas = super.defineClass(name, clazz, 0, clazz.length, domain);
+ m_definedClasses.put(name, clas);
+ return clas;
+ }
+
+ /**
+ * Returns the URL of the required resource.
+ *
+ * @param arg the name of the resource to find.
+ * @return the URL of the resource.
+ * @see java.lang.ClassLoader#getResource(java.lang.String)
+ */
+ public URL getResource(String arg) {
+ return m_context.getBundle().getResource(arg);
+ }
+
+ /**
+ * Loads the given class.
+ *
+ * @param name the name of the class
+ * @param resolve should be the class resolve now ?
+ * @return the loaded class object
+ * @throws ClassNotFoundException if the class to load is not found
+ * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
+ * @see java.lang.ClassLoader#loadClass(String, boolean)
+ */
+ protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ return m_context.getBundle().loadClass(name);
+ }
+ }
+
+ /**
+ * This class defines the description of primitive (non-composite) component
+ * types. An instance of this class will be returned when invoking the
+ * {@link ComponentFactory#getComponentDescription()} method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+ private final class PrimitiveTypeDescription extends ComponentTypeDescription {
+
+ /*
+ * Set to keep component's all super-class class-names.
+ */
+ private Set m_superClasses = new HashSet();
+
+ /*
+ * Set to keep component's all interface class-names.
+ */
+ private Set m_interfaces = new HashSet();
+
+ /**
+ * Creates a PrimitiveTypeDescription object.
+ *
+ * @param factory the factory attached to this component type description.
+ */
+ public PrimitiveTypeDescription(IPojoFactory factory) {
+ super(factory);
+
+ try {
+ // The inspection can be done only for primitive components
+ if (m_classname != null) {
+ // Read inherited classes and interfaces into given Sets.
+ new InheritanceInspector(getPojoMetadata(), getBundleContext().getBundle()).
+ computeInterfacesAndSuperClasses(m_interfaces, m_superClasses);
+ }
+ } catch (ClassNotFoundException e) {
+ m_interfaces.clear();
+ m_superClasses.clear();
+ }
+
+ }
+
+ /**
+ * Computes the properties to publish.
+ * The <code>component.class</code> property contains the implementation class name.
+ *
+ * @return the dictionary of properties to publish
+ * @see org.apache.felix.ipojo.architecture.ComponentTypeDescription#getPropertiesToPublish()
+ */
+ public Dictionary getPropertiesToPublish() {
+ Dictionary dict = super.getPropertiesToPublish();
+ if (m_classname != null) {
+ dict.put("component.class", m_classname);
+ }
+ return dict;
+ }
+
+ /**
+ * Adds the "implementation-class" attribute to the type description.
+ *
+ * @return the component type description.
+ * @see org.apache.felix.ipojo.architecture.ComponentTypeDescription#getDescription()
+ */
+ public Element getDescription() {
+ Element elem = super.getDescription();
+ elem.addAttribute(new Attribute("Implementation-Class", m_classname));
+
+ /* Adding interfaces and super-classes of component into description */
+ Element inheritance = new Element("Inherited", "");
+
+ inheritance.addAttribute(new Attribute("Interfaces", m_interfaces.toString()));
+ inheritance.addAttribute(new Attribute("SuperClasses", m_superClasses.toString()));
+
+ elem.addElement(inheritance);
+
+ return elem;
+ }
+
+ /**
+ * This class is used to collect interfaces and super-classes of given component in specified Sets.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+ private final class InheritanceInspector {
+ /*
+ * PojoMetadata of target Component.
+ */
+ private PojoMetadata m_pojoMetadata;
+
+ /*
+ * Bundle exposing target component.
+ */
+ private Bundle m_bundle;
+
+
+ /**
+ * Creates a TypeCollector object
+ *
+ * @param pojoMetadata PojoMetadata describing Component.
+ * @param bundle Bundle which has been exposed the intended Component.
+ */
+ public InheritanceInspector(PojoMetadata pojoMetadata, Bundle bundle) {
+ m_pojoMetadata = pojoMetadata;
+ m_bundle = bundle;
+ }
+
+ /**
+ * Collect interfaces implemented by the POJO into given Sets.
+ *
+ * @param interfaces : the set of implemented interfaces
+ * @param classes : the set of extended classes
+ * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
+ */
+ public void computeInterfacesAndSuperClasses(Set interfaces, Set classes) throws ClassNotFoundException {
+ String[] immediateInterfaces = m_pojoMetadata.getInterfaces();
+ String parentClass = m_pojoMetadata.getSuperClass();
+
+ // First iterate on found specification in manipulation metadata
+ for (int i = 0; i < immediateInterfaces.length; i++) {
+ interfaces.add(immediateInterfaces[i]);
+ // Iterate on interfaces implemented by the current interface
+ Class clazz = m_bundle.loadClass(immediateInterfaces[i]);
+ collectInterfaces(clazz, interfaces, m_bundle);
+ }
+
+ // Look for parent class.
+ if (parentClass != null) {
+ Class clazz = m_bundle.loadClass(parentClass);
+ collectInterfacesFromClass(clazz, interfaces, m_bundle);
+ classes.add(parentClass);
+ collectParentClassesFromClass(clazz, classes, m_bundle);
+ }
+
+ // Removing Object Class from the inherited classes list.
+ classes.remove(Object.class.getName());
+ }
+
+ /**
+ * Look for inherited interfaces.
+ *
+ * @param clazz : interface name to explore (class object)
+ * @param acc : set (accumulator)
+ * @param bundle : bundle
+ * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
+ */
+ private void collectInterfaces(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class[] clazzes = clazz.getInterfaces();
+ for (int i = 0; i < clazzes.length; i++) {
+ acc.add(clazzes[i].getName());
+ collectInterfaces(clazzes[i], acc, bundle);
+ }
+ }
+
+ /**
+ * Collect interfaces for the given class.
+ * This method explores super class to.
+ *
+ * @param clazz : class object.
+ * @param acc : set of implemented interface (accumulator)
+ * @param bundle : bundle.
+ * @throws ClassNotFoundException : occurs if an interface cannot be load.
+ */
+ private void collectInterfacesFromClass(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class[] clazzes = clazz.getInterfaces();
+ for (int i = 0; i < clazzes.length; i++) {
+ acc.add(clazzes[i].getName());
+ collectInterfaces(clazzes[i], acc, bundle);
+ }
+ // Iterate on parent classes
+ Class sup = clazz.getSuperclass();
+ if (sup != null) {
+ collectInterfacesFromClass(sup, acc, bundle);
+ }
+ }
+
+ /**
+ * Collect parent classes for the given class.
+ *
+ * @param clazz : class object.
+ * @param acc : set of extended classes (accumulator)
+ * @param bundle : bundle.
+ * @throws ClassNotFoundException : occurs if an interface cannot be load.
+ */
+ private void collectParentClassesFromClass(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class parent = clazz.getSuperclass();
+ if (parent != null) {
+ acc.add(parent.getName());
+ collectParentClassesFromClass(parent, acc, bundle);
+ }
+ }
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentInstance.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentInstance.java
new file mode 100644
index 0000000..4f2ca74
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ComponentInstance.java
@@ -0,0 +1,128 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class defines the iPOJO's component instance concept.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentInstance {
+
+ /**
+ * Component Instance State : DISPOSED. The component instance was disposed.
+ */
+ int DISPOSED = -1;
+
+ /**
+ * Component Instance State : STOPPED. The component instance is not
+ * started.
+ */
+ int STOPPED = 0;
+
+ /**
+ * Component Instance State : INVALID. The component instance is invalid when it
+ * starts or when a component dependency is invalid.
+ */
+ int INVALID = 1;
+
+ /**
+ * Component Instance State : VALID. The component instance is resolved when it is
+ * running and all its attached handlers are valid.
+ */
+ int VALID = 2;
+
+ /**
+ * Starts the component instance.
+ */
+ void start();
+
+ /**
+ * Stops the component instance.
+ * A stopped instance can be re-started.
+ */
+ void stop();
+
+ /**
+ * Disposes the component instance.
+ * A disposed instance cannot be re-started.
+ */
+ void dispose();
+
+ /**
+ * Returns the actual state of the instance.
+ * @return the actual state of the component instance.
+ */
+ int getState();
+
+ /**
+ * Returns the instance description.
+ * @return the instance description of the current instance
+ */
+ InstanceDescription getInstanceDescription();
+
+ /**
+ * Returns the factory who created this instance.
+ * @return the factory of the component instance.
+ */
+ ComponentFactory getFactory();
+
+ /**
+ * Returns the bundle context of this instance.
+ * @return the context of the component instance
+ */
+ BundleContext getContext();
+
+ /**
+ * Returns the name of the instance.
+ * @return the name of the component instance
+ */
+ String getInstanceName();
+
+ /**
+ * Checks if the instance is started.
+ * @return <code>true</code> if {@link ComponentInstance#getState()}
+ * returns {@link ComponentInstance#INVALID} or {@link ComponentInstance#VALID}.
+ */
+ boolean isStarted();
+
+ /**
+ * Re-configures an instance. Do nothing if the instance does not support
+ * dynamic reconfiguration. The reconfiguration does not stop the instance.
+ * @param configuration the new configuration.
+ */
+ void reconfigure(Dictionary configuration);
+
+ /**
+ * Adds an instance state listener on the current instance.
+ * @param listener the listener to add.
+ */
+ void addInstanceStateListener(InstanceStateListener listener);
+
+ /**
+ * Removes an instance state listener on the current instance.
+ * @param listener the listener to remove.
+ */
+ void removeInstanceStateListener(InstanceStateListener listener);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConfigurationException.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConfigurationException.java
new file mode 100644
index 0000000..0bed6f4
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConfigurationException.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.felix.ipojo;
+
+/**
+ * This class defines the exception thrown when an instance cannot be configured correctly.
+ * This exception occurs when component metadata are not correct.
+ * @see Exception
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationException extends Exception {
+
+ /**
+ * Serialization Id.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The component type on which the error occurs.
+ * Uses the factory name.
+ */
+ private String m_type;
+
+ /**
+ * Creates a new configuration exception.
+ * @param mes the error message
+ * @param typ the component type
+ * @see Exception#Exception(String)
+ */
+ ConfigurationException(String mes, String typ) {
+ super(mes);
+ m_type = typ;
+ }
+
+ /**
+ * Creates a new configuration exception.
+ * @param mes the error message
+ * @see Exception#Exception(String)
+ */
+ public ConfigurationException(String mes) {
+ super(mes);
+ }
+
+ /**
+ * Gets the error message.
+ * @return the error message.
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ if (m_type == null) {
+ return super.getMessage();
+ } else {
+ return "The configuration is not correct for the type " + m_type + " : " + super.getMessage();
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConstructorInjector.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConstructorInjector.java
new file mode 100644
index 0000000..dffd3bc
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ConstructorInjector.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo;
+
+/**
+ * Interface implemented to support constructor parameter injection.
+ * When a new POJO object has to be created, all constructor injectors are
+ * called to gets the objects to injects as well as the type (to discover
+ * the constructor).
+ * Handlers willing to inject constructor parameters must register themselves
+ * using {@link InstanceManager#register(int, ConstructorInjector)} where
+ * the integer is the parameter index. Only one injector can inject a value
+ * for a specific index. If several injectors are registered for the same index,
+ * the component type is declared as invalid.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ConstructorInjector {
+
+ /**
+ * Gets the type of the object to inject in the constructor parameter.
+ * This is the type looked into the Pojo class, so it must match.
+ * Returning <code>null</code> will try to get the class from the
+ * injected object, however this can be wrong (implementation instead of interface,
+ * boxed objects...) and error-prone.
+ * @param index the parameter index
+ * @return the Class object (must fit for primitive type)
+ */
+ Object getConstructorParameter(int index);
+
+ /**
+ * Gets the type of the object to
+ * @param index
+ * @return
+ */
+ Class getConstructorParameterType(int index);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextListener.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextListener.java
new file mode 100644
index 0000000..a2d0fa0
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.felix.ipojo;
+
+
+/**
+ * Context Source Listener interface.
+ * A context source listener is notified when a monitored context
+ * property value changed.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ContextListener {
+
+ /**
+ * A monitored value has been modified.
+ * @param source the context source containing the property
+ * @param property the modified property name
+ * @param value the new value of the property
+ */
+ void update(ContextSource source, String property, Object value);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextSource.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextSource.java
new file mode 100644
index 0000000..de9a8eb
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ContextSource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.Dictionary;
+
+
+/**
+ * Context Source service interface.
+ * A context source advertises of context changes.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ContextSource {
+
+ /**
+ * Gets the current value of the given property.
+ * @param property property name
+ * @return the property value (<code>null</code> if unknown)
+ */
+ Object getProperty(String property);
+
+ /**
+ * Gets the entire context.
+ * @return the dictionary [Property, Value]
+ */
+ Dictionary getContext();
+
+ /**
+ * Registers a context listener on the given set of properties.
+ * The listener will be notified of every changes made on monitored properties.
+ * @param listener the context listener to register.
+ * @param properties property set monitored by the listener.
+ */
+ void registerContextListener(ContextListener listener, String[] properties);
+
+ /**
+ * Unregisters the given context listener.
+ * @param listener the listener to unregister.
+ */
+ void unregisterContextListener(ContextListener listener);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java
new file mode 100644
index 0000000..4792594
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java
@@ -0,0 +1,45 @@
+/*
+ * 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.felix.ipojo;
+
+/**
+ * Error Handler Service Definition.
+ * When exposed, this service is invoked when iPOJO throws a warning
+ * or an error. It's a hook on the internal iPOJO logger.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ErrorHandler {
+
+ /**
+ * Method invokes when an error occurred.
+ * @param instance the instance (can be <code>null</null>)
+ * @param message the error message
+ * @param error the error itself (can be <code>null</code>)
+ */
+ public void onError(ComponentInstance instance, String message, Throwable error);
+
+ /**
+ * Method invokes when a warning occurred.
+ * @param instance the instance (can be <code>null</null>)
+ * @param message the error message
+ * @param error the error itself (can be <code>null</code>)
+ */
+ public void onWarning(ComponentInstance instance, String message, Throwable error);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/EventDispatcher.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/EventDispatcher.java
new file mode 100644
index 0000000..8219422
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/EventDispatcher.java
@@ -0,0 +1,181 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * iPOJO Internal event dispatcher.
+ * This class provides an internal service event dispatcher in order to tackle the
+ * event storm that can happen when starting large-scale applications.
+ * @see Extender
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class EventDispatcher implements ServiceListener {
+
+ /**
+ * The internal event dispatcher.
+ * This dispatcher is a singleton.
+ */
+ private static EventDispatcher DISPATCHER;
+
+ /**
+ * The list of listeners.
+ * Service interface -> List of {@link ServiceListener}
+ */
+ private Map m_listeners;
+ /**
+ * The global bundle context.
+ * This is the bundle context from iPOJO.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Creates the EventDispatcher.
+ * @param bc the bundle context used to register and unregister
+ * {@link ServiceListener}.
+ */
+ private EventDispatcher(BundleContext bc) {
+ m_context = bc;
+ m_listeners = new HashMap();
+ // Only one thread can call the start method.
+ m_context.addServiceListener(this);
+ }
+
+ /**
+ * Creates the internal event
+ * dispatcher.
+ * @param bc the iPOJO bundle context to send to the
+ * internal event dispatcher.
+ */
+ public static void create(BundleContext bc) {
+ DISPATCHER = new EventDispatcher(bc);
+ }
+
+ /**
+ * Stops and delete the internal event dispatcher.
+ * This method must be call only
+ * if iPOJO is stopping.
+ */
+ public static void dispose() {
+ DISPATCHER.stop();
+ DISPATCHER = null;
+ }
+
+
+ /**
+ * Gets the iPOJO event dispatcher.
+ * @return the event dispatcher or
+ * <code>null</code> if not created.
+ */
+ public static EventDispatcher getDispatcher() {
+ return DISPATCHER;
+ }
+
+
+ /**
+ * Stops the event dispatcher.
+ * This method unregisters the {@link ServiceListener}.
+ * This methods must be called only when the iPOJO bundle
+ * stops.
+ */
+ private void stop() {
+ synchronized (this) {
+ m_context.removeServiceListener(this);
+ m_listeners.clear();
+ }
+ }
+
+ /**
+ * Method called when a {@link ServiceEvent} is
+ * fired by the OSGi framework.
+ * According to the event, this method dispatches
+ * to interested registered listers from
+ * the {@link EventDispatcher#m_listeners} map.
+ * @param event the service event
+ * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
+ */
+ public void serviceChanged(ServiceEvent event) {
+ String[] itfs = (String[]) event.getServiceReference().getProperty(Constants.OBJECTCLASS);
+ for (int s = 0; s < itfs.length; s++) {
+ List list;
+ synchronized (this) {
+ List stored = (List) m_listeners.get(itfs[s]);
+ if (stored == null) {
+ return; // Nothing to do
+ }
+ // Creates a new list (stack confinement)
+ list = new ArrayList(stored);
+ }
+ for (int i = 0; i < list.size(); i++) {
+ ((ServiceListener) list.get(i)).serviceChanged(event);
+ }
+ }
+ }
+
+ /**
+ * Adds a new service listener to the {@link EventDispatcher#m_listeners}
+ * map. This method specifies the listen service interface
+ * @param itf the service interface
+ * @param listener the service listener
+ */
+ public void addListener(String itf, ServiceListener listener) {
+ synchronized (this) {
+ List list = (List) m_listeners.get(itf);
+ if (list == null) {
+ list = new ArrayList(1);
+ list.add(listener);
+ m_listeners.put(itf, list);
+ } else {
+ list.add(listener);
+ }
+ }
+ }
+
+ /**
+ * Removes a service listener.
+ * @param listener the service listener to remove
+ * @return <code>true</code> if the listener is
+ * successfully removed.
+ */
+ public boolean removeListener(ServiceListener listener) {
+ boolean removed = false;
+ synchronized (this) {
+ Set keys = m_listeners.keySet();
+ Iterator it = keys.iterator();
+ while (it.hasNext()) {
+ String itf = (String) it.next();
+ List list = (List) m_listeners.get(itf);
+ removed = removed || list.remove(listener);
+ }
+ }
+ return removed;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java
new file mode 100644
index 0000000..146f06e
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java
@@ -0,0 +1,782 @@
+/*
+ * 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.felix.ipojo;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.util.Logger;
+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;
+
+/**
+ * iPOJO Extender.
+ * This class listens bundle arrivals and departures in order to detect and manage
+ * iPOJO powered bundles. This class creates factories and ask for instance creation.
+ * @see SynchronousBundleListener
+ * @see BundleActivator
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Extender implements SynchronousBundleListener, BundleActivator {
+
+ /**
+ * Enables the iPOJO internal dispatcher.
+ * This internal dispatcher helps the OSGi framework to support large
+ * scale applications. The internal dispatcher is disabled by default.
+ */
+ static boolean DISPATCHER_ENABLED = true;
+
+ /**
+ * Disables the iPOJO asynchronous processing.
+ * When set to false, the bundles are processed in the listener thread
+ * making iPOJO usable on Google App Engine. By default, the processing
+ * is asynchronous.
+ */
+ static boolean SYNCHRONOUS_PROCESSING_ENABLED = false;
+
+ /**
+ * Property allowing to set if the internal dispatcher is enabled or disabled.
+ * Possible value are either <code>true</code> or <code>false</code>.
+ */
+ private static final String ENABLING_DISPATCHER = "ipojo.internal.dispatcher";
+
+ /**
+ * Property allowing to disable the asynchronous process (and so enables the
+ * synchronous processing).
+ * Possible value are either <code>true</code> or <code>false</code>.
+ */
+ private static final String SYNCHRONOUS_PROCESSING = "ipojo.processing.synchronous";
+
+ /**
+ * iPOJO Component Type and Instance declaration header.
+ */
+ private static final String IPOJO_HEADER = "iPOJO-Components";
+
+ /**
+ * iPOJO Component Type and Instance declaration header
+ * (alternative).
+ * This header was introduced because of BND supporting only header
+ * starting with an uppercase.
+ */
+ private static final String IPOJO_HEADER_ALT = "IPOJO-Components";
+
+ /**
+ * iPOJO Extension declaration header.
+ */
+ private static final String IPOJO_EXTENSION = "IPOJO-Extension";
+
+ /**
+ * The Bundle Context of the iPOJO Core bundle.
+ */
+ private static BundleContext m_context;
+
+ /**
+ * The iPOJO Extender logger.
+ */
+ private Logger m_logger;
+
+ /**
+ * The instance creator used to create instances.
+ * (Singleton)
+ */
+ private InstanceCreator m_creator;
+
+ /**
+ * The iPOJO Bundle.
+ */
+ private Bundle m_bundle;
+
+ /**
+ * The list of factory types.
+ */
+ private List m_factoryTypes = new ArrayList();
+
+ /**
+ * The list of unbound types.
+ * A type is unbound if the matching extension is not deployed.
+ */
+ private final List m_unboundTypes = new ArrayList();
+
+ /**
+ * The processor analyzing arriving bundles and creating iPOJO contributions.
+ */
+ private final CreatorThread m_processor = new CreatorThread();
+
+ /**
+ * Bundle Listener Notification.
+ * @param event the bundle event.
+ * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
+ */
+ public void bundleChanged(final BundleEvent event) {
+ if (event.getBundle() == m_bundle) { return; }
+
+ switch (event.getType()) {
+ case BundleEvent.STARTED:
+ // Put the bundle in the queue
+ m_processor.addBundle(event.getBundle());
+ break;
+ case BundleEvent.STOPPING:
+ m_processor.removeBundle(event.getBundle());
+ //TODO Should be done in another thread in the asynchronous case.
+ closeManagementFor(event.getBundle());
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ /**
+ * Ends the iPOJO Management for the given bundle.
+ * Generally the bundle is leaving. This method
+ * stops every factories declared is the bundle and
+ * disposed every declared instances.
+ * @param bundle the bundle.
+ */
+ private void closeManagementFor(Bundle bundle) {
+ List toRemove = new ArrayList();
+ // Delete instances declared in the leaving bundle.
+ m_creator.removeInstancesFromBundle(bundle.getBundleId());
+ for (int k = 0; k < m_factoryTypes.size(); k++) {
+ ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) m_factoryTypes.get(k);
+
+ // Look for component type created from this bundle.
+ if (mft.m_created != null) {
+ List cfs = (List) mft.m_created.remove(bundle);
+ for (int i = 0; cfs != null && i < cfs.size(); i++) {
+ IPojoFactory factory = (IPojoFactory) cfs.get(i);
+ m_creator.removeFactory(factory);
+ factory.stop();
+ }
+ }
+
+ // If the leaving bundle has declared mft : destroy all created factories.
+ if (mft.m_bundle == bundle) {
+ if (mft.m_created != null) {
+ Iterator iterator = mft.m_created.keySet().iterator();
+ while (iterator.hasNext()) {
+ Bundle key = (Bundle) iterator.next();
+ List list = (List) mft.m_created.get(key);
+ for (int i = 0; i < list.size(); i++) {
+ IPojoFactory factory = (IPojoFactory) list.get(i);
+ factory.stop();
+ m_unboundTypes.add(new UnboundComponentType(mft.m_type, factory.m_componentMetadata, factory.getBundleContext()
+ .getBundle()));
+ }
+ }
+ }
+ toRemove.add(mft);
+ }
+ }
+
+ for (int i = 0; i < toRemove.size(); i++) {
+ ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) toRemove.get(i);
+ m_logger.log(Logger.INFO, "The factory type: " + mft.m_type + " is no more available");
+ mft.m_bundle = null;
+ mft.m_clazz = null;
+ mft.m_created = null;
+ mft.m_type = null;
+ m_factoryTypes.remove(mft);
+ }
+ }
+
+ /**
+ * Checks if the given bundle is an iPOJO bundle, and begin
+ * the iPOJO management is true.
+ * @param bundle the bundle to check.
+ */
+ private void startManagementFor(Bundle bundle) {
+ Dictionary dict = bundle.getHeaders();
+ // Check for abstract factory type
+ String typeHeader = (String) dict.get(IPOJO_EXTENSION);
+ if (typeHeader != null) {
+ parseAbstractFactoryType(bundle, typeHeader);
+ }
+
+ // Check bundle
+ String header = (String) dict.get(IPOJO_HEADER);
+ // Check the alternative header
+ if (header == null) {
+ header = (String) dict.get(IPOJO_HEADER_ALT);
+ }
+
+ if (header != null) {
+ try {
+ parse(bundle, header);
+ } catch (IOException e) {
+ m_logger.log(Logger.ERROR, "An exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
+ } catch (ParseException e) {
+ m_logger.log(Logger.ERROR, "A parse exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
+ }
+ }
+ }
+
+ /**
+ * Parses an IPOJO-Extension manifest header and then creates
+ * iPOJO extensions (factory types).
+ * @param bundle the bundle containing the header.
+ * @param header the header to parse.
+ */
+ private void parseAbstractFactoryType(Bundle bundle, String header) {
+ String[] arr = ParseUtils.split(header, ",");
+ for (int i = 0; arr != null && i < arr.length; i++) {
+ String[] arr2 = ParseUtils.split(arr[i], ":");
+
+ /*
+ * Get the fully qualified type name.
+ * type = [namespace] name
+ */
+ String[] nameparts = ParseUtils.split(arr2[0].trim(), " \t");
+ String type = nameparts.length == 1 ? nameparts[0] : nameparts[0]+":"+nameparts[1];
+
+ Class clazz;
+ try {
+ clazz = bundle.loadClass(arr2[1]);
+ } catch (ClassNotFoundException e) {
+ m_logger.log(Logger.ERROR, "Cannot load the extension " + type, e);
+ return;
+ }
+ ManagedAbstractFactoryType mft = new ManagedAbstractFactoryType(clazz, type, bundle);
+ m_factoryTypes.add(mft);
+ m_logger.log(Logger.DEBUG, "New factory type available: " + type);
+
+ for (int j = m_unboundTypes.size() - 1; j >= 0; j--) {
+ UnboundComponentType unbound = (UnboundComponentType) m_unboundTypes.get(j);
+ if (unbound.m_type.equals(type)) {
+ createAbstractFactory(unbound.m_bundle, unbound.m_description);
+ m_unboundTypes.remove(unbound);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses the internal metadata (from the manifest
+ * (in the iPOJO-Components property)). This methods
+ * creates factories and add instances to the instance creator.
+ * @param bundle the owner bundle.
+ * @param components The iPOJO Header String.
+ * @throws IOException if the manifest can not be found
+ * @throws ParseException if the parsing process failed
+ */
+ private void parse(Bundle bundle, String components) throws IOException, ParseException {
+ ManifestMetadataParser parser = new ManifestMetadataParser();
+ parser.parseHeader(components);
+
+ // Get the component type declaration
+ Element[] metadata = parser.getComponentsMetadata();
+ for (int i = 0; i < metadata.length; i++) {
+ createAbstractFactory(bundle, metadata[i]);
+ }
+
+ Dictionary[] instances = parser.getInstances();
+ for (int i = 0; instances != null && i < instances.length; i++) {
+ m_creator.addInstance(instances[i], bundle.getBundleId());
+ }
+ }
+
+ /**
+ * iPOJO Start method.
+ * @param context the iPOJO bundle context.
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) {
+ m_context = context;
+ m_bundle = context.getBundle();
+ m_creator = new InstanceCreator(context);
+
+ m_logger = new Logger(m_context, "IPOJO-Extender");
+
+ enablingDispatcher(context, m_logger);
+ enablingSynchronousProcessing(context, m_logger);
+
+ // Create the dispatcher only if required.
+ if (DISPATCHER_ENABLED) {
+ EventDispatcher.create(context);
+ }
+
+ // Begin by initializing core handlers
+ startManagementFor(m_bundle);
+
+ if (! SYNCHRONOUS_PROCESSING_ENABLED) {
+ new Thread(m_processor).start();
+ }
+
+ synchronized (this) {
+ // listen to any changes in bundles.
+ m_context.addBundleListener(this);
+ // compute already started bundles.
+ for (int i = 0; i < context.getBundles().length; i++) {
+ if (context.getBundles()[i].getState() == Bundle.ACTIVE) {
+ m_processor.addBundle(context.getBundles()[i]); // Bundles are processed in another thread.
+ }
+ }
+ }
+
+ m_logger.log(Logger.INFO, "iPOJO Runtime started");
+ }
+
+ /**
+ * Stops the iPOJO Bundle.
+ * @param context the bundle context.
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) {
+ m_processor.stop(); // Stop the thread processing bundles.
+ m_context.removeBundleListener(this);
+
+ if (DISPATCHER_ENABLED) {
+ EventDispatcher.dispose();
+ }
+
+ for (int k = 0; k < m_factoryTypes.size(); k++) {
+ ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) m_factoryTypes.get(k);
+
+ if (mft.m_created != null) {
+ Iterator iterator = mft.m_created.keySet().iterator();
+ while (iterator.hasNext()) {
+ Bundle key = (Bundle) iterator.next();
+ List list = (List) mft.m_created.get(key);
+ for (int i = 0; i < list.size(); i++) {
+ IPojoFactory factory = (IPojoFactory) list.get(i);
+ m_creator.removeFactory(factory);
+ factory.dispose();
+ }
+ }
+ }
+ }
+
+ m_factoryTypes = null;
+ m_creator = null;
+
+ m_logger.log(Logger.INFO, "iPOJO Runtime stopped");
+ m_context = null;
+ }
+
+ /**
+ * Gets iPOJO bundle context.
+ * @return the iPOJO Bundle Context
+ */
+ public static BundleContext getIPOJOBundleContext() {
+ return m_context;
+ }
+
+ /**
+ * Enables or disables the internal dispatcher, so sets the
+ * {@link Extender#DISPATCHER_ENABLED} flag.
+ * This method checks if the {@link Extender#ENABLING_DISPATCHER}
+ * property is set to <code>true</code>. Otherwise, the internal
+ * dispatcher is disabled. The property can be set as a system
+ * property (<code>ipojo.internal.dispatcher</code>) or inside the
+ * iPOJO bundle manifest (<code>ipojo-internal-dispatcher</code>).
+ * @param context the bundle context.
+ * @param logger the logger to indicates if the internal dispatcher is set.
+ */
+ private static void enablingDispatcher(BundleContext context, Logger logger) {
+ // First check in the framework and in the system properties
+ String flag = context.getProperty(ENABLING_DISPATCHER);
+
+ // If null, look in bundle manifest
+ if (flag == null) {
+ String key = ENABLING_DISPATCHER.replace('.', '-');
+ flag = (String) context.getBundle().getHeaders().get(key);
+ }
+
+ if (flag != null) {
+ if (flag.equalsIgnoreCase("true")) {
+ Extender.DISPATCHER_ENABLED = true;
+ logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher enables");
+ return;
+ }
+ }
+
+ // Either l is null, or the specified value was false
+ Extender.DISPATCHER_ENABLED = false;
+ logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher disables");
+
+ }
+
+ /**
+ * Enables or disables the asynchronous processing, so sets the
+ * {@link Extender#SYNCHRONOUS_PROCESSING_ENABLED} flag.
+ * Disabling asynchronous processing avoids iPOJO to create a new
+ * thread to process bundles. So, iPOJO can be used on the
+ * Google App Engine.
+ * This method checks if the {@link Extender#SYNCHRONOUS_PROCESSING}
+ * property is set to <code>true</code>. Otherwise, asynchronous processing
+ * is used (default). The property can be set as a system
+ * property (<code>ipojo.processing.synchronous</code>) or inside the
+ * iPOJO bundle manifest.
+ * @param context the bundle context.
+ * @param logger the logger to indicates if the internal dispatcher is set.
+ */
+ private static void enablingSynchronousProcessing(BundleContext context, Logger logger) {
+ String flag = context.getProperty(SYNCHRONOUS_PROCESSING);
+
+ // If null, look in bundle manifest
+ if (flag == null) {
+ String key = SYNCHRONOUS_PROCESSING.replace('.', '-');
+ flag = (String) context.getBundle().getHeaders().get(key);
+ }
+
+ if (flag != null) {
+ if (flag.equalsIgnoreCase("true")) {
+ Extender.SYNCHRONOUS_PROCESSING_ENABLED = true;
+ logger.log(Logger.INFO, "iPOJO Asynchronous processing disabled");
+ return;
+ }
+ }
+
+ // Either l is null, or the specified value was false
+ Extender.SYNCHRONOUS_PROCESSING_ENABLED = false;
+ logger.log(Logger.INFO, "iPOJO synchrnous processing disables");
+
+ }
+
+ /**
+ * Adds a component factory to the factory list.
+ * @param metadata the new component metadata.
+ * @param bundle the bundle.
+ */
+ private void createAbstractFactory(Bundle bundle, Element metadata) {
+ ManagedAbstractFactoryType factoryType = null;
+ // First, look for factory-type (component, handler, composite ...)
+
+ // TODO : Should Element.getQualifiedName() be public ?
+ String typeName = metadata.getNameSpace() == null ? metadata.getName() : metadata.getNameSpace()+":"+metadata.getName();
+
+ for (int i = 0; i < m_factoryTypes.size(); i++) {
+ ManagedAbstractFactoryType type = (ManagedAbstractFactoryType) m_factoryTypes.get(i);
+ if (type.m_type.equals(typeName)) {
+ factoryType = type;
+ break;
+ }
+ }
+
+ // If not found, return. It will wait for a new component type factory.
+ if (factoryType == null) {
+ m_logger.log(Logger.WARNING, "Type of component not available: " + typeName);
+ m_unboundTypes.add(new UnboundComponentType(typeName, metadata, bundle));
+ return;
+ }
+
+ // Once found, we invoke the AbstractFactory constructor to create the component factory.
+ Class clazz = factoryType.m_clazz;
+ try {
+ // Look for the constructor, and invoke it.
+ Constructor cst = clazz.getConstructor(new Class[] { BundleContext.class, Element.class });
+ IPojoFactory factory = (IPojoFactory) cst.newInstance(new Object[] { getBundleContext(bundle), metadata });
+
+ // Add the created factory in the m_createdFactories map.
+ if (factoryType.m_created == null) {
+ factoryType.m_created = new HashMap();
+ List list = new ArrayList();
+ list.add(factory);
+ factoryType.m_created.put(bundle, list);
+ } else {
+ List list = (List) factoryType.m_created.get(bundle);
+ if (list == null) {
+ list = new ArrayList();
+ list.add(factory);
+ factoryType.m_created.put(bundle, list);
+ } else {
+ list.add(factory);
+ }
+ }
+
+ // Start the created factory.
+ factory.start();
+ // Then add the factory to the instance creator.
+ m_creator.addFactory(factory);
+
+ } catch (SecurityException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
+ } catch (NoSuchMethodException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName() + ": the given class constructor cannot be found");
+ } catch (IllegalArgumentException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
+ } catch (InstantiationException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
+ } catch (IllegalAccessException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
+ } catch (InvocationTargetException e) {
+ m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e.getTargetException());
+ }
+ }
+
+ /**
+ * Structure storing an iPOJO extension.
+ */
+ private static final class ManagedAbstractFactoryType {
+ /**
+ * The type (i.e.) name of the extension.
+ */
+ String m_type;
+
+ /**
+ * The abstract Factory class.
+ */
+ Class m_clazz;
+
+ /**
+ * The bundle object containing the declaration of the extension.
+ */
+ Bundle m_bundle;
+
+ /**
+ * The factories created by this extension.
+ */
+ private Map m_created;
+
+ /**
+ * Creates a ManagedAbstractFactoryType.
+ * @param factory the abstract factory class.
+ * @param type the name of the extension.
+ * @param bundle the bundle declaring the extension.
+ */
+ protected ManagedAbstractFactoryType(Class factory, String type, Bundle bundle) {
+ m_bundle = bundle;
+ m_clazz = factory;
+ m_type = type;
+ }
+ }
+
+ /**
+ * Structure storing unbound component type declarations.
+ * Unbound means that there is no extension able to manage the extension.
+ */
+ private static final class UnboundComponentType {
+ /**
+ * The component type description.
+ */
+ private final Element m_description;
+
+ /**
+ * The bundle declaring this type.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * The required extension name.
+ */
+ private final String m_type;
+
+ /**
+ * Creates a UnboundComponentType.
+ * @param description the description of the component type.
+ * @param bundle the bundle declaring this type.
+ * @param type the required extension name.
+ */
+ protected UnboundComponentType(String type, Element description, Bundle bundle) {
+ m_type = type;
+ m_description = description;
+ m_bundle = bundle;
+ }
+ }
+
+ /**
+ * Computes the bundle context from the bundle class by introspection.
+ * @param bundle the bundle.
+ * @return the bundle context object or <code>null</code> if not found.
+ */
+ public BundleContext getBundleContext(Bundle bundle) {
+ if (bundle == null) { return null; }
+
+ // getBundleContext (OSGi 4.1)
+ Method meth = null;
+ try {
+ meth = bundle.getClass().getMethod("getBundleContext", new Class[0]); // This method is public and is specified in the Bundle interface.
+ } catch (SecurityException e) {
+ // Nothing do to, will try the Equinox method
+ } catch (NoSuchMethodException e) {
+ // Nothing do to, will try the Equinox method
+ }
+
+ // try Equinox getContext if not found.
+ if (meth == null) {
+ try {
+ meth = bundle.getClass().getMethod("getContext", new Class[0]);
+ } catch (SecurityException e) {
+ // Nothing do to, will try field inspection
+ } catch (NoSuchMethodException e) {
+ // Nothing do to, will try field inspection
+ }
+ }
+
+ if (meth != null) {
+ if (! meth.isAccessible()) {
+ // If not accessible, try to set the accessibility.
+ meth.setAccessible(true);
+ }
+ try {
+ return (BundleContext) meth.invoke(bundle, new Object[0]);
+ } catch (IllegalArgumentException e) {
+ m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e);
+ return null;
+ } catch (IllegalAccessException e) {
+ m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e);
+ return null;
+ } catch (InvocationTargetException e) {
+ m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e.getTargetException());
+ return null;
+ }
+ }
+
+ // Else : Field inspection (KF and Prosyst)
+ Field[] fields = bundle.getClass().getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ if (BundleContext.class.isAssignableFrom(fields[i].getType())) {
+ if (!fields[i].isAccessible()) {
+ fields[i].setAccessible(true);
+ }
+ try {
+ return (BundleContext) fields[i].get(bundle);
+ } catch (IllegalArgumentException e) {
+ m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + fields[i].getName(), e);
+ return null;
+ } catch (IllegalAccessException e) {
+ m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + fields[i].getName(), e);
+ return null;
+ }
+ }
+ }
+ m_logger.log(Logger.ERROR, "Cannot find the BundleContext for " + bundle.getSymbolicName(), null);
+ return null;
+ }
+
+
+ /**
+ * The creator thread analyzes arriving bundles to create iPOJO contribution.
+ */
+ private class CreatorThread implements Runnable {
+
+ /**
+ * Is the creator thread started?
+ */
+ private boolean m_started = true;
+
+ /**
+ * The list of bundle that are going to be analyzed.
+ */
+ private List m_bundles = new ArrayList();
+
+ /**
+ * A bundle is arriving.
+ * This method is synchronized to avoid concurrent modification of the waiting list.
+ * @param bundle the new bundle
+ */
+ public synchronized void addBundle(Bundle bundle) {
+ if (SYNCHRONOUS_PROCESSING_ENABLED) {
+ m_logger.log(Logger.DEBUG, "Analyzing " + bundle.getBundleId());
+ startManagementFor(bundle);
+ } else {
+ // Asynchronous case, we add the bundle to the queue
+ m_bundles.add(bundle);
+ notifyAll(); // Notify the thread to force the process.
+ m_logger.log(Logger.DEBUG, "Creator thread is going to analyze the bundle " + bundle.getBundleId() + " List : " + m_bundles);
+ }
+ }
+
+ /**
+ * A bundle is leaving.
+ * If the bundle was not already processed, the bundle is remove from the waiting list.
+ * This method is synchronized to avoid concurrent modification of the waiting list.
+ * @param bundle the leaving bundle.
+ */
+ public synchronized void removeBundle(Bundle bundle) {
+ m_bundles.remove(bundle);
+ }
+
+ /**
+ * Stops the creator thread.
+ */
+ public synchronized void stop() {
+ m_started = false;
+ m_bundles.clear();
+ notifyAll();
+ }
+
+ /**
+ * Creator thread's run method.
+ * While the list is not empty, the thread launches the bundle analyzing on the next bundle.
+ * When the list is empty, the thread sleeps until the arrival of a new bundle
+ * or until iPOJO stops.
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ m_logger.log(Logger.DEBUG, "Creator thread is starting");
+ boolean started;
+ synchronized (this) {
+ started = m_started;
+ }
+ while (started) {
+ Bundle bundle;
+ synchronized (this) {
+ while (m_started && m_bundles.isEmpty()) {
+ try {
+ m_logger.log(Logger.DEBUG, "Creator thread is waiting - Nothing to do");
+ wait();
+ } catch (InterruptedException e) {
+ // Interruption, re-check the condition
+ }
+ }
+ if (!m_started) {
+ m_logger.log(Logger.DEBUG, "Creator thread is stopping");
+ return; // The thread must be stopped immediately.
+ } else {
+ // The bundle list is not empty, get the bundle.
+ // The bundle object is collected inside the synchronized block to avoid
+ // concurrent modification. However the real process is made outside the
+ // mutual exclusion area
+ bundle = (Bundle) m_bundles.remove(0);
+ }
+ }
+ // Process ...
+ m_logger.log(Logger.DEBUG, "Creator thread is processing " + bundle.getBundleId());
+ try {
+ startManagementFor(bundle);
+ } catch (Throwable e) {
+ // To be sure to not kill the thread, we catch all exceptions and errors
+ m_logger.log(Logger.ERROR, "An error occurs when analyzing the content or starting the management of " + bundle.getBundleId(), e);
+ }
+ synchronized (this) {
+ started = m_started;
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
new file mode 100644
index 0000000..f18c1d3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
@@ -0,0 +1,164 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Component Type Factory Service. This service is exposed by a instance manager factory, and allows the dynamic creation of component instance.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Factory {
+
+ /**
+ * Factory State.
+ * A valid factory is a factory where all required handlers are available.
+ */
+ int VALID = 1;
+
+ /**
+ * Factory State.
+ * An invalid factory is a factory where at least one required handler is
+ * unavailable. Creating an instance with an invalid factory failed.
+ */
+ int INVALID = 0;
+
+ /**
+ * Creates an instance manager (i.e. component type instance).
+ * @param configuration the configuration properties for this component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration if the given configuration is not valid.
+ * @throws MissingHandlerException if an handler is missing.
+ * @throws ConfigurationException if the instance configuration failed.
+ */
+ ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException;
+
+ /**
+ * Creates an instance manager (i.e. component type instance).
+ * The instance is created in the scope given in argument.
+ * @param configuration the configuration properties for this component.
+ * @param serviceContext the service context of the component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration if the given configuration is not valid.
+ * @throws MissingHandlerException if an handler is missing.
+ * @throws ConfigurationException if the instance configuration failed.
+ */
+ ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException;
+
+ /**
+ * Gets the component type information containing provided service,
+ * configuration properties ...
+ * @return the component type information.
+ */
+ Element getDescription();
+
+ /**
+ * Gets the component type description.
+ * @return the component type description object
+ */
+ ComponentTypeDescription getComponentDescription();
+
+ /**
+ * Checks if the given configuration is acceptable as a configuration
+ * of a component instance.
+ * @param conf the configuration to test
+ * @return <code>true</code> if the configuration is acceptable
+ */
+ boolean isAcceptable(Dictionary conf);
+
+ /**
+ * Returns the factory name.
+ * @return the name of the factory.
+ */
+ String getName();
+
+ /**
+ * Reconfigures an instance already created. This configuration needs to
+ * have the name property to identify the instance.
+ * @param conf the configuration to reconfigure the instance.
+ * @throws UnacceptableConfiguration if the given configuration is not consistent for the targeted instance.
+ * @throws MissingHandlerException if an handler is missing.
+ */
+ void reconfigure(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException;
+
+ /**
+ * Adds a factory state listener on the current factory.
+ * @param listener the listener to add
+ */
+ void addFactoryStateListener(FactoryStateListener listener);
+
+ /**
+ * Removes the given factory state listener from the listener list.
+ * @param listener the listener to remove
+ */
+ void removeFactoryStateListener(FactoryStateListener listener);
+
+ /**
+ * Gets the list of missing handlers.
+ * The handlers are given under the form namespace:name
+ * @return the list containing the name of missing handlers
+ */
+ List getMissingHandlers();
+
+ /**
+ * Get the list of required handlers.
+ * The handlers are given under the form namespace:name
+ * @return the list containing the name of required handlers
+ */
+ List getRequiredHandlers();
+
+ /**
+ * Returns the class name of the component type.
+ * For factories which does not contains a class, return "composite"
+ * @return the class name of the component type or "composite"
+ * @deprecated
+ */
+ String getClassName();
+
+ /**
+ * Returns the state of the factory.
+ * @return the state of the factory
+ */
+ int getState();
+
+ /**
+ * Gets the bundle context of the factory.
+ * @return the bundle context of the factory.
+ */
+ BundleContext getBundleContext();
+
+ /**
+ * Gets the version of the component type.
+ * @return the component type version or <code>null</code> if
+ * not specified.
+ */
+ String getVersion();
+
+ /**
+ * Gets the component type metadata (Element - Attribute structure)
+ * @return the root element of the component metadata. The result must <b>not</b> be modified.
+ */
+ Element getComponentMetadata();
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FactoryStateListener.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FactoryStateListener.java
new file mode 100644
index 0000000..3d9dadd
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FactoryStateListener.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 org.apache.felix.ipojo;
+
+/**
+ * A factory state listener received notification about monitored factory state changes.
+ * This listener allows anyone to be notified when the listened factory state changes.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface FactoryStateListener {
+
+ /**
+ * Notification listener.
+ * Each time an instance state changes, this method is called
+ * with the new factory state.
+ * @param factory the changing factory
+ * @param newState the new factory state
+ */
+ void stateChanged(Factory factory, int newState);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FieldInterceptor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FieldInterceptor.java
new file mode 100644
index 0000000..22e308d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/FieldInterceptor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.felix.ipojo;
+
+/**
+* A field interceptor is notified when a monitored field asks for a value or
+* receives a new value. A class implementing this interface is able to be
+* notified of field accesses, and is able to inject a value to this field.
+* The listener needs to be register on the instance manager.
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public interface FieldInterceptor {
+
+ /**
+ * This method is called when a PUTFIELD operation is detected,
+ * e.g. an assignation.
+ * @param pojo the pojo object setting the value
+ * @param fieldName the field name
+ * @param value the value passed to the field
+ */
+ void onSet(Object pojo, String fieldName, Object value);
+
+ /**
+ * This method is called when a GETFIELD operation is detected.
+ * This method allows to inject a value to the field.
+ * @param pojo the pojo object getting the value
+ * @param fieldName the field name
+ * @param value the value passed to the field (by the previous call)
+ * @return the managed value of the field
+ */
+ Object onGet(Object pojo, String fieldName, Object value);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Handler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Handler.java
new file mode 100644
index 0000000..9e14cd5
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Handler.java
@@ -0,0 +1,264 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Logger;
+
+/**
+ * Handler Abstract Class.
+ * A handler is a 'piece' of
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class Handler {
+
+ /**
+ * The handler namespace property.
+ */
+ public static final String HANDLER_NAMESPACE_PROPERTY = "handler.namespace";
+
+ /**
+ * The handler name property.
+ */
+ public static final String HANDLER_NAME_PROPERTY = "handler.name";
+
+ /**
+ * The handler type property.
+ */
+ public static final String HANDLER_TYPE_PROPERTY = "handler.type";
+
+ /**
+ * The handler priority.
+ */
+ public static final String HANDLER_LEVEL_PROPERTY = "handler.level";
+
+ /**
+ * The current handler validity.
+ * This impacts directly the instance state.
+ */
+ protected boolean m_isValid = true;
+
+ /**
+ * The HandlerManager managing the current handler.
+ */
+ protected HandlerManager m_instance;
+
+ /**
+ * Sets the factory attached to this handler object.
+ * This method must be override to depend on each component factory type.
+ * @param factory the factory to attach.
+ */
+ public abstract void setFactory(Factory factory);
+
+ /**
+ * Gets the logger to use in the handler.
+ * This method must be override to depend on each component factory type logging policy.
+ * @return the logger.
+ */
+ public abstract Logger getLogger();
+
+ /**
+ * Log method (warning).
+ * Log a warning message to the handler logger.
+ * @param message the message to log
+ */
+ public final void warn(String message) {
+ getLogger().log(Logger.WARNING, message);
+ }
+
+ /**
+ * Log method (error).
+ * Log an error message to the handler logger.
+ * @param message the message to log
+ */
+ public final void error(String message) {
+ getLogger().log(Logger.ERROR, message);
+ }
+
+ /**
+ * Log method (info).
+ * Log an info message to the handler logger.
+ * @param message the message to log
+ */
+ public final void info(String message) {
+ getLogger().log(Logger.INFO, message);
+ }
+
+ /**
+ * Log method (debug).
+ * Log a debug message to the handler logger.
+ * @param message the message to log
+ */
+ public final void debug(String message) {
+ getLogger().log(Logger.DEBUG, message);
+ }
+
+ /**
+ * Log method (warning).
+ * Log a warning message to the handler logger.
+ * The message is sent with an attached exception.
+ * @param message the message to log
+ * @param exception the exception to attach with the message
+ */
+ public final void warn(String message, Throwable exception) {
+ getLogger().log(Logger.WARNING, message, exception);
+ }
+
+ /**
+ * Log method (error).
+ * Log an error message to the handler logger.
+ * The message is sent with an attached exception.
+ * @param message the message to log
+ * @param exception the exception to attach to the message
+ */
+ public final void error(String message, Throwable exception) {
+ getLogger().log(Logger.ERROR, message, exception);
+ }
+
+ /**
+ * Get a plugged handler of the same container.
+ * This method must be call only in the start method (or after).
+ * In the configure method, this method can not return a consistent
+ * result as all handlers are not plugged.
+ * @param name : name of the handler to find (class name or qualified handler name (ns:name)).
+ * @return the handler object or null if the handler is not found.
+ */
+ public abstract Handler getHandler(String name);
+
+ /**
+ * Attaches the current handler object to the given component instance.
+ * An attached handler becomes a part of the instance container.
+ * @param instance the component instance on which the current handler will be attached.
+ */
+ protected abstract void attach(ComponentInstance instance);
+
+ /**
+ * Checks if the current handler is valid.
+ * This check tests the handler validity.
+ * This method must not be override.
+ * @return <code>true</code> if the handler is valid.
+ */
+ public final boolean isValid() {
+ return ((Pojo) this).getComponentInstance().getState() == ComponentInstance.VALID;
+ }
+
+ /**
+ * Sets the validity of the current handler.
+ * @param isValid if <code>true</code> the handler becomes valid, else it becomes invalid.
+ */
+ public final void setValidity(boolean isValid) {
+ if (m_isValid != isValid) {
+ m_isValid = isValid;
+ HandlerManager instance = getHandlerManager();
+ if (isValid) {
+ instance.stateChanged(instance, ComponentInstance.VALID);
+ } else {
+ instance.stateChanged(instance, ComponentInstance.INVALID);
+ }
+ }
+ }
+
+ /**
+ * Is the current handler valid.
+ * @return <code>true</code> if the handler is valid, <code>false</code> otherwise.
+ */
+ public final boolean getValidity() {
+ return m_isValid;
+ }
+
+ /**
+ * Gets the component instance of the current handler.
+ * @return the component instance.
+ */
+ public final HandlerManager getHandlerManager() {
+ if (m_instance != null) { return m_instance; }
+ m_instance = (HandlerManager) ((Pojo) this).getComponentInstance();
+ return m_instance;
+ }
+
+ /**
+ * Initializes the component factory.
+ * This method aims to collect component factory properties.
+ * Each handler wanting to contribute needs to override this
+ * method and adds properties to the given component description.
+ * By default, this method does nothing.
+ * @param typeDesc the component description.
+ * @param metadata the component type metadata.
+ * @throws ConfigurationException if the metadata are not correct (early detection).
+ */
+ public void initializeComponentFactory(ComponentTypeDescription typeDesc, Element metadata) throws ConfigurationException {
+ // The default implementation does nothing.
+ }
+
+ /**
+ * Configures the handler.
+ * @param metadata the metadata of the component
+ * @param configuration the instance configuration
+ * @throws ConfigurationException if the metadata are not correct.
+ */
+ public abstract void configure(Element metadata, Dictionary configuration) throws ConfigurationException;
+
+ /**
+ * Stops the handler
+ * This method stops the management.
+ */
+ public abstract void stop();
+
+ /**
+ * Starts the handler
+ * This method starts the management.
+ */
+ public abstract void start();
+
+ /**
+ * This method is called when the component state changed.
+ * By default, this method does nothing.
+ * @param state the new instance state {@link ComponentInstance}
+ */
+ public void stateChanged(int state) {
+ // The default implementation does nothing.
+ }
+
+ /**
+ * Returns the current handler description.
+ * The simplest description contains only the name and the validity of the handler.
+ * If the handler override this method, it can customize the description.
+ * By default, this method returns the simplest description.
+ * @return the description of the handler.
+ */
+ public HandlerDescription getDescription() {
+ return new HandlerDescription(this);
+ }
+
+ /**
+ * Reconfigures the instance.
+ * This method is called, when the instance is under reconfiguration.
+ * The reconfiguration does not stops the instance, and so the handler supporting
+ * the reconfiguration must override this method and handles this case.
+ * By default, this method does nothing.
+ * @param configuration the new instance configuration.
+ */
+ public void reconfigure(Dictionary configuration) {
+ // The default implementation does nothing.
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerFactory.java
new file mode 100644
index 0000000..c4a969c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerFactory.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.felix.ipojo;
+
+
+/**
+ * Service interface published by handler factory.
+ * This interface allows interacting the the handler factory to create
+ * {@link Handler} objects.
+ * @see Factory
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface HandlerFactory extends Factory {
+
+ /**
+ * The iPOJO Default Namespace.
+ */
+ String IPOJO_NAMESPACE = "org.apache.felix.ipojo";
+
+ /**
+ * Gets the namespace associated with this handler factory.
+ * @return the namespace used by this handler
+ */
+ String getNamespace();
+
+ /**
+ * Gets the name associated with this handler factory.
+ * @return the name used by this handler
+ */
+ String getHandlerName();
+
+ /**
+ * Gets the type of the handler factory.
+ * The handler can only be plugged on instance container with the same type.
+ * Basically, types are <code>primitive</code> and <code>composite</code>.
+ * @return the type of the handler
+ */
+ String getType();
+
+ /**
+ * Gets the start level of the handler objects created by this factory.
+ * Handlers with a low start level are configured and started before
+ * handlers with an higher start level. Moreover, these handlers are
+ * stopped and disposed after.
+ * @return the handler's start level
+ */
+ int getStartLevel();
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManager.java
new file mode 100644
index 0000000..4472200
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManager.java
@@ -0,0 +1,244 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The handler manager manages an handler instance.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class HandlerManager extends InstanceManager {
+
+ /**
+ * The internal handler object.
+ * Immutable once set.
+ */
+ private Handler m_handler;
+
+ /**
+ * Creates a handler manager.
+ * @param factory the handler factory
+ * @param context the bundle context
+ * @param handlers the handler array
+ */
+ public HandlerManager(ComponentFactory factory, BundleContext context, HandlerManager[] handlers) {
+ super(factory, context, handlers);
+ }
+
+ /**
+ * Gets the contained handler object.
+ * If not already created it creates the object.
+ * @return the handler object.
+ */
+ public Handler getHandler() {
+ if (m_handler == null) {
+ createHandlerObject();
+ }
+ return m_handler;
+ }
+
+ /**
+ * Creates and initializes the handler object.
+ * @param instance the component instance on which the handler will be attached.
+ * @param metadata the component metadata.
+ * @param configuration the instance configuration.
+ * @throws ConfigurationException if the handler configuration failed.
+ */
+ public void init(ComponentInstance instance, Element metadata, Dictionary configuration) throws ConfigurationException {
+ createHandlerObject();
+ m_handler.setFactory(instance.getFactory());
+ m_handler.attach(instance);
+ m_handler.configure(metadata, configuration);
+ }
+
+ /**
+ * Creates the handler object.
+ * This method does nothing if the object is already created.
+ * This method does not need locking protocol as only one thread (the creator thread) can create an instance.
+ */
+ private void createHandlerObject() {
+ if (m_handler != null) { return; }
+ m_handler = (Handler) createPojoObject();
+ }
+
+ /**
+ * Creates an instance of the content.
+ * This method needs to be called once only for singleton provided service.
+ * This methods call the {@link InstanceManager#createObject()} method, and adds
+ * the created object to the {@link InstanceManager#m_pojoObjects} list. Then,
+ * it calls the {@link PrimitiveHandler#onCreation(Object)} methods on attached
+ * handlers.
+ * @return a new instance or <code>null</code> if an error occurs during the
+ * creation.
+ */
+ public Object createPojoObject() {
+ Object instance = createObject();
+
+ // Add the new instance in the instance list.
+ synchronized (this) {
+ if (m_pojoObjects == null) {
+ m_pojoObjects = new ArrayList(1);
+ }
+ m_pojoObjects.add(instance);
+ }
+
+ //Do not call onCreation, this will be done in the start method.
+
+ return instance;
+ }
+
+ /**
+ * Starts the instance manager.
+ */
+ public void start() {
+ synchronized (this) {
+ if (m_state != STOPPED) {
+ return; // Instance already started
+ } else {
+ m_state = -2; // Temporary starting state, avoiding concurrent starts.
+ }
+ }
+
+ // Start attached handler.
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].addInstanceStateListener(this);
+ m_handlers[i].start();
+ }
+
+ // Call the onCreation method.
+ for (int i = 0; i < m_handlers.length; i++) {
+ ((PrimitiveHandler) m_handlers[i].getHandler()).onCreation(m_handler);
+ }
+
+
+ m_handler.start(); // Call the handler start method, the instance might be invalid.
+
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (!m_handlers[i].getHandler().isValid()) {
+ setState(INVALID);
+ return;
+ }
+ }
+ if (m_handler.getValidity()) {
+ setState(VALID);
+ } else {
+ setState(INVALID);
+ }
+
+ // Now, the state is necessary different from the temporary state.
+ }
+
+ /**
+ * Stops the instance manager.
+ */
+ public void stop() {
+ synchronized (this) {
+ if (m_state == STOPPED) {
+ return; // Instance already stopped
+ } else {
+ m_state = -2; // Temporary state avoiding concurrent stopping.
+ }
+ }
+
+ setState(INVALID);
+
+ if (m_handler != null) {
+ m_handler.stop();
+ }
+
+ // Stop all the handlers
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].removeInstanceStateListener(this);
+ m_handlers[i].stop();
+ }
+
+ List listeners = null;
+ synchronized (this) {
+ m_state = STOPPED;
+ if (m_listeners != null) {
+ listeners = new ArrayList(m_listeners); // Stack confinement.
+ }
+ }
+
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ ((InstanceStateListener) listeners.get(i)).stateChanged(this, STOPPED);
+ }
+ }
+ }
+
+ /**
+ * Disposes the instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#dispose()
+ */
+ public void dispose() {
+ super.dispose();
+ m_handler = null;
+ }
+
+ /**
+ * Kills the current instance.
+ * Only the factory of this instance can call this method.
+ */
+ protected void kill() {
+ super.dispose();
+ m_handler = null;
+ }
+
+ /**
+ * State Change listener callback.
+ * This method is notified at each time a plugged handler becomes invalid.
+ * @param instance the changing instance
+ * @param newState the new state
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public void stateChanged(ComponentInstance instance, int newState) {
+ int state;
+ synchronized (this) {
+ if (m_state <= STOPPED) {
+ return;
+ } else {
+ state = m_state; // Stack confinement
+ }
+ }
+ // Update the component state if necessary
+ if (newState == INVALID && state == VALID) {
+ // Need to update the state to UNRESOLVED
+ setState(INVALID);
+ return;
+ }
+ if (newState == VALID && state == INVALID) {
+ // An handler becomes valid => check if all handlers are valid
+ if (!m_handler.getValidity()) { return; }
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) { return; }
+ }
+ setState(VALID);
+ return;
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManagerFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManagerFactory.java
new file mode 100644
index 0000000..8cef70e
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/HandlerManagerFactory.java
@@ -0,0 +1,215 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Implementation of the handler factory interface.
+ * This factory is able to create handler manager.
+ * A handler manager is an iPOJO instance containing a handler object.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class HandlerManagerFactory extends ComponentFactory implements HandlerFactory {
+
+ /**
+ * The Handler type (<code>composite</code> or <code>primitive</code>).
+ */
+ private final String m_type;
+
+ /**
+ * The iPOJO Handler Namespace.
+ * (Uses the iPOJO default namespace is not specified)
+ */
+ private final String m_namespace;
+
+ /**
+ * The handler start level.
+ * Lower levels are priority and so are configured and started
+ * before higher levels, and are stopped after.
+ */
+ private final int m_level;
+
+ /**
+ * Creates a handler factory.
+ * @param context the bundle context
+ * @param metadata the metadata of the component to create
+ * @throws ConfigurationException if the element describing the factory is malformed.
+ */
+ public HandlerManagerFactory(BundleContext context, Element metadata) throws ConfigurationException {
+ super(context, metadata);
+
+ // Get the name
+ m_factoryName = metadata.getAttribute("name");
+ if (m_factoryName == null) { throw new ConfigurationException("An Handler needs a name"); }
+
+ // Get the type
+ String type = metadata.getAttribute("type");
+ if (type != null) {
+ m_type = type;
+ } else {
+ m_type = "primitive"; // Set to primitive if not specified.
+ }
+
+ String level = metadata.getAttribute("level");
+ if (level != null) {
+ m_level = new Integer(level).intValue();
+ } else {
+ m_level = Integer.MAX_VALUE; // Set to max if not specified.
+ }
+
+ // Get the namespace
+ String namespace = metadata.getAttribute("namespace");
+ if (namespace != null) {
+ m_namespace = namespace.toLowerCase();
+ } else {
+ m_namespace = IPOJO_NAMESPACE; // Set to the iPOJO default namespace if not specified.
+ }
+ }
+
+ public String getNamespace() {
+ return m_namespace;
+ }
+
+ public String getHandlerName() {
+ return m_namespace + ":" + getName();
+ }
+
+ public String getType() {
+ return m_type;
+ }
+
+ public int getStartLevel() {
+ return m_level;
+ }
+
+ public ComponentTypeDescription getComponentTypeDescription() {
+ return new HandlerTypeDescription(this);
+ }
+
+ /**
+ * Stops the factory.
+ * This method does not disposed created instances.
+ * These instances will be disposed by the instance managers.
+ * This method is called with the lock.
+ */
+ public void stopping() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ m_tracker = null;
+ }
+ }
+
+ /**
+ * Creates an instance. The given configuration needs to contain the 'name'
+ * property. This method is called when holding the lock.
+ * @param configuration the configuration of the created instance.
+ * @param context the service context to push for this instance.
+ * @param handlers the handler array to attach to the instance.
+ * @return the created {@link HandlerManager}.
+ * @throws org.apache.felix.ipojo.ConfigurationException if the instance configuration failed.
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary)
+ */
+ public ComponentInstance createInstance(Dictionary configuration, IPojoContext context, HandlerManager[] handlers) throws ConfigurationException {
+ HandlerManager instance = new HandlerManager(this, context, handlers);
+ instance.configure(m_componentMetadata, configuration);
+ return instance;
+ }
+
+
+ /**
+ * Computes required handlers. This method does not manipulate any
+ * non-immutable fields, so does not need to be synchronized.
+ * This method is overridden to avoid using the same detection rules
+ * than 'primitive' components. Indeed, architecture is disable by default,
+ * and a handler is never immediate.
+ * @return the required handler list.
+ */
+ public List getRequiredHandlerList() {
+ List list = new ArrayList();
+ Element[] elems = m_componentMetadata.getElements();
+ for (int i = 0; i < elems.length; i++) {
+ Element current = elems[i];
+ if (!"manipulation".equals(current.getName())) { // Remove the manipulation element
+ RequiredHandler req = new RequiredHandler(current.getName(),
+ current.getNameSpace());
+ if (!list.contains(req)) {
+ list.add(req);
+ }
+ }
+ }
+
+ // Unlike normal components, the architecture is enable only when
+ // specified.
+ String arch = m_componentMetadata.getAttribute("architecture");
+ if (arch != null && arch.equalsIgnoreCase("true")) {
+ list.add(new RequiredHandler("architecture", null));
+ }
+
+ // The auto-attached handler list is ignored for handlers to avoid loops.
+
+ return list;
+ }
+
+ /**
+ * Defines the handler type description.
+ * @see ComponentTypeDescription
+ */
+ private class HandlerTypeDescription extends ComponentTypeDescription {
+
+ /**
+ * Creates the HandlerTypeDescription.
+ * @param factory the factory.
+ */
+ public HandlerTypeDescription(IPojoFactory factory) {
+ super(factory);
+ }
+
+ /**
+ * Add properties to publish.
+ * <li>handler.name</li>
+ * <li>handler.namespace</li>
+ * <li>handler.type</li>
+ * <li>handler.level if the level is not Integer.MAX</li>
+ * @return returns the dictionary to publish.
+ * @see org.apache.felix.ipojo.architecture.ComponentTypeDescription#getPropertiesToPublish()
+ */
+ public Dictionary getPropertiesToPublish() {
+ Dictionary props = super.getPropertiesToPublish();
+
+ props.put(Handler.HANDLER_NAME_PROPERTY, m_factoryName);
+ props.put(Handler.HANDLER_NAMESPACE_PROPERTY, m_namespace);
+ props.put(Handler.HANDLER_TYPE_PROPERTY, m_type);
+ if (m_level != Integer.MAX_VALUE) {
+ props.put(Handler.HANDLER_LEVEL_PROPERTY, new Integer(m_level));
+ }
+ return props;
+ }
+
+ public String[] getFactoryInterfacesToPublish() {
+ return new String[] {HandlerFactory.class.getName()};
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOServiceFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOServiceFactory.java
new file mode 100644
index 0000000..a74f22d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOServiceFactory.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.felix.ipojo;
+
+
+/**
+ * iPOJO Service Factory is a special service factory handling to get the
+ * instance consuming the service. The mechanism is equivalent to the OSGi Service
+ * Factory.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface IPOJOServiceFactory {
+
+ /**
+ * Gets a service object.
+ * @param instance the instance asking the for the service object.
+ * @return the service object.
+ */
+ Object getService(ComponentInstance instance);
+
+ /**
+ * Un-gets a service object.
+ * @param instance the instance un-getting the service object.
+ * @param svcObject the service object used
+ */
+ void ungetService(ComponentInstance instance, Object svcObject);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOURLHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOURLHandler.java
new file mode 100644
index 0000000..9a046ac
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPOJOURLHandler.java
@@ -0,0 +1,239 @@
+/*
+ * 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.felix.ipojo;
+
+import org.apache.felix.ipojo.manipulator.Pojoization;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.url.URLStreamHandlerService;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+* iPOJO URL Handler allowing installation time manipulation.
+* When a bundle is installed with the <code>ipojo:</code> URL
+* prefix, the bundle is downloaded and manipulated by this
+* handler.
+* The metadata.xml file can either be provided inside the bundle (root,
+* or in META-INF) or given in the URL:
+* ipojo:URL_BUNDLE!URL_METADATA.
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public class IPOJOURLHandler extends org.osgi.service.url.AbstractURLStreamHandlerService implements URLStreamHandlerService {
+
+ /**
+ * The bundle context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * The directory storing bundles.
+ */
+ private File m_temp;
+
+ /**
+ * Creates a IPOJOURLHandler.
+ * Gets the bundle context and create the working
+ * directory.
+ * @param bc the bundle context
+ */
+ public IPOJOURLHandler(BundleContext bc) {
+ m_context = bc;
+ m_temp = m_context.getDataFile("temp");
+ if (! m_temp.exists()) {
+ m_temp.mkdir();
+ }
+ }
+
+ /**
+ * Stops the URL handler:
+ * Deletes the working directory.
+ */
+ public void stop() {
+ File[] files = m_temp.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ files[i].delete();
+ }
+ }
+ m_temp.delete();
+ }
+
+ /**
+ * Opens a connection using the ipojo url handler.
+ * This methods parses the URL and manipulate the given bundle.
+ * @param url the url.
+ * @return the URL connection on the manipulated bundle
+ * @throws java.io.IOException occurs when the bundle cannot be either downloaded, or manipulated or
+ * installed correctly.
+ * @see org.osgi.service.url.AbstractURLStreamHandlerService#openConnection(java.net.URL)
+ */
+ public URLConnection openConnection(URL url) throws IOException {
+ System.out.println("Processing URL : " + url);
+
+ // Parse the url:
+ String full = url.toExternalForm();
+ // Remote ipojo://
+ if (full.startsWith("ipojo:")) {
+ full = full.substring(6);
+ }
+ // Remove '/' or '//'
+ while (full.startsWith("/")) {
+ full = full.substring(1);
+ }
+
+ full = full.trim();
+
+ // Now full is like : URL,URL or URL
+ String[] urls = full.split("!");
+ URL bundleURL = null;
+ URL metadataURL = null;
+ if (urls.length == 1) {
+ // URL form
+ System.out.println("Extracted URL : " + urls[0]);
+ bundleURL = new URL(urls[0]);
+ } else if (urls.length == 2) {
+ // URL,URL form
+ bundleURL = new URL(urls[0]);
+ metadataURL = new URL(urls[1]);
+ } else {
+ throw new MalformedURLException("The iPOJO url is not formatted correctly, ipojo:bundle_url[!metadata_url] expected");
+ }
+
+ File bundle = File.createTempFile("ipojo_", ".jar", m_temp);
+ save(bundleURL, bundle);
+ File metadata = null;
+ if (metadataURL != null) {
+ metadata = File.createTempFile("ipojo_", ".xml", m_temp);
+ save(metadataURL, metadata);
+ } else {
+ // Check that the metadata are in the jar file
+ JarFile jar = new JarFile(bundle);
+ metadata = findMetadata(jar);
+ }
+
+ // Pojoization
+ Pojoization pojoizator = new Pojoization();
+ File out = new File(m_temp, bundle.getName() + "-ipojo.jar");
+ System.out.println("Pojoization " + bundle.exists() + " - " + metadata.exists());
+ try {
+ pojoizator.pojoization(bundle, out, metadata);
+ } catch (Exception e) {
+ if (! pojoizator.getErrors().isEmpty()) {
+ throw new IOException("Errors occured during the manipulation : " + pojoizator.getErrors());
+ }
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage());
+ }
+
+ if (! pojoizator.getErrors().isEmpty()) {
+ throw new IOException("Errors occured during the manipulation : " + pojoizator.getErrors());
+ }
+ if (! pojoizator.getWarnings().isEmpty()) {
+ System.err.println("Warnings occured during the manipulation : " + pojoizator.getWarnings());
+ }
+
+ System.out.println("Manipulation done : " + out.exists());
+
+ // Cleanup
+ bundle.delete();
+ if (metadata != null) {
+ metadata.delete();
+ }
+ out.deleteOnExit();
+ // Returns the URL Connection
+ return out.toURI().toURL().openConnection();
+
+
+ }
+
+ /**
+ * Downloads the content pointed by the given url to
+ * the given file.
+ * @param url the url
+ * @param file the file
+ * @throws java.io.IOException occurs if the content cannot be read
+ * and save inside the file
+ */
+ private void save(URL url, File file) throws IOException {
+ InputStream is = url.openStream();
+ save(is, file);
+ }
+
+ /**
+ * Saves the content of the input stream to the given file.
+ * @param is the input stream to read
+ * @param file the file
+ * @throws java.io.IOException occurs if the content cannot be read
+ * and save inside the file
+ */
+ private void save(InputStream is, File file) throws IOException {
+ FileOutputStream writer = new FileOutputStream(file);
+ int cc = 0;
+ do {
+ int i = is.read();
+ if (i == -1) {
+ break;
+ }
+ cc++;
+ writer.write(i);
+ } while (true);
+ System.out.println(cc + " bytes copied");
+ is.close();
+ writer.close();
+ }
+
+ /**
+ * Looks for the metadata.xml file in the jar file.
+ * Two locations are checked:
+ * <ol>
+ * <li>the root of the jar file</li>
+ * <li>the META-INF directory</li>
+ * </ol>
+ * @param jar the jar file
+ * @return the founded file or <code>null</code> if not found.
+ * @throws java.io.IOException occurs when the Jar file cannot be read.
+ */
+ private File findMetadata(JarFile jar) throws IOException {
+ JarEntry je = jar.getJarEntry("metadata.xml");
+ if (je == null) {
+ je = jar.getJarEntry("META-INF/metadata.xml");
+ }
+
+ if (je == null) {
+ System.out.println("Metadata file not found, use annotations only.");
+ return null; // Not Found, use annotation only
+ } else {
+ System.out.println("Metadata file found: " + je.getName());
+ File metadata = File.createTempFile("ipojo_", ".xml", m_temp);
+ save(jar.getInputStream(je), metadata);
+ System.out.println("Metadata file saved to " + metadata.getAbsolutePath());
+ return metadata;
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java
new file mode 100644
index 0000000..77cf3b3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java
@@ -0,0 +1,497 @@
+/*
+ * 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.felix.ipojo;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Dictionary;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The iPOJO Context is a BundleContext implementation allowing the separation
+ * between Bundle context and Service (Bundle) Context.
+ * This is used inside composition to differentiate the classloading context (i.e.
+ * Bundle) and the service registry access.
+ * This class delegates calls to the good internal context (either the BundleContext
+ * or the ServiceContext) according to the method. If the instance does not have a valid
+ * service context, the bundle context is always used.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class IPojoContext implements BundleContext, ServiceContext {
+
+ /**
+ * The bundleContext used to access bundle methods.
+ */
+ private BundleContext m_bundleContext;
+
+ /**
+ * The service context used to access to the service registry.
+ */
+ private ServiceContext m_serviceContext;
+
+ /**
+ * Creates an iPOJO Context.
+ * No service context is specified.
+ * This constructor is used when the
+ * instance lives in the global context.
+ * @param context the bundle context
+ */
+ public IPojoContext(BundleContext context) {
+ m_bundleContext = context;
+ }
+
+ /**
+ * Creates an iPOJO Context.
+ * A service context is used to refer to the
+ * service registry. The service context will be
+ * used for all service accesses.
+ * @param bundleContext the bundle context
+ * @param serviceContext the service context
+ */
+ public IPojoContext(BundleContext bundleContext, ServiceContext serviceContext) {
+ m_bundleContext = bundleContext;
+ m_serviceContext = serviceContext;
+ }
+
+ /**
+ * Adds a bundle listener.
+ * @param listener the listener to add
+ * @see org.osgi.framework.BundleContext#addBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void addBundleListener(BundleListener listener) {
+ m_bundleContext.addBundleListener(listener);
+ }
+
+ /**
+ * Adds a framework listener.
+ * @param listener the listener object to add
+ * @see org.osgi.framework.BundleContext#addFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void addFrameworkListener(FrameworkListener listener) {
+ m_bundleContext.addFrameworkListener(listener);
+ }
+
+ /**
+ * Adds a service listener.
+ * This methods registers the listener on the service context
+ * if it specified. Otherwise, if the internal dispatcher is enabled,
+ * it registers the listener inside the internal dispatcher (if
+ * the filter match against the iPOJO Filter format
+ * {@link IPojoContext#match(String)}). Finally, if the internal
+ * dispatcher is disabled, it uses the "regular" bundle context.
+ * @param listener the service listener to add.
+ * @param filter the LDAP filter
+ * @throws InvalidSyntaxException if LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener, java.lang.String)
+ */
+ public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+ if (m_serviceContext == null) {
+ EventDispatcher dispatcher = EventDispatcher.getDispatcher();
+ if (dispatcher != null) { // getDispatcher returns null if not enable.
+ String itf = match(filter);
+ if (itf != null) {
+ dispatcher.addListener(itf, listener);
+ } else {
+ m_bundleContext.addServiceListener(listener, filter);
+ }
+ } else {
+ m_bundleContext.addServiceListener(listener, filter);
+ }
+ } else {
+ m_serviceContext.addServiceListener(listener, filter);
+ }
+ }
+
+ /**
+ * Add a service listener.
+ * This methods registers the listener on the service context
+ * if it specified. Otherwise, it uses the "regular" bundle context.
+ * @param listener the service listener to add.
+ * @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void addServiceListener(ServiceListener listener) {
+ if (m_serviceContext == null) {
+ m_bundleContext.addServiceListener(listener);
+ } else {
+ m_serviceContext.addServiceListener(listener);
+ }
+ }
+
+ /**
+ * This method checks if the filter matches with the iPOJO
+ * filter format: <code>(OBJECTCLASS=$ITF)</code>. It tries
+ * to extract the required interface (<code>$ITF</code>).
+ * @param filter the filter to analyze
+ * @return the required interface or <code>null</code>
+ * if the filter doesn't match with the iPOJO format.
+ */
+ private String match(String filter) {
+ if (filter != null && filter.startsWith("(" + Constants.OBJECTCLASS + "=") // check the beginning (OBJECTCLASS
+ && filter.lastIndexOf(')') == filter.indexOf(')')) { // check that there is only one )
+ return filter.substring(("(" + Constants.OBJECTCLASS + "=").length(), filter.length() - 1);
+ }
+ return null;
+ }
+
+
+
+ /**
+ * Creates a filter objects.
+ * This method always uses the bundle context.
+ * @param filter the string form of the LDAP filter to create
+ * @return the filter object.
+ * @throws InvalidSyntaxException if the given filter is malformed
+ * @see org.osgi.framework.BundleContext#createFilter(java.lang.String)
+ */
+ public Filter createFilter(String filter) throws InvalidSyntaxException {
+ return m_bundleContext.createFilter(filter);
+ }
+
+ /**
+ * Gets a bundle by symbolic name
+ * @param s the name
+ * @return the matching bundle or <code>null</code> if not found
+ */
+ public Bundle getBundle(String s) {
+ return m_bundleContext.getBundle(s);
+ }
+
+ /**
+ * Gets the service references matching with the given query.
+ * Uses the service context if specified, used the bundle context
+ * otherwise.
+ * @param clazz the required interface
+ * @param filter the LDAP filter
+ * @return the array of available service references
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#getAllServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getAllServiceReferences(clazz, filter);
+ } else {
+ return m_serviceContext.getAllServiceReferences(clazz, filter);
+ }
+ }
+
+ /**
+ * Gets the current bundle object.
+ * @return the bundle declaring the component type of the instance
+ * using the current IPojoContext.
+ * @see org.osgi.framework.BundleContext#getBundle()
+ */
+ public Bundle getBundle() {
+ return m_bundleContext.getBundle();
+ }
+
+ /**
+ * Gets the bundle object with the given id.
+ * @param bundleId the bundle id
+ * @return the bundle object
+ * @see org.osgi.framework.BundleContext#getBundle(long)
+ */
+ public Bundle getBundle(long bundleId) {
+ return m_bundleContext.getBundle(bundleId);
+ }
+
+ /**
+ * Gets installed bundles.
+ * @return the list of installed bundles
+ * @see org.osgi.framework.BundleContext#getBundles()
+ */
+ public Bundle[] getBundles() {
+ return m_bundleContext.getBundles();
+ }
+
+ /**
+ * Gets a data file.
+ * @param filename the file name.
+ * @return the File object
+ * @see org.osgi.framework.BundleContext#getDataFile(java.lang.String)
+ */
+ public File getDataFile(String filename) {
+ return m_bundleContext.getDataFile(filename);
+ }
+
+ /**
+ * Gets a property value.
+ * @param key the key of the asked property
+ * @return the property value (object) or <code>null</code> if no
+ * property are associated with the given key
+ * @see org.osgi.framework.BundleContext#getProperty(java.lang.String)
+ */
+ public String getProperty(String key) {
+ return m_bundleContext.getProperty(key);
+ }
+
+ /**
+ * Gets a service object.
+ * The given service reference must come from the same context than
+ * where the service is get.
+ * This method uses the service context if specified, the bundle
+ * context otherwise.
+ * This method may throw {@link IllegalStateException} if the used bundle
+ * context is no more valid (because we're leaving).
+ * @param ref the required service reference
+ * @return the service object or <code>null</code> if the service reference
+ * is no more valid or if the service object is not accessible.
+ * @see org.osgi.framework.BundleContext#getService(org.osgi.framework.ServiceReference)
+ */
+ public <S> S getService(ServiceReference<S> ref) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getService(ref);
+ } else {
+ return (S) m_serviceContext.getService(ref);
+ }
+ }
+
+ /**
+ * Gets a service reference for the given interface.
+ * This method uses the service context if specified, the bundle
+ * context otherwise.
+ * @param clazz the required interface name
+ * @return a service reference on a available provider or <code>null</code>
+ * if no providers available
+ * @see org.osgi.framework.BundleContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference getServiceReference(String clazz) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getServiceReference(clazz);
+ } else {
+ return m_serviceContext.getServiceReference(clazz);
+ }
+ }
+
+ /**
+ * Gets a service reference for the given interface.
+ * This method uses the service context if specified, the bundle
+ * context otherwise.
+ * @param sClass the required interface class
+ * @param <S> the service class
+ * @return a service reference on a available provider or <code>null</code>
+ * if no providers available
+ * @see org.osgi.framework.BundleContext#getServiceReference(java.lang.String)
+ */
+ public <S> ServiceReference<S> getServiceReference(Class<S> sClass) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getServiceReference(sClass);
+ } else {
+ return m_serviceContext.getServiceReference(sClass);
+ }
+ }
+
+ /**
+ * Gets service reference list for the given query.
+ * This method uses the service context if specified, the bundle
+ * context otherwise.
+ * @param sClass the name of the required service interface
+ * @param filter the LDAP filter to apply on service provider
+ * @param <S> the service class
+ * @return the array of consistent service reference or <code>null</code>
+ * if no available providers
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ */
+ public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> sClass, String filter) throws InvalidSyntaxException {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getServiceReferences(sClass, filter);
+ } else {
+ return m_serviceContext.getServiceReferences(sClass, filter);
+ }
+ }
+
+ /**
+ * Gets service reference list for the given query.
+ * This method uses the service context if specified, the bundle
+ * context otherwise.
+ * @param clazz the name of the required service interface
+ * @param filter the LDAP filter to apply on service provider
+ * @return the array of consistent service reference or <code>null</code>
+ * if no available providers
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#getServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ if (m_serviceContext == null) {
+ return m_bundleContext.getServiceReferences(clazz, filter);
+ } else {
+ return m_serviceContext.getServiceReferences(clazz, filter);
+ }
+ }
+
+ /**
+ * Installs a bundle.
+ * @param location the URL of the bundle to install
+ * @return the installed bundle
+ * @throws BundleException if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String)
+ */
+ public Bundle installBundle(String location) throws BundleException {
+ return m_bundleContext.installBundle(location);
+ }
+
+ /**
+ * Installs a bundle.
+ * @param location the URL of the bundle to install
+ * @param input the input stream to load the bundle.
+ * @return the installed bundle
+ * @throws BundleException if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String, java.io.InputStream)
+ */
+ public Bundle installBundle(String location, InputStream input) throws BundleException {
+ return m_bundleContext.installBundle(location, input);
+ }
+
+ /**
+ * Registers a service.
+ * This method uses the service context if specified (and so, registers
+ * the service in this service registry), the bundle context otherwise (the
+ * service will be available to every global instances).
+ * @param clazzes the interfaces provided by the service.
+ * @param service the service object.
+ * @param properties the service properties to publish
+ * @return the service registration for this service publication.
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.registerService(clazzes, service, properties);
+ } else {
+ return m_serviceContext.registerService(clazzes, service, properties);
+ }
+ }
+
+ /**
+ * Registers a service.
+ * This method uses the service context if specified (and so, registers
+ * the service in this service registry), the bundle context otherwise (the
+ * service will be available to every global instances).
+ * @param clazz the interface provided by the service.
+ * @param service the the service object.
+ * @param properties the service properties to publish.
+ * @return the service registration for this service publication.
+ * @see org.osgi.framework.BundleContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.registerService(clazz, service, properties);
+ } else {
+ return m_serviceContext.registerService(clazz, service, properties);
+ }
+ }
+
+ /**
+ * Removes a bundle listener.
+ * @param listener the listener to remove
+ * @see org.osgi.framework.BundleContext#removeBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void removeBundleListener(BundleListener listener) {
+ m_bundleContext.removeBundleListener(listener);
+ }
+
+ /**
+ * Removes a framework listener.
+ * @param listener the listener to remove
+ * @see org.osgi.framework.BundleContext#removeFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void removeFrameworkListener(FrameworkListener listener) {
+ m_bundleContext.removeFrameworkListener(listener);
+ }
+
+ /**
+ * Registers a service
+ * @param sClass the service class
+ * @param s the service object (must implement sClass)
+ * @param stringDictionary service properties
+ * @param <S> the Service Class (specification)
+ * @return the service registration
+ */
+ public <S> ServiceRegistration<S> registerService(Class<S> sClass, S s, Dictionary<String, ?> stringDictionary) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.registerService(sClass, s, stringDictionary);
+ } else {
+ return m_serviceContext.registerService(sClass, s, stringDictionary);
+ }
+ }
+
+ /**
+ * Removes a service listener.
+ * Removes the service listener from where it was registered so either in
+ * the global context, or in the service context or in the internal dispatcher.
+ * @param listener the service listener to remove
+ * @see org.apache.felix.ipojo.ServiceContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ * @see org.osgi.framework.BundleContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void removeServiceListener(ServiceListener listener) {
+ if (m_serviceContext == null) {
+ EventDispatcher dispatcher = EventDispatcher.getDispatcher();
+ if (dispatcher == null || ! dispatcher.removeListener(listener)) {
+ m_bundleContext.removeServiceListener(listener);
+ }
+ } else {
+ m_serviceContext.removeServiceListener(listener);
+ }
+ }
+
+ /**
+ * Ungets the service reference.
+ * This method uses the service context if specified,
+ * the bundle context otherwise.
+ * @param reference the reference to unget
+ * @return <code>true</code> if you are the last user of the reference
+ * @see org.osgi.framework.BundleContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ public boolean ungetService(ServiceReference reference) {
+ if (m_serviceContext == null) {
+ return m_bundleContext.ungetService(reference);
+ } else {
+ return m_serviceContext.ungetService(reference);
+ }
+ }
+
+ /**
+ * Gets the global context, i.e. the bundle context of the factory.
+ * @return the global bundle context.
+ */
+ public BundleContext getGlobalContext() {
+ return m_bundleContext;
+ }
+
+ /**
+ * Gets the service context, i.e. the composite context.
+ * Returns <code>null</code> if the instance does not live
+ * inside a composite.
+ * @return the service context or <code>null</code>.
+ */
+ public ServiceContext getServiceContext() {
+ return m_serviceContext;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
new file mode 100644
index 0000000..511ea30
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
@@ -0,0 +1,1009 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Logger;
+import org.apache.felix.ipojo.util.SecurityHelper;
+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.ManagedServiceFactory;
+
+/**
+ * This class defines common mechanisms of iPOJO component factories
+ * (i.e. component type).
+ * This class implements both the Factory and ManagedServiceFactory
+ * services.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class IPojoFactory implements Factory, ManagedServiceFactory {
+ /*
+ * TODO there is potentially an issue when calling FactoryStateListener callbacks with the lock
+ * It should be called by a separate thread dispatching events to listeners.
+ */
+
+ /**
+ * The list of the managed instance name.
+ * This list is shared by all factories and is
+ * used to assert name unicity.
+ */
+ protected static final List INSTANCE_NAME = new ArrayList();
+
+ /**
+ * The component type description exposed by the {@link Factory} service.
+ */
+ protected ComponentTypeDescription m_componentDesc;
+
+ /**
+ * The list of the managed instance managers.
+ * The key of this map is the name (i.e. instance names) of the created instance
+ */
+ protected final Map m_componentInstances = new HashMap();
+
+ /**
+ * The component type metadata.
+ */
+ protected final Element m_componentMetadata;
+
+ /**
+ * The bundle context reference.
+ */
+ protected final BundleContext m_context;
+
+ /**
+ * The factory name.
+ * Could be the component class name if the factory name is not set.
+ * Immutable once set.
+ */
+ protected String m_factoryName;
+
+ /**
+ * The list of required handlers.
+ */
+ protected List m_requiredHandlers = new ArrayList();
+
+ /**
+ * The list of factory state listeners.
+ * @see FactoryStateListener
+ */
+ protected List m_listeners = new ArrayList(1);
+
+ /**
+ * The logger for the factory.
+ */
+ protected final Logger m_logger;
+
+ /**
+ * Is the factory public (exposed as services).
+ */
+ protected final boolean m_isPublic;
+
+ /**
+ * The version of the component type.
+ */
+ protected final String m_version;
+
+ /**
+ * The service registration of this factory (Factory & ManagedServiceFactory).
+ * @see ManagedServiceFactory
+ * @see Factory
+ */
+ protected ServiceRegistration m_sr;
+
+ /**
+ * The factory state.
+ * Can be:
+ * <li>{@link Factory#INVALID}</li>
+ * <li>{@link Factory#VALID}</li>
+ * The factory is invalid at the beginning.
+ * A factory becomes valid if every required handlers
+ * are available (i.e. can be created).
+ */
+ protected int m_state = Factory.INVALID;
+
+ /**
+ * The index used to generate instance name if not set.
+ */
+ private long m_index = 0;
+
+ /**
+ * The flag indicating if this factory has already a
+ * computed description or not.
+ */
+ private boolean m_described;
+
+ /**
+ * Creates an iPOJO Factory.
+ * At the end of this method, the required set of handler is computed.
+ * But the result is computed by a sub-class.
+ * @param context the bundle context of the bundle containing the factory.
+ * @param metadata the description of the component type.
+ * @throws ConfigurationException if the element describing the factory is malformed.
+ */
+ public IPojoFactory(BundleContext context, Element metadata) throws ConfigurationException {
+ m_context = context;
+ m_componentMetadata = metadata;
+ m_factoryName = getFactoryName();
+ String fac = metadata.getAttribute("public");
+ m_isPublic = fac == null || !fac.equalsIgnoreCase("false");
+ m_logger = new Logger(m_context, m_factoryName);
+
+ // Compute the component type version.
+ String version = metadata.getAttribute("version");
+ if ("bundle".equalsIgnoreCase(version)) { // Handle the "bundle" constant: use the bundle version.
+ m_version = (String) m_context.getBundle().getHeaders().get(Constants.BUNDLE_VERSION);
+ } else {
+ m_version = version;
+ }
+
+ m_requiredHandlers = getRequiredHandlerList(); // Call sub-class to get the list of required handlers.
+
+ m_logger.log(Logger.INFO, "New factory created : " + m_factoryName);
+ }
+
+ /**
+ * Gets the component type description.
+ * @return the component type description
+ */
+ public ComponentTypeDescription getComponentTypeDescription() {
+ return new ComponentTypeDescription(this);
+ }
+
+ /**
+ * Adds a factory listener.
+ * @param listener the factory listener to add.
+ * @see org.apache.felix.ipojo.Factory#addFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void addFactoryStateListener(FactoryStateListener listener) {
+ synchronized (this) {
+ m_listeners.add(listener);
+ }
+ }
+
+ /**
+ * Gets the logger used by instances created by the current factory.
+ * @return the factory logger.
+ */
+ public Logger getLogger() {
+ return m_logger;
+ }
+
+ /**
+ * Computes the factory name.
+ * Each sub-type must override this method.
+ * @return the factory name.
+ */
+ public abstract String getFactoryName();
+
+ /**
+ * Computes the required handler list.
+ * Each sub-type must override this method.
+ * @return the required handler list
+ */
+ public abstract List getRequiredHandlerList();
+
+ /**
+ * Creates an instance.
+ * This method is called with the monitor lock.
+ * @param config the instance configuration
+ * @param context the iPOJO context to use
+ * @param handlers the handler array to use
+ * @return the new component instance.
+ * @throws ConfigurationException if the instance creation failed during the configuration process.
+ */
+ public abstract ComponentInstance createInstance(Dictionary config, IPojoContext context, HandlerManager[] handlers)
+ throws ConfigurationException;
+
+ /**
+ * Creates an instance.
+ * This method creates the instance in the global context.
+ * @param configuration the configuration of the created instance.
+ * @return the created component instance.
+ * @throws UnacceptableConfiguration if the given configuration is not consistent with the component type of this factory.
+ * @throws MissingHandlerException if an handler is unavailable when the instance is created.
+ * @throws org.apache.felix.ipojo.ConfigurationException if the instance or type configuration are not correct.
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary)
+ */
+ public ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException,
+ ConfigurationException {
+ return createComponentInstance(configuration, null);
+ }
+
+ /**
+ * Creates an instance in the specified service context.
+ * This method is synchronized to assert the validity of the factory during the creation.
+ * Callbacks to sub-class and created instances need to be aware that they are holding the monitor lock.
+ * This method call the override {@link IPojoFactory#createInstance(Dictionary, IPojoContext, HandlerManager[])
+ * method.
+ * @param configuration the configuration of the created instance.
+ * @param serviceContext the service context to push for this instance.
+ * @return the created component instance.
+ * @throws UnacceptableConfiguration if the given configuration is not consistent with the component type of this factory.
+ * @throws MissingHandlerException if an handler is unavailable when creating the instance.
+ * @throws org.apache.felix.ipojo.ConfigurationException if the instance configuration failed.
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary)
+ */
+ public synchronized ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration, // NOPMD
+ MissingHandlerException, ConfigurationException {
+ if (configuration == null) {
+ configuration = new Properties();
+ }
+
+ IPojoContext context = null;
+ if (serviceContext == null) {
+ context = new IPojoContext(m_context);
+ } else {
+ context = new IPojoContext(m_context, serviceContext);
+ }
+
+ try {
+ checkAcceptability(configuration);
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "The configuration is not acceptable : " + e.getMessage());
+ throw new UnacceptableConfiguration("The configuration "
+ + configuration + " is not acceptable for " + m_factoryName
+ + ": " + e.getMessage());
+ }
+
+ String name;
+ if (configuration.get("instance.name") == null && configuration.get("name") == null) { // Support both instance.name & name
+ name = generateName();
+ configuration.put("instance.name", name);
+ } else {
+ name = (String) configuration.get("instance.name");
+ if (name == null) {
+ name = (String) configuration.get("name");
+ getLogger().log(Logger.WARNING, "The 'name' (" + name + ") attribute, used as the instance name, is deprecated, please use the 'instance.name' attribute");
+ configuration.put("instance.name", name);
+ }
+ if (INSTANCE_NAME.contains(name)) {
+ m_logger.log(Logger.ERROR, "The configuration is not acceptable : Name already used");
+ throw new UnacceptableConfiguration(getFactoryName() + " : Name already used : " + name);
+ }
+ }
+ // Here we are sure to be valid until the end of the method.
+ HandlerManager[] handlers = new HandlerManager[m_requiredHandlers.size()];
+ for (int i = 0; i < handlers.length; i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ handlers[i] = getHandler(req, serviceContext);
+ }
+
+ try {
+ ComponentInstance instance = createInstance(configuration, context, handlers); // This method is called with the lock.
+ INSTANCE_NAME.add(name);
+ m_componentInstances.put(name, instance);
+ m_logger.log(Logger.INFO, "Instance " + name + " from factory " + m_factoryName + " created");
+ return instance;
+ } catch (ConfigurationException e) {
+ m_logger.log(Logger.ERROR, e.getMessage());
+ throw new ConfigurationException(e.getMessage(), m_factoryName);
+ }
+
+
+ }
+
+ /**
+ * Gets the bundle context of the factory.
+ * @return the bundle context of the factory.
+ * @see org.apache.felix.ipojo.Factory#getBundleContext()
+ */
+ public BundleContext getBundleContext() {
+ return m_context;
+ }
+
+ /**
+ * Gets the factory class name.
+ * @return the factory class name.
+ * @see org.apache.felix.ipojo.Factory#getClassName()
+ */
+ public abstract String getClassName();
+
+ /**
+ * Gets the component type description.
+ * @return the component type description object. <code>Null</code> if not already computed.
+ */
+ public synchronized ComponentTypeDescription getComponentDescription() {
+ return m_componentDesc;
+ }
+
+ /**
+ * Gets the component type description (Element-Attribute form).
+ * @return the component type description.
+ * @see org.apache.felix.ipojo.Factory#getDescription()
+ */
+ public synchronized Element getDescription() {
+ // Can be null, if not already computed.
+ if (m_componentDesc == null) {
+ return new Element("No description available for " + m_factoryName, "");
+ }
+ return m_componentDesc.getDescription();
+ }
+
+ /**
+ * Gets the component metadata.
+ * @return the component metadata
+ * @see org.apache.felix.ipojo.Factory#getComponentMetadata()
+ */
+ public Element getComponentMetadata() {
+ return m_componentMetadata;
+ }
+
+ /**
+ * Computes the list of missing handlers. This method is called with the monitor lock.
+ * @return the list of missing handlers.
+ * @see org.apache.felix.ipojo.Factory#getMissingHandlers()
+ */
+ public List getMissingHandlers() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ if (req.getReference() == null) {
+ list.add(req.getFullName());
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Gets the factory name.
+ * This name is immutable once set.
+ * @return the factory name.
+ * @see org.apache.felix.ipojo.Factory#getName()
+ */
+ public String getName() {
+ return m_factoryName;
+ }
+
+ /**
+ * Gets the list of required handlers.
+ * This method is synchronized to avoid the concurrent modification
+ * of the required handlers.
+ * @return the list of required handlers.
+ * @see org.apache.felix.ipojo.Factory#getRequiredHandlers()
+ */
+ public synchronized List getRequiredHandlers() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ list.add(req.getFullName());
+ }
+ return list;
+ }
+
+ /**
+ * Gets the actual factory state.
+ * Must be synchronized as this state is dependent of handler availability.
+ * @return the actual factory state.
+ * @see org.apache.felix.ipojo.Factory#getState()
+ */
+ public synchronized int getState() {
+ return m_state;
+ }
+
+ /**
+ * Checks if the configuration is acceptable.
+ * @param conf the configuration to test.
+ * @return <code>true</code> if the configuration is acceptable.
+ * @see org.apache.felix.ipojo.Factory#isAcceptable(java.util.Dictionary)
+ */
+ public boolean isAcceptable(Dictionary conf) {
+ try {
+ checkAcceptability(conf);
+ } catch (MissingHandlerException e) {
+ return false;
+ } catch (UnacceptableConfiguration e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the configuration is acceptable.
+ * This method checks the following assertions:
+ * <li>All handlers can be creates</li>
+ * <li>The configuration does not override immutable properties</li>
+ * <li>The configuration contains a value for every unvalued property</li>
+ * @param conf the configuration to test.
+ * @throws UnacceptableConfiguration if the configuration is unacceptable.
+ * @throws MissingHandlerException if an handler is missing.
+ */
+ public void checkAcceptability(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException {
+ PropertyDescription[] props;
+ synchronized (this) {
+ if (m_state == Factory.INVALID) {
+ throw new MissingHandlerException(getMissingHandlers());
+ }
+ props = m_componentDesc.getProperties(); // Stack confinement.
+ // The property list is up to date, as the factory is valid.
+ }
+
+ // Check that the configuration does not override immutable properties.
+
+ for (int i = 0; i < props.length; i++) {
+ // Is the property immutable
+ if (props[i].isImmutable() && conf.get(props[i].getName()) != null) {
+ throw new UnacceptableConfiguration("The property " + props[i] + " cannot be overide : immutable property"); // The instance configuration tries to override an immutable property.
+ }
+ // Is the property required ?
+ if (props[i].isMandatory() && props[i].getValue() == null && conf.get(props[i].getName()) == null) {
+ throw new UnacceptableConfiguration("The mandatory property " + props[i].getName() + " is missing"); // The property must be set.
+ }
+ }
+ }
+
+ /**
+ * Reconfigures an existing instance.
+ * The acceptability of the configuration is checked before the reconfiguration. Moreover,
+ * the configuration must contain the 'instance.name' property specifying the instance
+ * to reconfigure.
+ * This method is synchronized to assert the validity of the factory during the reconfiguration.
+ * @param properties the new configuration to push.
+ * @throws UnacceptableConfiguration if the new configuration is not consistent with the component type.
+ * @throws MissingHandlerException if the current factory is not valid.
+ * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
+ */
+ public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException {
+ if (properties == null || (properties.get("instance.name") == null && properties.get("name") == null)) { // Support both instance.name and name
+ throw new UnacceptableConfiguration("The configuration does not contains the \"instance.name\" property");
+ }
+
+ String name = (String) properties.get("instance.name");
+ if (name == null) {
+ name = (String) properties.get("name");
+ }
+
+ ComponentInstance instance = (ComponentInstance) m_componentInstances.get(name);
+ if (instance == null) { // The instance does not exists.
+ return;
+ }
+
+ checkAcceptability(properties); // Test if the configuration is acceptable
+ instance.reconfigure(properties); // re-configure the instance
+ }
+
+ /**
+ * Removes a factory listener.
+ * @param listener the factory listener to remove.
+ * @see org.apache.felix.ipojo.Factory#removeFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void removeFactoryStateListener(FactoryStateListener listener) {
+ synchronized (this) {
+ m_listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Stopping method.
+ * This method is call when the factory is stopping.
+ * This method is called when holding the lock on the factory.
+ */
+ public abstract void stopping();
+
+ /**
+ * Stops all the instance managers.
+ * This method calls the {@link IPojoFactory#stopping()} method,
+ * notifies listeners, and disposes created instances. Moreover,
+ * if the factory is public, services are also unregistered.
+ *
+ */
+ public synchronized void stop() {
+ ComponentInstance[] instances;
+ if (m_sr != null) {
+ m_sr.unregister();
+ m_sr = null;
+ }
+ stopping(); // Method called when holding the lock.
+ m_state = INVALID; // Set here to avoid to create instances during the stops.
+
+ Set col = m_componentInstances.keySet();
+ Iterator it = col.iterator();
+ instances = new ComponentInstance[col.size()]; // Stack confinement
+ int index = 0;
+ while (it.hasNext()) {
+ instances[index] = (ComponentInstance) (m_componentInstances.get(it.next()));
+ index++;
+ }
+
+ if (m_state == VALID) {
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((FactoryStateListener) m_listeners.get(i)).stateChanged(this, INVALID);
+ }
+ }
+
+ // Dispose created instances.
+ for (int i = 0; i < instances.length; i++) {
+ ComponentInstance instance = instances[i];
+ if (instance.getState() != ComponentInstance.DISPOSED) {
+ instance.dispose();
+ }
+ }
+
+ // Release each handler
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ ((RequiredHandler) m_requiredHandlers.get(i)).unRef();
+ }
+ m_described = false;
+ m_componentDesc = null;
+ m_componentInstances.clear();
+
+ m_logger.log(Logger.INFO, "Factory " + m_factoryName + " stopped");
+
+ }
+
+ /**
+ * Destroys the factory.
+ * The factory cannot be restarted. Only the {@link Extender} can call this method.
+ */
+ synchronized void dispose() {
+ stop(); // Does not hold the lock.
+ m_requiredHandlers = null;
+ m_listeners = null;
+ }
+
+ /**
+ * Starting method.
+ * This method is called when the factory is starting.
+ * This method is called when holding the lock on the factory.
+ */
+ public abstract void starting();
+
+ /**
+ * Starts the factory.
+ * Tries to compute the component type description,
+ * calls the {@link IPojoFactory#starting()} method,
+ * and published services if the factory is public.
+ */
+ public synchronized void start() {
+ if (m_described) { // Already started.
+ return;
+ }
+
+ m_componentDesc = getComponentTypeDescription();
+
+ starting();
+
+ computeFactoryState();
+
+ if (m_isPublic) {
+ // Exposition of the factory service
+ BundleContext bc = SecurityHelper.selectContextToRegisterServices(m_componentDesc.getFactoryInterfacesToPublish(),
+ m_context, getIPOJOBundleContext());
+ m_sr =
+ bc.registerService(m_componentDesc.getFactoryInterfacesToPublish(), this, m_componentDesc
+ .getPropertiesToPublish());
+ }
+
+ m_logger.log(Logger.INFO, "Factory " + m_factoryName + " started");
+
+ }
+
+ /**
+ * For testing purpose <b>ONLY</b>.
+ * This method recomputes the required handler list.
+ */
+ public void restart() {
+ // Call sub-class to get the list of required handlers.
+ m_requiredHandlers = getRequiredHandlerList();
+ }
+
+ /**
+ * Gets the iPOJO Bundle Context.
+ * @return the iPOJO Bundle Context
+ */
+ protected final BundleContext getIPOJOBundleContext() {
+ return Extender.getIPOJOBundleContext();
+ }
+
+ /**
+ * Creates or updates an instance.
+ * @param name the name of the instance
+ * @param properties the new configuration of the instance
+ * @throws org.osgi.service.cm.ConfigurationException if the configuration is not consistent for this component type
+ * @see org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String, java.util.Dictionary)
+ */
+ public void updated(String name, Dictionary properties) throws org.osgi.service.cm.ConfigurationException {
+ ComponentInstance instance;
+ synchronized (this) {
+ instance = (ComponentInstance) m_componentInstances.get(name);
+ }
+
+ if (instance == null) {
+ try {
+ properties.put("instance.name", name); // Add the name in the configuration
+ // If an instance with this name was created before, this creation will failed.
+ createComponentInstance(properties);
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "The configuration is not acceptable : " + e.getMessage());
+ throw new org.osgi.service.cm.ConfigurationException(properties.toString(), e.getMessage());
+ } catch (MissingHandlerException e) {
+ m_logger.log(Logger.ERROR, "Handler not available : " + e.getMessage());
+ throw new org.osgi.service.cm.ConfigurationException(properties.toString(), e.getMessage());
+ } catch (ConfigurationException e) {
+ m_logger.log(Logger.ERROR, "The Component Type metadata are not correct : " + e.getMessage());
+ throw new org.osgi.service.cm.ConfigurationException(properties.toString(), e.getMessage());
+ }
+ } else {
+ try {
+ properties.put("instance.name", name); // Add the name in the configuration
+ reconfigure(properties); // re-configure the component
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "The configuration is not acceptable : " + e.getMessage());
+ throw new org.osgi.service.cm.ConfigurationException(properties.toString(), e.getMessage());
+ } catch (MissingHandlerException e) {
+ m_logger.log(Logger.ERROR, "The factory is not valid, at least one handler is missing : " + e.getMessage());
+ throw new org.osgi.service.cm.ConfigurationException(properties.toString(), e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Deletes an instance.
+ * @param name the name of the instance to delete
+ * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String)
+ */
+ public synchronized void deleted(String name) {
+ INSTANCE_NAME.remove(name);
+ ComponentInstance instance = (ComponentInstance) m_componentInstances.remove(name);
+ if (instance != null) {
+ instance.dispose();
+ }
+ }
+
+ /**
+ * Callback called by instance when disposed.
+ * @param instance the destroyed instance
+ */
+ public void disposed(ComponentInstance instance) {
+ String name = instance.getInstanceName();
+ synchronized (this) {
+ INSTANCE_NAME.remove(name);
+ m_componentInstances.remove(name);
+ }
+ }
+
+ /**
+ * Computes the component type description.
+ * To do this, it creates a 'ghost' instance of the handler
+ * and calls the {@link Handler#initializeComponentFactory(ComponentTypeDescription, Element)}
+ * method. The handler instance is then deleted.
+ * The factory must be valid when calling this method.
+ * This method is called with the lock.
+ */
+ protected void computeDescription() {
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ Handler handler = getHandler(req, null).getHandler();
+ try {
+ handler.setFactory(this);
+ handler.initializeComponentFactory(m_componentDesc, m_componentMetadata);
+ ((Pojo) handler).getComponentInstance().dispose();
+ } catch (org.apache.felix.ipojo.ConfigurationException e) {
+ ((Pojo) handler).getComponentInstance().dispose();
+ m_logger.log(Logger.ERROR, e.getMessage());
+ stop();
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Computes factory state.
+ * The factory is valid if every required handler are available.
+ * If the factory becomes valid for the first time, the component
+ * type description is computed.
+ * This method is called when holding the lock on the current factory.
+ */
+ protected void computeFactoryState() {
+ boolean isValid = true;
+ for (int i = 0; i < m_requiredHandlers.size(); i++) {
+ RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
+ if (req.getReference() == null) {
+ isValid = false;
+ break;
+ }
+
+ }
+
+ if (isValid) {
+ if (m_state == INVALID) {
+
+ if (!m_described) {
+ computeDescription();
+ m_described = true;
+ }
+
+ m_state = VALID;
+ if (m_sr != null) {
+ m_sr.setProperties(m_componentDesc.getPropertiesToPublish());
+ }
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((FactoryStateListener) m_listeners.get(i)).stateChanged(this, VALID);
+ }
+ return;
+ }
+ } else {
+ if (m_state == VALID) {
+ m_state = INVALID;
+
+ // Notify listeners.
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((FactoryStateListener) m_listeners.get(i)).stateChanged(this, INVALID);
+ }
+
+ // Dispose created instances.
+ Set col = m_componentInstances.keySet();
+ String[] keys = (String[]) col.toArray(new String[col.size()]);
+ for (int i = 0; i < keys.length; i++) {
+ ComponentInstance instance = (ComponentInstance) m_componentInstances.get(keys[i]);
+ if (instance.getState() != ComponentInstance.DISPOSED) {
+ instance.dispose();
+ }
+ INSTANCE_NAME.remove(instance.getInstanceName());
+ }
+
+ m_componentInstances.clear();
+
+ if (m_sr != null) {
+ m_sr.setProperties(m_componentDesc.getPropertiesToPublish());
+ }
+
+ return;
+ }
+ }
+ }
+
+ /**
+ * Checks if the given handler identifier and the service reference match.
+ * Does not need to be synchronized as the method does not use any fields.
+ * @param req the handler identifier.
+ * @param ref the service reference.
+ * @return <code>true</code> if the service reference can fulfill the handler requirement
+ */
+ protected boolean match(RequiredHandler req, ServiceReference ref) {
+ String name = (String) ref.getProperty(Handler.HANDLER_NAME_PROPERTY);
+ String namespace = (String) ref.getProperty(Handler.HANDLER_NAMESPACE_PROPERTY);
+ if (HandlerFactory.IPOJO_NAMESPACE.equals(namespace)) {
+ return name.equalsIgnoreCase(req.getName()) && req.getNamespace() == null;
+ }
+ return name.equalsIgnoreCase(req.getName()) && namespace.equalsIgnoreCase(req.getNamespace());
+ }
+
+ /**
+ * Returns the handler object for the given required handler.
+ * The handler is instantiated in the given service context.
+ * This method is called with the lock.
+ * @param req the handler to create.
+ * @param context the service context in which the handler is created (same as the instance context).
+ * @return the handler object.
+ */
+ protected HandlerManager getHandler(RequiredHandler req, ServiceContext context) {
+ try {
+ return (HandlerManager) req.getFactory().createComponentInstance(null, context);
+ } catch (MissingHandlerException e) {
+ m_logger.log(Logger.ERROR, "The creation of the handler " + req.getFullName() + " has failed: " + e.getMessage());
+ stop();
+ return null;
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "The creation of the handler "
+ + req.getFullName()
+ + " has failed (UnacceptableConfiguration): "
+ + e.getMessage());
+ stop();
+ return null;
+ } catch (org.apache.felix.ipojo.ConfigurationException e) {
+ m_logger.log(Logger.ERROR, "The configuration of the handler "
+ + req.getFullName()
+ + " has failed (ConfigurationException): "
+ + e.getMessage());
+ stop();
+ return null;
+ }
+ }
+
+ /**
+ * Helper method generating a new unique name.
+ * This method is call when holding the lock to assert generated name unicity.
+ * @return a non already used name
+ */
+ protected String generateName() {
+ String name = m_factoryName + "-" + m_index;
+ while (INSTANCE_NAME.contains(name)) {
+ m_index = m_index + 1;
+ name = m_factoryName + "-" + m_index;
+ }
+ return name;
+ }
+
+ /**
+ * Structure storing required handlers.
+ * Access to this class must mostly be with the lock on the factory.
+ * (except to access final fields)
+ */
+ protected class RequiredHandler implements Comparable {
+ /**
+ * The factory to create this handler.
+ */
+ private HandlerFactory m_factory;
+
+ /**
+ * The handler name.
+ */
+ private final String m_name;
+
+ /**
+ * The handler start level.
+ */
+ private int m_level = Integer.MAX_VALUE;
+
+ /**
+ * The handler namespace.
+ */
+ private final String m_namespace;
+
+ /**
+ * The Service Reference of the handler factory.
+ */
+ private ServiceReference m_reference;
+
+ /**
+ * Crates a Required Handler.
+ * @param name the handler name.
+ * @param namespace the handler namespace.
+ */
+ public RequiredHandler(String name, String namespace) {
+ m_name = name;
+ m_namespace = namespace;
+ }
+
+ /**
+ * Equals method.
+ * Two handlers are equals if they have same name and namespace or they share the same service reference.
+ * @param object the object to compare to the current object.
+ * @return <code>true</code> if the two compared object are equals
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object object) {
+ if (object instanceof RequiredHandler) {
+ RequiredHandler req = (RequiredHandler) object;
+ if (m_namespace == null) {
+ return req.m_name.equalsIgnoreCase(m_name) && req.m_namespace == null;
+ } else {
+ return req.m_name.equalsIgnoreCase(m_name) && m_namespace.equalsIgnoreCase(req.m_namespace);
+ }
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Hashcode method.
+ * This method delegates to the {@link Object#hashCode()}.
+ * @return the object hashcode.
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ * Gets the factory object used for this handler.
+ * The object is get when used for the first time.
+ * This method is called with the lock avoiding concurrent modification and on a valid factory.
+ * @return the factory object.
+ */
+ public HandlerFactory getFactory() {
+ if (m_reference == null) {
+ return null;
+ }
+ if (m_factory == null) {
+ m_factory = (HandlerFactory) m_context.getService(getReference());
+ }
+ return m_factory;
+ }
+
+ /**
+ * Gets the handler qualified name (<code>namespace:name</code>).
+ * @return the handler full name
+ */
+ public String getFullName() {
+ if (m_namespace == null) {
+ return HandlerFactory.IPOJO_NAMESPACE + ":" + m_name;
+ } else {
+ return m_namespace + ":" + m_name;
+ }
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public String getNamespace() {
+ return m_namespace;
+ }
+
+ public ServiceReference getReference() {
+ return m_reference;
+ }
+
+ public int getLevel() {
+ return m_level;
+ }
+
+ /**
+ * Releases the reference of the used factory.
+ * This method is called with the lock on the current factory.
+ */
+ public void unRef() {
+ if (m_reference != null) {
+ m_factory = null;
+ m_reference = null;
+ }
+ }
+
+ /**
+ * Sets the service reference. If the new service reference is <code>null</code>, it ungets the used factory (if already get).
+ * This method is called with the lock on the current factory.
+ * @param ref the new service reference.
+ */
+ public void setReference(ServiceReference ref) {
+ m_reference = ref;
+ Integer level = (Integer) m_reference.getProperty(Handler.HANDLER_LEVEL_PROPERTY);
+ if (level != null) {
+ m_level = level.intValue();
+ }
+ }
+
+ /**
+ * Start level Comparison.
+ * This method is used to sort the handler array.
+ * This method is called with the lock.
+ * @param object the object on which compare.
+ * @return <code>-1</code>, <code>0</code>, <code>+1</code> according to the comparison of their start levels.
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object object) {
+ if (object instanceof RequiredHandler) {
+ RequiredHandler req = (RequiredHandler) object;
+ if (this.m_level == req.m_level) {
+ return 0;
+ } else if (this.m_level < req.m_level) {
+ return -1;
+ } else {
+ return +1;
+ }
+ }
+ return 0;
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
new file mode 100644
index 0000000..25b0cce
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
@@ -0,0 +1,369 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The instance creator creates instances and tracks their factories.
+ * It allows creating instances from external factories.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceCreator implements FactoryStateListener {
+
+ /**
+ * The logger to log messages if errors occur.
+ */
+ private Logger m_logger;
+
+ /**
+ * The configurations to create and to maintain.
+ */
+ private List m_idle = new ArrayList();
+
+ /**
+ * The map storing created instances.
+ * This map contains [AbstractFactory, List [ManagedInstance]] couples.
+ */
+ private Map m_attached = new HashMap();
+
+ /**
+ * The abstract factory list.
+ */
+ private List m_factories = new ArrayList();
+
+ /**
+ * Creates the instance creator.
+ * This object is generally a singleton.
+ * @param context the bundle context of the iPOJO bundle.
+ */
+ public InstanceCreator(BundleContext context) {
+ m_logger = new Logger(context, "iPOJO Instance Creator");
+ }
+
+ /**
+ * Adds an instance to manage.
+ * @param instance the instance configuration
+ * @param bundle the bundle id declaring the instance
+ */
+ synchronized void addInstance(Dictionary instance, long bundle) {
+ if (instance.get("factory.version") != null) {
+ m_logger.log(Logger.DEBUG, "New instance to managed, looking for " + instance.get("component") + "-" + instance.get("factory.version"));
+ } else {
+ m_logger.log(Logger.DEBUG, "New instance to managed, looking for " + instance.get("component"));
+ }
+
+ ManagedInstance managed = new ManagedInstance(instance, bundle);
+ for (int i = 0; i < m_factories.size(); i++) {
+ IPojoFactory factory = (IPojoFactory) m_factories.get(i);
+ if (managed.matchNameAndVersion(factory)) {
+ // Subscribe to the factory state change
+ m_logger.log(Logger.DEBUG, "Listen factory " + factory.getName() + " events");
+ factory.addFactoryStateListener(this);
+ if (factory.getState() == Factory.VALID && managed.match(factory)) {
+ managed.create(factory);
+ List list = (List) m_attached.get(factory);
+ if (list == null) {
+ list = new ArrayList();
+ list.add(managed);
+ m_attached.put(factory, list);
+ } else {
+ list.add(managed);
+ }
+ return;
+ }
+ }
+ }
+ // If there is no matching factory, add the instance to the idle list
+ m_idle.add(managed);
+ }
+
+ /**
+ * Disposes all instances declared by the given (leaving) bundle.
+ * @param bundle the bundle.
+ */
+ void removeInstancesFromBundle(long bundle) {
+ // Disposes instance from attached instances
+ Collection col = m_attached.keySet();
+ Iterator iterator = col.iterator();
+ List instanceToRemove = new ArrayList();
+ List factoryToRemove = new ArrayList();
+ while (iterator.hasNext()) {
+ IPojoFactory factory = (IPojoFactory) iterator.next();
+ List list = (List) m_attached.get(factory);
+ for (int i = 0; i < list.size(); i++) {
+ ManagedInstance managed = (ManagedInstance) list.get(i);
+ if (managed.m_bundleId == bundle) {
+ managed.dispose();
+ instanceToRemove.add(managed);
+ }
+ }
+ if (!instanceToRemove.isEmpty()) {
+ list.removeAll(instanceToRemove);
+ if (list.isEmpty()) {
+ factory.removeFactoryStateListener(this);
+ factoryToRemove.add(factory);
+ }
+ }
+ }
+
+ for (int i = 0; i < factoryToRemove.size(); i++) {
+ m_attached.remove(factoryToRemove.get(i));
+ }
+
+ // Delete idle instances
+ instanceToRemove.clear();
+ for (int i = 0; i < m_idle.size(); i++) {
+ ManagedInstance managed = (ManagedInstance) m_idle.get(i);
+ if (managed.m_bundleId == bundle) {
+ instanceToRemove.add(managed);
+ }
+ }
+ m_idle.removeAll(instanceToRemove);
+ }
+
+ /**
+ * This method is called when a factory appears.
+ * @param factory the new factory.
+ */
+ public synchronized void addFactory(IPojoFactory factory) {
+ List createdInstances = new ArrayList(1);
+ m_logger.log(Logger.DEBUG, "Add the factory " + factory.getName());
+ m_factories.add(factory);
+ for (int i = 0; i < m_idle.size(); i++) {
+ ManagedInstance managed = (ManagedInstance) m_idle.get(i);
+ if (managed.matchNameAndVersion(factory)) {
+ // We have to subscribe to the factory.
+ factory.addFactoryStateListener(this);
+ if (factory.getState() == Factory.VALID && managed.match(factory)) {
+ managed.create(factory);
+ List list = (List) m_attached.get(factory);
+ if (list == null) {
+ list = new ArrayList();
+ list.add(managed);
+ m_attached.put(factory, list);
+ } else {
+ list.add(managed);
+ }
+ createdInstances.add(managed);
+ }
+ }
+ }
+ if (!createdInstances.isEmpty()) {
+ m_idle.removeAll(createdInstances);
+ }
+ }
+
+ /**
+ * This method is called when a factory is leaving.
+ * @param factory the leaving factory
+ */
+ void removeFactory(IPojoFactory factory) {
+ factory.removeFactoryStateListener(this);
+ m_factories.remove(factory);
+ onInvalidation(factory);
+ m_attached.remove(factory);
+ }
+
+ /**
+ * This method is called when the given factory becomes valid.
+ * @param factory the factory becoming valid.
+ */
+ private void onValidation(IPojoFactory factory) {
+ List toRemove = new ArrayList();
+ for (int i = 0; i < m_idle.size(); i++) {
+ ManagedInstance managed = (ManagedInstance) m_idle.get(i);
+ if (managed.match(factory)) {
+ managed.create(factory);
+ List list = (List) m_attached.get(factory);
+ if (list == null) {
+ list = new ArrayList();
+ list.add(managed);
+ m_attached.put(factory, list);
+ } else {
+ list.add(managed);
+ }
+ toRemove.add(managed);
+ }
+ }
+ if (!toRemove.isEmpty()) {
+ m_idle.removeAll(toRemove);
+ }
+ }
+
+ /**
+ * This method is called when the given factory becomes invalid.
+ * @param factory the factory becoming invalid.
+ */
+ private void onInvalidation(IPojoFactory factory) {
+ List instances = (List) m_attached.remove(factory);
+ if (instances != null) {
+ for (int i = 0; i < instances.size(); i++) {
+ ManagedInstance managed = (ManagedInstance) instances.get(i);
+ managed.dispose();
+ m_idle.add(managed);
+ }
+ }
+ }
+
+ /**
+ * This method is called when the state of a factory changes.
+ * @param factory the factory.
+ * @param newState the new state.
+ * @see org.apache.felix.ipojo.FactoryStateListener#stateChanged(org.apache.felix.ipojo.Factory, int)
+ */
+ public void stateChanged(Factory factory, int newState) {
+ if (newState == Factory.VALID) {
+ m_logger.log(Logger.DEBUG, "A factory is becoming valid : " + factory.getName());
+ onValidation((IPojoFactory) factory);
+ } else {
+ m_logger.log(Logger.DEBUG, "A factory is becoming invalid : " + factory.getName());
+ onInvalidation((IPojoFactory) factory);
+ }
+ }
+
+ /**
+ * This structure aims to manage a configuration.
+ * It stores all necessary information to create an instance
+ * and to track the factory.
+ */
+ private class ManagedInstance {
+ /**
+ * The configuration of the instance to create.
+ */
+ private Dictionary m_configuration;
+
+ /**
+ * The bundle which creates the instance.
+ */
+ private long m_bundleId;
+
+ /**
+ * The factory used to create the instance.
+ */
+ private IPojoFactory m_factory;
+
+ /**
+ * The created instance.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Creates a ManagedInstance.
+ * @param conf the configuration to create.
+ * @param bundle the bundle in which the instance is declared.
+ */
+ ManagedInstance(Dictionary conf, long bundle) {
+ m_configuration = conf;
+ m_bundleId = bundle;
+ }
+
+ /**
+ * Checks if the required factory name match with the given factory.
+ * This methods checks only the name, and not the configuration.
+ * @param factory the factory to test
+ * @return <code>true</code> if the factory name and the version (if set) match, <code>false</code>
+ * otherwise.
+ */
+ public boolean matchNameAndVersion(IPojoFactory factory) {
+ String component = (String) m_configuration.get("component");
+ String v = (String) m_configuration.get("factory.version");
+ if (v == null) {
+ return factory.getName().equals(component) || factory.getClassName().equalsIgnoreCase(component);
+ } else {
+ return (factory.getName().equals(component) || factory.getClassName().equalsIgnoreCase(component))
+ && v.equals(factory.getVersion());
+ }
+ }
+
+ /**
+ * Checks if the given factory match with the factory
+ * required by this instance. A factory matches if its
+ * name or its class name is equals to the 'component'
+ * property of the instance. Then the acceptability of
+ * the configuration is checked.
+ * @param factory the factory to confront against the current instance.
+ * @return <code>true</code> if the factory matches.
+ */
+ public boolean match(IPojoFactory factory) {
+ // Test factory name (and classname)
+ if (matchNameAndVersion(factory)) {
+ // Test factory accessibility
+ if (factory.m_isPublic || factory.getBundleContext().getBundle().getBundleId() == m_bundleId) {
+ // Test the configuration validity.
+ try {
+ factory.checkAcceptability(m_configuration);
+ return true;
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "An instance can be bound to a matching factory, however the configuration seems unacceptable : "
+ + e.getMessage());
+ return false;
+ } catch (MissingHandlerException e) {
+ m_logger.log(Logger.ERROR, "An instance can be bound to a matching factory, but this factory cannot be used : "
+ + e.getMessage());
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Creates the instance by using the given factory.
+ * @param factory the factory to use to create the instance. The factory must match.
+ */
+ public void create(IPojoFactory factory) {
+ try {
+ m_factory = factory;
+ m_instance = m_factory.createComponentInstance(m_configuration);
+ m_logger.log(Logger.INFO, "Instance created");
+ } catch (UnacceptableConfiguration e) {
+ m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
+ + e.getMessage());
+ } catch (MissingHandlerException e) {
+ m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
+ + e.getMessage());
+ } catch (ConfigurationException e) {
+ m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Disposes the current instance if not <code>null</code>.
+ */
+ public void dispose() {
+ if (m_instance != null) {
+ m_instance.dispose();
+ }
+ m_instance = null;
+ m_factory = null;
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
new file mode 100644
index 0000000..1d9c6b9
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -0,0 +1,1371 @@
+/*
+ * 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.felix.ipojo;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class defines the container of primitive instances. It manages content initialization
+ * and handlers cooperation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceManager implements ComponentInstance, InstanceStateListener {
+ /**
+ * The name of the component instance.
+ */
+ protected String m_name;
+
+ /**
+ * The name of the component type implementation class.
+ */
+ protected String m_className;
+
+ /**
+ * The handler object list.
+ */
+ protected final HandlerManager[] m_handlers;
+
+ /**
+ * The current instance state ({@link ComponentInstance#STOPPED} at the beginning).
+ * Possible value are
+ * <li>{@link ComponentInstance#INVALID}</li>
+ * <li>{@link ComponentInstance#VALID}</li>
+ * <li>{@link ComponentInstance#DISPOSED}</li>
+ * <li>{@link ComponentInstance#STOPPED}</li>
+ */
+ protected int m_state = STOPPED;
+
+ /**
+ * The instance state listener list.
+ * @see InstanceStateListener
+ */
+ protected List m_listeners = null;
+
+ /**
+ * The content of the current instance.
+ */
+ protected List m_pojoObjects;
+
+ /**
+ * The instance factory.
+ */
+ private final ComponentFactory m_factory;
+
+ /**
+ * The instance logger.
+ */
+ private final Logger m_logger;
+
+ /**
+ * The instance description.
+ */
+ private final PrimitiveInstanceDescription m_description;
+
+ /**
+ * The bundle context of the instance.
+ */
+ private final BundleContext m_context;
+
+ /**
+ * The map [field, {@link FieldInterceptor} list] storing interceptors monitoring fields.
+ * Once configured, this map can't change.
+ */
+ private Map m_fieldRegistration;
+
+ /**
+ * the map [method identifier, {@link MethodInterceptor} list] interested
+ * by the method.
+ * Once configured, this map can't change.
+ */
+ private Map m_methodRegistration;
+
+ /**
+ * the map (sorted by parameter index) or {@link ConstructorInjector} interested by
+ * injecting constructor parameter.
+ * Once configured, this list can't change.
+ */
+ private Map m_constructorRegistration;
+
+ /**
+ * The manipulated class.
+ * Once set, this field doesn't change.
+ */
+ private Class m_clazz;
+
+ /**
+ * The factory method used to create content objects.
+ * If <code>null</code>, the regular constructor is used.
+ * Once set, this field is immutable.
+ */
+ private String m_factoryMethod = null;
+
+ /**
+ * Is the component instance state changing?
+ */
+ private boolean m_inTransition = false;
+
+ /**
+ * The queue of stored state changed.
+ */
+ private List m_stateQueue = new ArrayList();
+
+ /**
+ * The map of [field, value], storing POJO managed
+ * field value.
+ */
+ private Map m_fields = new HashMap();
+
+ /**
+ * The Map storing the Method objects by ids.
+ * [id=>{@link Method}].
+ */
+ private Map m_methods = new HashMap();
+
+
+ /**
+ * Creates a new Component Manager.
+ * The instance is not initialized.
+ * @param factory the factory managing the instance manager
+ * @param context the bundle context to give to the instance
+ * @param handlers handler object array
+ */
+ public InstanceManager(ComponentFactory factory, BundleContext context, HandlerManager[] handlers) {
+ m_factory = factory;
+ m_context = context;
+ m_handlers = handlers;
+ m_description = new PrimitiveInstanceDescription(m_factory.getComponentDescription(), this);
+ m_logger = new Logger(m_context, this);
+ }
+
+ /**
+ * The instance logger.
+ * @return the logger
+ */
+ public Logger getLogger() {
+ return m_logger;
+ }
+
+ /**
+ * Configures the instance manager.
+ * Sets the class name, and the instance name as well as the factory method.
+ * Initializes handlers.
+ * @param metadata the component type metadata
+ * @param configuration the configuration of the instance
+ * @throws ConfigurationException if the metadata are not correct
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_className = metadata.getAttribute("classname");
+
+ // Add the name
+ m_name = (String) configuration.get("instance.name");
+
+ // Check if an object is injected in the instance
+ Object obj = configuration.get("instance.object");
+ if (obj != null) {
+ m_pojoObjects = new ArrayList(1);
+ m_pojoObjects.add(obj);
+ }
+
+ // Get the factory method if presents.
+ m_factoryMethod = (String) metadata.getAttribute("factory-method");
+
+ // Create the standard handlers and add these handlers to the list
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].init(this, metadata, configuration);
+ }
+
+ // Check that the constructor parameter are continuous.
+ if (m_constructorRegistration != null) {
+ for (int i = 0; i < m_constructorRegistration.size(); i++) {
+ if (! m_constructorRegistration.containsKey(new Integer(i))) {
+ throw new ConfigurationException("The constructor parameter " + i + " is not managed");
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the description of the current instance.
+ * @return the instance description.
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ return m_description;
+ }
+
+ /**
+ * Gets the list of handlers plugged (i.e. attached) on the instance.
+ * This method does not need a synchronized block as the handler set is constant.
+ * @return the handler array of plugged handlers.
+ */
+ public Handler[] getRegistredHandlers() {
+ Handler[] handler = new Handler[m_handlers.length];
+ for (int i = 0; i < m_handlers.length; i++) {
+ handler[i] = m_handlers[i].getHandler();
+ }
+ return handler;
+ }
+
+ /**
+ * Returns a specified handler.
+ * This method allows cross-handler interactions.
+ * This must does not need a synchronized block as the handler set is constant.
+ * @param name the class name of the handler to find or its qualified name (namespace:name)
+ * @return the handler, or null if not found
+ */
+ public Handler getHandler(String name) {
+ for (int i = 0; i < m_handlers.length; i++) {
+ HandlerFactory fact = (HandlerFactory) m_handlers[i].getHandler().getHandlerManager().getFactory();
+ if (fact.getHandlerName().equals(name)) {
+ return m_handlers[i].getHandler();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gives access to a field value of the first created pojo.
+ * This method processes by analyzing both managed fields and pojo fields (by reflection).
+ * If no pojo were already created try only on managed fields.
+ * @param fieldName the field name.
+ * @return the field value, <code>null</code> is returned if the value is managed and not already set.
+ */
+ public synchronized Object getFieldValue(String fieldName) {
+ if (m_pojoObjects == null) {
+ return getFieldValue(fieldName, null);
+ } else {
+ return getFieldValue(fieldName, m_pojoObjects.get(0)); // Use the first pojo.
+ }
+ }
+
+ /**
+ * Gives access to a field value to the given created pojo.
+ * This method processes by analyzing both managed fields and pojo fields (by reflection).
+ * If the given pojo is <code>null</code>, tries only on managed fields.
+ * @param fieldName the field name.
+ * @param pojo the pojo on which computing field value.
+ * @return the field value, <code>null</code> is returned if the value is managed and not already set.
+ */
+ public synchronized Object getFieldValue(String fieldName, Object pojo) {
+ Object setByContainer = null;
+
+ if (m_fields != null) {
+ setByContainer = m_fields.get(fieldName);
+ }
+
+ if (setByContainer == null && pojo != null) { // In the case of no given pojo, return null.
+ // If null either the value was not already set or has the null value.
+ try {
+ Field field = pojo.getClass().getDeclaredField(fieldName);
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ return field.get(pojo);
+ } catch (SecurityException e) {
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ } catch (NoSuchFieldException e) {
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ }
+ return null;
+ } else {
+ return setByContainer;
+ }
+ }
+
+ /**
+ * Starts the instance manager.
+ * This method activates plugged handlers,
+ * and computes the initial instance state.
+ */
+ public void start() {
+ synchronized (this) {
+ if (m_state != STOPPED) { // Instance already started
+ return;
+ } else {
+ m_state = -2; // Temporary state.
+ }
+ }
+
+ // Plug handler descriptions
+ Handler[] handlers = getRegistredHandlers();
+ for (int i = 0; i < handlers.length; i++) {
+ m_description.addHandler(handlers[i].getDescription());
+ }
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].addInstanceStateListener(this);
+ try {
+ m_handlers[i].start();
+ } catch (IllegalStateException e) {
+ m_logger.log(Logger.ERROR, e.getMessage());
+ stop();
+ throw e;
+ }
+ }
+
+ // Is an object already contained (i.e. injected)
+ if (m_pojoObjects != null && ! m_pojoObjects.isEmpty()) {
+ managedInjectedObject();
+ }
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) {
+ setState(INVALID);
+ return;
+ }
+ }
+ setState(VALID);
+ }
+
+ /**
+ * Stops the instance manager.
+ * This methods sets the instance state to {@link ComponentInstance#STOPPED},
+ * disables attached handlers, and notifies listeners ({@link InstanceStateListener})
+ * of the instance stopping process.
+ */
+ public void stop() {
+ List listeners = null;
+ synchronized (this) {
+ if (m_state == STOPPED) { // Instance already stopped
+ return;
+ }
+ m_stateQueue.clear();
+ m_inTransition = false;
+ }
+
+ setState(INVALID); // Must be called outside a synchronized block.
+
+ // Stop all the handlers
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].removeInstanceStateListener(this);
+ m_handlers[i].stop();
+ }
+
+ synchronized (this) {
+ m_state = STOPPED;
+ if (m_listeners != null) {
+ listeners = new ArrayList(m_listeners); // Stack confinement
+ }
+ m_pojoObjects = null;
+ }
+
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ ((InstanceStateListener) listeners.get(i)).stateChanged(this, STOPPED);
+ }
+ }
+ }
+
+ /**
+ * Disposes the instance.
+ * This method does the following process:
+ * <li>Stop the instance is not {@link ComponentInstance#STOPPED}</li>
+ * <li>Notifies listeners {@link InstanceStateListener} of the destruction</li>
+ * <li>Disposes attached handlers</li>
+ * <li>Clears structures</li>
+ * @see org.apache.felix.ipojo.ComponentInstance#dispose()
+ */
+ public void dispose() {
+ List listeners = null;
+ int state = -2; // Temporary state
+ synchronized (this) {
+ state = m_state; // Stack confinement
+ if (m_listeners != null) {
+ listeners = new ArrayList(m_listeners); // Stack confinement
+ }
+ m_listeners = null;
+ }
+
+ if (state > STOPPED) { // Valid or invalid
+ stop(); // Does not hold the lock.
+ }
+
+ synchronized (this) {
+ m_state = DISPOSED;
+ }
+
+ for (int i = 0; listeners != null && i < listeners.size(); i++) {
+ ((InstanceStateListener) listeners.get(i)).stateChanged(this, DISPOSED);
+ }
+
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].dispose();
+ }
+
+ synchronized (this) {
+ m_factory.disposed(this);
+ m_clazz = null;
+ // Do not clean registration map, so injection still works
+ // after disposal for late callbacks.
+ }
+ }
+
+ /**
+ * Sets the state of the component instance.
+ * If the state changes, calls the {@link PrimitiveHandler#stateChanged(int)} method on the attached handlers.
+ * This method has a reentrant mechanism. If in the flow of the first call the method is called another times,
+ * the second call is stored and executed after the first one finished.
+ * @param state the new state
+ */
+ public void setState(int state) {
+ int originalState = -2;
+ List listeners = null;
+ synchronized (this) {
+ if (m_inTransition) {
+ m_stateQueue.add(new Integer(state));
+ return;
+ }
+
+ if (m_state != state) {
+ m_inTransition = true;
+ originalState = m_state; // Stack confinement.
+ m_state = state;
+ if (m_listeners != null) {
+ listeners = new ArrayList(m_listeners); // Stack confinement.
+ }
+ }
+ }
+
+ // This section can be executed only by one thread at the same time. The m_inTransition pseudo semaphore block access to this section.
+ if (m_inTransition) { // Check that we are really changing.
+ if (state > originalState) {
+ // The state increases (Stopped = > IV, IV => V) => invoke handlers from the higher priority to the lower
+ try {
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ } catch (IllegalStateException e) {
+ // When an illegal state exception happens, the instance manager must be stopped immediately.
+ stop();
+ m_logger.log(Logger.ERROR, e.getMessage(), e);
+ return;
+ }
+ } else {
+ // The state decreases (V => IV, IV = > Stopped, Stopped => Disposed)
+ try {
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ } catch (IllegalStateException e) {
+ // When an illegal state exception happens, the instance manager must be stopped immediately.
+ stop();
+ m_logger.log(Logger.ERROR, e.getMessage());
+ return;
+ }
+ }
+ }
+
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ ((InstanceStateListener) listeners.get(i)).stateChanged(this, state);
+ }
+ }
+
+ synchronized (this) {
+ m_inTransition = false;
+ if (!m_stateQueue.isEmpty()) {
+ int newState = ((Integer) (m_stateQueue.remove(0))).intValue();
+ setState(newState);
+ }
+ }
+ }
+
+ /**
+ * Gets the actual state of the instance.
+ * Possible values are:
+ * <li>{@link ComponentInstance#INVALID}</li>
+ * <li>{@link ComponentInstance#VALID}</li>
+ * <li>{@link ComponentInstance#DISPOSED}</li>
+ * <li>{@link ComponentInstance#STOPPED}</li>
+ * @return the actual state of the component instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#getState()
+ */
+ public synchronized int getState() {
+ return m_state;
+ }
+
+ /**
+ * Checks if the instance is started.
+ * An instance is started if the state is either
+ * {@link ComponentInstance#VALID} or {@link ComponentInstance#INVALID}.
+ * @return <code>true</code> if the instance is started.
+ * @see org.apache.felix.ipojo.ComponentInstance#isStarted()
+ */
+ public synchronized boolean isStarted() {
+ return m_state > STOPPED;
+ }
+
+ /**
+ * Registers an instance state listener.
+ * @param listener the listener to register.
+ * @see org.apache.felix.ipojo.ComponentInstance#addInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public synchronized void addInstanceStateListener(InstanceStateListener listener) {
+ if (m_listeners == null) {
+ m_listeners = new ArrayList();
+ }
+ m_listeners.add(listener);
+ }
+
+ /**
+ * Unregisters an instance state listener.
+ * @param listener the listener to unregister.
+ * @see org.apache.felix.ipojo.ComponentInstance#removeInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public synchronized void removeInstanceStateListener(InstanceStateListener listener) {
+ if (m_listeners != null) {
+ m_listeners.remove(listener);
+ if (m_listeners.isEmpty()) {
+ m_listeners = null;
+ }
+ }
+ }
+
+ /**
+ * Gets the factory which has created the current instance.
+ * @return the factory of the component
+ * @see org.apache.felix.ipojo.ComponentInstance#getFactory()
+ */
+ public ComponentFactory getFactory() {
+ return m_factory;
+ }
+
+ /**
+ * Loads the manipulated class.
+ */
+ private void load() {
+ try {
+ m_clazz = m_factory.loadClass(m_className);
+ } catch (ClassNotFoundException e) {
+ m_logger.log(Logger.ERROR, "[" + m_name + "] Class not found during the loading phase : " + e.getMessage(), e);
+ stop();
+ return;
+ }
+ }
+
+ /**
+ * Gets the object array created by the instance.
+ * @return the created content objects of the component instance.
+ */
+ public synchronized Object[] getPojoObjects() {
+ if (m_pojoObjects == null) {
+ return null;
+ }
+ return m_pojoObjects.toArray(new Object[m_pojoObjects.size()]);
+ }
+
+ /**
+ * Creates a POJO objects.
+ * This method is not synchronized and does not require any locks.
+ * If a {@link InstanceManager#m_factoryMethod} is specified,
+ * this method called this static method to creates the object.
+ * Otherwise, the methods uses the regular constructor.
+ * All those methods can receive the {@link BundleContext} in
+ * argument.
+ * @return the created object or <code>null</code> if an error
+ * occurs during the creation.
+ */
+ protected Object createObject() {
+ if (m_clazz == null) {
+ load();
+ }
+
+ // The following code doesn't need to be synchronized as is deal only with immutable fields.
+ Object instance = null;
+ if (m_factoryMethod == null) {
+ // No factory-method, we use the constructor.
+ try {
+ // Try to find the correct constructor.
+ if (m_constructorRegistration != null) {
+ // Initialize the injected values and types
+ // We have the IM first.
+ Object[] values = new Object[m_constructorRegistration.size() + 1];
+ Class[] types = new Class[m_constructorRegistration.size() + 1];
+ values[0] = this;
+ types[0] = InstanceManager.class;
+
+ // Iterate over the constructor injector
+ for (int i = 0; i < m_constructorRegistration.size(); i++) {
+ ConstructorInjector injector = (ConstructorInjector)
+ m_constructorRegistration.get(new Integer(i));
+ Object v = injector.getConstructorParameter(i);
+ if (v != null) {
+ values[i + 1] = v;
+ Class t = injector.getConstructorParameterType(i);
+ if (t == null) {
+ t = v.getClass();
+ }
+ types[i + 1] = t;
+ }
+ }
+ // Find the constructor.
+ Constructor cst = m_clazz.getDeclaredConstructor(types);
+ if (! cst.isAccessible()) {
+ cst.setAccessible(true);
+ }
+ String methodId = MethodMetadata.computeMethodId(cst);
+ onEntry(null, methodId, values);
+ instance = cst.newInstance(values);
+ onExit(instance, methodId, instance);
+ } else {
+ // Old semantic
+ // Try to find if there is a constructor with a bundle context as parameter :
+ try {
+ Constructor cst = m_clazz.getDeclaredConstructor(new Class[] { InstanceManager.class, BundleContext.class });
+ if (! cst.isAccessible()) {
+ cst.setAccessible(true);
+ }
+ Object[] args = new Object[] { this, m_context };
+ onEntry(null, MethodMetadata.BC_CONSTRUCTOR_ID, new Object[] {m_context});
+ instance = cst.newInstance(args);
+ onExit(instance, MethodMetadata.BC_CONSTRUCTOR_ID, instance);
+ } catch (NoSuchMethodException e) {
+ // Create an instance if no instance are already created with <init>()BundleContext
+ if (instance == null) {
+ Constructor cst = m_clazz.getDeclaredConstructor(new Class[] { InstanceManager.class });
+ if (! cst.isAccessible()) {
+ cst.setAccessible(true);
+ }
+ Object[] args = new Object[] {this};
+ onEntry(null, MethodMetadata.EMPTY_CONSTRUCTOR_ID, new Object[0]);
+ instance = cst.newInstance(args);
+ onExit(instance, MethodMetadata.EMPTY_CONSTRUCTOR_ID, instance);
+ }
+ }
+ }
+
+ } catch (IllegalAccessException e) {
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The POJO constructor is not accessible : " + e.getMessage(), e);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the POJO constructor is not accessible : " + e.getMessage());
+ } catch (SecurityException e) {
+ m_logger.log(
+ Logger.ERROR,
+ "["
+ + m_name
+ + "] createInstance -> The POJO constructor is not accessible (security reason) : "
+ + e.getMessage(), e);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the POJO constructor is not accessible : " + e.getMessage());
+ } catch (InvocationTargetException e) {
+ m_logger.log(
+ Logger.ERROR,
+ "["
+ + m_name
+ + "] createInstance -> Cannot invoke the constructor method - the constructor throws an exception : "
+ + e.getTargetException().getMessage(), e.getTargetException());
+ onError(null, m_className, e.getTargetException());
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the POJO constructor has thrown an exception: " + e.getTargetException().getMessage());
+ } catch (NoSuchMethodException e) {
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> Cannot invoke the constructor (method not found) : " + e.getMessage(), e);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the POJO constructor cannot be found : " + e.getMessage());
+ } catch (Throwable e) {
+ // Catch every other possible error and runtime exception.
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage(), e);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the POJO constructor invocation has thrown an exception : " + e.getMessage());
+ }
+ } else {
+ try {
+ // Build the pojo object with the factory-method.
+ Method factory = null;
+ // Try with the bundle context
+ try {
+ factory = m_clazz.getDeclaredMethod(m_factoryMethod, new Class[] { BundleContext.class });
+ if (! factory.isAccessible()) {
+ factory.setAccessible(true);
+ }
+ Object[] args = new Object[] { m_context };
+ onEntry(null, m_className, args);
+ instance = factory.invoke(null, new Object[] { m_context });
+ } catch (NoSuchMethodException e1) {
+ // Try without the bundle context
+ try {
+ factory = m_clazz.getDeclaredMethod(m_factoryMethod, new Class[0]);
+ if (! factory.isAccessible()) {
+ factory.setAccessible(true);
+ }
+ Object[] args = new Object[0];
+ onEntry(null, m_className, args);
+ instance = factory.invoke(null, args);
+ } catch (NoSuchMethodException e2) {
+ // Error : factory-method not found
+ m_logger.log(
+ Logger.ERROR,
+ "["
+ + m_name
+ + "] createInstance -> Cannot invoke the factory-method (method not found) : "
+ + e2.getMessage(), e2);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the factory-method cannot be found : " + e2.getMessage());
+ }
+ }
+
+ // Now call the setInstanceManager method.
+ // Find declaring super class.
+ Class declaringClass = instance.getClass();
+ Method method = null;
+ while (declaringClass != null && method == null) {
+ try {
+ method = declaringClass.getDeclaredMethod("_setInstanceManager",
+ new Class[] { InstanceManager.class });
+ } catch (NoSuchMethodException e) {
+ //Do nothing
+ }
+
+ declaringClass = declaringClass.getSuperclass();
+ }
+
+ if (method == null) {
+ // Error : _setInstanceManager method is missing
+ m_logger
+ .log(
+ Logger.ERROR,
+ "["
+ + m_name
+ + "] createInstance -> Cannot invoke the factory-method (the _setInstanceManager method does not exist");
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the factory-method cannot be found");
+ }
+
+ if (!method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ method.invoke(instance, new Object[] { this });
+ onExit(null, m_className, instance);
+
+ } catch (InvocationTargetException e) {
+ // Error : invocation failed
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The factory-method throws an exception : " + e.getTargetException(), e.getTargetException());
+ onError(null, m_className, e.getTargetException());
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the factory-method has thrown an exception: " + e.getTargetException().getMessage());
+ } catch (Throwable e) {
+ // Catch every other possible error and runtime exception.
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The factory-method invocation failed : " + e.getMessage(), e);
+ stop();
+ throw new RuntimeException("Cannot create a POJO instance, the factory-method invocation has thrown an exception : " + e.getMessage());
+ }
+ }
+ return instance;
+ }
+
+ /**
+ * Creates an instance of the content.
+ * This method needs to be called once only for singleton provided service.
+ * This methods call the {@link InstanceManager#createObject()} method, and adds
+ * the created object to the {@link InstanceManager#m_pojoObjects} list. Then,
+ * it calls the {@link PrimitiveHandler#onCreation(Object)} methods on attached
+ * handlers.
+ * @return a new instance or <code>null</code> if an error occurs during the
+ * creation.
+ */
+ public Object createPojoObject() {
+ Object instance = createObject();
+
+ // Add the new instance in the instance list.
+ synchronized (this) {
+ if (m_pojoObjects == null) {
+ m_pojoObjects = new ArrayList(1);
+ }
+ m_pojoObjects.add(instance);
+ }
+ // Call createInstance on Handlers :
+ for (int i = 0; i < m_handlers.length; i++) {
+ // This methods must be call without the monitor lock.
+ ((PrimitiveHandler) m_handlers[i].getHandler()).onCreation(instance);
+ }
+
+ return instance;
+ }
+
+ /**
+ * Deletes a POJO object.
+ * @param pojo the pojo to remove from the list of created pojos.
+ */
+ public synchronized void deletePojoObject(Object pojo) {
+ if (m_pojoObjects != null) {
+ m_pojoObjects.remove(pojo);
+ }
+ }
+
+ /**
+ * Gets the first object created by the instance.
+ * If no object created, creates and returns a POJO object.
+ * This methods call the {@link InstanceManager#createObject()} method, and adds
+ * the created object to the {@link InstanceManager#m_pojoObjects} list. Then,
+ * it calls the {@link PrimitiveHandler#onCreation(Object)} methods on attached
+ * handlers.
+ * <br/>
+ * <p>
+ * <b>TODO</b> this method has a potential race condition if two threads require a pojo
+ * object at the same time. Only one object will be created, but the second thread
+ * can receive the created object before the {@link PrimitiveHandler#onCreation(Object)}
+ * calls.
+ * </p>
+ * @return the pojo object of the component instance to use for singleton component
+ */
+ public Object getPojoObject() {
+ Object pojo = null;
+ boolean newPOJO = false;
+ synchronized (this) {
+ if (m_pojoObjects != null) {
+ pojo = m_pojoObjects.get(0); // Stack confinement
+ } else {
+ pojo = createObject(); // Stack confinement
+ if (m_pojoObjects == null) {
+ m_pojoObjects = new ArrayList(1);
+ }
+ m_pojoObjects.add(pojo);
+ newPOJO = true;
+ }
+ }
+
+ // Call createInstance on Handlers :
+ for (int i = 0; newPOJO && i < m_handlers.length; i++) {
+ ((PrimitiveHandler) m_handlers[i].getHandler()).onCreation(pojo);
+ }
+ //NOTE this method allows returning a POJO object before calling the onCreation on handler:
+ // a second thread get the created object before the first one (which created the object),
+ // call onCreation.
+
+ return pojo;
+ }
+
+ /**
+ * Gets the manipulated class.
+ * The method does not need to be synchronized.
+ * Reassigning the internal class will use the same class object.
+ * @return the manipulated class
+ */
+ public Class getClazz() {
+ if (m_clazz == null) {
+ load();
+ }
+ return m_clazz;
+ }
+
+ /**
+ * Configures an injected object in this container.
+ */
+ private void managedInjectedObject() {
+ Object obj = m_pojoObjects.get(0); // Get first object.
+
+ if (! (obj instanceof Pojo)) {
+ // Error, the injected object is not a POJO.
+ throw new RuntimeException("The injected object in " + m_name + " is not a POJO");
+ }
+
+ load(); // Load the class.
+
+ if (! m_clazz.isInstance(obj)) {
+ throw new RuntimeException("The injected object in " + m_name + " is not an instance of " + m_className);
+ }
+
+ // Call _setInstanceManager
+ try {
+ Method setIM = m_clazz.getDeclaredMethod("_setInstanceManager", new Class[] {this.getClass()});
+ setIM.setAccessible(true); // Necessary as the method is private
+ setIM.invoke(obj, new Object[] {this});
+ } catch (Exception e) {
+ // If anything wrong happened...
+ throw new RuntimeException("Cannot attach the injected object with the container of " + m_name + " : " + e.getMessage());
+ }
+
+ // Call createInstance on Handlers :
+ for (int i = 0; i < m_handlers.length; i++) {
+ // This methods must be call without the monitor lock.
+ ((PrimitiveHandler) m_handlers[i].getHandler()).onCreation(obj);
+ }
+
+
+ }
+
+ /**
+ * Registers an handler.
+ * This methods is called by handler wanting to monitor
+ * fields and/or methods of the implementation class.
+ * @param handler the handler to register
+ * @param fields the field metadata list
+ * @param methods the method metadata list
+ * @deprecated use {@link InstanceManager#register(FieldMetadata, FieldInterceptor)}
+ * and {@link InstanceManager#register(FieldMetadata, MethodInterceptor)} instead.
+ */
+ public void register(PrimitiveHandler handler, FieldMetadata[] fields, MethodMetadata[] methods) {
+ for (int i = 0; fields != null && i < fields.length; i++) {
+ register(fields[i], handler);
+ }
+ for (int i = 0; methods != null && i < methods.length; i++) {
+ register(methods[i], handler);
+ }
+
+ }
+
+ /**
+ * Registers a field interceptor.
+ * A field interceptor will be notified of field access of the
+ * implementation class. Note that handlers are field interceptors.
+ * @param field the field to monitor
+ * @param interceptor the field interceptor object
+ */
+ public void register(FieldMetadata field, FieldInterceptor interceptor) {
+ if (m_fieldRegistration == null) {
+ m_fieldRegistration = new HashMap();
+ m_fieldRegistration.put(field.getFieldName(), new FieldInterceptor[] { interceptor });
+ } else {
+ FieldInterceptor[] list = (FieldInterceptor[]) m_fieldRegistration.get(field.getFieldName());
+ if (list == null) {
+ m_fieldRegistration.put(field.getFieldName(), new FieldInterceptor[] { interceptor });
+ } else {
+ for (int j = 0; j < list.length; j++) {
+ if (list[j] == interceptor) {
+ return;
+ }
+ }
+ FieldInterceptor[] newList = new FieldInterceptor[list.length + 1];
+ System.arraycopy(list, 0, newList, 0, list.length);
+ newList[list.length] = interceptor;
+ m_fieldRegistration.put(field.getFieldName(), newList);
+ }
+ }
+ }
+
+ /**
+ * Registers a method interceptor.
+ * A method interceptor will be notified of method entries, exits
+ * and errors. Note that handlers are method interceptors.
+ * @param method the field to monitor
+ * @param interceptor the field interceptor object
+ */
+ public void register(MethodMetadata method, MethodInterceptor interceptor) {
+ if (m_methodRegistration == null) {
+ m_methodRegistration = new HashMap();
+ m_methodRegistration.put(method.getMethodIdentifier(), new MethodInterceptor[] { interceptor });
+ } else {
+ MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(method.getMethodIdentifier());
+ if (list == null) {
+ m_methodRegistration.put(method.getMethodIdentifier(), new MethodInterceptor[] { interceptor });
+ } else {
+ for (int j = 0; j < list.length; j++) {
+ if (list[j] == interceptor) {
+ return;
+ }
+ }
+ MethodInterceptor[] newList = new MethodInterceptor[list.length + 1];
+ System.arraycopy(list, 0, newList, 0, list.length);
+ newList[list.length] = interceptor;
+ m_methodRegistration.put(method.getMethodIdentifier(), newList);
+ }
+ }
+ }
+
+ /**
+ * Registers a constructor injector.
+ * The constructor injector will be called when a pojo object is going to be
+ * created.
+ * @param index the index of the parameter. Only one injector per index can
+ * be registered.
+ * @param injector the injector object.
+ * @throws ConfigurationException if the given index is already injected by another
+ * injector
+ */
+ public void register(int index, ConstructorInjector injector) throws ConfigurationException {
+ Integer key = new Integer(index);
+ if (m_constructorRegistration == null) {
+ m_constructorRegistration = new HashMap();
+ }
+ if (! m_constructorRegistration.containsKey(key)) {
+ m_constructorRegistration.put(key, injector);
+ } else {
+ throw new ConfigurationException("Another constructor injector " +
+ "manages the parameter " + index);
+ }
+ }
+
+ /**
+ * This method is called by the manipulated class each time that a GETFIELD instruction is executed.
+ * The method asks to each attached handler monitoring this field which value need
+ * to be injected (i.e. returned) by invoking the {@link PrimitiveHandler#onGet(Object, String, Object)}
+ * method. If the field value changes, this method call the {@link PrimitiveHandler#onSet(Object, String, Object)}
+ * method on each field interceptor monitoring the field in order to advertize the new value.
+ * @param pojo the pojo object on which the field was get
+ * @param fieldName the field name on which the GETFIELD instruction is called
+ * @return the value decided by the last asked handler (throws a warning if two fields decide two different values)
+ */
+ public Object onGet(Object pojo, String fieldName) {
+ Object initialValue = null;
+ synchronized (this) { // Stack confinement.
+ initialValue = m_fields.get(fieldName);
+ }
+ Object result = initialValue;
+ boolean hasChanged = false;
+ // Get the list of registered handlers
+ FieldInterceptor[] list = (FieldInterceptor[]) m_fieldRegistration.get(fieldName); // Immutable list.
+ for (int i = 0; list != null && i < list.length; i++) {
+ // Call onGet outside of a synchronized block.
+ Object handlerResult = list[i].onGet(null, fieldName, initialValue);
+ if (handlerResult == initialValue) {
+ continue; // Non-binding case (default implementation).
+ } else {
+ if (result != initialValue) {
+ //TODO analyze impact of removing conflict detection
+ if ((handlerResult != null && !handlerResult.equals(result)) || (result != null && handlerResult == null)) {
+ m_logger.log(
+ Logger.WARNING,
+ "A conflict was detected on the injection of "
+ + fieldName);
+ }
+ }
+ result = handlerResult;
+ hasChanged = true;
+ }
+ }
+ if (hasChanged) {
+ // A change occurs => notify the change
+ //TODO consider just changing the reference, however multiple thread can be an issue
+ synchronized (this) {
+ m_fields.put(fieldName, result);
+ }
+ // Call onset outside of a synchronized block.
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onSet(null, fieldName, result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Dispatches entry method events on registered method interceptors.
+ * This method calls the {@link PrimitiveHandler#onEntry(Object, Method, Object[])}
+ * methods on method interceptors monitoring the method.
+ * @param pojo the pojo object on which method is invoked.
+ * @param methodId the method id used to compute the {@link Method} object.
+ * @param args the argument array
+ */
+ public void onEntry(Object pojo, String methodId, Object[] args) {
+ if (m_methodRegistration == null) { // Immutable field.
+ return;
+ }
+ MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(methodId);
+ Member method = getMethodById(methodId);
+ // In case of a constructor, the method is null, and the list is null too.
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onEntry(pojo, method, args); // Outside a synchronized block.
+ }
+ }
+
+ /**
+ * Dispatches exit method events on registered method interceptors.
+ * The given returned object is an instance of {@link Exception} if the method thrown an
+ * exception. If the given object is <code>null</code>, either the method returns <code>void</code>,
+ * or the method has returned <code>null</code>
+ * This method calls the {@link PrimitiveHandler#onExit(Object, Method, Object[])} and the
+ * {@link PrimitiveHandler#onFinally(Object, Method)} methods on method interceptors monitoring the method.
+ * @param pojo the pojo object on which method was invoked.
+ * @param methodId the method id used to compute the {@link Method} object.
+ * @param result the returned object.
+ */
+ public void onExit(Object pojo, String methodId, Object result) {
+ if (m_methodRegistration == null) {
+ return;
+ }
+ MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(methodId);
+ Member method = getMethodById(methodId);
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onExit(pojo, method, result);
+ }
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onFinally(pojo, method);
+ }
+ }
+
+ /**
+ * Dispatches error method events on registered method interceptors.
+ * or the method has returned <code>null</code>
+ * This method calls the {@link PrimitiveHandler#onExit(Object, Method, Object[])} and the
+ * {@link PrimitiveHandler#onFinally(Object, Method)} methods on method interceptors monitoring
+ * the method.
+ * @param pojo the pojo object on which the method was invoked
+ * @param methodId the method id used to compute the {@link Method} object.
+ * @param error the Throwable object.
+ */
+ public void onError(Object pojo, String methodId, Throwable error) {
+ if (m_methodRegistration == null) {
+ return;
+ }
+ MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(methodId);
+ Member method = getMethodById(methodId);
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onError(pojo, method, error);
+ }
+ for (int i = 0; list != null && i < list.length; i++) {
+ list[i].onFinally(pojo, method);
+ }
+ }
+
+ /**
+ * Computes the {@link Method} object from the given id.
+ * Once computes, a map is used as a cache to avoid to recompute for
+ * the same id.
+ * @param methodId the method id
+ * @return the method object or <code>null</code> if the method cannot be found.
+ */
+ private Member getMethodById(String methodId) {
+ // Not necessary synchronized as recomputing the methodID will give the same Method twice.
+ Member member = (Member) m_methods.get(methodId);
+ if (member == null && m_clazz != null) {
+
+ // First try on methods.
+ Method[] mets = m_clazz.getDeclaredMethods();
+ for (int i = 0; i < mets.length; i++) {
+ // Check if the method was not already computed. If not, compute the Id and check.
+ if (!m_methods.containsValue(mets[i]) && (MethodMetadata.computeMethodId(mets[i]).equals(methodId))) {
+ // Store the new methodId
+ m_methods.put(methodId, mets[i]);
+ return mets[i];
+ }
+ }
+
+ // If not found, it is a constructor, return the constructor object in this case.
+ if (methodId.startsWith(MethodMetadata.CONSTRUCTOR_PREFIX)) {
+ Constructor[] constructors = m_clazz.getDeclaredConstructors();
+ for (int i = 0; i < constructors.length; i++) {
+ // Check if the constructor was not already computed. If not, compute the Id and check.
+ if (!m_methods.containsValue(constructors[i]) && (MethodMetadata.computeMethodId(constructors[i]).equals(methodId))) {
+ // Store the new methodId
+ m_methods.put(methodId, constructors[i]);
+ return constructors[i];
+ }
+ }
+ }
+
+ // Cannot happen
+ m_logger.log(Logger.INFO, "A methodID cannot be associated with a method from the POJO class: " + methodId);
+ return null;
+ } else {
+ return member;
+ }
+ }
+
+ /**
+ * This method is called by the manipulated class each time that a PUTFIELD instruction is executed.
+ * The method calls the {@link PrimitiveHandler#onSet(Object, String, Object)} method on each field
+ * interceptors monitoring this field.
+ * This method can be invoked with a <code>null</code> pojo argument when the changes comes from another
+ * handler.
+ * @param pojo the pojo object on which the field was set
+ * @param fieldName the field name on which the PUTFIELD instruction is called
+ * @param objectValue the new value of the field
+ */
+ public void onSet(final Object pojo, final String fieldName, final Object objectValue) {
+ synchronized (this) {
+ // First, store the new value.
+ // This must be done in a synchronized block to avoid
+ // concurrent modification
+ m_fields.put(fieldName, objectValue);
+ }
+ // The registrations cannot be modified, so we can directly access
+ // the interceptor list.
+ FieldInterceptor[] list = (FieldInterceptor[]) m_fieldRegistration
+ .get(fieldName);
+ for (int i = 0; list != null && i < list.length; i++) {
+ // The callback must be call outside the synchronization block.
+ list[i].onSet(null, fieldName, objectValue);
+ }
+ }
+
+
+ /**
+ * Gets the bundle context used by this component instance.
+ * @return the context of the component.
+ * @see org.apache.felix.ipojo.ComponentInstance#getContext()
+ */
+ public BundleContext getContext() {
+ return m_context; // Immutable
+ }
+
+ /**
+ * Gets the global bundle context. This is the bundle context
+ * of the bundle declaring the component type.
+ * @return the bundle context of the bundle declaring the component
+ * type.
+ */
+ public BundleContext getGlobalContext() {
+ return ((IPojoContext) m_context).getGlobalContext(); // Immutable
+ }
+
+
+ /**
+ * Gets the local service context. This service context gives
+ * access to the 'local' service registry (the composite one).
+ * If the instance lives in the global (i.e. OSGi) context,
+ * this method returns <code>null</code>
+ * @return the local service context or <code>null</code> if the
+ * instance doesn't live in a composite.
+ */
+ public ServiceContext getLocalServiceContext() {
+ return ((IPojoContext) m_context).getServiceContext(); // Immutable
+ }
+
+ /**
+ * Gets the instance name.
+ * @return the instance name.
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceName()
+ */
+ public String getInstanceName() {
+ return m_name; // Immutable
+ }
+
+ /**
+ * Reconfigures the current instance.
+ * Reconfiguring an instance means re-injecting a new
+ * instance configuration. Some properties are immutable
+ * such as the instance name.
+ * This methods calls the {@link PrimitiveHandler#reconfigure(Dictionary)}
+ * methods on each attached handler, and then recompute the instance
+ * state. Note that the reconfiguration process does not deactivate the
+ * instance.
+ * @param configuration the new configuration to push
+ * @see org.apache.felix.ipojo.ComponentInstance#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary configuration) {
+ m_logger.log(Logger.INFO, "Reconfiguring " + getInstanceName());
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].getHandler().reconfigure(configuration);
+ }
+ // We synchronized the state computation.
+ synchronized (this) {
+ if (m_state == STOPPED) {
+ m_logger.log(Logger.INFO, "Instance stopped during reconfiguration - Try to restart");
+ start();
+ } else if (m_state == INVALID) {
+ m_logger.log(Logger.INFO, "Instance invalid during reconfiguration - Recompute state");
+ // Try to revalidate the instance after reconfiguration
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) {
+ return;
+ }
+ }
+ setState(VALID);
+ }
+ }
+ }
+
+ /**
+ * Gets the implementation class of the component type.
+ * This method does not need to be synchronized as the
+ * class name is constant once set.
+ * @return the class name of the component implementation.
+ */
+ public String getClassName() {
+ return m_className;
+ }
+
+ /**
+ * State Change listener callback.
+ * This method is called every time that a plugged handler becomes valid or invalid.
+ * This method computes the new instance state and applies it (by calling the
+ * {@link InstanceManager#setState(int)} method.
+ * @param instance the handler becoming valid or invalid
+ * @param newState the new state of the handler
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public void stateChanged(ComponentInstance instance, int newState) {
+ int state;
+ synchronized (this) {
+ if (m_state <= STOPPED) {
+ return;
+ } else {
+ state = m_state; // Stack confinement
+ }
+ }
+
+ // Update the component state if necessary
+ if (newState == INVALID && state == VALID) {
+ // Need to update the state to UNRESOLVED
+ setState(INVALID);
+ return;
+ }
+ if (newState == VALID && state == INVALID) {
+ // An handler becomes valid => check if all handlers are valid
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) {
+ return;
+ }
+ }
+ setState(VALID);
+ return;
+ }
+ }
+
+ /**
+ * Gets the list of registered fields (containing field names).
+ * This method is invoked by the POJO itself during
+ * its initialization.
+ * @return the set of registered fields.
+ */
+ public Set getRegistredFields() {
+ if (m_fieldRegistration == null) {
+ return null;
+ }
+ return m_fieldRegistration.keySet();
+ }
+
+ /**
+ * Gets the list of registered methods (containing method ids).
+ * This method is invoked by the POJO itself during its
+ * initialization.
+ * @return the set of registered methods.
+ */
+ public Set getRegistredMethods() {
+ if (m_methodRegistration == null) {
+ return null;
+ } else {
+ return m_methodRegistration.keySet();
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceStateListener.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceStateListener.java
new file mode 100644
index 0000000..d6ec53a
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceStateListener.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.felix.ipojo;
+
+/**
+ * This class defines instance state listeners.
+ * An instance state listener is notified of instance state changes. It needs to be
+ * registered on the instance by invoking the ({@link ComponentInstance#addInstanceStateListener(InstanceStateListener)}
+ * method. Once registered, the listener can track instance state changes.
+ * Received states are:
+ * <li>{@link ComponentInstance#VALID}</li>
+ * <li>{@link ComponentInstance#INVALID}</li>
+ * <li>{@link ComponentInstance#STOPPED}</li>
+ * <li>{@link ComponentInstance#DISPOSED}</li>
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface InstanceStateListener {
+
+ /**
+ * State change listener callback method.
+ * Every time that a monitored instance's state changes,
+ * this method is called with the instance and the new state.
+ * @param instance the changing instance
+ * @param newState the new instance state
+ */
+ void stateChanged(ComponentInstance instance, int newState);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MethodInterceptor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MethodInterceptor.java
new file mode 100644
index 0000000..15560fe
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MethodInterceptor.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.felix.ipojo;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+* Method interceptor.
+* A class implementing this interface is able to be notified of method invocations (
+* i.e. entries, exits, and errors).
+* The listener needs to be register on the instance manager with the
+* {@link InstanceManager#register(org.apache.felix.ipojo.parser.MethodMetadata, MethodInterceptor)}
+* method.
+* Events are sent before the method entry (onEntry), after the method returns (onExit),
+* when an error is thrown by the method (onError), and before the after either a returns or an error (onFinally).
+*
+* Instead of a {@link Method} object, the callbacks received a {@link Member} object which can be either a {@link Method}
+* or a {@link Constructor}.
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public interface MethodInterceptor {
+
+ /**
+ * This method is called when a thread enters in a method.
+ * The given argument array is created from the method argument.
+ * @param pojo the pojo on which the method is called.
+ * @param method the invoked method.
+ * @param args the arguments array.
+ */
+ void onEntry(Object pojo, Member method, Object[] args);
+
+ /**
+ * This method is called when the execution exits a method :
+ * before a <code>return</code>.
+ * If the given returned object is <code>null</code>, either the method is
+ * <code>void</code>, or it returns <code>null</code>.
+ * This method must not modify the returned object.
+ * @param pojo the pojo on which the method exits.
+ * @param method the exiting method.
+ * @param returnedObj the the returned object (boxed for primitive type)
+ */
+ void onExit(Object pojo, Member method, Object returnedObj);
+
+ /**
+ * This method is called when the execution throws an exception in the given
+ * method.
+ * @param pojo the pojo on which the method was accessed.
+ * @param method the invoked method.
+ * @param throwable the thrown exception
+ */
+ void onError(Object pojo, Member method, Throwable throwable);
+
+ /**
+ * This method is called when the execution of a method is going to terminate :
+ * just before to throw an exception or before to return.
+ * (onError or onExit was already called).
+ * @param pojo the pojo on which the method was accessed.
+ * @param method the invoked method.
+ */
+ void onFinally(Object pojo, Member method);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MissingHandlerException.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MissingHandlerException.java
new file mode 100644
index 0000000..9bc4efb
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/MissingHandlerException.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.felix.ipojo;
+
+import java.util.List;
+
+/**
+ * Missing Handler Exception.
+ * This exception occurs when an handler factory is missing to create an instance.
+ * In fact, when a required handler factory is not avialable or valid, the {@link Handler}
+ * object cannot be created, and so the instance container cannot be completed.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MissingHandlerException extends Exception {
+
+ /**
+ * Serialization Id.
+ */
+ private static final long serialVersionUID = 5047792897590881478L;
+
+ /**
+ * The message.
+ */
+ private String m_message;
+
+ /**
+ * Creates a MissingHandlerException.
+ * This methods computes the message from
+ * the given list.
+ * @param missing the list of missing handlers.
+ */
+ public MissingHandlerException(List missing) {
+ super();
+ m_message = "Missing handlers : ";
+ for (int i = 0; i < missing.size(); i++) {
+ m_message += (String) missing.get(i) + " ";
+ }
+ }
+
+ /**
+ * Gets the error message.
+ * @return the error message
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ return m_message;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Nullable.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Nullable.java
new file mode 100644
index 0000000..075f4c7
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Nullable.java
@@ -0,0 +1,33 @@
+/*
+ * 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.felix.ipojo;
+
+/**
+ * A Nullable object must implement this interface.
+ * This allows to the code checking if the injected object is
+ * a Nullable object or not such as in:<br/>
+ * <code>if(foo instanceof Nullable){...}</code>
+ * <br/>
+ * This interface does not define any methods. Nullable objects owns
+ * the same methods than the 'real' object.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Nullable {
+ // Nothing
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Pojo.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Pojo.java
new file mode 100644
index 0000000..8bc7779
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Pojo.java
@@ -0,0 +1,35 @@
+/*
+ * 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.felix.ipojo;
+
+/**
+ * Interface implemented by each manipulated class.
+ * This interface allows getting the component instance from the object.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Pojo {
+
+ /**
+ * Gets the instance container which creates the current object.
+ * @return the component instance which creates this object.
+ */
+ ComponentInstance getComponentInstance();
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java
new file mode 100644
index 0000000..0153683
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java
@@ -0,0 +1,494 @@
+/*
+ * 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.felix.ipojo;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The policy service context is a service context aiming to resolve service dependencies
+ * inside different service context according to a policy.
+ * So, the policy service context behavior follows one of the three following policy:
+ * <li> Local : services are only resolved in the local service context.</li>
+ * <li> Global : services are only resolved in the global context (hte OSGi one)</li>
+ * <li> Local and Global : services are resolved inside the local context and inside
+ * the global context</li>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PolicyServiceContext implements ServiceContext {
+
+ /**
+ * Resolving policy, resolves services only in the composite
+ * context (local).
+ * This policy is the default one for services inherited from
+ * service-level dependencies.
+ */
+ public static final int LOCAL = 0;
+
+ /**
+ * Resolving policy, resolves services only in the composite
+ * (local) and in the global context.
+ * This policy is the default one for implementation dependency.
+ */
+ public static final int LOCAL_AND_GLOBAL = 1;
+
+ /**
+ * Resolving policy, resolves services inside the global context
+ * only.
+ */
+ public static final int GLOBAL = 2;
+
+ /**
+ * The global service registry.
+ * Targets the OSGi service registry.
+ */
+ public BundleContext m_global;
+
+ /**
+ * The local (Composite) Service Registry.
+ */
+ public ServiceContext m_local;
+
+ /**
+ * The resolving policy to use to resolve
+ * dependencies.
+ */
+ private int m_policy = LOCAL_AND_GLOBAL;
+
+
+ /**
+ * Creates a PolicyServiceContext.
+ * If the local context is null, sets the policy to
+ * {@link PolicyServiceContext#GLOBAL}, else use the
+ * given policy.
+ * @param global the global bundle context
+ * @param local the parent (local) service context
+ * @param policy the resolution policy
+ */
+ public PolicyServiceContext(BundleContext global, ServiceContext local, int policy) {
+ m_global = global;
+ m_local = local;
+ if (m_local == null) {
+ m_policy = GLOBAL;
+ } else {
+ m_policy = policy;
+ }
+ }
+
+ /**
+ * Adds a service listener according to the policy.
+ * Be aware, that the listener can be registered both in the local and in the global context
+ * if the {@link PolicyServiceContext#LOCAL_AND_GLOBAL} is used.
+ * @param listener the listener to add
+ * @param filter the LDAP filter
+ * @throws InvalidSyntaxException if the filter is malformed.
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener, java.lang.String)
+ */
+ public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.addServiceListener(listener, filter);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.addServiceListener(listener, filter);
+ }
+
+ }
+
+ /**
+ * Adds a service listener according to the policy.
+ * Be aware, that the listener can be registered both in the local and in the global context
+ * if the {@link PolicyServiceContext#LOCAL_AND_GLOBAL} is used.
+ * @param listener the listener to add
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void addServiceListener(ServiceListener listener) {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.addServiceListener(listener);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.addServiceListener(listener);
+ }
+ }
+
+ /**
+ * Gets all service references.
+ * These references are found inside the local registry, global registry or both according to the policy.
+ * The returned array can contain service references from both context.
+ * @param clazz the required service specification.
+ * @param filter the LDAP filter
+ * @return the array of service reference, <code>null</code> if no service available
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getAllServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getAllServiceReferences(String clazz,
+ String filter) throws InvalidSyntaxException {
+ switch (m_policy) {
+ case LOCAL:
+ return m_local.getAllServiceReferences(clazz, filter);
+ case GLOBAL:
+ return m_global.getAllServiceReferences(clazz, filter);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference[] refLocal = m_local.getAllServiceReferences(clazz, filter);
+ ServiceReference[] refGlobal = m_global.getAllServiceReferences(clazz, filter);
+ return computeServiceReferencesFromBoth(refLocal, refGlobal);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the service object for the given references.
+ * The service is get inside the context according to the policy.
+ * @param ref the service reference
+ * @return the service object
+ * @see org.apache.felix.ipojo.ServiceContext#getService(org.osgi.framework.ServiceReference)
+ */
+ public Object getService(ServiceReference ref) {
+ switch(m_policy) { // NOPMD No break needed as we return in each branch.
+ case LOCAL:
+ // The reference comes from the local scope
+ return m_local.getService(ref);
+ case GLOBAL:
+ // The reference comes from the global registry
+ return m_global.getService(ref);
+ case LOCAL_AND_GLOBAL:
+ if (ref instanceof org.apache.felix.ipojo.context.ServiceReferenceImpl) {
+ return m_local.getService(ref);
+ } else {
+ return m_global.getService(ref);
+ }
+ default :
+ return null;
+ }
+ }
+
+ /**
+ * Gets a service reference for the required service specification.
+ * The service is looked inside the context according to the policy.
+ * @param clazz the required service specification
+ * @return a service reference or <code>null</code> if no matching service available
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference getServiceReference(String clazz) {
+ switch (m_policy) { // NOPMD No break needed as we return in each branch.
+ case LOCAL:
+ return m_local.getServiceReference(clazz);
+ case GLOBAL:
+ return m_global.getServiceReference(clazz);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference refLocal = m_local.getServiceReference(clazz);
+ if (refLocal == null) {
+ return m_global.getServiceReference(clazz);
+ } else {
+ return refLocal;
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets a service reference.
+ * This method is delegated on PolicyServiceContext#getServiceReference(String)
+ * @param sClass the service interface class
+ * @param <S> the service interface class
+ * @return the service reference of <code>null</code> if not found
+ * @see PolicyServiceContext#getServiceReference(String)
+ */
+ public <S> ServiceReference<S> getServiceReference(Class<S> sClass) {
+ return (ServiceReference<S>) getServiceReference(sClass.getName());
+ }
+
+ /**
+ * Gets a collection of service references
+ * @param clazz the service interface class
+ * @param filter the filter
+ * @param <S> the service interface class
+ * @return the set of service reference
+ * @throws InvalidSyntaxException the filter is invalid
+ * @see PolicyServiceContext#getServiceReferences(String, String)
+ */
+ public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter) throws InvalidSyntaxException {
+ ServiceReference<S>[] refs =
+ (ServiceReference<S>[]) getServiceReferences(clazz.getName(), filter);
+ return (refs == null)
+ ? Collections.EMPTY_LIST
+ : (Collection<ServiceReference<S>>) Arrays.asList(refs);
+ }
+
+ /**
+ * Get a service reference for the required service specification.
+ * The services are looked inside the context according to the policy.
+ * @param clazz the required service specification
+ * @param filter the LDAP filter
+ * @return a service reference array or <code>null</code> if not consistent service available
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ switch (m_policy) {
+ case LOCAL:
+ return m_local.getServiceReferences(clazz, filter);
+ case GLOBAL:
+ return m_global.getServiceReferences(clazz, filter);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference[] refLocal = m_local.getServiceReferences(clazz, filter);
+ ServiceReference[] refGlobal = m_global.getServiceReferences(clazz, filter);
+ return computeServiceReferencesFromBoth(refLocal, refGlobal);
+ default:
+ return null;
+ }
+
+ }
+
+ /**
+ * Computes the service reference array from the two given set of service references
+ * according to the policy.
+ * @param refLocal the set of local references
+ * @param refGlobal the set of global references
+ * @return the set of service references
+ */
+ private ServiceReference[] computeServiceReferencesFromBoth(ServiceReference[] refLocal, ServiceReference[] refGlobal) {
+ if (refLocal == null) {
+ return refGlobal; // If refGlobal is null, return null, else return refGlobal
+ } else if (refGlobal == null) { // refLocal != null && refGlobal == null
+ return refLocal;
+ } else { // Both refGlobal and refLocal are not null
+ ServiceReference[] refs = new ServiceReference[refLocal.length + refGlobal.length];
+ System.arraycopy(refLocal, 0, refs, 0, refLocal.length);
+ System.arraycopy(refGlobal, 0, refs, refLocal.length, refGlobal.length);
+ return refs;
+ }
+ }
+
+ /**
+ * This method is not supported.
+ * This context can only be used to resolve service dependencies.
+ * @param clazzes the specifications
+ * @param service the service object
+ * @param properties the service properties
+ * @return the service registration object
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+ throw new UnsupportedOperationException("PolicyServiceContext can only be used for service dependency and not to provide services");
+ }
+
+ /**
+ * This method is not supported.
+ * This context can only be used to resolve service dependencies.
+ * @param clazz the specification
+ * @param service the service object
+ * @param properties the service properties to publish
+ * @return the service registration object
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+ throw new UnsupportedOperationException("PolicyServiceContext can only be used for service dependency and not to provide services");
+ }
+
+ /**
+ * Removes a service listener.
+ * @param listener the service listener to remove
+ * @see org.apache.felix.ipojo.ServiceContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void removeServiceListener(ServiceListener listener) {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.removeServiceListener(listener);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.removeServiceListener(listener);
+ }
+ }
+
+ /**
+ * Ungets the service reference.
+ * @param reference the service reference to unget.
+ * @return <code>true</code> if the service release if the reference is no more used.
+ * @see org.apache.felix.ipojo.ServiceContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ public boolean ungetService(ServiceReference reference) {
+ if (reference instanceof org.apache.felix.ipojo.context.ServiceReferenceImpl) {
+ return m_local.ungetService(reference);
+ } else {
+ return m_global.ungetService(reference);
+ }
+ }
+
+ /**
+ * Adds a bundle listener.
+ * Delegate on the global bundle context.
+ * @param arg0 : bundle listener to add
+ * @see org.osgi.framework.BundleContext#addBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void addBundleListener(BundleListener arg0) {
+ m_global.addBundleListener(arg0);
+ }
+
+ /**
+ * Adds a framework listener.
+ * Delegates on the global bundle context.
+ * @param arg0 : framework listener to add.
+ * @see org.osgi.framework.BundleContext#addFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void addFrameworkListener(FrameworkListener arg0) {
+ m_global.addFrameworkListener(arg0);
+ }
+
+ /**
+ * Creates a LDAP filter.
+ * @param arg0 the String-form of the filter
+ * @return the created filter object
+ * @throws InvalidSyntaxException if the given argument is not a valid against the LDAP grammar.
+ * @see org.osgi.framework.BundleContext#createFilter(java.lang.String)
+ */
+ public Filter createFilter(String arg0) throws InvalidSyntaxException {
+ return m_global.createFilter(arg0);
+ }
+
+ /**
+ * Gets a bundle by name
+ * @param s the name
+ * @return the matching bundle or <code>null</code> if not found
+ */
+ public Bundle getBundle(String s) {
+ return m_global.getBundle(s);
+ }
+
+ /**
+ * Gets the current bundle.
+ * @return the current bundle
+ * @see org.osgi.framework.BundleContext#getBundle()
+ */
+ public Bundle getBundle() {
+ return m_global.getBundle();
+ }
+
+ /**
+ * Gets the bundle object with the given id.
+ * @param bundleId the bundle id
+ * @return the bundle object
+ * @see org.osgi.framework.BundleContext#getBundle(long)
+ */
+ public Bundle getBundle(long bundleId) {
+ return m_global.getBundle(bundleId);
+ }
+
+ /**
+ * Gets installed bundles.
+ * @return the list of installed bundles
+ * @see org.osgi.framework.BundleContext#getBundles()
+ */
+ public Bundle[] getBundles() {
+ return m_global.getBundles();
+ }
+
+
+ /**
+ * Gets a data file.
+ * @param filename the File name.
+ * @return the File object
+ * @see org.osgi.framework.BundleContext#getDataFile(java.lang.String)
+ */
+ public File getDataFile(String filename) {
+ return m_global.getDataFile(filename);
+ }
+
+ /**
+ * Gets a property value.
+ * @param key the key of the asked property
+ * @return the property value (object) or <code>null</code> if no property
+ * are associated with the given key
+ * @see org.osgi.framework.BundleContext#getProperty(java.lang.String)
+ */
+ public String getProperty(String key) {
+ return m_global.getProperty(key);
+ }
+
+ /**
+ * Installs a bundle.
+ * @param location the URL of the bundle to install
+ * @return the installed bundle
+ * @throws BundleException if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String)
+ */
+ public Bundle installBundle(String location) throws BundleException {
+ return m_global.installBundle(location);
+ }
+
+ /**
+ * Installs a bundle.
+ * @param location the URL of the bundle to install
+ * @param input the input stream to load the bundle content
+ * @return the installed bundle
+ * @throws BundleException if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String, java.io.InputStream)
+ */
+ public Bundle installBundle(String location, InputStream input) throws BundleException {
+ return m_global.installBundle(location, input);
+ }
+
+ /**
+ * Removes the bundle listener.
+ * @param listener the listener to remove
+ * @see org.osgi.framework.BundleContext#removeBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void removeBundleListener(BundleListener listener) {
+ m_global.removeBundleListener(listener);
+ }
+
+ /**
+ * Removes a framework listener.
+ * @param listener the listener to remove
+ * @see org.osgi.framework.BundleContext#removeFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void removeFrameworkListener(FrameworkListener listener) {
+ m_global.removeFrameworkListener(listener);
+ }
+
+ /**
+ * Registers a service.
+ * Operation not supported.
+ * @param sClass
+ * @param s
+ * @param stringDictionary
+ * @param <S>
+ * @return
+ */
+ public <S> ServiceRegistration<S> registerService(Class<S> sClass, S s, Dictionary<String, ?> stringDictionary) {
+ throw new UnsupportedOperationException("PolicyServiceContext can only be used for service dependency and not to provide services");
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java
new file mode 100644
index 0000000..2d70f25
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java
@@ -0,0 +1,257 @@
+/*
+ * 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.felix.ipojo;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.util.Logger;
+
+
+/**
+* This class defines common mechanisms of primitive handlers.
+* Primitive handlers are handler composing the container of primitive
+* component instances (declared by the 'component' element inside the
+* iPOJO descriptor).
+* Note that this class also defines default method implementation.
+* Classes overriding this class can change the behavior of those methods.
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public abstract class PrimitiveHandler extends Handler implements FieldInterceptor, MethodInterceptor,
+ ConstructorInjector {
+
+ /**
+ * The "Primitive" Handler type (value).
+ */
+ public static final String HANDLER_TYPE = "primitive";
+
+ /**
+ * The reference on the instance manager.
+ */
+ private InstanceManager m_manager;
+
+
+ /**
+ * The factory of the instance manager.
+ */
+ private ComponentFactory m_factory;
+
+ /**
+ * Instance Logger used by the handler.
+ */
+ private Logger m_instanceLogger;
+
+ /**
+ * Attaches the current handler to the given instance.
+ * @param manager the instance on which the current handler will be attached.
+ * @see org.apache.felix.ipojo.Handler#attach(org.apache.felix.ipojo.ComponentInstance)
+ */
+ protected final void attach(ComponentInstance manager) {
+ m_manager = (InstanceManager) manager;
+ m_instanceLogger = m_manager.getLogger();
+ }
+
+ /**
+ * Sets the factory of the managed instance.
+ * @param factory the factory
+ * @see org.apache.felix.ipojo.Handler#setFactory(org.apache.felix.ipojo.Factory)
+ */
+ public final void setFactory(Factory factory) {
+ m_factory = (ComponentFactory) factory;
+ }
+
+ /**
+ * Gets the logger of the managed instance.
+ * IF no instance attached yet, use the factory logger.
+ * @return the logger to use to log messages.
+ * @see org.apache.felix.ipojo.Handler#getLogger()
+ */
+ public Logger getLogger() {
+ if (m_instanceLogger == null) {
+ return m_factory.getLogger();
+ }
+ return m_instanceLogger;
+ }
+
+ /**
+ * Gets the instance manager managing the instance.
+ * @return the instance manager
+ */
+ public InstanceManager getInstanceManager() {
+ return m_manager;
+ }
+
+ /**
+ * Gets the factory which creates the managed instance.
+ * @return the factory which creates the managed instance.
+ */
+ public ComponentFactory getFactory() {
+ return m_factory;
+ }
+
+ /**
+ * Gets the PojoMetadata of the content of the managed
+ * instance. This method allows getting manipulation
+ * metadata.
+ * @return the information about the content of the
+ * managed instance.
+ */
+ public PojoMetadata getPojoMetadata() {
+ return m_factory.getPojoMetadata();
+ }
+
+ /**
+ * Gets a plugged handler of the same container.
+ * This method must be called only in the start method (or after).
+ * In the configure method, this method can be inconsistent
+ * as all handlers are not initialized.
+ * @param name the name of the handler to find (class name or qualified
+ * handler name (<code>ns:name</code>)).
+ * @return the handler object or <code>null</code> if the handler is not found.
+ */
+ public final Handler getHandler(String name) {
+ return m_manager.getHandler(name);
+ }
+
+ /**
+ * Callback method called when a managed field
+ * receives a new value. The given pojo can be
+ * null if the new value is set by another handler.
+ * This default implementation does nothing.
+ * @param pojo the pojo object setting the value
+ * @param fieldName the field name
+ * @param value the value received by the field
+ * @see FieldInterceptor#onSet(Object, String, Object)
+ */
+ public void onSet(Object pojo, String fieldName, Object value) {
+ // Nothing to do in the default implementation
+ }
+
+ /**
+ * Callback method called when a managed field
+ * asks for a value.
+ * The default implementation returned the previously
+ * injected value.
+ * @param pojo the pojo object requiring the value
+ * @param fieldName the field name
+ * @param value the value passed to the field (by the previous call)
+ * @return the value to inject, of the previous value if the handler
+ * don't want to inject a value.
+ * @see FieldInterceptor#onGet(Object, String, Object)
+ */
+ public Object onGet(Object pojo, String fieldName, Object value) {
+ return value;
+ }
+
+ /**
+ * Gets the object to inject as a constructor parameter
+ * @param index the index of the parameter
+ * @return the object to inject, or <code>null</code> if no
+ * objects are injected. This implementation returns <code>null</code>
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameter(int)
+ */
+ public Object getConstructorParameter(int index) {
+ return null;
+ }
+
+ /**
+ * Gets the type of the object to inject in the constructor parameter.
+ * This is the type looked into the Pojo class, so it must match.
+ * Returning <code>null</code> will try to get the class from the
+ * injected object, however this can be wrong (implementation instead of interface,
+ * boxed objects...) and error-prone.
+ * @param index the parameter index
+ * @return the Class object (must fit for primitive type), this implementation
+ * just returns <code>null</code>
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameterType(int)
+ */
+ public Class getConstructorParameterType(int index) {
+ return null;
+ }
+
+ /**
+ * Callback method called when a method will be invoked.
+ * This default implementation does nothing.
+ * @param pojo the pojo on which the method is called.
+ * @param method the method invoked.
+ * @param args the arguments array.
+ * @see MethodInterceptor#onEntry(Object, Method, Object[])
+ */
+ public void onEntry(Object pojo, Member method, Object[] args) {
+ // Nothing to do in the default implementation
+ }
+
+ /**
+ * Callback method called when a method ends.
+ * This method is called when a thread exits a method (before a return or a throw).
+ * If the given returned object is <code>null</code>, either the method is
+ * <code>void</code>, or it returns <code>null</code>.
+ * You must not modified the returned object.
+ * The default implementation does nothing.
+ * @param pojo the pojo on which the method exits.
+ * @param method the exiting method.
+ * @param returnedObj the returned object (boxed for primitive type)
+ * @see MethodInterceptor#onExit(Object, Method, Object)
+ */
+ public void onExit(Object pojo, Member method, Object returnedObj) {
+ // Nothing to do in the default implementation
+ }
+
+ /**
+ * Callback method called when an error occurs.
+ * This method is called when the execution throws an exception
+ * in the given method.
+ * The default implementation does nothing.
+ * @param pojo the pojo on which the method was accessed.
+ * @param method the invoked method.
+ * @param throwable the thrown exception
+ * @see org.apache.felix.ipojo.MethodInterceptor#onError(java.lang.Object, java.lang.reflect.Method, java.lang.Throwable)
+ */
+ public void onError(Object pojo, Member method, Throwable throwable) {
+ // Nothing to do in the default implementation
+ }
+
+ /**
+ * Callback method called when the execution of a method will terminate :
+ * just before to throw an exception or before to return.
+ * {@link MethodInterceptor#onExit(Object, Method, Object)} or
+ * {@link MethodInterceptor#onError(Object, Method, Throwable)}
+ * were already called.
+ * This default implementation does nothing.
+ * @param pojo the pojo on which the method was accessed.
+ * @param method the invoked method.
+ */
+ public void onFinally(Object pojo, Member method) {
+ // Nothing to do in the default implementation
+ }
+
+ /**
+ * Callback method called when an instance of the component is created, but
+ * before someone can use it.
+ * The default implementation does nothing.
+ * @param instance the created instance
+ */
+ public void onCreation(Object instance) {
+ // Nothing to do in the default implementation
+ }
+
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveInstanceDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveInstanceDescription.java
new file mode 100644
index 0000000..e066251
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/PrimitiveInstanceDescription.java
@@ -0,0 +1,178 @@
+/*
+ * 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.felix.ipojo;
+
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.handlers.configuration.ConfigurationHandler;
+import org.apache.felix.ipojo.handlers.configuration.ConfigurationHandlerDescription;
+import org.apache.felix.ipojo.handlers.dependency.DependencyDescription;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandler;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandlerDescription;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceDescription;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandler;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandlerDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Primitive Instance Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PrimitiveInstanceDescription extends InstanceDescription {
+
+ /**
+ * Creates a Primitive Instance Description.
+ * @param type the component type description
+ * @param instance the instance description
+ */
+ public PrimitiveInstanceDescription(ComponentTypeDescription type, InstanceManager instance) {
+ super(type, instance);
+ }
+
+ /**
+ * Gets the list of object created by the described instance.
+ * @return the created objects.
+ */
+ public String[] getCreatedObjects() {
+ Object [] objs = ((InstanceManager) m_instance).getPojoObjects();
+ if (objs != null) {
+ String[] result = new String[objs.length];
+ for (int i = 0; i < objs.length; i++) {
+ result[i] = objs[i].toString();
+ }
+ return result;
+ } else {
+ return new String[0];
+ }
+ }
+
+ /**
+ * Gets the instance service dependencies.
+ * @return the set of dependency description or an empty array if
+ * no dependencies.
+ */
+ public DependencyDescription[] getDependencies() {
+ Handler handler = ((InstanceManager) m_instance).getHandler("org.apache.felix.ipojo:requires");
+ if (handler == null) {
+ return new DependencyDescription[0];
+ } else {
+ return ((DependencyHandlerDescription) ((DependencyHandler) handler)
+ .getDescription()).getDependencies();
+ }
+ }
+
+ /**
+ * Gets the instance service dependency matching with the given service specification or id.
+ * @param specification the service specification of the looked specification.
+ * @return the dependency description matching with the given service specification or id.
+ * <code>null</code> is not found.
+ * no dependencies.
+ */
+ public DependencyDescription getDependency(String specification) {
+ DependencyDescription[] deps = getDependencies();
+ if (deps == null) {
+ return null;
+ } else {
+ for (int i = 0; i < deps.length; i++) {
+ if (specification.equals(deps[i].getId())
+ || specification.equals(deps[i].getSpecification())) {
+ return deps[i];
+ }
+
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the instance provided service matching with the given service specification.
+ * @param specification the provided specification of the looked provided service.
+ * @return the provided service description matching with the given service specification.
+ * <code>null</code> is not found.
+ */
+ public ProvidedServiceDescription getProvidedService(String specification) {
+ ProvidedServiceDescription[] pss = getProvidedServices();
+ if (pss == null) {
+ return null;
+ } else {
+ for (int i = 0; i < pss.length; i++) {
+ String[] str = pss[i].getServiceSpecifications();
+ for (int j = 0; j < str.length; j++) {
+ if (specification.equals(str[j])) {
+ return pss[i];
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the instance provided service.
+ * @return the set of provided service description or an empty array if
+ * no provided services.
+ */
+ public ProvidedServiceDescription[] getProvidedServices() {
+ Handler handler = ((InstanceManager) m_instance).getHandler("org.apache.felix.ipojo:provides");
+ if (handler == null) {
+ return new ProvidedServiceDescription[0];
+ } else {
+ return ((ProvidedServiceHandlerDescription) ((ProvidedServiceHandler) handler)
+ .getDescription()).getProvidedServices();
+ }
+ }
+
+ /**
+ * Gets the instance properties.
+ * @return the set of property descriptions or an empty array if
+ * no properties.
+ */
+ public PropertyDescription[] getProperties() {
+ Handler handler = ((InstanceManager) m_instance).getHandler("org.apache.felix.ipojo:properties");
+ if (handler == null) {
+ return new PropertyDescription[0];
+ } else {
+ return ((ConfigurationHandlerDescription) ((ConfigurationHandler) handler)
+ .getDescription()).getProperties();
+ }
+ }
+
+ /**
+ * Gets the instance description.
+ * Overridden to add created objects.
+ * @return the instance description
+ */
+ public Element getDescription() {
+ Element elem = super.getDescription();
+ // Created Object (empty is composite)
+ String[] objs = getCreatedObjects();
+ for (int i = 0; objs != null && i < objs.length; i++) {
+ Element obj = new Element("Object", "");
+ obj.addAttribute(new Attribute("name", ((Object) objs[i]).toString()));
+ elem.addElement(obj);
+ }
+ return elem;
+ }
+
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ServiceContext.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ServiceContext.java
new file mode 100644
index 0000000..7d3765c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/ServiceContext.java
@@ -0,0 +1,149 @@
+/*
+ * 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.felix.ipojo;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * A service context is the facade of a service registry.
+ * It gives the access to a service broker. All service
+ * interactions should use a service context to garanty
+ * the service isolation.
+ * This class is a subset of {@link BundleContext} methods.
+ * (methods implying interactions with the service registry).
+ * So, refer to this class for further information.
+ * @see BundleContext
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceContext extends BundleContext {
+
+ /**
+ * Adds a service listener.
+ * The listener is added to this service context.
+ * So only services from this context will be tracked.
+ * @param listener the service listener to add.
+ * @param filter the LDAP filter
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener, java.lang.String)
+ */
+ void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException;
+
+ /**
+ * Adds a service listener.
+ * The listener is added to this service context.
+ * So only services from this context will be tracked.
+ * @param listener the service listener to add.
+ * @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ void addServiceListener(ServiceListener listener);
+
+ /**
+ * Gets the service references matching with the given query.
+ * The query is executed inside this service context.
+ * @param clazz the required interface
+ * @param filter a LDAP filter
+ * @return the array of available service references or <code>null</code>
+ * if no providers are available.
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#getAllServiceReferences(java.lang.String, java.lang.String)
+ */
+ ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
+
+ /**
+ * Gets a service object.
+ * The given service reference must comes from this
+ * service context.
+ * @param reference the required service reference
+ * @return the service object or null if the service reference is no more valid or if the service object is not accessible
+ * @see org.osgi.framework.BundleContext#getService(org.osgi.framework.ServiceReference)
+ */
+ <S> S getService(ServiceReference<S> reference);
+
+ /**
+ * Gets a service reference for the given interface.
+ * The query is executed inside this service context.
+ * @param clazz the required interface name
+ * @return a service reference on a available provider or
+ * <code>null</code> if no providers are available
+ * @see org.osgi.framework.BundleContext#getServiceReference(java.lang.String)
+ */
+ ServiceReference getServiceReference(String clazz);
+
+ /**
+ * Gets service reference list for the given query.
+ * The query is executed inside this service context.
+ * @param clazz : the name of the required service interface
+ * @param filter : LDAP filter to apply on service provider
+ * @return : the array of consistent service reference or <code>null</code>
+ * if no available providers
+ * @throws InvalidSyntaxException if the LDAP filter is malformed
+ * @see org.osgi.framework.BundleContext#getServiceReferences(java.lang.String, java.lang.String)
+ */
+ ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
+
+ /**
+ * Registers a service inside this service context.
+ * This service is then isolated inside this context.
+ * @param clazzes the interfaces provided by the service.
+ * @param service the service object.
+ * @param properties service properties to publish
+ * @return the service registration for this service publication.
+ * This service registration is attached to the current service context,
+ * and does not have any meaning in other contexts.
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ ServiceRegistration registerService(String[] clazzes, Object service, Dictionary<String, ?> properties);
+
+ /**
+ * Registers a service inside this service context.
+ * This service is then isolated inside this context.
+ * @param clazz the interface provided by the service.
+ * @param service the service object.
+ * @param properties service properties to publish.
+ * @return the service registration for this service publication.
+ * This service registration is attached to the current service context,
+ * and does not have any meaning in other contexts.
+ * @see org.osgi.framework.BundleContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ ServiceRegistration registerService(String clazz, Object service, Dictionary<String, ?> properties);
+
+ /**
+ * Removes a service listener.
+ * The listener must be registered inside this service context.
+ * @param listener the listener to remove
+ * @see org.osgi.framework.BundleContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ void removeServiceListener(ServiceListener listener);
+
+ /**
+ * Ungets the service reference.
+ * The service reference must comes from this service context.
+ * @param reference the reference to unget
+ * @return <code>true</code> if you are the last user of the reference.
+ * @see org.osgi.framework.BundleContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ boolean ungetService(ServiceReference<?> reference);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/UnacceptableConfiguration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/UnacceptableConfiguration.java
new file mode 100644
index 0000000..dab2c99
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/UnacceptableConfiguration.java
@@ -0,0 +1,44 @@
+/*
+ * 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.felix.ipojo;
+
+/**
+ * UnacceptableConfiguration occurs when a factory refuses to create an
+ * instance. This exception is thrown when the instance configuration does not
+ * specify a value for a required property or when the configuration tries to override
+ * the value of an immutable property
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class UnacceptableConfiguration extends Exception {
+
+ /**
+ * Exception UID.
+ */
+ private static final long serialVersionUID = 2998931848886223965L;
+
+ /**
+ * Creates an UnacceptableConfiguration.
+ * @param message : message about the error.
+ */
+ public UnacceptableConfiguration(String message) {
+ super(message);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/Architecture.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/Architecture.java
new file mode 100644
index 0000000..db23941
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/Architecture.java
@@ -0,0 +1,35 @@
+/*
+ * 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.felix.ipojo.architecture;
+
+/**
+ * Architecture service.
+ * This service allows collecting information on the component instance,
+ * such as its state, plugged handlers ...
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Architecture {
+
+ /**
+ * Returns the description of the instance.
+ * @return the component instance description
+ */
+ InstanceDescription getInstanceDescription();
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java
new file mode 100644
index 0000000..f7a3ce5
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java
@@ -0,0 +1,266 @@
+/*
+ * 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.felix.ipojo.architecture;
+
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.IPojoFactory;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+/**
+ * Component Type description.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentTypeDescription {
+
+ /**
+ * Provided service by the component type.
+ */
+ private String[] m_providedServiceSpecification = new String[0];
+
+ /**
+ * Configuration Properties accepted by the component type.
+ */
+ private PropertyDescription[] m_properties = new PropertyDescription[0];
+
+ /**
+ * Represented factory.
+ */
+ private final IPojoFactory m_factory;
+
+ /**
+ * Constructor.
+ * @param factory : represented factory.
+ */
+ public ComponentTypeDescription(IPojoFactory factory) {
+ m_factory = factory;
+ }
+
+ /**
+ * Gets the attached factory.
+ * @return the factory
+ */
+ public IPojoFactory getFactory() {
+ return m_factory;
+ }
+
+ /**
+ * Gets a printable form of the current component type description.
+ * @return printable form of the component type description
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return getDescription().toString();
+ }
+
+ /**
+ * Gets the implementation class of this component type.
+ * @return the component type implementation class name.
+ * @deprecated
+ */
+ public String getClassName() {
+ return m_factory.getClassName();
+ }
+
+ /**
+ * Gets the component type version.
+ * @return the component type version or
+ * <code>null</code> if not set.
+ */
+ public String getVersion() {
+ return m_factory.getVersion();
+ }
+
+ /**
+ * Gets component-type properties.
+ * @return the list of configuration properties accepted by the component type type.
+ */
+ public PropertyDescription[] getProperties() {
+ return m_properties;
+ }
+
+ /**
+ * Adds a String property in the component type.
+ * @param name : property name.
+ * @param value : property value.
+ */
+ public void addProperty(String name, String value) {
+ addProperty(name, value, false);
+ }
+
+ /**
+ * Adds a String property in the component type.
+ * @param name : property name.
+ * @param value : property value.
+ * @param immutable : the property is immutable.
+ */
+ public void addProperty(String name, String value, boolean immutable) {
+ PropertyDescription prop = new PropertyDescription(name, String.class.getName(), value);
+ addProperty(prop);
+ }
+
+ /**
+ * Adds a configuration properties to the component type.
+ * @param pd : the property to add
+ */
+ public void addProperty(PropertyDescription pd) { //NOPMD remove the instance name of the 'name' property.
+ String name = pd.getName();
+
+ // Check if the property is not already in the array
+ for (int i = 0; i < m_properties.length; i++) {
+ PropertyDescription desc = m_properties[i];
+ if (desc.getName().equals(name)) { return; }
+ }
+
+ PropertyDescription[] newProps = new PropertyDescription[m_properties.length + 1];
+ System.arraycopy(m_properties, 0, newProps, 0, m_properties.length);
+ newProps[m_properties.length] = pd;
+ m_properties = newProps;
+ }
+
+ /**
+ * Gets the list of provided service offered by instances of this type.
+ * @return the list of the provided service.
+ */
+ public String[] getprovidedServiceSpecification() {
+ return m_providedServiceSpecification;
+ }
+
+ /**
+ * Adds a provided service to the component type.
+ * @param serviceSpecification : the provided service to add (interface name)
+ */
+ public void addProvidedServiceSpecification(String serviceSpecification) {
+ String[] newSs = new String[m_providedServiceSpecification.length + 1];
+ System.arraycopy(m_providedServiceSpecification, 0, newSs, 0, m_providedServiceSpecification.length);
+ newSs[m_providedServiceSpecification.length] = serviceSpecification;
+ m_providedServiceSpecification = newSs;
+ }
+
+ /**
+ * Returns the component-type name.
+ * @return the name of this component type
+ */
+ public String getName() {
+ return m_factory.getName();
+ }
+
+ /**
+ * Computes the default service properties to publish :
+ * factory.name, service.pid, component.providedServiceSpecification, component.properties, component.description, factory.State.
+ * @return : the dictionary of properties to publish.
+ */
+ public Dictionary getPropertiesToPublish() {
+ Properties props = new Properties();
+
+ props.put("factory.name", m_factory.getName());
+ props.put(Constants.SERVICE_PID, m_factory.getName()); // Service PID is required for the integration in the configuration admin.
+
+ // Add the version if set
+ String v = getVersion();
+ if (v != null) {
+ props.put("factory.version", v);
+ }
+
+ props.put("component.providedServiceSpecifications", m_providedServiceSpecification);
+ props.put("component.properties", m_properties);
+ props.put("component.description", this);
+
+ // add every immutable property
+ for (int i = 0; i < m_properties.length; i++) {
+ if (m_properties[i].isImmutable() && m_properties[i].getValue() != null) {
+ props.put(m_properties[i].getName(), m_properties[i].getObjectValue(m_factory.getBundleContext()));
+ }
+ }
+
+ // Add factory state
+ props.put("factory.state", new Integer(m_factory.getState()));
+
+ return props;
+
+ }
+
+ /**
+ * Gets the interfaces published by the factory.
+ * By default publish both {@link Factory} and {@link ManagedServiceFactory}.
+ * @return : the list of interface published by the factory.
+ */
+ public String[] getFactoryInterfacesToPublish() {
+ return new String[] {Factory.class.getName(), ManagedServiceFactory.class.getName()};
+ }
+
+ /**
+ * Gets the component type description.
+ * @return : the description
+ */
+ public Element getDescription() {
+ Element desc = new Element("Factory", "");
+
+ desc.addAttribute(new Attribute("name", m_factory.getName()));
+ desc.addAttribute(
+ new Attribute("bundle",
+ Long.toString(((ComponentFactory) m_factory).getBundleContext().getBundle().getBundleId())));
+
+ String state = "valid";
+ if (m_factory.getState() == Factory.INVALID) {
+ state = "invalid";
+ }
+ desc.addAttribute(new Attribute("state", state));
+
+ // Display required & missing handlers
+ Element req = new Element("RequiredHandlers", "");
+ req.addAttribute(new Attribute("list", m_factory.getRequiredHandlers().toString()));
+ Element missing = new Element("MissingHandlers", "");
+ missing.addAttribute(new Attribute("list", m_factory.getMissingHandlers().toString()));
+ desc.addElement(req);
+ desc.addElement(missing);
+
+ for (int i = 0; i < m_providedServiceSpecification.length; i++) {
+ Element prov = new Element("provides", "");
+ prov.addAttribute(new Attribute("specification", m_providedServiceSpecification[i]));
+ desc.addElement(prov);
+ }
+
+ for (int i = 0; i < m_properties.length; i++) {
+ Element prop = new Element("property", "");
+ prop.addAttribute(new Attribute("name", m_properties[i].getName()));
+ prop.addAttribute(new Attribute("type", m_properties[i].getType()));
+ if (m_properties[i].isMandatory() && m_properties[i].getValue() == null) {
+ prop.addAttribute(new Attribute("value", "REQUIRED"));
+ } else {
+ prop.addAttribute(new Attribute("value", m_properties[i].getValue()));
+ }
+ desc.addElement(prop);
+ }
+
+ return desc;
+ }
+
+ public BundleContext getBundleContext() {
+ return m_factory.getBundleContext();
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/HandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/HandlerDescription.java
new file mode 100644
index 0000000..ffc64c8
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/HandlerDescription.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.felix.ipojo.architecture;
+
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Handler Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class HandlerDescription {
+
+ /**
+ * The Handler Qualified Name.
+ */
+ private String m_handlerName;
+
+
+ /**
+ * The described handler instance.
+ */
+ private Handler m_handler;
+
+ /**
+ * Creates a handler description.
+ * @param handler the handler.
+ */
+ public HandlerDescription(Handler handler) {
+ HandlerFactory factory = (HandlerFactory) handler.getHandlerManager().getFactory();
+ m_handlerName = factory.getHandlerName();
+ m_handler = handler;
+ }
+
+ /**
+ * Checks if the handler is valid.
+ * @return true if the handler is valid.
+ */
+ public boolean isValid() {
+ return m_handler.isValid();
+ }
+
+ /**
+ * Gets the handler name.
+ * @return the handler qualified name (i.e. namespace:name).
+ */
+ public String getHandlerName() {
+ return m_handlerName;
+ }
+
+ /**
+ * Gets handler information.
+ * This represent the actual state of the handler.
+ * @return the handler information.
+ */
+ public Element getHandlerInfo() {
+ Element elem = new Element("Handler", "");
+ elem.addAttribute(new Attribute("name", m_handlerName));
+ if (isValid()) {
+ elem.addAttribute(new Attribute("state", "valid"));
+ } else {
+ elem.addAttribute(new Attribute("state", "invalid"));
+ }
+ return elem;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/InstanceDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/InstanceDescription.java
new file mode 100644
index 0000000..632722c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/InstanceDescription.java
@@ -0,0 +1,195 @@
+/*
+ * 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.felix.ipojo.architecture;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.InstanceStateListener;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Instance Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceDescription implements InstanceStateListener {
+
+ /**
+ * The list of handlers plugged on the component instance.
+ */
+ protected HandlerDescription[] m_handlers = new HandlerDescription[0];
+
+ /**
+ * The Underlying component instance.
+ */
+ protected ComponentInstance m_instance;
+
+ /**
+ * Component Type of the instance.
+ */
+ protected ComponentTypeDescription m_type;
+
+ /**
+ * Creates the instance description.
+ * @param ci the state of the instance.
+ * @param desc the component type description of this instance.
+ */
+ public InstanceDescription(ComponentTypeDescription desc, ComponentInstance ci) {
+ m_handlers = new HandlerDescription[0];
+ m_type = desc;
+ m_instance = ci;
+ m_instance.addInstanceStateListener(this);
+ }
+
+ /**
+ * Gets the instance name.
+ * @return the name of the instance.
+ */
+ public String getName() {
+ return m_instance.getInstanceName();
+ }
+
+ /**
+ * Gets the component type description of the described instance.
+ * @return : the component type description of this instance.
+ */
+ public ComponentTypeDescription getComponentDescription() {
+ return m_type;
+ }
+
+ /**
+ * Gets the plugged handler list.
+ * @return the live handler list
+ */
+ public HandlerDescription[] getHandlers() {
+ return m_handlers;
+ }
+
+ /**
+ * Adds an handler description to the list.
+ * @param desc : the handler description to add
+ */
+ public void addHandler(HandlerDescription desc) {
+ // Verify that the dependency description is not already in the array.
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i] == desc) {
+ return; // NOTHING TO DO, the description is already in the
+ // array
+ }
+ }
+ // The component Description is not in the array, add it
+ HandlerDescription[] newHd = new HandlerDescription[m_handlers.length + 1];
+ System.arraycopy(m_handlers, 0, newHd, 0, m_handlers.length);
+ newHd[m_handlers.length] = desc;
+ m_handlers = newHd;
+ }
+
+ /**
+ * Gets a handler description by specifying the handler qualified name.
+ * This method ignores case of the given handler name.
+ * @param handler the handler name
+ * @return the handler description or <code>null</code> if not found
+ */
+ public HandlerDescription getHandlerDescription(String handler) {
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getHandlerName().equalsIgnoreCase(handler)) {
+ return m_handlers[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the state of the described instance.
+ * @return the state of the instance.
+ */
+ public int getState() {
+ waitForStability();
+ return m_instance.getState();
+ }
+
+ /**
+ * Gets the bundle id of the bundle containing the component type of the instance.
+ * @return the bundle id owning the component implementation class.
+ */
+ public long getBundleId() {
+ return m_instance.getFactory().getBundleContext().getBundle().getBundleId();
+ }
+
+ /**
+ * Gets the instance description.
+ * @return the instance description
+ */
+ public Element getDescription() {
+ Element instance = new Element("Instance", "");
+ instance.addAttribute(new Attribute("name", getName())); // Name
+
+ int state = getState();
+ if (state == ComponentInstance.STOPPED) {
+ instance.addAttribute(new Attribute("state", "stopped"));
+ }
+ if (state == ComponentInstance.VALID) {
+ instance.addAttribute(new Attribute("state", "valid"));
+ }
+ if (state == ComponentInstance.INVALID) {
+ instance.addAttribute(new Attribute("state", "invalid"));
+ }
+ if (state == ComponentInstance.DISPOSED) {
+ instance.addAttribute(new Attribute("state", "disposed"));
+ }
+ // Bundle
+ instance.addAttribute(new Attribute("bundle", Long.toString(getBundleId())));
+
+ // Component Type
+ instance.addAttribute(new Attribute("component.type", m_type.getName()));
+
+ // Handlers
+ for (int i = 0; i < m_handlers.length; i++) {
+ instance.addElement(m_handlers[i].getHandlerInfo());
+ }
+
+ return instance;
+
+ }
+
+ /**
+ * Waits for state stability before returning results.
+ */
+ private synchronized void waitForStability() {
+ while (m_instance.getState() == -2) { // Transition
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // We're interrupted, will re-check the condition.
+ }
+ }
+
+ }
+
+ /**
+ * The underlying instance state changes.
+ * @param instance the instance
+ * @param newState the new state
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public synchronized void stateChanged(ComponentInstance instance, int newState) {
+ notifyAll(); // if we was in a transition, the transition is now done.
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java
new file mode 100644
index 0000000..4b037b4
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java
@@ -0,0 +1,200 @@
+/*
+ * 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.felix.ipojo.architecture;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.handlers.configuration.ConfigurationHandler;
+import org.apache.felix.ipojo.util.Property;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Property Information.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropertyDescription {
+
+ /**
+ * Name of the property.
+ */
+ private String m_name;
+
+ /**
+ * Type of the property.
+ */
+ private String m_type;
+
+ /**
+ * Value of the property.
+ */
+ private String m_value = null;
+
+ /**
+ * Attached property object.
+ */
+ private Property m_property;
+
+
+ /**
+ * Immutable property flag
+ * If set to <code>true</code>, the property cannot be override by the instance configuration.
+ * Moreover, immutable properties are exposed on the factory service too.
+ */
+ private boolean m_immutable = false;
+
+ /**
+ * A property is mandatory. So, either the component type description provides a value or
+ * the instance configuration must provide a value. Immutable properties are mandatories.
+ */
+ private boolean m_isMandatory = false;
+
+ /**
+ * Constructor.
+ *
+ * @param name the name of the property
+ * @param type the type of the property
+ * @param value the default value of the property, can be <code>null</code>
+ */
+ public PropertyDescription(String name, String type, String value) {
+ m_name = name;
+ m_type = type;
+ m_value = value;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param prop the attache Property object.
+ */
+ public PropertyDescription(Property prop) {
+ m_property = prop;
+ m_name = prop.getName();
+ m_type = prop.getType();
+ m_value = null; // Living property, value will be asked at runtime.
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name the name of the property
+ * @param type the type of the property
+ * @param value the default value (String form) of the property, can be <code>null</code>
+ * @param immutable the property is immutable.
+ */
+ public PropertyDescription(String name, String type, String value, boolean immutable) {
+ m_name = name;
+ m_type = type;
+ m_value = value;
+ m_immutable = immutable;
+ }
+
+ /**
+ * Gets the current property name.
+ * @return the property name.
+ */
+ public String getName() {
+ return m_name;
+ }
+
+ /**
+ * Gets the current property type.
+ * @return the property type.
+ */
+ public String getType() {
+ return m_type;
+ }
+
+ /**
+ * Gets the current property value.
+ * @return the default value for the property,
+ * <code>null</code> if the property hasn't a value..
+ */
+ public String getValue() {
+ if (m_property == null) {
+ return m_value;
+ } else {
+ Object value = m_property.getValue();
+ if (value == null) {
+ return "null";
+ } else {
+ return value.toString();
+ }
+ }
+ }
+
+ /**
+ * Sets the property value.
+ * This method can only be called on 'living' property
+ * (properties with a {@link Property} object).
+ * @param value the new value.
+ */
+ public void setValue(Object value) {
+ if (m_property == null) {
+ throw new UnsupportedOperationException("Cannot set the value of a non 'living' property");
+ } else {
+ ConfigurationHandler handler = (ConfigurationHandler) m_property.getHandler();
+ handler.reconfigureProperty(m_property, value);
+ }
+ }
+
+ /**
+ * Is the property immutable.
+ * @return <code>true</code> if the property is immutable.
+ */
+ public boolean isImmutable() {
+ return m_immutable;
+ }
+
+ /**
+ * Sets the property as mandatory.
+ */
+ public void setMandatory() {
+ m_isMandatory = true;
+ }
+
+ /**
+ * Is the property mandatory.
+ * @return <code>true</code> if the property is mandatory,
+ * <code>false</code> otherwise.
+ */
+ public boolean isMandatory() {
+ return m_isMandatory;
+ }
+
+ /**
+ * Gets the object value of the current immutable property.
+ * @param context the bundle context to use to load classes.
+ * @return the object value of the current property or <code>
+ * null</code> if the current value is <code>null</code>.
+ */
+ public Object getObjectValue(BundleContext context) {
+ if (m_value == null) {
+ return null;
+ }
+
+ Class type = null;
+ try {
+ type = Property.computeType(m_type, context);
+ return Property.create(type, m_value);
+ } catch (ConfigurationException e) {
+ return m_value; // Cannot create the object.
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceReferenceImpl.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceReferenceImpl.java
new file mode 100644
index 0000000..9889882
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceReferenceImpl.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 org.apache.felix.ipojo.context;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Internal service reference implementation. This class is used for in the
+ * composition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceReferenceImpl implements ServiceReference {
+
+ /**
+ * Service Registration attached to the service reference.
+ */
+ private ServiceRegistrationImpl m_registration = null;
+
+ /**
+ * Component Instance.
+ */
+ private ComponentInstance m_cm;
+
+ /**
+ * Constructor.
+ *
+ * @param instance : component instance publishing the service.
+ * @param ref : registration attached to this service reference.
+ */
+ public ServiceReferenceImpl(ComponentInstance instance, ServiceRegistrationImpl ref) {
+ m_registration = ref;
+ m_cm = instance;
+ }
+
+ /**
+ * Not supported in composite.
+ * @return null
+ * @see org.osgi.framework.ServiceReference#getBundle()
+ */
+ public Bundle getBundle() {
+ return m_cm.getContext().getBundle();
+ }
+
+ /**
+ * Get the service registration for this reference.
+ * @return the service registration for this service reference.
+ */
+ public ServiceRegistrationImpl getServiceRegistration() {
+ return m_registration;
+ }
+
+
+ /**
+ * Get a property value.
+ * @param name : the key of the required property.
+ * @return the property value or null if no property for the given key.
+ * @see org.osgi.framework.ServiceReference#getProperty(java.lang.String)
+ */
+ public Object getProperty(String name) {
+ return m_registration.getProperty(name);
+ }
+
+ /**
+ * Get the String arrays of service property keys.
+ * @return : the list of property keys.
+ * @see org.osgi.framework.ServiceReference#getPropertyKeys()
+ */
+ public String[] getPropertyKeys() {
+ return m_registration.getPropertyKeys();
+ }
+
+ public Dictionary getProperties() {
+ return m_registration.getProperties();
+ }
+
+
+ /**
+ * Unsupported Operation inside composite.
+ * @return bundles using this reference.
+ * @see org.osgi.framework.ServiceReference#getUsingBundles()
+ */
+ public Bundle[] getUsingBundles() {
+ throw new UnsupportedOperationException("getUsingBundles is not supported in service context");
+ }
+
+ /**
+ * Check if the current service reference is assignable to the given bundle.
+ * @param arg0 : the bundle to check
+ * @param arg1 : the class name to check.
+ * @return true in the case of composite
+ * @see org.osgi.framework.ServiceReference#isAssignableTo(org.osgi.framework.Bundle, java.lang.String)
+ */
+ public boolean isAssignableTo(Bundle arg0, String arg1) {
+ return true;
+ }
+
+ /**
+ * Service Reference compare method.
+ * @param reference the service reference
+ * @return this methods is not yet supported, and throws an
+ * {@link UnsupportedOperationException}.
+ * @see org.osgi.framework.ServiceReference#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object reference) {
+
+ ServiceReference other = (ServiceReference) reference;
+
+ Long id = (Long) getProperty(Constants.SERVICE_ID);
+ Long otherId = (Long) other.getProperty(Constants.SERVICE_ID);
+
+ if (id.equals(otherId)) {
+ return 0; // same service
+ }
+
+ Integer rank = (Integer) getProperty(Constants.SERVICE_RANKING);
+ Integer otherRank = (Integer) other
+ .getProperty(Constants.SERVICE_RANKING);
+
+ // If no rank, then spec says it defaults to zero.
+ rank = (rank == null) ? new Integer(0) : rank;
+ otherRank = (otherRank == null) ? new Integer(0) : otherRank;
+
+ // Sort by rank in ascending order.
+ if (rank.compareTo(otherRank) < 0) {
+ return -1; // lower rank
+ } else if (rank.compareTo(otherRank) > 0) {
+ return 1; // higher rank
+ }
+
+ // If ranks are equal, then sort by service id in descending order.
+ return (id.compareTo(otherId) < 0) ? 1 : -1;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistrationImpl.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistrationImpl.java
new file mode 100644
index 0000000..0dc8dfb
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistrationImpl.java
@@ -0,0 +1,272 @@
+/*
+ * 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.felix.ipojo.context;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.InstanceManager;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Internal service registration implementation. This class is used for in the
+ * composition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRegistrationImpl implements ServiceRegistration {
+
+ /**
+ * Service Registry.
+ */
+ private ServiceRegistry m_registry = null;
+
+ /**
+ * Interfaces associated with the service object.
+ */
+ private String[] m_classes = null;
+
+ /**
+ * Service Id associated with the service object.
+ */
+ private Long m_serviceId = null;
+
+ /**
+ * Service object.
+ */
+ private Object m_svcObj = null;
+
+ /**
+ * Service factory interface.
+ */
+ private ServiceFactory m_factory = null;
+
+ /**
+ * Associated property dictionary.
+ */
+ private Map m_propMap = null;
+
+ /**
+ * Re-usable service reference.
+ */
+ private ServiceReferenceImpl m_ref = null;
+
+ /**
+ * Property Keys List.
+ */
+ private List m_list = new ArrayList();
+
+ /**
+ * Constructor.
+ *
+ * @param registry : the service registry
+ * @param instance : component instance
+ * @param classes : published interfaces array
+ * @param serviceId : the unique service id
+ * @param svcObj : the service object or the service factory object
+ * @param dict : service properties
+ */
+ public ServiceRegistrationImpl(ServiceRegistry registry, ComponentInstance instance, String[] classes, Long serviceId, Object svcObj, Dictionary dict) {
+ m_registry = registry;
+ m_classes = classes;
+ m_serviceId = serviceId;
+ m_svcObj = svcObj;
+ if (m_svcObj instanceof ServiceFactory) { m_factory = (ServiceFactory) m_svcObj; }
+ initializeProperties(dict);
+
+ // This reference is the "standard" reference for this service and will
+ // always be returned by getReference().
+ // Since all reference to this service are supposed to be equal, we use
+ // the hash code of this reference for
+ // a references to this service in ServiceReference.
+ m_ref = new ServiceReferenceImpl(instance, this);
+ }
+
+ /**
+ * Check if the service registration still valid.
+ * @return true if the service registration is valid.
+ */
+ protected boolean isValid() {
+ return m_svcObj != null;
+ }
+
+ /**
+ * Get the service reference attached with this service registration.
+ * @return the service reference
+ * @see org.osgi.framework.ServiceRegistration#getReference()
+ */
+ public ServiceReference getReference() {
+ return m_ref;
+ }
+
+ /**
+ * Add properties to a service registration.
+ * @param dict : the properties to add
+ * @see org.osgi.framework.ServiceRegistration#setProperties(java.util.Dictionary)
+ */
+ public void setProperties(Dictionary dict) {
+ // Make sure registration is valid.
+ if (!isValid()) {
+ throw new IllegalStateException("The service registration is no longer valid.");
+ }
+ // Set the properties.
+ initializeProperties(dict);
+ // Tell registry about it.
+ m_registry.servicePropertiesModified(this);
+ }
+
+ /**
+ * Unregister the service.
+ * @see org.osgi.framework.ServiceRegistration#unregister()
+ */
+ public void unregister() {
+ if (m_svcObj == null) {
+ throw new IllegalStateException("Service already unregistered.");
+ } else {
+ m_registry.unregisterService(this);
+ m_svcObj = null;
+ m_factory = null;
+ }
+ }
+
+ /**
+ * Look for a property in the service properties.
+ * @param key : property key
+ * @return the object associated with the key or null if the key is not
+ * present.
+ */
+ protected Object getProperty(String key) {
+ return m_propMap.get(key);
+ }
+
+ /**
+ * Get the property keys.
+ * @return the property keys list.
+ */
+ protected String[] getPropertyKeys() {
+ synchronized (m_propMap) {
+ m_list.clear();
+ Iterator iterator = m_propMap.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ m_list.add(entry.getKey());
+ }
+ return (String[]) m_list.toArray(new String[m_list.size()]);
+ }
+ }
+
+ /**
+ * Gets the published properties.
+ * @return the dictionary containing each published properties.
+ */
+ protected Dictionary getProperties() {
+ synchronized (m_propMap) {
+ Dictionary dict = new Properties();
+ Iterator keys = m_propMap.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ dict.put(key, m_propMap.get(key));
+ }
+ return dict;
+ }
+ }
+
+ /**
+ * Get the service object.
+ * @return the service object. Call the service factory if needed.
+ */
+ protected Object getService() {
+ // If the service object is a service factory, then
+ // let it create the service object.
+ if (m_factory == null) {
+ return m_svcObj;
+ } else {
+ return getFactoryUnchecked();
+ }
+ }
+
+ /**
+ * Initialize properties.
+ * @param dict : service properties to publish.
+ */
+ private void initializeProperties(Dictionary dict) {
+ // Create a case insensitive map.
+ if (m_propMap == null) {
+ m_propMap = new StringMap(false);
+ } else {
+ m_propMap.clear();
+ }
+
+ if (dict != null) {
+ Enumeration keys = dict.keys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ m_propMap.put(key, dict.get(key));
+ }
+ }
+ // Add the framework assigned properties.
+ m_propMap.put(Constants.OBJECTCLASS, m_classes);
+ m_propMap.put(Constants.SERVICE_ID, m_serviceId);
+ }
+
+ /**
+ * Get a service object via a service factory.
+ * @return the service object via the service factory invocation.
+ */
+ private Object getFactoryUnchecked() {
+ return m_factory.getService(null, this);
+ }
+
+ /**
+ * Unget a service. (Internal Method)
+ *
+ * @param instance : component instance using the service.
+ * @param svcObj : the unget service object.
+ */
+ private void ungetFactoryUnchecked(ComponentInstance instance, Object svcObj) {
+ if (instance instanceof InstanceManager) {
+ m_factory.ungetService(((InstanceManager) instance).getContext().getBundle(), this, svcObj);
+ }
+
+ }
+
+ /**
+ * Unget a service.
+ *
+ * @param instance : component instance using the service.
+ * @param srvObj : the unget service object.
+ */
+ public void ungetService(ComponentInstance instance, Object srvObj) {
+ // If the service object is a service factory, then let is release the
+ // service object.
+ if (m_factory != null) {
+ ungetFactoryUnchecked(instance, srvObj);
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistry.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistry.java
new file mode 100644
index 0000000..81c1a7c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/ServiceRegistry.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.felix.ipojo.context;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Internal Service Registry. This class is used for in the composition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRegistry {
+
+ /**
+ * Service Id index.
+ */
+ private long m_serviceId = 1L;
+
+ /**
+ * List of service listeners.
+ */
+ private List m_listeners = new ArrayList(); // ListenerInfo List
+
+ /**
+ * List of service registration.
+ */
+ private List m_regs = new ArrayList();
+
+ /**
+ * A "real" bundle context to create LDAP filter.
+ */
+ private BundleContext m_context; // BundleContext to create Filter
+
+ /**
+ * Registry logger.
+ */
+ private Logger m_logger;
+
+ /**
+ * Listener info structure.
+ */
+ private static class ListenerInfo {
+ /**
+ * Listener object.
+ */
+ private ServiceListener m_listener;
+ /**
+ * Filter associated with the filter.
+ */
+ private Filter m_filter;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context : bundle context.
+ */
+ public ServiceRegistry(BundleContext context) {
+ m_context = context;
+ m_logger = new Logger(m_context, "Registry logger " + m_context.getBundle().getBundleId());
+ }
+
+ /**
+ * Add a given service listener with no filter.
+ *
+ * @param arg0 : the service listener to add
+ */
+ public void addServiceListener(ServiceListener arg0) {
+ ListenerInfo info = new ListenerInfo();
+ info.m_listener = arg0;
+ info.m_filter = null;
+ m_listeners.add(info);
+ }
+
+ /**
+ * Unget a service.
+ *
+ * @param instance : instance releasing the service.
+ * @param ref : released reference.
+ * @return true if the unget success
+ */
+ public boolean ungetService(ComponentInstance instance, ServiceReference ref) {
+
+ ServiceRegistrationImpl reg = ((ServiceReferenceImpl) ref).getServiceRegistration();
+ if (reg.isValid()) {
+ reg.ungetService(instance, reg.getService());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Unregister a service listener.
+ *
+ * @param arg0 : the service listener to remove
+ */
+ public void removeServiceListener(ServiceListener arg0) {
+ m_listeners.remove(arg0);
+ }
+
+ /**
+ * Register a service.
+ *
+ * @param instance : provider instance.
+ * @param clazz : provided interface.
+ * @param svcObj : service object of service factory object.
+ * @param dict : service properties.
+ * @return the created service registration.
+ */
+ public ServiceRegistration registerService(ComponentInstance instance, String clazz, Object svcObj, Dictionary dict) {
+ synchronized (m_regs) {
+ ServiceRegistrationImpl reg = new ServiceRegistrationImpl(this, instance, new String[] { clazz }, new Long(m_serviceId++), svcObj, dict);
+ m_regs.add(reg);
+ fireServiceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()));
+ return reg;
+ }
+ }
+
+ /**
+ * Register a service.
+ *
+ * @param instance : provider instance.
+ * @param clazzes : provided interfaces.
+ * @param svcObj : service object of service factory object.
+ * @param dict : service properties.
+ * @return the created service registration.
+ */
+ public ServiceRegistration registerService(ComponentInstance instance, String[] clazzes, Object svcObj, Dictionary dict) {
+ synchronized (m_regs) {
+ ServiceRegistrationImpl reg = new ServiceRegistrationImpl(this, instance, clazzes, new Long(m_serviceId++), svcObj, dict);
+ m_regs.add(reg);
+ fireServiceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()));
+ return reg;
+ }
+ }
+
+ /**
+ * Dispatch a service event.
+ * @param event : the service to dispatch
+ */
+ private void fireServiceChanged(ServiceEvent event) {
+ synchronized (m_listeners) {
+ // Iterate on the service listener list to notify service listener
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ListenerInfo info = (ListenerInfo) m_listeners.get(i);
+ ServiceReference ref = event.getServiceReference();
+ if (info.m_filter == null) {
+ info.m_listener.serviceChanged(event);
+ }
+ Dictionary props = ((ServiceReferenceImpl) ref).getProperties();
+ if (info.m_filter != null && info.m_filter.match(props)) {
+ info.m_listener.serviceChanged(event);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get available (and accessible) service references.
+ *
+ * @param className : required interface
+ * @param expr : LDAP filter
+ * @return : the list of available service references.
+ * @throws InvalidSyntaxException
+ * occurs when the LDAP filter is malformed.
+ */
+ public ServiceReference[] getServiceReferences(String className, String expr) throws InvalidSyntaxException {
+ synchronized (m_regs) {
+ // Define filter if expression is not null.
+ Filter filter = null;
+ if (expr != null) {
+ filter = m_context.createFilter(expr);
+ }
+
+ List refs = new ArrayList();
+
+ for (int i = 0; i < m_regs.size(); i++) {
+ ServiceRegistrationImpl reg = (ServiceRegistrationImpl) m_regs.get(i);
+ // Determine if the registered services matches the search
+ // criteria.
+ boolean matched = false;
+
+ // If className is null, then look at filter only.
+ if ((className == null) && ((filter == null) || filter.match(reg.getProperties()))) {
+ matched = true;
+ } else if (className != null) {
+ // If className is not null, then first match the
+ // objectClass property before looking at the
+ // filter.
+ Dictionary props = ((ServiceRegistrationImpl) reg).getProperties();
+ String[] objectClass = (String[]) props.get(Constants.OBJECTCLASS);
+ for (int classIdx = 0; classIdx < objectClass.length; classIdx++) {
+ if (objectClass[classIdx].equals(className) && ((filter == null) || filter.match(props))) {
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ // Add reference if it was a match.
+ if (matched) {
+ refs.add(reg.getReference());
+ }
+ }
+
+ if (! refs.isEmpty()) {
+ return (ServiceReference[]) refs.toArray(new ServiceReference[refs.size()]);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Look for a service reference.
+ *
+ * @param clazz : required interface.
+ * @return the first available provider or null if none available.
+ */
+ public ServiceReference getServiceReference(String clazz) {
+ synchronized (m_regs) {
+ try {
+ ServiceReference[] refs = getServiceReferences(clazz, null);
+ if (refs != null) {
+ return refs[0];
+ } // If the refs != null we are sure that it exists one reference or more.
+ } catch (InvalidSyntaxException ex) {
+ // Cannot happen : null filter.
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Get a service object.
+ * @param instance : component instance requiring the service.
+ * @param ref : the required reference.
+ * @return the service object.
+ */
+ public Object getService(ComponentInstance instance, ServiceReference ref) {
+ synchronized (m_regs) {
+ // Look for the service registration for this ref
+ ServiceRegistrationImpl reg = ((ServiceReferenceImpl) ref).getServiceRegistration();
+ if (reg.isValid()) {
+ // Delegate the service providing to the service registration
+ return reg.getService();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Get all service references consistent with the given interface and
+ * filter.
+ * @param clazz : the required interface.
+ * @param filter : the LDAP filter.
+ * @return the list of all service reference or null if none available.
+ * @throws InvalidSyntaxException occurs when the LDAP filter is malformed.
+ */
+ public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ synchronized (m_regs) {
+ // Can delegate on getServiceReference, indeed their is no test on
+ // the "modularity" conflict.
+ return getServiceReferences(clazz, filter);
+ }
+ }
+
+ /**
+ * Add a service listener with a filter.
+ * @param listener : the service listener to add
+ * @param filter : LDAP filter
+ */
+ public void addServiceListener(ServiceListener listener, String filter) {
+ // If the filter is null, subscribe with no filter.
+ if (filter == null) {
+ addServiceListener(listener);
+ return;
+ }
+
+ try {
+ ListenerInfo info = new ListenerInfo();
+ info.m_listener = listener;
+ info.m_filter = m_context.createFilter(filter);
+ m_listeners.add(info);
+ } catch (InvalidSyntaxException ex) {
+ m_logger.log(Logger.ERROR, ex.getMessage(), ex);
+ }
+
+ }
+
+ /**
+ * Dispatch a service properties modified event.
+ * @param reg : the implicated service registration.
+ */
+ public void servicePropertiesModified(ServiceRegistrationImpl reg) {
+ fireServiceChanged(new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()));
+ }
+
+ /**
+ * Unregister a service.
+ * @param reg : the service registration to unregister
+ */
+ public void unregisterService(ServiceRegistrationImpl reg) {
+ m_regs.remove(reg);
+ fireServiceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()));
+ }
+
+ /**
+ * Reset the service registry.
+ */
+ public void reset() {
+ m_serviceId = 1L;
+ m_listeners = new ArrayList();
+ m_regs = new ArrayList();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/StringMap.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/StringMap.java
new file mode 100644
index 0000000..737c95b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/context/StringMap.java
@@ -0,0 +1,148 @@
+/*
+ * 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.felix.ipojo.context;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Simple utility class that creates a map for string-based keys by extending
+ * <tt>TreeMap</tt>. This map can be set to use case-sensitive or
+ * case-insensitive comparison when searching for the key. Any keys put into
+ * this map will be converted to a <tt>String</tt> using the
+ * <tt>toString()</tt> method, since it is only intended to compare strings.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class StringMap extends TreeMap {
+
+ /**
+ * serialVersionUID.
+ */
+ private static final long serialVersionUID = 6948801857034259744L;
+
+ /**
+ * Constructor.
+ */
+ public StringMap() {
+ this(true);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param caseSensitive : fix if the map if case sensitive or not.
+ */
+ public StringMap(boolean caseSensitive) {
+ super(new StringComparator(caseSensitive));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param map : initial properties.
+ * @param caseSensitive : fix if the map if case sensitive or not.
+ */
+ public StringMap(Map map, boolean caseSensitive) {
+ this(caseSensitive);
+ putAll(map);
+ }
+
+ /**
+ * Put a record in the map.
+ * @param key : key
+ * @param value : value
+ * @return an object.
+ * @see java.util.TreeMap#put(K, V)
+ */
+ public Object put(Object key, Object value) {
+ return super.put(key.toString(), value);
+ }
+
+ /**
+ * Check if the map is case-sensitive.
+ * @return true if the map is case sensitive.
+ */
+ public boolean isCaseSensitive() {
+ return ((StringComparator) comparator()).isCaseSensitive();
+ }
+
+ /**
+ * Set the case sensitivity.
+ *
+ * @param flag : the new case sensitivity.
+ */
+ public void setCaseSensitive(boolean flag) {
+ ((StringComparator) comparator()).setCaseSensitive(flag);
+ }
+
+ private static class StringComparator implements Comparator, Serializable {
+ /**
+ * Id.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Is the map case sensitive?
+ */
+ private boolean m_isCaseSensitive = true;
+
+ /**
+ * Constructor.
+ *
+ * @param flag : true to enable the case sensitivity.
+ */
+ public StringComparator(boolean flag) {
+ m_isCaseSensitive = flag;
+ }
+
+ /**
+ * Compare to object.
+ * @param object1 : first object to compare
+ * @param object2 : second object to compare
+ * @return the comparison result
+ * @see java.util.Comparator#compare(T, T)
+ */
+ public int compare(Object object1, Object object2) {
+ if (m_isCaseSensitive) {
+ return object1.toString().compareTo(object2.toString());
+ } else {
+ return object1.toString().compareToIgnoreCase(object2.toString());
+ }
+ }
+
+ /**
+ * Check if the comparator is case sensitive.
+ * @return true if the map is case sensitive.
+ */
+ public boolean isCaseSensitive() {
+ return m_isCaseSensitive;
+ }
+
+ /**
+ * Set the case sensitivity.
+ *
+ * @param flag : true to enable the case sensitivity
+ */
+ public void setCaseSensitive(boolean flag) {
+ m_isCaseSensitive = flag;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java
new file mode 100644
index 0000000..a5f6c7e
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.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.felix.ipojo.handlers.architecture;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Architecture Handler : do reflection on your component.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ArchitectureHandler extends PrimitiveHandler implements Architecture {
+
+ /**
+ * Name of the component.
+ */
+ private String m_name;
+
+ /**
+ * Configure the handler.
+ * @param metadata : the metadata of the component
+ * @param configuration : the instance configuration
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) {
+ m_name = (String) configuration.get("instance.name");
+ }
+
+ /**
+ * Stop method.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ // Nothing do do when stopping.
+ }
+
+ /**
+ * Start method.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ debug("Start architecture handler with " + m_name + " name");
+ }
+
+ /**
+ * Get the instance description.
+ * @return the instance description
+ * @see org.apache.felix.ipojo.architecture.Architecture#getDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ return getInstanceManager().getInstanceDescription();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
new file mode 100644
index 0000000..b5659f2
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
@@ -0,0 +1,629 @@
+/*
+ * 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.felix.ipojo.handlers.configuration;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.util.Callback;
+import org.apache.felix.ipojo.util.Property;
+import org.apache.felix.ipojo.util.SecurityHelper;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * Handler managing the Configuration Admin.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationHandler extends PrimitiveHandler implements ManagedService {
+
+ /**
+ * List of the configurable fields.
+ */
+ private List/*<Property>*/ m_configurableProperties = new ArrayList(1);
+
+ /**
+ * ProvidedServiceHandler of the component. It is useful to propagate
+ * properties to service registrations.
+ */
+ private ProvidedServiceHandler m_providedServiceHandler;
+
+ /**
+ * Properties propagated during the last instance "update".
+ */
+ private Dictionary m_propagatedFromInstance = new Properties();
+
+ /**
+ * Properties to propagate.
+ */
+ private Dictionary m_toPropagate = new Properties();
+
+ /**
+ * Properties propagated from the configuration admin.
+ */
+ private Dictionary m_propagatedFromCA;
+
+ /**
+ * Check if the instance was already reconfigured by the configuration admin.
+ */
+ private boolean m_configurationAlreadyPushed;
+
+ /**
+ * should the component propagate configuration ?
+ */
+ private boolean m_mustPropagate;
+
+ /**
+ * Service Registration to publish the service registration.
+ */
+ private ServiceRegistration m_sr;
+
+ /**
+ * Managed Service PID.
+ * This PID must be different from the instance name if the instance was created
+ * with the Configuration Admin.
+ */
+ private String m_managedServicePID;
+
+ /**
+ * the handler description.
+ */
+ private ConfigurationHandlerDescription m_description;
+
+ /**
+ * Updated method.
+ * This method is called when a reconfiguration is completed.
+ */
+ private Callback m_updated;
+
+
+ /**
+ * Initialize the component type.
+ * @param desc : component type description to populate.
+ * @param metadata : component type metadata.
+ * @throws ConfigurationException : metadata are incorrect.
+ * @see org.apache.felix.ipojo.Handler#initializeComponentFactory(org.apache.felix.ipojo.architecture.ComponentTypeDescription, org.apache.felix.ipojo.metadata.Element)
+ */
+ public void initializeComponentFactory(ComponentTypeDescription desc, Element metadata) throws ConfigurationException {
+ Element[] confs = metadata.getElements("Properties", "");
+ if (confs == null) { return; }
+ Element[] configurables = confs[0].getElements("Property");
+ for (int i = 0; configurables != null && i < configurables.length; i++) {
+ String fieldName = configurables[i].getAttribute("field");
+ String methodName = configurables[i].getAttribute("method");
+ String paramIndex = configurables[i].getAttribute("constructor-parameter");
+
+ if (fieldName == null && methodName == null && paramIndex == null) {
+ throw new ConfigurationException("Malformed property : The property needs to contain" +
+ " at least a field, a method or a constructor-parameter");
+ }
+
+ String name = configurables[i].getAttribute("name");
+ if (name == null) {
+ if (fieldName == null && methodName != null) {
+ name = methodName;
+ } else if (fieldName == null && paramIndex != null) {
+ name = paramIndex;
+ } else {
+ name = fieldName;
+ }
+ configurables[i].addAttribute(new Attribute("name", name)); // Add the type to avoid configure checking
+ }
+
+ String value = configurables[i].getAttribute("value");
+
+ // Detect the type of the property
+ PojoMetadata manipulation = getFactory().getPojoMetadata();
+ String type = null;
+ if (methodName != null) {
+ MethodMetadata[] method = manipulation.getMethods(methodName);
+ if (method.length == 0) {
+ type = configurables[i].getAttribute("type");
+ if (type == null) {
+ throw new ConfigurationException("Malformed property : The type of the property cannot be discovered, add a 'type' attribute");
+ }
+ } else {
+ if (method[0].getMethodArguments().length != 1) {
+ throw new ConfigurationException("Malformed property : The method " + methodName + " does not have one argument");
+ }
+ type = method[0].getMethodArguments()[0];
+ configurables[i].addAttribute(new Attribute("type", type)); // Add the type to avoid configure checking
+ }
+ } else if (fieldName != null) {
+ FieldMetadata field = manipulation.getField(fieldName);
+ if (field == null) { throw new ConfigurationException("Malformed property : The field " + fieldName + " does not exist in the implementation class"); }
+ type = field.getFieldType();
+ configurables[i].addAttribute(new Attribute("type", type)); // Add the type to avoid configure checking
+ } else if (paramIndex != null) {
+ int index = Integer.parseInt(paramIndex);
+ type = configurables[i].getAttribute("type");
+ MethodMetadata[] cts = manipulation.getConstructors();
+ // If we don't have a type, try to get the first constructor and get the type of the parameter
+ // we the index 'index'.
+ if (type == null && cts.length > 0 && cts[0].getMethodArguments().length > index) {
+ type = cts[0].getMethodArguments()[index];
+ } else if (type == null) { // Applied only if type was not determined.
+ throw new ConfigurationException("Cannot determine the type of the property " + index +
+ ", please use the type attribute");
+ }
+ configurables[i].addAttribute(new Attribute("type", type));
+ }
+
+ // Is the property set to immutable
+ boolean immutable = false;
+ String imm = configurables[i].getAttribute("immutable");
+ immutable = imm != null && imm.equalsIgnoreCase("true");
+
+ boolean mandatory = false;
+ String man = configurables[i].getAttribute("mandatory");
+ mandatory = man != null && man.equalsIgnoreCase("true");
+
+ PropertyDescription pd = null;
+ if (value == null) {
+ pd = new PropertyDescription(name, type, null, false); // Cannot be immutable if we have no value.
+ } else {
+ pd = new PropertyDescription(name, type, value, immutable);
+ }
+
+ if (mandatory) {
+ pd.setMandatory();
+ }
+
+ desc.addProperty(pd);
+ }
+
+ }
+
+ /**
+ * Configures the handler.
+ * Access to field does not require synchronization as this method is executed
+ * before any thread access to this object.
+ * @param metadata the metadata of the component
+ * @param configuration the instance configuration
+ * @throws ConfigurationException one property metadata is not correct
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ // Build the map
+ Element[] confs = metadata.getElements("Properties", "");
+ Element[] configurables = confs[0].getElements("Property");
+
+ // Check if the component is dynamically configurable
+ // Propagation enabled by default.
+ m_mustPropagate = true;
+ m_toPropagate = configuration; // Instance configuration to propagate.
+ String propa = confs[0].getAttribute("propagation");
+ if (propa != null && propa.equalsIgnoreCase("false")) {
+ m_mustPropagate = false;
+ m_toPropagate = null;
+ }
+
+ // Check if the component support ConfigurationADmin reconfiguration
+ m_managedServicePID = confs[0].getAttribute("pid"); // Look inside the component type description
+ String instanceMSPID = (String) configuration.get("managed.service.pid"); // Look inside the instance configuration.
+ if (instanceMSPID != null) {
+ m_managedServicePID = instanceMSPID;
+ }
+
+ // updated method
+ String upd = confs[0].getAttribute("updated");
+ if (upd != null) {
+ MethodMetadata method = getPojoMetadata().getMethod(upd);
+ if (method == null) {
+ throw new ConfigurationException("The updated method is not found in the class "
+ + getInstanceManager().getClassName());
+ } else if (method.getMethodArguments().length == 0) {
+ m_updated = new Callback(upd, new Class[0], false, getInstanceManager());
+ } else if (method.getMethodArguments().length == 1
+ && method.getMethodArguments()[0].equals(Dictionary.class.getName())) {
+ m_updated = new Callback(upd, new Class[] {Dictionary.class}, false, getInstanceManager());
+ } else {
+ throw new ConfigurationException("The updated method is found in the class "
+ + getInstanceManager().getClassName() + " must have either no argument or a Dictionary");
+ }
+ }
+
+ for (int i = 0; configurables != null && i < configurables.length; i++) {
+ String fieldName = configurables[i].getAttribute("field");
+ String methodName = configurables[i].getAttribute("method");
+ String paramIndex = configurables[i].getAttribute("constructor-parameter");
+ int index = -1;
+
+ String name = configurables[i].getAttribute("name"); // The initialize method has fixed the property name.
+ String value = configurables[i].getAttribute("value");
+
+ String type = configurables[i].getAttribute("type"); // The initialize method has fixed the property name.
+
+ Property prop = null;
+ if (paramIndex == null) {
+ prop = new Property(name, fieldName, methodName, value, type, getInstanceManager(), this);
+ } else {
+ index = Integer.parseInt(paramIndex);
+ prop = new Property(name, fieldName, methodName, index,
+ value, type, getInstanceManager(), this);
+ }
+ addProperty(prop);
+
+ // Check if the instance configuration contains value for the current property :
+ if (configuration.get(name) == null) {
+ if (fieldName != null && configuration.get(fieldName) != null) {
+ prop.setValue(configuration.get(fieldName));
+ }
+ } else {
+ prop.setValue(configuration.get(name));
+ }
+
+ if (fieldName != null) {
+ FieldMetadata field = new FieldMetadata(fieldName, type);
+ getInstanceManager().register(field, prop);
+ }
+
+ if (index != -1) {
+ getInstanceManager().register(index, prop);
+ }
+ }
+
+ m_description = new ConfigurationHandlerDescription(this, m_configurableProperties, m_managedServicePID);
+
+ }
+
+ /**
+ * Stop method.
+ * This method is synchronized to avoid the configuration admin pushing a configuration during the un-registration.
+ * Do nothing.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public synchronized void stop() {
+ if (m_sr != null) {
+ m_sr.unregister();
+ m_sr = null;
+ }
+ }
+
+ /**
+ * Start method.
+ * This method is synchronized to avoid the config admin pushing a configuration before ending the method.
+ * Propagate properties if the propagation is activated.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public synchronized void start() {
+ // Get the provided service handler :
+ m_providedServiceHandler = (ProvidedServiceHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":provides");
+
+
+ // Propagation
+ if (m_mustPropagate) {
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ Property prop = (Property) m_configurableProperties.get(i);
+ if (prop.getValue() != Property.NO_VALUE && prop.getValue() != null) { // No injected value, or null
+ m_toPropagate.put(prop.getName(), prop.getValue());
+ }
+ }
+ reconfigure(m_toPropagate);
+ }
+
+
+ // Give initial values and reset the 'invoked' flag.
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ Property prop = (Property) m_configurableProperties.get(i);
+ prop.reset(); // Clear the invoked flag.
+ if (prop.hasField() && prop.getValue() != Property.NO_VALUE && prop.getValue() != null) {
+ getInstanceManager().onSet(null, prop.getField(), prop.getValue());
+ }
+ }
+
+ if (m_managedServicePID != null && m_sr == null) {
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, m_managedServicePID);
+ props.put("instance.name", getInstanceManager().getInstanceName());
+ props.put("factory.name", getInstanceManager().getFactory().getFactoryName());
+
+ // Security Check
+ if (SecurityHelper.hasPermissionToRegisterService(ManagedService.class.getName(),
+ getInstanceManager().getContext())) {
+ m_sr = getInstanceManager().getContext().registerService(ManagedService.class.getName(), this, (Dictionary) props);
+ } else {
+ error("Cannot register the ManagedService - The bundle "
+ + getInstanceManager().getContext().getBundle().getBundleId()
+ + " does not have the permission to register the service");
+ }
+ }
+ }
+
+ /**
+ * Adds the given property metadata to the property metadata list.
+ *
+ * @param prop : property metadata to add
+ */
+ protected void addProperty(Property prop) {
+ m_configurableProperties.add(prop);
+ }
+
+ /**
+ * Checks if the list contains the property.
+ *
+ * @param name : name of the property
+ * @return true if the property exist in the list
+ */
+ protected boolean containsProperty(String name) {
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ if (((Property) m_configurableProperties.get(i)).getName().equals(name)) { return true; }
+ }
+ return false;
+ }
+
+ /**
+ * Reconfigure the component instance.
+ * Check if the new configuration modifies the current configuration.
+ * Invokes the updated method is needed.
+ * @param configuration : the new configuration
+ * @see org.apache.felix.ipojo.Handler#reconfigure(java.util.Dictionary)
+ */
+ public synchronized void reconfigure(Dictionary configuration) {
+ info(getInstanceManager().getInstanceName() + " is reconfiguring the properties : " + configuration);
+ Properties props = reconfigureProperties(configuration);
+ propagate(props, m_propagatedFromInstance);
+ m_propagatedFromInstance = props;
+
+ if (getInstanceManager().getPojoObjects() != null) {
+ try {
+ notifyUpdated(null);
+ } catch (Throwable e) {
+ error("Cannot call the updated method : " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Reconfigured configuration properties and returns non matching properties.
+ * When called, it must hold the monitor lock.
+ * @param configuration : new configuration
+ * @return the properties that does not match with configuration properties
+ */
+ private Properties reconfigureProperties(Dictionary configuration) {
+ Properties toPropagate = new Properties();
+ Enumeration keysEnumeration = configuration.keys();
+ while (keysEnumeration.hasMoreElements()) {
+ String name = (String) keysEnumeration.nextElement();
+ Object value = configuration.get(name);
+ boolean found = false;
+ // Check if the name is a configurable property
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ Property prop = (Property) m_configurableProperties.get(i);
+ if (prop.getName().equals(name)) {
+ reconfigureProperty(prop, value);
+ found = true;
+ break; // Exit the search loop
+ }
+ }
+ if (!found) {
+ // The property is not a configurable property, add it to the toPropagate list.
+ toPropagate.put(name, value);
+ }
+ }
+
+ // Every removed configurable property gets reset to its default value
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ Property prop = (Property) m_configurableProperties.get(i);
+ if (configuration.get(prop.getName()) == null) {
+ reconfigureProperty(prop, prop.getDefaultValue());
+ }
+ }
+ return toPropagate;
+
+ }
+
+ /**
+ * Reconfigures the given property with the given value.
+ * This methods handles {@link org.apache.felix.ipojo.InstanceManager#onSet(Object, String, Object)}
+ * call and the callback invocation.
+ * The reconfiguration occurs only if the value changes.
+ * @param prop the property object to reconfigure
+ * @param value the new value.
+ */
+ public void reconfigureProperty(Property prop, Object value) {
+ if (prop.getValue() == null || ! prop.getValue().equals(value)) {
+ prop.setValue(value);
+ if (prop.hasField()) {
+ getInstanceManager().onSet(null, prop.getField(), prop.getValue()); // Notify other handler of the field value change.
+ }
+ if (prop.hasMethod()) {
+ if (getInstanceManager().getPojoObjects() != null) {
+ prop.invoke(null); // Call on all created pojo objects.
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the old properties from the provided services and propagate new properties.
+ * @param newProps : new properties to propagate
+ * @param oldProps : old properties to remove
+ */
+ private void propagate(Dictionary newProps, Dictionary oldProps) {
+ if (m_mustPropagate && m_providedServiceHandler != null) {
+ if (oldProps != null) {
+ m_providedServiceHandler.removeProperties(oldProps);
+ }
+
+ if (newProps != null) {
+ // Remove the name, the pid and the managed service pid props
+ newProps.remove("name");
+ newProps.remove("managed.service.pid");
+ newProps.remove(Constants.SERVICE_PID);
+ // Propagation of the properties to service registrations :
+ m_providedServiceHandler.addProperties(newProps);
+ }
+ }
+ }
+
+ /**
+ * Handler createInstance method.
+ * This method is override to allow delayed callback invocation.
+ * Invokes the updated method is needed.
+ * @param instance : the created object
+ * @see org.apache.felix.ipojo.PrimitiveHandler#onCreation(Object)
+ */
+ public void onCreation(Object instance) {
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ Property prop = (Property) m_configurableProperties.get(i);
+ if (prop.hasMethod()) {
+ prop.invoke(instance);
+ }
+ }
+
+ try {
+ notifyUpdated(instance);
+ } catch (Throwable e) {
+ error("Cannot call the updated method : " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Invokes the updated method.
+ * This method build the dictionary containing all valued properties,
+ * as well as properties propagated to the provided service handler (
+ * only if the propagation is enabled).
+ * @param instance the instance on which the callback must be called.
+ * If <code>null</code> the callback is called on all the existing
+ * object.
+ */
+ private void notifyUpdated(Object instance) {
+ if (m_updated == null) {
+ return;
+ }
+
+ if (m_updated.getArguments().length == 0) {
+ // We don't have to compute the properties,
+ // we just call the callback.
+ try {
+ if (instance == null) {
+ m_updated.call(new Object[0]);
+ } else {
+ m_updated.call(instance, new Object[0]);
+ }
+ } catch (Exception e) {
+ error("Cannot call the updated method " + m_updated.getMethod() + " : " + e.getMessage());
+ }
+ return;
+ }
+
+ // Else we must compute the properties.
+ Properties props = new Properties();
+ for (int i = 0; i < m_configurableProperties.size(); i++) {
+ String n = ((Property) m_configurableProperties.get(i)).getName();
+ Object v = ((Property) m_configurableProperties.get(i)).getValue();
+ if (v != Property.NO_VALUE) {
+ props.put(n, v);
+ }
+ }
+ // add propagated properties to the list if propagation enable
+ if (m_mustPropagate) {
+ // Start by properties from the configuration admin,
+ if (m_propagatedFromCA != null) {
+
+ Enumeration e = m_propagatedFromCA.keys();
+ while (e.hasMoreElements()) {
+ String k = (String) e.nextElement();
+ if (! k.equals("instance.name")) {
+ props.put(k, m_propagatedFromCA.get(k));
+ }
+ }
+ }
+ // Do also the one from the instance configuration
+ if (m_propagatedFromInstance != null) {
+ Enumeration e = m_propagatedFromInstance.keys();
+ while (e.hasMoreElements()) {
+ String k = (String) e.nextElement();
+ if (! k.equals("instance.name")) { // Skip instance.name
+ props.put(k, m_propagatedFromInstance.get(k));
+ }
+ }
+ }
+ }
+
+ try {
+ if (instance == null) {
+ m_updated.call(new Object[] {props});
+ } else {
+ m_updated.call(instance, new Object[] {props});
+ }
+ } catch (Exception e) {
+ error("Cannot call the updated method " + m_updated.getMethod() + " : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Managed Service method.
+ * This method is called when the instance is reconfigured by the ConfigurationAdmin.
+ * When called, it must hold the monitor lock.
+ * @param conf : pushed configuration.
+ * @throws org.osgi.service.cm.ConfigurationException the reconfiguration failed.
+ * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
+ */
+ public synchronized void updated(Dictionary conf) throws org.osgi.service.cm.ConfigurationException {
+ if (conf == null && ! m_configurationAlreadyPushed) {
+ return; // First call
+ } else if (conf != null) { // Configuration push
+ Properties props = reconfigureProperties(conf);
+ propagate(props, m_propagatedFromCA);
+ m_propagatedFromCA = props;
+ m_configurationAlreadyPushed = true;
+ } else if (m_configurationAlreadyPushed) { // Configuration deletion
+ propagate(null, m_propagatedFromCA);
+ m_propagatedFromCA = null;
+ m_configurationAlreadyPushed = false;
+ }
+
+ if (getInstanceManager().getPojoObjects() != null) {
+ try {
+ notifyUpdated(null);
+ } catch (Throwable e) {
+ error("Cannot call the updated method : " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Gets the configuration handler description.
+ * @return the configuration handler description.
+ * @see org.apache.felix.ipojo.Handler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandlerDescription.java
new file mode 100644
index 0000000..d8bdb4b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandlerDescription.java
@@ -0,0 +1,111 @@
+/*
+ * 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.felix.ipojo.handlers.configuration;
+
+import java.util.List;
+
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Property;
+
+/**
+ * Configuration handler description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationHandlerDescription extends HandlerDescription {
+
+ /**
+ * The property descriptions.
+ */
+ private PropertyDescription[] m_properties;
+
+ /**
+ * The Managed Service PID.
+ * <code>null</code> if not set.
+ */
+ private String m_pid;
+
+ /**
+ * Creates the description object for the configuration handler description.
+ * @param handler the configuration handler.
+ * @param props the list of properties.
+ * @param pid the managed service pid or <code>null</code> if not set.
+ */
+ public ConfigurationHandlerDescription(Handler handler, List/*<Property>*/ props, String pid) {
+ super(handler);
+ m_properties = new PropertyDescription[props.size()];
+ for (int i = 0; i < props.size(); i++) {
+ m_properties[i] = new PropertyDescription((Property) props.get(i));
+ }
+ m_pid = pid;
+ }
+
+ /**
+ * The handler information.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element elem = super.getHandlerInfo();
+
+ if (m_pid != null) {
+ elem.addAttribute(new Attribute("managed.service.pid", m_pid));
+ }
+
+ for (int i = 0; i < m_properties.length; i++) {
+ String name = m_properties[i].getName();
+ Object value = m_properties[i].getValue();
+ Element property = new Element("property", "");
+ elem.addElement(property);
+ if (name != null) {
+ property.addAttribute(new Attribute("name", name));
+ }
+ if (value != null) {
+ if (value == Property.NO_VALUE) {
+ property.addAttribute(new Attribute("value", "NO_VALUE"));
+ } else {
+ property.addAttribute(new Attribute("value", value.toString()));
+ }
+ }
+ }
+ return elem;
+ }
+
+ /**
+ * Gets the properties.
+ * @return the property set.
+ */
+ public PropertyDescription[] getProperties() {
+ return m_properties;
+ }
+
+ /**
+ * Gets the managed service pid.
+ * @return the managed service pid of <code>null</code>
+ * if not set.
+ */
+ public String getManagedServicePid() {
+ return m_pid;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
new file mode 100644
index 0000000..eb45ae7
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -0,0 +1,1068 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.ConstructorInjector;
+import org.apache.felix.ipojo.FieldInterceptor;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.MethodInterceptor;
+import org.apache.felix.ipojo.Nullable;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.handlers.dependency.ServiceUsage.Usage;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Represent a service dependency of the component instance.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Dependency extends DependencyModel implements FieldInterceptor, MethodInterceptor,
+ ConstructorInjector {
+
+ /**
+ * Reference on the Dependency Handler.
+ */
+ private final DependencyHandler m_handler;
+
+ /**
+ * Field of the dependency.
+ */
+ private final String m_field;
+
+ /**
+ * List of dependency callback.
+ * Immutable once set.
+ */
+ private DependencyCallback[] m_callbacks;
+
+ /**
+ * Is the dependency a service level dependency.
+ * Immutable once set.
+ */
+ private boolean m_isServiceLevelRequirement;
+
+ /**
+ * Is the provider set frozen ?
+ */
+ private boolean m_isFrozen;
+
+ /**
+ * Is the dependency started ?
+ */
+ private boolean m_isStarted;
+
+ /**
+ * Thread Local.
+ */
+ private ServiceUsage m_usage;
+
+ /**
+ * Type of the object to inject.
+ * Cannot change once set.
+ */
+ private int m_type;
+
+ /**
+ * Nullable object.
+ * Immutable once set.
+ */
+ private Object m_nullable;
+
+ /**
+ * Default-Implementation.
+ */
+ private final String m_di;
+
+ /**
+ * Is the Nullable pattern enable?
+ */
+ private final boolean m_supportNullable;
+
+ /**
+ * Id of the dependency.
+ * Immutable once set.
+ */
+ private String m_id;
+
+ /**
+ * Do we have to inject proxy?
+ */
+ private boolean m_isProxy;
+
+ /**
+ * Proxy Object.
+ */
+ private Object m_proxyObject;
+
+ /**
+ * Constructor paramter index.
+ * -1 if not used.
+ */
+ private int m_index = -1;
+
+ /**
+ * Dependency constructor. After the creation the dependency is not started.
+ *
+ * @param handler : the dependency handler managing this dependency
+ * @param field : field of the dependency
+ * @param spec : required specification
+ * @param filter : LDAP filter of the dependency
+ * @param isOptional : is the dependency an optional dependency ?
+ * @param isAggregate : is the dependency an aggregate dependency
+ * @param nullable : describe if the nullable ability is enable or disable
+ * @param isProxy : is the proxied dependency
+ * @param identity : id of the dependency, may be null
+ * @param context : bundle context (or service context) to use.
+ * @param policy : resolution policy
+ * @param cmp : comparator to sort references
+ * @param defaultImplem : default-implementation class
+ */
+ public Dependency(DependencyHandler handler, String field, Class spec, Filter filter, boolean isOptional, boolean isAggregate, boolean nullable, boolean isProxy, String identity, BundleContext context, int policy, Comparator cmp, String defaultImplem) {
+ super(spec, isAggregate, isOptional, filter, cmp, policy, context, handler, handler.getInstanceManager());
+ m_handler = handler;
+ m_field = field;
+ m_isProxy = isProxy;
+
+ if (field != null) {
+ m_usage = new ServiceUsage();
+ } else {
+ m_usage = null;
+ }
+
+ m_supportNullable = nullable;
+ m_di = defaultImplem;
+
+ if (identity == null) {
+ if (spec != null) {
+ m_id = spec.getName();
+ }
+ } else {
+ m_id = identity;
+ }
+
+ // Else wait the setSpecification call.
+ }
+
+ /**
+ * Set the specification of the current dependency.
+ * In order to store the id of the dependency, this
+ * method is override. This method is called during the
+ * configuration.
+ * @param spec : request service Class
+ * @see org.apache.felix.ipojo.util.DependencyModel#setSpecification(java.lang.Class)
+ */
+ public void setSpecification(Class spec) {
+ super.setSpecification(spec);
+ if (m_id == null) {
+ m_id = spec.getName();
+ }
+ }
+
+ public String getField() {
+ return m_field;
+ }
+
+ /**
+ * Add a callback to the dependency.
+ * This method is called during the configuration.
+ * @param callback : callback to add
+ */
+ protected void addDependencyCallback(DependencyCallback callback) {
+ if (m_callbacks == null) {
+ m_callbacks = new DependencyCallback[] { callback };
+ } else {
+ DependencyCallback[] newCallbacks = new DependencyCallback[m_callbacks.length + 1];
+ System.arraycopy(m_callbacks, 0, newCallbacks, 0, m_callbacks.length);
+ newCallbacks[m_callbacks.length] = callback;
+ m_callbacks = newCallbacks;
+ }
+ }
+
+
+ protected void addConstructorInjection(int index) throws ConfigurationException {
+ m_index = index;
+ m_usage = new ServiceUsage();
+ m_handler.getInstanceManager().register(index, this);
+ }
+
+ /**
+ * Stop the current dependency.
+ * @see org.apache.felix.ipojo.util.DependencyModel#stop()
+ */
+ public synchronized void stop() {
+ m_isStarted = false;
+ super.stop();
+ }
+
+ public DependencyHandler getHandler() {
+ return m_handler;
+ }
+
+ public synchronized boolean isFrozen() {
+ return m_isFrozen;
+ }
+
+ /**
+ * Unfreeze the dependency.
+ * @see org.apache.felix.ipojo.util.DependencyModel#unfreeze()
+ */
+ public synchronized void unfreeze() {
+ m_isFrozen = false;
+ }
+
+ /**
+ * Call the bind method.
+ * @param pojo : pojo instance on which calling the bind method.
+ */
+ protected void onObjectCreation(Object pojo) {
+
+ ServiceReference[] refs;
+ synchronized (this) {
+ if (!m_isStarted) { return; }
+
+ // We are notified of an instance creation, we have to freeze when the static policy is used
+ if (getBindingPolicy() == STATIC_BINDING_POLICY) {
+ m_isFrozen = true;
+ }
+
+ // Check optional case : nullable object case : do not call bind on nullable object
+ if (isOptional() && getSize() == 0) { return; }
+
+ refs = getServiceReferences(); // Stack confinement.
+ }
+
+ // This is a pretty strange case, but we don't have any service.
+ // This may happen during refresh.
+ // So we just return.
+ if (refs == null) {
+ return;
+ }
+
+ // Call bind callback.
+ for (int j = 0; m_callbacks != null && j < m_callbacks.length; j++) { // The array is constant.
+ if (m_callbacks[j].getMethodType() == DependencyCallback.BIND) {
+ if (isAggregate()) {
+ for (int i = 0; i < refs.length; i++) {
+ invokeCallback(m_callbacks[j], refs[i], pojo);
+ }
+ } else {
+ // Take the first reference.
+ invokeCallback(m_callbacks[j], refs[0], pojo);
+ }
+ }
+ }
+ }
+
+ /**
+ * Call unbind callback method.
+ * @param ref : reference to send (if accepted) to the method
+ */
+ private void callUnbindMethod(ServiceReference ref) {
+ if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) {
+ for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) {
+ if (m_callbacks[i].getMethodType() == DependencyCallback.UNBIND) {
+ invokeCallback(m_callbacks[i], ref, null); // Call on each created pojo objects.
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method calling the given callback.
+ * @param callback : callback to call.
+ * @param ref : service reference.
+ * @param pojo : pojo on which calling the callback, if null call on each created pojo objects.
+ */
+ private void invokeCallback(DependencyCallback callback, ServiceReference ref, Object pojo) {
+ try {
+ if (pojo == null) {
+ callback.call(ref, getService(ref));
+ } else {
+ callback.callOnInstance(pojo, ref, getService(ref));
+ }
+ } catch (NoSuchMethodException e) {
+ m_handler.error("The method " + callback.getMethodName() + " does not exist in the implementation class " + m_handler.getInstanceManager().getClassName(), e);
+ m_handler.getInstanceManager().stop();
+ } catch (IllegalAccessException e) {
+ m_handler.error("The method " + callback.getMethodName() + " is not accessible in the implementation class " + m_handler.getInstanceManager().getClassName(), e);
+ m_handler.getInstanceManager().stop();
+ } catch (InvocationTargetException e) {
+ m_handler.error("The method " + callback.getMethodName() + " in the implementation class " + m_handler.getInstanceManager().getClassName() + " throws an exception : " + e.getTargetException().getMessage(), e.getTargetException());
+ m_handler.getInstanceManager().stop();
+ }
+
+ }
+
+ /**
+ * Call 'modify' method with the service reference in parameter (if accepted).
+ * @param ref : the service reference of the modified service
+ */
+ private void callModifyMethod(ServiceReference ref) {
+ if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) {
+ for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) {
+ if (m_callbacks[i].getMethodType() == DependencyCallback.MODIFIED) {
+ invokeCallback(m_callbacks[i], ref, null); // Call on each created pojo objects.
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Call method with the service reference in parameter (if accepted).
+ * @param ref : the service reference of the new service
+ */
+ private void callBindMethod(ServiceReference ref) {
+ // call bind method :
+ // if (m_handler.getInstanceManager().getState() == InstanceManager.VALID) {
+ if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) {
+ for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) {
+ if (m_callbacks[i].getMethodType() == DependencyCallback.BIND) {
+ invokeCallback(m_callbacks[i], ref, null);
+ }
+ }
+ }
+ }
+
+ private Object createNullableObject() {
+ // To load the proxy we use the POJO class loader. Indeed, this classloader imports iPOJO (so can access to Nullable) and has
+ // access to the service specification.
+ try {
+ ClassLoader cl = new NullableClassLoader(
+ getHandler().getInstanceManager().getClazz().getClassLoader(),
+ getSpecification().getClassLoader());
+
+ m_nullable =
+ Proxy.newProxyInstance(cl, new Class[] {
+ getSpecification(), Nullable.class }, new NullableObject()); // NOPMD
+
+ } catch (NoClassDefFoundError e) {
+ // A NoClassDefFoundError is thrown if the specification uses a class not accessible by the actual instance.
+ // It generally comes from a missing import.
+ throw new IllegalStateException("Cannot create the Nullable object, a referenced class cannot be loaded: " + e.getMessage());
+ } catch (Throwable e) { // Catch any other exception that can occurs
+ throw new IllegalStateException(
+ "Cannot create the Nullable object, an unexpected error occurs: "
+ + e.getMessage());
+ }
+
+ return m_nullable;
+ }
+
+ /**
+ * Start the dependency.
+ */
+ public void start() {
+
+ if (isOptional() && !isAggregate()) {
+ if (m_di == null) {
+ // If nullable are supported, create the nullable object.
+ if (m_supportNullable) {
+ createNullableObject();
+ }
+ } else {
+ // Create the default-implementation object.
+ try {
+ Class clazz = getHandler().getInstanceManager().getContext().getBundle().loadClass(m_di);
+ m_nullable = clazz.newInstance();
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("Cannot load the default-implementation " + m_di + " : " + e.getMessage());
+ } catch (InstantiationException e) {
+ throw new IllegalStateException("Cannot load the default-implementation " + m_di + " : " + e.getMessage());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Cannot load the default-implementation " + m_di + " : " + e.getMessage());
+ } catch (Throwable e) { // Catch any other exception
+ throw new IllegalStateException("Cannot load the default-implementation (unexpected exception) " + m_di + " : " + e.getMessage());
+ }
+ }
+ }
+
+ if (m_isProxy) {
+ if (isAggregate()) {
+ m_proxyObject = new ServiceCollection(this);
+ } else {
+ // Can we really proxy ? We can proxy only interfaces.
+ if (getSpecification().isInterface()) {
+ String type = getHandler().getInstanceManager().getContext().getProperty(DependencyHandler.PROXY_TYPE_PROPERTY);
+ if (type == null || type.equals(DependencyHandler.SMART_PROXY)) {
+ SmartProxyFactory proxyFactory = new SmartProxyFactory(this.getClass().getClassLoader());
+ m_proxyObject = proxyFactory.getProxy(getSpecification(), this);
+ } else {
+ DynamicProxyFactory proxyFactory = new DynamicProxyFactory();
+ m_proxyObject = proxyFactory.getProxy(getSpecification());
+ }
+ } else {
+ m_handler.warn("Cannot create a proxy for a service dependency which is not an interface " +
+ "- disabling proxy for " + getId());
+ }
+ }
+ }
+
+ super.start();
+ // Once the dependency is started, access to fields must be synchronized.
+ synchronized (this) {
+ if (getBindingPolicy() == STATIC_BINDING_POLICY && m_handler.getInstanceManager().getPojoObjects() != null) {
+ m_isFrozen = true;
+ }
+
+ m_isStarted = true;
+ }
+
+
+ }
+
+ protected DependencyCallback[] getCallbacks() {
+ return m_callbacks;
+ }
+
+ /**
+ * Set that this dependency is a service level dependency.
+ * This forces the scoping policy to be STRICT.
+ */
+ public void setServiceLevelDependency() {
+ m_isServiceLevelRequirement = true;
+ setBundleContext(new PolicyServiceContext(m_handler.getInstanceManager().getGlobalContext(), m_handler.getInstanceManager().getLocalServiceContext(), PolicyServiceContext.LOCAL));
+ }
+
+ public String getId() {
+ return m_id;
+ }
+
+ public boolean isServiceLevelRequirement() {
+ return m_isServiceLevelRequirement;
+ }
+
+
+ /**
+ * A new service has to be injected.
+ * @param reference : the new matching service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference reference) {
+ callBindMethod(reference);
+ //The method is only called when a new service arrives, or when the used one is replaced.
+ }
+
+ /**
+ * An already injected service is modified.
+ * @param reference : the modified service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceModification(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceModification(ServiceReference reference) {
+ callModifyMethod(reference);
+ }
+
+ /**
+ * A used (already injected) service disappears.
+ * @param ref : leaving service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceDeparture(ServiceReference ref) {
+ callUnbindMethod(ref);
+ }
+
+ /**
+ * The dependency has been reconfigured.
+ * Call unbind method and then bind methods. If the dependency cache is not reset,
+ * the thread continues to get older services.
+ * @param departs : no more matching services.
+ * @param arrivals : new services
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ for (int i = 0; departs != null && i < departs.length; i++) {
+ callUnbindMethod(departs[i]);
+ }
+
+ for (int i = 0; arrivals != null && i < arrivals.length; i++) {
+ callBindMethod(arrivals[i]);
+ }
+ }
+
+ /**
+ * Reset the thread local cache if used.
+ */
+ public void resetLocalCache() {
+ if (m_usage != null) {
+ Usage usage = (Usage) m_usage.get();
+ if (usage.m_stack > 0) {
+ createServiceObject(usage);
+ }
+ }
+ }
+
+ /**
+ * Get the used service references list.
+ * @return the used service reference or null if no service reference are available.
+ */
+ public List getServiceReferencesAsList() {
+ ServiceReference[] refs = super.getServiceReferences();
+ if (refs == null) {
+ return null;
+ } else {
+ return Arrays.asList(refs);
+ }
+ }
+
+ /**
+ * Called by the proxy to get service objects to delegate a method.
+ * On aggregate dependencies, it returns a list.
+ * @return a service object or a nullable/default-implementation object.
+ * For aggregate dependencies it returns a list or an empty list.
+ */
+ public Object getService() {
+ // Check that we're in proxy mode.
+ if (! m_isProxy) {
+ throw new IllegalStateException("The dependency is not a proxied dependency");
+ }
+
+ Usage usage = (Usage) m_usage.get();
+ if (usage.m_stack == 0) { // uninitialized usage.
+ if (usage.m_componentStack > 0) {
+ // We comes from the component who didn't touch the service.
+ // So we initialize the usage.
+ createServiceObject(usage);
+ usage.inc(); // Start the caching, so set the stack level to 1
+ m_usage.set(usage);
+ if (isAggregate()) {
+ Object obj = usage.m_object;
+ if (obj instanceof Set) {
+ List list = new ArrayList();
+ list.addAll((Set) obj);
+ return list;
+ } else {
+ // We already have a list
+ return obj;
+ }
+ } else {
+ return usage.m_object;
+ }
+ } else {
+ // External access => Immediate get.
+ if (isAggregate()) {
+ ServiceReference[] refs = getServiceReferences();
+ if (refs == null) {
+ return new ArrayList(0); // Create an empty list.
+ } else {
+ List objs = new ArrayList(refs.length);
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ ServiceReference ref = refs[i];
+ objs.add(getService(ref));
+ }
+ return objs;
+ }
+ } else { // Scalar dependency.
+ ServiceReference ref = getServiceReference();
+ if (ref != null) {
+ return getService(ref);
+ } else {
+ // No service available.
+ // TODO Decide what we have to do.
+ throw new RuntimeException("Service " + getSpecification() + " unavailable");
+ }
+ }
+ }
+ } else {
+ // Use the copy.
+ // if the copy is a set, transform to a list
+ if (isAggregate()) {
+ Object obj = usage.m_object;
+ if (obj instanceof Set) {
+ List list = new ArrayList();
+ list.addAll((Set) obj);
+ return list;
+ } else {
+ // We already have a list
+ return obj;
+ }
+ } else {
+ return usage.m_object;
+ }
+
+ }
+ }
+
+ /**
+ * This method is called by the replaced code in the component
+ * implementation class. Construct the service object list is necessary.
+ * @param pojo : POJO object.
+ * @param fieldName : field
+ * @param value : last value.
+ * @return the service object or a nullable / default implementation if defined.
+ * @see org.apache.felix.ipojo.FieldInterceptor#onGet(java.lang.Object, java.lang.String, java.lang.Object)
+ */
+ public Object onGet(Object pojo, String fieldName, Object value) {
+
+ // Initialize the thread local object is not already touched.
+ Usage usage = (Usage) m_usage.get();
+ if (usage.m_stack == 0) { // uninitialized usage.
+ createServiceObject(usage);
+ usage.inc(); // Start the caching, so set the stack level to 1
+ m_usage.set(usage);
+ }
+ if (! m_isProxy) {
+ return usage.m_object;
+ } else {
+ return m_proxyObject;
+ }
+
+ }
+
+
+ /**
+ * Creates the object to store in the given Thread Local.
+ * This object will be injected inside the POJO field.
+ * @param usage : Thread Local to populate.
+ */
+ private void createServiceObject(Usage usage) {
+ ServiceReference[] refs = getServiceReferences();
+ if (! isAggregate()) {
+ if (refs == null) {
+ if (m_nullable == null && m_supportNullable) {
+ m_handler.warn("[" + m_handler.getInstanceManager().getInstanceName() + "] The dependency is not optional, however no service object can be injected in " + m_field + " -> " + getSpecification().getName());
+ createNullableObject();
+ }
+ usage.m_object = m_nullable; // Add null if the Nullable pattern is disable.
+ } else {
+ ServiceReference ref = getServiceReference();
+ usage.m_object = getService(ref);
+ }
+ } else {
+ if (m_type == 0) { // Array
+ try {
+ if (refs == null) {
+ usage.m_object = (Object[]) Array.newInstance(getSpecification(), 0); // Create an empty array.
+ } else {
+ // Use a reflective construction to avoid class cast exception. This method allows setting the component type.
+ Object[] objs = (Object[]) Array.newInstance(getSpecification(), refs.length);
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ ServiceReference ref = refs[i];
+ objs[i] = getService(ref);
+ }
+ usage.m_object = objs;
+ }
+ } catch (ArrayStoreException e) {
+ m_handler.error("Cannot create the array - Check that the bundle can access the service interface", e);
+ throw new RuntimeException("Cannot create the array - Check that the bundle can access the service interface : " + e.getMessage());
+ }
+ } else if (m_type == DependencyHandler.LIST) {
+ if (refs == null) {
+ usage.m_object = new ArrayList(0); // Create an empty list.
+ } else {
+ // Use a list to store service objects
+ List objs = new ArrayList(refs.length);
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ ServiceReference ref = refs[i];
+ objs.add(getService(ref));
+ }
+ usage.m_object = objs;
+ }
+ } else if (m_type == DependencyHandler.VECTOR) {
+ if (refs == null) {
+ usage.m_object = new Vector(0); // Create an empty vector.
+ } else {
+ // Use a vector to store service objects
+ Vector objs = new Vector(refs.length);
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ ServiceReference ref = refs[i];
+ objs.add(getService(ref));
+ }
+ usage.m_object = objs;
+ }
+ } else if (m_type == DependencyHandler.SET) {
+ if (refs == null) {
+ usage.m_object = new HashSet(0); // Create an empty vector.
+ } else {
+ // Use a vector to store service objects
+ Set objs = new HashSet(refs.length);
+ for (int i = 0; refs != null && i < refs.length; i++) {
+ ServiceReference ref = refs[i];
+ objs.add(getService(ref));
+ }
+ usage.m_object = objs;
+ }
+ }
+ }
+ }
+
+ /**
+ * The field was set.
+ * This method should not be call if the POJO is written correctly.
+ * @param pojo : POJO object
+ * @param fieldName : field name
+ * @param value : set value.
+ * @see org.apache.felix.ipojo.FieldInterceptor#onSet(java.lang.Object, java.lang.String, java.lang.Object)
+ */
+ public void onSet(Object pojo, String fieldName, Object value) {
+ // Nothing to do.
+ }
+
+ /**
+ * A POJO method will be invoked.
+ * @param pojo : Pojo object
+ * @param method : called method
+ * @param args : arguments
+ * @see org.apache.felix.ipojo.MethodInterceptor#onEntry(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public void onEntry(Object pojo, Member method, Object[] args) {
+ if (m_usage != null) {
+ Usage usage = (Usage) m_usage.get();
+ usage.incComponentStack(); // Increment the number of component access.
+ if (usage.m_stack > 0) {
+ usage.inc();
+ m_usage.set(usage); // Set the Thread local as value has been modified
+ }
+ }
+ }
+
+ /**
+ * A POJO method has thrown an error.
+ * This method does nothing and wait for the finally.
+ * @param pojo : POJO object.
+ * @param method : Method object.
+ * @param throwable : thrown error
+ * @see org.apache.felix.ipojo.MethodInterceptor#onError(java.lang.Object, java.lang.reflect.Method, java.lang.Throwable)
+ */
+ public void onError(Object pojo, Member method, Throwable throwable) {
+ // Nothing to do : wait onFinally
+ }
+
+ /**
+ * A POJO method has returned.
+ * @param pojo : POJO object.
+ * @param method : Method object.
+ * @param returnedObj : returned object (null for void method)
+ * @see org.apache.felix.ipojo.MethodInterceptor#onExit(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
+ */
+ public void onExit(Object pojo, Member method, Object returnedObj) {
+ // Nothing to do : wait onFinally
+ }
+
+ /**
+ * A POJO method is finished.
+ * @param pojo : POJO object.
+ * @param method : Method object.
+ * @see org.apache.felix.ipojo.MethodInterceptor#onFinally(java.lang.Object, java.lang.reflect.Method)
+ */
+ public void onFinally(Object pojo, Member method) {
+ if (m_usage != null) {
+ Usage usage = (Usage) m_usage.get();
+ usage.decComponentStack();
+ if (usage.m_stack > 0) {
+ if (usage.dec()) {
+ // Exit the method flow => Release all objects
+ usage.clear();
+ m_usage.set(usage); // Set the Thread local as value has been modified
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets true if the dependency use Nullable objects.
+ * @return true if the dependency is optional and supports nullable objects.
+ */
+ public boolean supportsNullable() {
+ return m_supportNullable;
+ }
+
+ public String getDefaultImplementation() {
+ return m_di;
+ }
+
+ public boolean isProxy() {
+ return m_isProxy;
+ }
+
+ public void setProxy(boolean proxy) {
+ m_isProxy = proxy;
+ }
+
+ /**
+ * Set the type to inject.
+ * This method set the dependency as aggregate.
+ * @param type either list of vector
+ */
+ protected void setType(int type) {
+ setAggregate(true);
+ m_type = type;
+ }
+
+ /**
+ * Classloader for nullable objects.
+ */
+ private class NullableClassLoader extends ClassLoader {
+ /**
+ * Component classloader.
+ */
+ private ClassLoader m_component;
+
+ /**
+ * Specification classloader.
+ */
+ private ClassLoader m_specification;
+
+ /**
+ * Creates a NullableClassLoader.
+ * @param cmp the component class loader.
+ * @param spec the specification class loader.
+ */
+ public NullableClassLoader(ClassLoader cmp, ClassLoader spec) {
+ m_component = cmp;
+ m_specification = spec;
+ }
+
+ /**
+ * Loads the given class.
+ * This method uses the classloader of the component class
+ * and (if not found) the specification classloader.
+ * @param name the class name
+ * @return the class object
+ * @throws ClassNotFoundException if the class is not found by the two classloaders.
+ * @see java.lang.ClassLoader#loadClass(java.lang.String)
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ try {
+ return m_component.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return m_specification.loadClass(name);
+ }
+ }
+
+
+ }
+
+ /**
+ * Creates smart proxy object for proxied scalar dependencies.
+ */
+ private class SmartProxyFactory extends ClassLoader {
+
+ /**
+ * Handler classloader, used to load the temporal dependency class.
+ */
+ private ClassLoader m_handlerCL;
+
+ /**
+ * Creates the proxy classloader.
+ * @param parent the handler classloader.
+ */
+ public SmartProxyFactory(ClassLoader parent) {
+ super(getHandler().getInstanceManager().getFactory().getBundleClassLoader());
+ m_handlerCL = parent;
+ }
+
+ /**
+ * Loads a proxy class generated for the given (interface) class.
+ * @param clazz the service specification to proxy
+ * @return the Class object of the proxy.
+ */
+ protected Class getProxyClass(Class clazz) {
+ byte[] clz = ProxyGenerator.dumpProxy(clazz); // Generate the proxy.
+ // Turn around the VM changes (FELIX-2716) about java.* classes.
+ String cn = clazz.getName();
+ if (cn.startsWith("java.")) {
+ cn = "$" + cn;
+ }
+ return defineClass(cn + "$$Proxy", clz, 0, clz.length);
+ }
+
+ /**
+ * Create a proxy object for the given specification. The proxy
+ * uses the given dependency to get the service object.
+ * @param spec the service specification (interface)
+ * @param dep the dependency used to get the service
+ * @return the proxy object.
+ */
+ public Object getProxy(Class spec, Dependency dep) {
+ try {
+ Class clazz = getProxyClass(getSpecification());
+ Constructor constructor = clazz.getConstructor(
+ new Class[]{clazz.getClassLoader().loadClass(Dependency.class.getName())});
+ return constructor.newInstance(new Object[] {dep});
+ } catch (Throwable e) {
+ m_handler.error("Cannot create the proxy object", e);
+ m_handler.getInstanceManager().stop();
+ return null;
+ }
+ }
+
+ /**
+ * Loads the given class.
+ * This method uses the classloader of the specification class
+ * or the handler class loader.
+ * @param name the class name
+ * @return the class object
+ * @throws ClassNotFoundException if the class is not found by the two classloaders.
+ * @see java.lang.ClassLoader#loadClass(java.lang.String)
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ try {
+ return getHandler().getInstanceManager().getContext().getBundle().loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return m_handlerCL.loadClass(name);
+ }
+ }
+ }
+
+ /**
+ * Creates java dynamic proxy object for proxied scalar dependencies.
+ */
+ private class DynamicProxyFactory implements InvocationHandler {
+
+ /**
+ * HashCode method.
+ */
+ private Method m_hashCodeMethod;
+
+ /**
+ * Equals method.
+ */
+ private Method m_equalsMethod;
+
+ /**
+ * toStirng method.
+ */
+ private Method m_toStringMethod;
+
+ /**
+ * Creates a DynamicProxyFactory.
+ */
+ public DynamicProxyFactory() {
+ try {
+ m_hashCodeMethod = Object.class.getMethod("hashCode", null);
+ m_equalsMethod = Object.class
+ .getMethod("equals", new Class[] { Object.class });
+ m_toStringMethod = Object.class.getMethod("toString", null);
+ } catch (NoSuchMethodException e) {
+ throw new NoSuchMethodError(e.getMessage());
+ }
+ }
+
+ /**
+ * Creates a proxy object for the given specification. The proxy
+ * uses the given dependency to get the service object.
+ * @param spec the service specification (interface)
+ * @return the proxy object.
+ */
+ public Object getProxy(Class spec) {
+ return java.lang.reflect.Proxy.newProxyInstance(
+ getHandler().getInstanceManager().getClazz().getClassLoader(),
+ new Class[] {spec},
+ this);
+ }
+
+ /**
+ * Invocation Handler delegating invocation on the
+ * service object.
+ * @param proxy the proxy object
+ * @param method the method
+ * @param args the arguments
+ * @return a proxy object.
+ * @throws Exception if the invocation throws an exception
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
+ Object svc = getService();
+ Class declaringClass = method.getDeclaringClass();
+ if (declaringClass == Object.class) {
+ if (method.equals(m_hashCodeMethod)) {
+ return new Integer(this.hashCode());
+ } else if (method.equals(m_equalsMethod)) {
+ return proxy == args[0] ? Boolean.TRUE : Boolean.FALSE;
+ } else if (method.equals(m_toStringMethod)) {
+ return this.toString();
+ } else {
+ throw new InternalError(
+ "Unexpected Object method dispatched: " + method);
+ }
+ }
+
+ return method.invoke(svc, args);
+ }
+
+ }
+
+ /**
+ * Gets the constructor parameter.
+ * @return the index of the constructor parameter,
+ * or <code>-1</code> if not set.
+ */
+ public int getConstructorParameterIndex() {
+ return m_index;
+ }
+
+ /**
+ * Gets the object to inject in the constructor parameter.
+ * @param index the index of the parameter
+ * @return the created proxy object
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameter(int)
+ */
+ public Object getConstructorParameter(int index) {
+ if (m_index == index && m_proxyObject != null) {
+ return m_proxyObject;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the type of the constructor parameter.
+ * @param index the parameter index
+ * @return the class of the object. For scalar dependency, it's the
+ * specification, for aggregate it depends of the container object:
+ * {@link List} or {@link Set}.
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameterType(int)
+ */
+ public Class getConstructorParameterType(int index) {
+ if (m_index == index && m_proxyObject != null) {
+ if (isAggregate()) {
+ switch (m_type) {
+ case DependencyHandler.LIST: return List.class;
+ case DependencyHandler.SET : return Set.class;
+ //TODO We should also manage the Collection type.
+ default: return null; // Should never happen, it was checked before.
+ }
+ } else {
+ return getSpecification();
+ }
+ } else {
+ return null;
+ }
+ }
+
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyCallback.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyCallback.java
new file mode 100644
index 0000000..571a92c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyCallback.java
@@ -0,0 +1,325 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.util.Callback;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class allwos the creation of callback when service dependency arrives or
+ * disappear.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyCallback extends Callback {
+
+ /**
+ * Bind method (called when a service arrives).
+ */
+ public static final int BIND = 0;
+
+ /**
+ * Unbind method (called when a service disappears).
+ */
+ public static final int UNBIND = 1;
+
+ /**
+ * Updated method (called when a service is modified).
+ */
+ public static final int MODIFIED = 2;
+
+ /**
+ * Is the method a bind method or an unbind method ?
+ */
+ private int m_methodType;
+
+ /**
+ * Arguments of the callback.
+ */
+ private String[] m_argument;
+
+ /**
+ * Callback method name.
+ */
+ private String m_method;
+
+ /**
+ * Service Dependency.
+ */
+ private Dependency m_dependency;
+
+ /**
+ * Constructor.
+ *
+ * @param dep : the dependency attached to this dependency callback
+ * @param method : the method to call
+ * @param methodType : is the method to call a bind method or an unbind
+ * method
+ */
+ public DependencyCallback(Dependency dep, String method, int methodType) {
+ super(method, (String[]) null, false, dep.getHandler().getInstanceManager());
+ m_methodType = methodType;
+ m_dependency = dep;
+ m_method = method;
+ }
+
+
+ public int getMethodType() {
+ return m_methodType;
+ }
+
+ public String getMethodName() {
+ return m_method;
+ }
+
+ /**
+ * Set the argument type (Empty or the class name).
+ * @param arg : the array of argument types.
+ */
+ public void setArgument(String[] arg) {
+ m_argument = arg;
+ }
+
+ /**
+ * Search the method object in the POJO by analyzing present method.
+ * If not found in the pojo it tests the parent classes.
+ * The name of the method and the argument type are checked.
+ */
+ protected void searchMethod() {
+ if (m_argument != null) {
+ Method[] methods = m_dependency.getHandler().getInstanceManager().getClazz().getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ // First check the method name
+ if (methods[i].getName().equals(m_method)) {
+ // Check arguments
+ Class[] clazzes = methods[i].getParameterTypes();
+ if (clazzes.length == m_argument.length) { // Test size to avoid useless loop // NOPMD
+ int argIndex = 0;
+ for (; argIndex < m_argument.length; argIndex++) {
+ if (!m_argument[argIndex].equals(clazzes[argIndex].getName())) {
+ break;
+ }
+ }
+ if (argIndex == m_argument.length) { // If the array was completely read.
+ m_methodObj = methods[i]; // It is the looked method.
+ if (! m_methodObj.isAccessible()) {
+ // If not accessible, try to set the accessibility.
+ m_methodObj.setAccessible(true);
+ }
+ return;
+ }
+ }
+
+ }
+ }
+ }
+
+ // Not found => Try parent method.
+ searchParentMethod();
+
+ if (m_methodObj == null) {
+ // If not found, stop the instance (fatal error)
+ m_dependency.getHandler().error("The method " + m_method + " cannot be called : method not found");
+ m_dependency.getHandler().getInstanceManager().stop();
+ } else {
+ if (! m_methodObj.isAccessible()) {
+ // If not accessible, try to set the accessibility.
+ m_methodObj.setAccessible(true);
+ }
+ }
+ }
+
+ /**
+ * Introspect parent class to find the method.
+ */
+ private void searchParentMethod() {
+ // look at parent classes
+ Method[] methods = m_dependency.getHandler().getInstanceManager().getClazz().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ // First check the method name
+ if (methods[i].getName().equals(m_method)) {
+ // Check arguments
+ Class[] clazzes = methods[i].getParameterTypes();
+ switch (clazzes.length) {
+ case 0:
+ // Callback with no arguments.
+ m_methodObj = methods[i];
+ m_argument = new String[0];
+ return;
+ case 1:
+ // The callback receives a ServiceReference
+ if (clazzes[0].getName().equals(ServiceReference.class.getName())) {
+ // Callback with a service reference.
+ m_methodObj = methods[i];
+ m_argument = new String[] { ServiceReference.class.getName() };
+ return;
+ }
+ // The callback receives a Service object
+ if (clazzes[0].getName().equals(m_dependency.getSpecification().getName())) {
+ // Callback with the service object.
+ m_methodObj = methods[i];
+ m_argument = new String[] { m_dependency.getSpecification().getName() };
+ return;
+ }
+ break;
+ case 2:
+ // The callback receives the service object and the service reference
+ if (clazzes[0].getName().equals(m_dependency.getSpecification().getName()) && clazzes[1].getName().equals(ServiceReference.class.getName())) {
+ // Callback with two arguments.
+ m_methodObj = methods[i];
+ m_argument = new String[] { m_dependency.getSpecification().getName(), ServiceReference.class.getName() };
+ return;
+ }
+ // The callback receives the service object and the service properties (in a Map)
+ if (clazzes[0].getName().equals(m_dependency.getSpecification().getName()) && clazzes[1].getName().equals(Map.class.getName())) {
+ // Callback with two arguments.
+ m_methodObj = methods[i];
+ m_argument = new String[] { m_dependency.getSpecification().getName(), Map.class.getName() };
+ return;
+ }
+ // The callback receives the service object and the service properties (in a Dictionary)
+ if (clazzes[0].getName().equals(m_dependency.getSpecification().getName()) && clazzes[1].getName().equals(Dictionary.class.getName())) {
+ // Callback with two arguments.
+ m_methodObj = methods[i];
+ m_argument = new String[] { m_dependency.getSpecification().getName(), Dictionary.class.getName() };
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Call the callback method with a service reference.
+ *
+ * @param ref : the service reference to send to the method
+ * @param obj : the service object
+ * @throws NoSuchMethodException : Method is not found in the class
+ * @throws InvocationTargetException : The method is not static
+ * @throws IllegalAccessException : The method can not be invoked
+ */
+ protected void call(ServiceReference ref, Object obj) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ if (m_methodObj == null) {
+ searchMethod();
+ }
+ switch (m_argument.length) {
+ case 0 :
+ call(new Object[0]);
+ break;
+ case 1 :
+ if (m_argument[0].equals(ServiceReference.class.getName())) {
+ call(new Object[] {ref});
+ } else {
+ call(new Object[] {obj});
+ }
+ break;
+ case 2 :
+ if (m_argument[1].equals(ServiceReference.class.getName())) {
+ call(new Object[] {obj, ref});
+ } else if (m_argument[1].equals(Dictionary.class.getName())) {
+ call(new Object[] {obj, getPropertiesInDictionary(ref)});
+ } else {
+ call(new Object[] {obj, getPropertiesInMap(ref)});
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
+ /**
+ * Creates a {@link Dictionary} containing service properties of the
+ * given service reference.
+ * @param ref the service reference
+ * @return a {@link Dictionary} containing the service properties.
+ */
+ private Dictionary getPropertiesInDictionary(ServiceReference ref) {
+ String[] keys = ref.getPropertyKeys(); // Can't be null
+ Dictionary dict = new Properties();
+ for (int i = 0; i < keys.length; i++) {
+ dict.put(keys[i], ref.getProperty(keys[i]));
+ }
+ return dict;
+ }
+
+ /**
+ * Creates a {@link Map} containing service properties of the
+ * given service reference.
+ * @param ref the service reference
+ * @return a {@link Map} containing the service properties.
+ */
+ private Map getPropertiesInMap(ServiceReference ref) {
+ String[] keys = ref.getPropertyKeys(); // Can't be null
+ Map map = new HashMap();
+ for (int i = 0; i < keys.length; i++) {
+ map.put(keys[i], ref.getProperty(keys[i]));
+ }
+ return map;
+ }
+
+
+ /**
+ * Call the callback on the given instance with the given argument.
+ *
+ * @param instance : the instance on which call the callback
+ * @param ref : the service reference to send to the callback
+ * @param obj : the service object
+ * @throws NoSuchMethodException : the method is not found
+ * @throws IllegalAccessException : the method could not be called
+ * @throws InvocationTargetException : an error happens in the called method
+ */
+ protected void callOnInstance(Object instance, ServiceReference ref, Object obj) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ if (m_methodObj == null) {
+ searchMethod();
+ }
+ switch (m_argument.length) {
+ case 0 :
+ call(instance, new Object[0]);
+ break;
+ case 1 :
+ if (m_argument[0].equals(ServiceReference.class.getName())) {
+ call(instance, new Object[] {ref});
+ } else {
+ call(instance, new Object[] {obj});
+ }
+ break;
+ case 2 :
+ if (m_argument[1].equals(ServiceReference.class.getName())) {
+ call(instance, new Object[] {obj, ref});
+ } else if (m_argument[1].equals(Dictionary.class.getName())) {
+ call(instance, new Object[] {obj, getPropertiesInDictionary(ref)});
+ } else {
+ call(instance, new Object[] {obj, getPropertiesInMap(ref)});
+ }
+ break;
+ default :
+ break;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java
new file mode 100644
index 0000000..c4c4531
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java
@@ -0,0 +1,146 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Service Dependency Description.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyDescription {
+ /**
+ * The described dependency.
+ */
+ private Dependency m_dependency;
+
+ /**
+ * Creates a dependency description.
+ * @param dep the described dependency
+ */
+ public DependencyDescription(Dependency dep) {
+ m_dependency = dep;
+ }
+
+ public boolean isMultiple() { return m_dependency.isAggregate(); }
+
+ public boolean isOptional() { return m_dependency.isOptional(); }
+
+ public String getFilter() { return m_dependency.getFilter(); }
+
+ public String getInterface() { return m_dependency.getSpecification().getName(); }
+
+ public int getState() { return m_dependency.getState(); }
+
+ public String getId() { return m_dependency.getId(); }
+
+ public boolean isProxy() { return m_dependency.isProxy(); }
+
+
+ /**
+ * Gets <code>true</code> if the dependency uses Nullable objects.
+ * @return true if the dependency is optional and supports nullable object.
+ */
+ public boolean supportsNullable() { return m_dependency.supportsNullable(); }
+
+ public String getDefaultImplementation() { return m_dependency.getDefaultImplementation(); }
+
+ public int getPolicy() { return m_dependency.getBindingPolicy(); }
+
+ public String getComparator() { return m_dependency.getComparator(); }
+
+ public boolean isFrozen() { return m_dependency.isFrozen(); }
+
+ /**
+ * Gets the service reference list.
+ * @return the list of matching service reference,
+ * <code>null</code> if no service reference.
+ */
+ public List getServiceReferences() { return m_dependency.getServiceReferencesAsList(); }
+
+ /**
+ * Gets the service reference if only one service reference is used.
+ * @return the ServiceReference (only if the cardinality could be 1),
+ * or <code>null</code> if no service reference.
+ */
+ public ServiceReference getServiceReference() {
+ List list = getServiceReferences();
+ if (list == null) {
+ return null;
+ } else {
+ return (ServiceReference) list.get(0);
+ }
+ }
+
+ /**
+ * Gets the used service set.
+ * @return the list [service reference] containing the used services,
+ * <code>null</code> if no providers are used
+ */
+ public List getUsedServices() { return m_dependency.getUsedServiceReferences(); }
+
+ /**
+ * Sets the dependency comparator.
+ * The reference set will be sort at the next usage.
+ * @param cmp the comparator
+ */
+ public void setComparator(Comparator cmp) {
+ m_dependency.setComparator(cmp);
+ }
+
+ /**
+ * Sets the dependency filter.
+ * @param filter the new LDAP filter
+ */
+ public void setFilter(Filter filter) {
+ m_dependency.setFilter(filter);
+ }
+
+ /**
+ * Sets the dependency cardinality.
+ * @param isAgg if <code>true</code> sets the dependency to aggregate,
+ * if <code>false</code> sets the dependency to scalar.
+ */
+ public void setAggregate(boolean isAgg) {
+ m_dependency.setAggregate(isAgg);
+ }
+
+ /**
+ * Sets the dependency optionality.
+ * @param isOpt if <code>true</code> sets the dependency to optional,
+ * if <code>false</code> sets the dependency to mandatory.
+ */
+ public void setOptional(boolean isOpt) {
+ m_dependency.setOptionality(isOpt);
+ }
+
+ /**
+ * Gets the required service specification name.
+ * @return the required service specification class name.
+ */
+ public String getSpecification() {
+ return m_dependency.getSpecification().getName();
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
new file mode 100644
index 0000000..a9e3eec
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -0,0 +1,656 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The dependency handler manages a list of service dependencies.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyHandler extends PrimitiveHandler implements DependencyStateListener {
+
+ /**
+ * Proxy settings property.
+ */
+ public static final String PROXY_SETTINGS_PROPERTY = "ipojo.proxy";
+
+ /**
+ * Proxy type property.
+ */
+ public static final String PROXY_TYPE_PROPERTY = "ipojo.proxy.type";
+
+ /**
+ * Proxy type value: smart.
+ */
+ public static final String SMART_PROXY = "smart";
+
+ /**
+ * Proxy type value: dynamic-proxy.
+ */
+ public static final String DYNAMIC_PROXY = "dynamic-proxy";
+
+ /**
+ * Proxy settings value: enabled.
+ */
+ public static final String PROXY_ENABLED = "enabled";
+
+ /**
+ * Proxy settings value: disabled.
+ */
+ public static final String PROXY_DISABLED = "disabled";
+
+ /**
+ * Dependency field type : Vector
+ * The dependency will be injected as a vector.
+ */
+ protected static final int VECTOR = 2;
+
+ /**
+ * Dependency Field Type : List.
+ * The dependency will be injected as a list.
+ */
+ protected static final int LIST = 1;
+
+ /**
+ * Dependency Field Type : Set.
+ * The dependency will be injected as a set.
+ */
+ protected static final int SET = 3;
+
+ /**
+ * List of dependencies of the component.
+ */
+ private Dependency[] m_dependencies = new Dependency[0];
+
+ /**
+ * Is the handler started.
+ */
+ private boolean m_started;
+
+ /**
+ * The handler description.
+ */
+ private DependencyHandlerDescription m_description;
+
+ /**
+ * Add a dependency.
+ * @param dep : the dependency to add
+ */
+ private void addDependency(Dependency dep) {
+ for (int i = 0; m_dependencies != null && i < m_dependencies.length; i++) {
+ if (m_dependencies[i] == dep) {
+ return;
+ }
+ }
+ if (m_dependencies == null) {
+ m_dependencies = new Dependency[] { dep };
+ } else {
+ Dependency[] newDep = new Dependency[m_dependencies.length + 1];
+ System.arraycopy(m_dependencies, 0, newDep, 0, m_dependencies.length);
+ newDep[m_dependencies.length] = dep;
+ m_dependencies = newDep;
+ }
+ }
+
+ /**
+ * Get the list of managed dependency.
+ * @return the dependency list
+ */
+ public Dependency[] getDependencies() {
+ return m_dependencies;
+ }
+
+ /**
+ * Validate method. This method is invoked by an AbstractServiceDependency when this dependency becomes RESOLVED.
+ * @param dep : the dependency becoming RESOLVED.
+ * @see org.apache.felix.ipojo.util.DependencyStateListener#validate(org.apache.felix.ipojo.util.DependencyModel)
+ */
+ public void validate(DependencyModel dep) {
+ checkContext();
+ }
+
+ /**
+ * Invalidate method. This method is invoked by an AbstractServiceDependency when this dependency becomes UNRESOLVED or BROKEN.
+ * @param dep : the dependency becoming UNRESOLVED or BROKEN.
+ * @see org.apache.felix.ipojo.util.DependencyStateListener#invalidate(org.apache.felix.ipojo.util.DependencyModel)
+ */
+ public void invalidate(DependencyModel dep) {
+ setValidity(false);
+ }
+
+ /**
+ * Check the validity of the dependencies.
+ */
+ protected void checkContext() {
+ if (!m_started) {
+ return;
+ }
+ synchronized (m_dependencies) {
+ // Store the initial state
+ boolean initialState = getValidity();
+
+ boolean valid = true;
+ for (int i = 0; i < m_dependencies.length; i++) {
+ Dependency dep = m_dependencies[i];
+ if (dep.getState() != Dependency.RESOLVED) {
+ valid = false;
+ break;
+ }
+ }
+
+ // Check the component dependencies
+ if (valid) {
+ // The dependencies are valid
+ if (!initialState) {
+ // There is a state change
+ setValidity(true);
+ }
+ // Else do nothing, the component state stay VALID
+ } else {
+ // The dependencies are not valid
+ if (initialState) {
+ // There is a state change
+ setValidity(false);
+ }
+ // Else do nothing, the component state stay UNRESOLVED
+ }
+
+ }
+ }
+
+ /**
+ * Check if the dependency given is valid in the sense that metadata are consistent.
+ * @param dep : the dependency to check
+ * @param manipulation : the component-type manipulation metadata
+ * @return true if the dependency is valid
+ * @throws ConfigurationException : the checked dependency is not correct
+ */
+ private boolean checkDependency(Dependency dep, PojoMetadata manipulation) throws ConfigurationException {
+ // Check the internal type of dependency
+ String field = dep.getField();
+ DependencyCallback[] callbacks = dep.getCallbacks();
+ int index = dep.getConstructorParameterIndex();
+
+ if (callbacks == null && field == null && index == -1) {
+ throw new ConfigurationException("A service requirement requires at least binding methods, " +
+ "a field or a constructor parameter");
+ }
+
+ for (int i = 0; callbacks != null && i < callbacks.length; i++) {
+ MethodMetadata[] mets = manipulation.getMethods(callbacks[i].getMethodName());
+ if (mets.length == 0) {
+ debug("A requirement callback " + callbacks[i].getMethodName() + " does not exist in the implementation class, will try the super classes");
+ } else {
+ if (mets[0].getMethodArguments().length > 2) {
+ throw new ConfigurationException("Requirement Callback : A requirement callback "
+ + callbacks[i].getMethodName()
+ + " must have 0, 1 or 2 arguments");
+ }
+
+ callbacks[i].setArgument(mets[0].getMethodArguments());
+
+ if (mets[0].getMethodArguments().length == 1) {
+ if (!mets[0].getMethodArguments()[0].equals(ServiceReference.class.getName())) {
+ // The callback receives the service object.
+ setSpecification(dep, mets[0].getMethodArguments()[0], false); // Just warn if a mismatch is discovered.
+ }
+ } else if (mets[0].getMethodArguments().length == 2) {
+ // The callback receives service object, service reference. Check that the second argument is a service reference
+ if (!(mets[0].getMethodArguments()[1].equals(ServiceReference.class.getName()) // callback with (service object, service reference)
+ || mets[0].getMethodArguments()[1].equals(Dictionary.class.getName()) // callback with (service object, service properties in a dictionary)
+ || mets[0].getMethodArguments()[1].equals(Map.class.getName()))) { // callback with (service object, service properties in a map)
+ String message =
+ "The requirement callback " + callbacks[i].getMethodName() + " must have a ServiceReference, a Dictionary or a Map as the second argument";
+ throw new ConfigurationException(message);
+ }
+ setSpecification(dep, mets[0].getMethodArguments()[0], false); // Just warn if a mismatch is discovered.
+ }
+ }
+
+ }
+
+ if (field != null) {
+ FieldMetadata meta = manipulation.getField(field);
+ if (meta == null) {
+ throw new ConfigurationException("Requirement Callback : A requirement field "
+ + field
+ + " does not exist in the implementation class");
+ }
+ String type = meta.getFieldType();
+ if (type.endsWith("[]")) {
+ if (dep.isProxy()) {
+ info("Arrays cannot be used for proxied dependencies - Disabling the proxy mode");
+ dep.setProxy(false);
+ }
+ // Set the dependency to multiple
+ dep.setAggregate(true);
+ type = type.substring(0, type.length() - 2);
+ } else if (type.equals(List.class.getName()) || type.equals(Collection.class.getName())) {
+ dep.setType(LIST);
+ type = null;
+ } else if (type.equals(Vector.class.getName())) {
+ dep.setType(VECTOR);
+ if (dep.isProxy()) {
+ warn("Vectors cannot be used for proxied dependencies - Disabling the proxy mode");
+ dep.setProxy(false);
+ }
+ type = null;
+ } else if (type.equals(Set.class.getName())) {
+ dep.setType(SET);
+ type = null;
+ } else {
+ if (dep.isAggregate()) {
+ throw new ConfigurationException("A required service is not correct : the field "
+ + meta.getFieldName()
+ + " must be an array to support aggregate injections");
+ }
+ }
+ setSpecification(dep, type, true); // Throws an exception if the field type mismatch.
+ }
+
+ // Constructor parameter
+ if (index != -1) {
+ if (! dep.isProxy()) {
+ throw new ConfigurationException("Services injected into constructor must be proxied");
+ }
+
+ MethodMetadata[] cts = manipulation.getConstructors();
+ // If we don't have a type, try to get the first constructor and get the type of the parameter
+ // we the index 'index'.
+ if (cts.length > 0 && cts[0].getMethodArguments().length > index) {
+ String type = cts[0].getMethodArguments()[index];
+ if (type.endsWith("[]")) {
+ throw new ConfigurationException("Services injected into constructor cannot be arrays");
+ } else if (type.equals(List.class.getName()) || type.equals(Collection.class.getName())) {
+ dep.setType(LIST);
+ type = null;
+ } else if (type.equals(Vector.class.getName())) {
+ throw new ConfigurationException("Services injected into constructor cannot be Vectors");
+ } else if (type.equals(Set.class.getName())) {
+ dep.setType(SET);
+ type = null;
+ } else {
+ if (dep.isAggregate()) {
+ throw new ConfigurationException("A required service is not correct : the constructor parameter "
+ + index
+ + " must be an aggregate type to support aggregate injections");
+ }
+ }
+ setSpecification(dep, type, true); // Throws an exception if the field type mismatch.
+ } else {
+ throw new ConfigurationException("Cannot determine the specification of the dependency " + index +
+ ", please use the specification attribute");
+ }
+ }
+
+ // Disable proxy on scalar dependency targeting non-interface specification
+ if (! dep.isAggregate() && dep.isProxy()) {
+ if (! dep.getSpecification().isInterface()) {
+ warn("Proxies cannot be used on service dependency targetting non interface " +
+ "service specification " + dep.getSpecification().getName());
+ dep.setProxy(false);
+ }
+ }
+
+ // Disables proxy on null (nullable=false)
+// if (dep.isProxy() && dep.isOptional() && ! dep.supportsNullable()) {
+// dep.setProxy(false);
+// warn("Optional Null Dependencies do not support proxying - Disable the proxy mode");
+// }
+
+ // Check that all required info are set
+ return dep.getSpecification() != null;
+ }
+
+ /**
+ * Check if we have to set the dependency specification with the given class name.
+ * @param dep : dependency to check
+ * @param className : class name
+ * @param error : set to true to throw an error if the set dependency specification and the given specification are different.
+ * @throws ConfigurationException : the specification class cannot be loaded correctly
+ */
+ private void setSpecification(Dependency dep, String className, boolean error) throws ConfigurationException {
+ if (className == null) {
+ // No found type (list and vector)
+ if (dep.getSpecification() == null) {
+ if (error) {
+ String id = dep.getId();
+ if (id == null) {
+ id = dep.getField();
+ if (id == null) {
+ id = Integer.toString(dep.getConstructorParameterIndex());
+ }
+ }
+ throw new ConfigurationException("Cannot discover the required specification for " + id);
+ } else {
+ // If the specification is different, warn that we will override it.
+ info("Cannot discover the required specification for " + dep.getField());
+ }
+ }
+ } else { // In all other case, className is not null.
+ if (dep.getSpecification() == null || !dep.getSpecification().getName().equals(className)) {
+ if (dep.getSpecification() != null) {
+ if (error) {
+ throw new ConfigurationException("A required service is not correct : the discovered type ["
+ + className
+ + "] and the specified (or already discovered) service interface ["
+ + dep.getSpecification().getName()
+ + "] are not the same");
+ } else {
+ // If the specification is different, warn that we will override it.
+ warn("["
+ + getInstanceManager().getInstanceName()
+ + "] The field type ["
+ + className
+ + "] and the required service interface ["
+ + dep.getSpecification()
+ + "] are not the same");
+ }
+ }
+
+ try {
+ dep.setSpecification(getInstanceManager().getContext().getBundle().loadClass(className));
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("The required service interface cannot be loaded : " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param componentMetadata : the component type metadata
+ * @param configuration : the instance configuration
+ * @throws ConfigurationException : one dependency metadata is not correct.
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element componentMetadata, Dictionary configuration) throws ConfigurationException {
+ PojoMetadata manipulation = getFactory().getPojoMetadata();
+ boolean atLeastOneField = false;
+
+ // Create the dependency according to the component metadata
+ Element[] deps = componentMetadata.getElements("Requires");
+
+ // Get instance filters.
+ Dictionary filtersConfiguration = getRequiresFilters(configuration.get("requires.filters"));
+ Dictionary fromConfiguration = (Dictionary) configuration.get("requires.from");
+
+ for (int i = 0; deps != null && i < deps.length; i++) {
+ // Create the dependency metadata
+ String field = deps[i].getAttribute("field");
+
+ String serviceSpecification = deps[i].getAttribute("interface");
+ // the 'interface' attribute is deprecated
+ if (serviceSpecification != null) {
+ warn("The 'interface' attribute is deprecated, use the 'specification' attribute instead");
+ } else {
+ serviceSpecification = deps[i].getAttribute("specification");
+ }
+
+ String filter = deps[i].getAttribute("filter");
+ String opt = deps[i].getAttribute("optional");
+ boolean optional = opt != null && opt.equalsIgnoreCase("true");
+ String defaultImplem = deps[i].getAttribute("default-implementation");
+
+ String agg = deps[i].getAttribute("aggregate");
+ boolean aggregate = agg != null && agg.equalsIgnoreCase("true");
+ String identitity = deps[i].getAttribute("id");
+
+ String nul = deps[i].getAttribute("nullable");
+ boolean nullable = nul == null || nul.equalsIgnoreCase("true");
+
+ boolean isProxy = true;
+ // Detect proxy default value.
+ String setting = getInstanceManager().getContext().getProperty(PROXY_SETTINGS_PROPERTY);
+ if (setting == null || PROXY_ENABLED.equals(setting)) { // If not set => Enabled
+ isProxy = true;
+ } else if (setting != null && PROXY_DISABLED.equals(setting)) {
+ isProxy = false;
+ }
+
+ String proxy = deps[i].getAttribute("proxy");
+ // If proxy == null, use default value
+ if (proxy != null) {
+ if (proxy.equals("false")) {
+ isProxy = false;
+ } else if (proxy.equals("true")) {
+ if (! isProxy) { // The configuration overrides the system setting
+ warn("The configuration of a service dependency overrides the proxy mode");
+ }
+ isProxy = true;
+ }
+ }
+
+ String scope = deps[i].getAttribute("scope");
+ BundleContext context = getInstanceManager().getContext(); // Get the default bundle context.
+ if (scope != null) {
+ // If we are not in a composite, the policy is set to global.
+ if (scope.equalsIgnoreCase("global") || ((((IPojoContext) getInstanceManager().getContext()).getServiceContext()) == null)) {
+ context =
+ new PolicyServiceContext(getInstanceManager().getGlobalContext(), getInstanceManager().getLocalServiceContext(),
+ PolicyServiceContext.GLOBAL);
+ } else if (scope.equalsIgnoreCase("composite")) {
+ context =
+ new PolicyServiceContext(getInstanceManager().getGlobalContext(), getInstanceManager().getLocalServiceContext(),
+ PolicyServiceContext.LOCAL);
+ } else if (scope.equalsIgnoreCase("composite+global")) {
+ context =
+ new PolicyServiceContext(getInstanceManager().getGlobalContext(), getInstanceManager().getLocalServiceContext(),
+ PolicyServiceContext.LOCAL_AND_GLOBAL);
+ }
+ }
+
+ // Get instance filter if available
+ if (filtersConfiguration != null && identitity != null && filtersConfiguration.get(identitity) != null) {
+ filter = (String) filtersConfiguration.get(identitity);
+ }
+
+ // Compute the 'from' attribute
+ String from = deps[i].getAttribute("from");
+ if (fromConfiguration != null && identitity != null && fromConfiguration.get(identitity) != null) {
+ from = (String) fromConfiguration.get(identitity);
+ }
+ if (from != null) {
+ String fromFilter = "(|(instance.name=" + from + ")(service.pid=" + from + "))";
+ if (aggregate) {
+ warn("The 'from' attribute is incompatible with aggregate requirements: only one provider will match : " + fromFilter);
+ }
+ if (filter != null) {
+ filter = "(&" + fromFilter + filter + ")"; // Append the two filters
+ } else {
+ filter = fromFilter;
+ }
+ }
+
+ Filter fil = null;
+ if (filter != null) {
+ try {
+ fil = getInstanceManager().getContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A requirement filter is invalid : " + filter + " - " + e.getMessage());
+ }
+ }
+
+
+ Class spec = null;
+ if (serviceSpecification != null) {
+ spec = DependencyModel.loadSpecification(serviceSpecification, getInstanceManager().getContext());
+ }
+
+ int policy = DependencyModel.getPolicy(deps[i]);
+ Comparator cmp = DependencyModel.getComparator(deps[i], getInstanceManager().getGlobalContext());
+
+
+ Dependency dep = new Dependency(this, field, spec, fil, optional, aggregate, nullable, isProxy, identitity, context, policy, cmp, defaultImplem);
+
+ // Look for dependency callback :
+ Element[] cbs = deps[i].getElements("Callback");
+ for (int j = 0; cbs != null && j < cbs.length; j++) {
+ if (!cbs[j].containsAttribute("method") && cbs[j].containsAttribute("type")) {
+ throw new ConfigurationException("Requirement Callback : a dependency callback must contain a method and a type (bind or unbind) attribute");
+ }
+ String method = cbs[j].getAttribute("method");
+ String type = cbs[j].getAttribute("type");
+ int methodType = 0;
+ if ("bind".equalsIgnoreCase(type)) {
+ methodType = DependencyCallback.BIND;
+ } else if ("modified".equalsIgnoreCase(type)) {
+ methodType = DependencyCallback.MODIFIED;
+ } else {
+ methodType = DependencyCallback.UNBIND;
+ }
+
+ DependencyCallback callback = new DependencyCallback(dep, method, methodType);
+ dep.addDependencyCallback(callback);
+ }
+
+ // Add the constructor parameter if needed
+ String paramIndex = deps[i].getAttribute("constructor-parameter");
+ if (paramIndex != null) {
+ int index = Integer.parseInt(paramIndex);
+ dep.addConstructorInjection(index);
+ }
+
+ // Check the dependency :
+ if (checkDependency(dep, manipulation)) {
+ addDependency(dep);
+ if (dep.getField() != null) {
+ getInstanceManager().register(manipulation.getField(dep.getField()), dep);
+ atLeastOneField = true;
+ }
+ }
+ }
+
+ if (atLeastOneField) { // Does register only if we have fields
+ MethodMetadata[] methods = manipulation.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ for (int j = 0; j < m_dependencies.length; j++) {
+ getInstanceManager().register(methods[i], m_dependencies[j]);
+ }
+ }
+ }
+
+ m_description = new DependencyHandlerDescription(this, m_dependencies); // Initialize the description.
+ }
+
+ /**
+ * Gets the requires filter configuration from the given object.
+ * The given object must come from the instance configuration.
+ * This method was made to fix FELIX-2688. It supports filter configuration using
+ * an array:
+ * <code>{"myFirstDep", "(property1=value1)", "mySecondDep", "(property2=value2)"});</code>
+ * @param requiresFiltersValue the value contained in the instance
+ * configuration.
+ * @return the dictionary. If the object in already a dictionary, just returns it,
+ * if it's an array, builds the dictionary.
+ * @throws ConfigurationException the dictionary cannot be built
+ */
+ private Dictionary getRequiresFilters(Object requiresFiltersValue)
+ throws ConfigurationException {
+ if (requiresFiltersValue != null
+ && requiresFiltersValue.getClass().isArray()) {
+ String[] filtersArray = (String[]) requiresFiltersValue;
+ if (filtersArray.length % 2 != 0) {
+ throw new ConfigurationException(
+ "A requirement filter is invalid : "
+ + requiresFiltersValue);
+ }
+ Dictionary requiresFilters = new Hashtable();
+ for (int i = 0; i < filtersArray.length; i += 2) {
+ requiresFilters.put(filtersArray[i], filtersArray[i + 1]);
+ }
+ return requiresFilters;
+ }
+
+ return (Dictionary) requiresFiltersValue;
+ }
+
+ /**
+ * Handler start method.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ // Start the dependencies
+ for (int i = 0; i < m_dependencies.length; i++) {
+ Dependency dep = m_dependencies[i];
+
+ dep.start();
+ }
+ // Check the state
+ m_started = true;
+ setValidity(false);
+ checkContext();
+ }
+
+ /**
+ * Handler stop method.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ m_started = false;
+ for (int i = 0; i < m_dependencies.length; i++) {
+ m_dependencies[i].stop();
+ }
+ }
+
+ /**
+ * Handler createInstance method. This method is override to allow delayed callback invocation.
+ * @param instance : the created object
+ * @see org.apache.felix.ipojo.PrimitiveHandler#onCreation(Object)
+ */
+ public void onCreation(Object instance) {
+ for (int i = 0; i < m_dependencies.length; i++) {
+ m_dependencies[i].onObjectCreation(instance);
+ }
+ }
+
+ /**
+ * Get the dependency handler description.
+ * @return the dependency handler description.
+ * @see org.apache.felix.ipojo.Handler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
new file mode 100644
index 0000000..0e6d77d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
@@ -0,0 +1,165 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Dependency Handler Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyHandlerDescription extends HandlerDescription {
+
+ /**
+ * Dependencies managed by the dependency handler.
+ */
+ private DependencyDescription[] m_dependencies = new DependencyDescription[0];
+
+ // TODO Define the DependencyStateListener Interface (in ipojo utils)
+
+ // TODO Add the list of listener.
+
+ // TODO Add register listener method.
+
+ // TODO Add unregister listener method.
+
+ // TODO Implement the validate method.
+
+ // TODO Implement the invalidate method.
+
+ // TODO Implement the onServiceArrival method.
+
+ // TODO Implement the onServiceDeparture method.
+
+ // TODO Implement the onServiceBound method.
+
+ // TODO Implement the onServiceUnbound method.
+
+ /**
+ * Creates the Dependency Handler description.
+ * @param handler the Dependency Handler.
+ * @param deps the Dependencies
+ */
+ public DependencyHandlerDescription(DependencyHandler handler, Dependency[] deps) {
+ super(handler);
+ m_dependencies = new DependencyDescription[deps.length];
+ for (int i = 0; i < m_dependencies.length; i++) {
+ m_dependencies[i] = new DependencyDescription(deps[i]);
+ //TODO Register callback there on the dependency model.
+ }
+ }
+
+ /**
+ * Get dependencies description.
+ * @return the dependencies list.
+ */
+ public DependencyDescription[] getDependencies() {
+ return m_dependencies;
+ }
+
+ /**
+ * Builds the Dependency Handler description.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element deps = super.getHandlerInfo();
+ for (int i = 0; i < m_dependencies.length; i++) {
+ String state = "resolved";
+ if (m_dependencies[i].getState() == DependencyModel.UNRESOLVED) {
+ state = "unresolved";
+ }
+ if (m_dependencies[i].getState() == DependencyModel.BROKEN) {
+ state = "broken";
+ }
+ Element dep = new Element("Requires", "");
+ dep.addAttribute(new Attribute("Specification", m_dependencies[i].getInterface()));
+ dep.addAttribute(new Attribute("Id", m_dependencies[i].getId()));
+
+ if (m_dependencies[i].getFilter() != null) {
+ dep.addAttribute(new Attribute("Filter", m_dependencies[i].getFilter()));
+ }
+
+ if (m_dependencies[i].isOptional()) {
+ dep.addAttribute(new Attribute("Optional", "true"));
+ if (m_dependencies[i].supportsNullable()) {
+ dep.addAttribute(new Attribute("Nullable", "true"));
+ }
+ if (m_dependencies[i].getDefaultImplementation() != null) {
+ dep.addAttribute(new Attribute("Default-Implementation", m_dependencies[i].getDefaultImplementation()));
+ }
+ } else {
+ dep.addAttribute(new Attribute("Optional", "false"));
+ }
+
+ if (m_dependencies[i].isMultiple()) {
+ dep.addAttribute(new Attribute("Aggregate", "true"));
+ } else {
+ dep.addAttribute(new Attribute("Aggregate", "false"));
+ }
+
+ if (m_dependencies[i].isProxy()) {
+ dep.addAttribute(new Attribute("Proxy", "true"));
+ } else {
+ dep.addAttribute(new Attribute("Proxy", "false"));
+ }
+
+ String policy = "dynamic";
+ if (m_dependencies[i].getPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
+ policy = "static";
+ } else if (m_dependencies[i].getPolicy() == DependencyModel.DYNAMIC_PRIORITY_BINDING_POLICY) {
+ policy = "dynamic-priority";
+ }
+ dep.addAttribute(new Attribute("Binding-Policy", policy));
+
+ if (m_dependencies[i].getComparator() != null) {
+ dep.addAttribute(new Attribute("Comparator", m_dependencies[i].getComparator()));
+ }
+
+ dep.addAttribute(new Attribute("State", state));
+ List set = m_dependencies[i].getUsedServices();
+ if (set != null) {
+ Iterator iterator = set.iterator();
+ while (iterator.hasNext()) {
+ Element use = new Element("Uses", "");
+ ServiceReference ref = (ServiceReference) iterator.next();
+ use.addAttribute(new Attribute("service.id", ref.getProperty(Constants.SERVICE_ID).toString()));
+ String instance = (String) ref.getProperty("instance.name");
+ if (instance != null) {
+ use.addAttribute(new Attribute("instance.name", instance));
+ }
+ dep.addElement(use);
+ }
+ }
+
+ deps.addElement(dep);
+ }
+ return deps;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/NullableObject.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/NullableObject.java
new file mode 100644
index 0000000..b11419f
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/NullableObject.java
@@ -0,0 +1,94 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * Default nullable object.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class NullableObject implements InvocationHandler {
+
+ /**
+ * Default boolean value.
+ */
+ private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
+
+ /**
+ * Default byte value.
+ */
+ private static final Byte DEFAULT_BYTE = new Byte((byte) 0);
+
+ /**
+ * Default short value.
+ */
+ private static final Short DEFAULT_SHORT = new Short((short) 0);
+
+ /**
+ * Default integer value.
+ */
+ private static final Integer DEFAULT_INT = new Integer(0);
+
+ /**
+ * Default long value.
+ */
+ private static final Long DEFAULT_LONG = new Long(0);
+
+ /**
+ * Default float value.
+ */
+ private static final Float DEFAULT_FLOAT = new Float(0.0f);
+
+ /**
+ * Default double value.
+ */
+ private static final Double DEFAULT_DOUBLE = new Double(0.0);
+
+ /**
+ * Invokes a method on this null object. The method will return a default
+ * value without doing anything.
+ * @param proxy : proxy object
+ * @param method : invoked method
+ * @param args : arguments.
+ * @return the returned value.
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ Class returnType = method.getReturnType();
+ if (Boolean.TYPE.equals(returnType)) {
+ return DEFAULT_BOOLEAN;
+ } else if (Byte.TYPE.equals(returnType)) {
+ return DEFAULT_BYTE;
+ } else if (Short.TYPE.equals(returnType)) {
+ return DEFAULT_SHORT;
+ } else if (Integer.TYPE.equals(returnType)) {
+ return DEFAULT_INT;
+ } else if (Long.TYPE.equals(returnType)) {
+ return DEFAULT_LONG;
+ } else if (Float.TYPE.equals(returnType)) {
+ return DEFAULT_FLOAT;
+ } else if (Double.TYPE.equals(returnType)) {
+ return DEFAULT_DOUBLE;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ProxyGenerator.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ProxyGenerator.java
new file mode 100644
index 0000000..1dd2cf3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ProxyGenerator.java
@@ -0,0 +1,212 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Generates proxy class delegating operation invocations thanks to a
+ * a dependency.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProxyGenerator implements Opcodes {
+
+ /**
+ * The dependency name.
+ */
+ private static final String DEPENDENCY = "m_dependency";
+
+ /**
+ * The dependency descriptor.
+ */
+ private static final String DEPENDENCY_DESC = Type.getDescriptor(Dependency.class);
+
+ /**
+ * Dependency internal class name.
+ */
+ private static final String DEPENDENCY_INTERNAL_NAME = "org/apache/felix/ipojo/handlers/dependency/Dependency";
+
+ /**
+ * Gets the internal names of the given class objects.
+ * @param classes the classes
+ * @return the array containing internal names of the given class array.
+ */
+ private static String[] getInternalClassNames(Class[] classes) {
+ final String[] names = new String[classes.length];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = Type.getInternalName(classes[i]);
+ }
+ return names;
+ }
+
+ /**
+ * Generates a proxy class.
+ * @param spec the proxied service specification
+ * @return the byte[] for the generated proxy class.
+ */
+ public static byte[] dumpProxy(Class spec) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ String internalClassName = Type.getInternalName(spec); // Specification class internal name.
+ String[] itfs = new String[0];
+ String parent = "java/lang/Object";
+ if (spec.isInterface()) {
+ itfs = new String[] {internalClassName}; // Implemented interface.
+ } else {
+ parent = internalClassName;
+ }
+ String className = internalClassName + "$$Proxy"; // Unique name.
+
+ // Turn around the VM changes (FELIX-2716) about java.* classes.
+ if (className.startsWith("java/")) {
+ className = "$" + className;
+ }
+
+ Method[] methods = spec.getMethods(); // Method to delegate
+
+ cw.visit(Opcodes.V1_3, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, className, null, parent, itfs);
+ addDependencyField(cw);
+
+ // We try to call super() on the parent, however this should not be used as proxing does work only for interface.
+ generateConstructor(cw, className, parent);
+
+ // For each method, create the delegator code.
+ for (int i = 0; i < methods.length; i++) {
+ if ((methods[i].getModifiers() & (Modifier.STATIC | Modifier.FINAL)) == 0) {
+ generateDelegator(cw, methods[i], className, internalClassName);
+ }
+ }
+
+ cw.visitEnd();
+
+ return cw.toByteArray();
+
+ }
+
+ /**
+ * Generates a delegated method.
+ * @param cw the class writer
+ * @param method the method object to delegate
+ * @param className the generated class name
+ * @param itfName the internal specification class name
+ */
+ private static void generateDelegator(ClassWriter cw, Method method,
+ String className, String itfName) {
+ String methodName = method.getName();
+ String desc = Type.getMethodDescriptor(method);
+ String[] exceptions = getInternalClassNames(method.getExceptionTypes());
+ int modifiers = method.getModifiers()
+ & ~(Modifier.ABSTRACT | Modifier.NATIVE | Modifier.SYNCHRONIZED);
+ Type[] types = Type.getArgumentTypes(method);
+
+ int freeRoom = 1;
+ for (int t = 0; t < types.length; t++) {
+ freeRoom = freeRoom + types[t].getSize();
+ }
+
+ MethodVisitor mv = cw.visitMethod(modifiers, methodName, desc, null,
+ exceptions);
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, DEPENDENCY, DEPENDENCY_DESC); // The dependency is on the stack.
+ mv.visitMethodInsn(INVOKEVIRTUAL, DEPENDENCY_INTERNAL_NAME, "getService", // Call getService
+ "()Ljava/lang/Object;"); // The service object is on the stack.
+ int varSvc = freeRoom;
+ freeRoom = freeRoom + 1; // Object Reference.
+ mv.visitVarInsn(ASTORE, varSvc); // Store the service object.
+
+ Label notNull = new Label();
+ Label isNull = new Label();
+ mv.visitVarInsn(ALOAD, varSvc); // Load the service
+ mv.visitJumpInsn(IFNONNULL, notNull); // If not null go to not null
+ // Null branch - throw the exception
+ mv.visitLabel(isNull);
+ mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn("No service available");
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
+ mv.visitInsn(ATHROW);
+ // End of the null branch
+
+ // Not null, go one the execution
+ mv.visitLabel(notNull);
+
+ // Invoke the method on the service object.
+ mv.visitVarInsn(ALOAD, varSvc);
+ // Push argument on the stack.
+ int i = 1; // Arguments. (non static method)
+ for (int t = 0; t < types.length; t++) {
+ mv.visitVarInsn(types[t].getOpcode(ILOAD), i);
+ i = i + types[t].getSize();
+ }
+ // Invocation
+ mv.visitMethodInsn(INVOKEINTERFACE, itfName, methodName, desc);
+
+ // Return the result
+ Type returnType = Type.getReturnType(desc);
+ if (returnType.getSort() != Type.VOID) {
+ mv.visitInsn(returnType.getOpcode(IRETURN));
+ } else {
+ mv.visitInsn(RETURN);
+ }
+
+ // End of the method.
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Generates the constructors. The constructor receives a dependency
+ * and set the {@link ProxyGenerator#DEPENDENCY} field.
+ * @param cw the class writer
+ * @param className the generated class name.
+ */
+ private static void generateConstructor(ClassWriter cw, String className, String parent) {
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", '(' + DEPENDENCY_DESC + ")V", null, null);
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0); // Load this
+ mv.visitInsn(DUP); // Dup
+ mv.visitMethodInsn(INVOKESPECIAL, parent, "<init>", "()V"); // Call super
+ mv.visitVarInsn(ALOAD, 1); // Load the argument
+ mv.visitFieldInsn(PUTFIELD, className, DEPENDENCY, DEPENDENCY_DESC); // Assign the dependency field
+ mv.visitInsn(RETURN); // Return void
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Adds the dependency field {@link ProxyGenerator#DEPENDENCY}.
+ * @param cw the class writer
+ */
+ private static void addDependencyField(ClassWriter cw) {
+ cw.visitField(Opcodes.ACC_FINAL, DEPENDENCY, DEPENDENCY_DESC, null, null);
+ cw.visitEnd();
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceCollection.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceCollection.java
new file mode 100644
index 0000000..46451aa
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceCollection.java
@@ -0,0 +1,422 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+/**
+* Maintains a service object collection.
+* This collection wrap the temporal dependency to be accessible from a
+* {@link Collection}, that can be passed to helper objects (Collaborators).
+*
+*
+* The {@link Collection#iterator()} method returns an {@link Iterator} iterating
+* on a cached copy of available service objects. In the case that there are no
+* available services, the policies act as follows:
+* <ul>
+* <li>'null' returns a null iterator</li>
+* <li>'nullable' and default-implementation returns an iterator iterating on one object (the
+* nullable or default-implementation object</li>
+* <li>'empty' returns an empty iterator.</li>
+* </ul>
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public class ServiceCollection implements Collection, List, Set {
+
+ /**
+ * The wrapped dependency.
+ */
+ private Dependency m_dependency;
+
+ /**
+ * Creates a Service Collection.
+ * @param dep the wrapped dependency
+ */
+ public ServiceCollection(Dependency dep) {
+ m_dependency = dep;
+ }
+
+ /**
+ * Unsupported method.
+ * @param o an object
+ * @return N/A
+ * @see java.util.Collection#add(java.lang.Object)
+ */
+ public boolean add(Object o) {
+ throw new UnsupportedOperationException("Cannot add elements inside this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param c an object
+ * @return N/A
+ * @see java.util.Collection#addAll(java.util.Collection)
+ */
+ public boolean addAll(Collection c) {
+ throw new UnsupportedOperationException("Cannot add elements inside this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param index an index
+ * @param obj an object
+ * @see java.util.List#add(int, java.lang.Object)
+ */
+ public void add(int index, Object obj) {
+ throw new UnsupportedOperationException("Cannot add elements inside this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param index an index
+ * @param c an object
+ * @return N/A
+ * @see java.util.List#addAll(int, java.util.Collection)
+ */
+ public boolean addAll(int index, Collection c) {
+ throw new UnsupportedOperationException("Cannot add elements inside this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @see java.util.Collection#clear()
+ */
+ public void clear() {
+ throw new UnsupportedOperationException("Cannot remove elements from this collection");
+ }
+
+ /**
+ * Checks if the wrapped dependency has always access to the
+ * given service object.The method allows knowing if the provider returning the
+ * service object has left.
+ * @param o the service object
+ * @return <code>true</code> if the object is still available,
+ * <code>false</code> otherwise.
+ * @see java.util.Collection#contains(java.lang.Object)
+ */
+ public boolean contains(Object o) {
+ List list = (List) m_dependency.getService();
+ return list.contains(o);
+ }
+
+ /**
+ * Checks if the wrapped dependencies has always access to the
+ * given service objects.The method allows knowing if providers returning the
+ * service objects have left.
+ * @param c the set of service object
+ * @return <code>true</code> if the objects are still available,
+ * <code>false</code> otherwise.
+ * @see java.util.Collection#contains(java.lang.Object)
+ */
+ public boolean containsAll(Collection c) {
+ List list = (List) m_dependency.getService();
+ return list.containsAll(c);
+ }
+
+ /**
+ * Checks if at least one provider matching with the dependency
+ * is available.
+ * @return <code>true</code> if one provider or more satisfying the
+ * dependency are available. Otherwise, returns <code>false</code>
+ * @see java.util.Collection#isEmpty()
+ */
+ public boolean isEmpty() {
+ return m_dependency.getSize() == 0;
+ }
+
+ /**
+ * Gets an iterator on the current list of available service objects.
+ * The returned iterator iterates on a cached copy of the service
+ * objects.
+ * @return a iterator giving access to service objects.
+ * @see java.util.Collection#iterator()
+ */
+ public Iterator iterator() {
+ List obj = (List) m_dependency.getService();
+ return new ServiceIterator(obj); // Create the service iterator with the service reference list.
+ }
+
+ /**
+ * Unsupported method.
+ * @param o a object
+ * @return N/A
+ * @see java.util.Collection#remove(java.lang.Object)
+ */
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException("Cannot remove elements from this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param index the index
+ * @return N/A
+ * @see java.util.Collection#remove(java.lang.Object)
+ */
+ public Object remove(int index) {
+ throw new UnsupportedOperationException("Cannot remove elements from this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param c a set of objects
+ * @return N/A
+ * @see java.util.Collection#removeAll(java.util.Collection)
+ */
+ public boolean removeAll(Collection c) {
+ throw new UnsupportedOperationException("Cannot remove elements from this collection");
+ }
+
+ /**
+ * Unsupported method.
+ * @param c a set of objects
+ * @return N/A
+ * @see java.util.Collection#retainAll(java.util.Collection)
+ */
+ public boolean retainAll(Collection c) {
+ throw new UnsupportedOperationException("Cannot remove elements from this collection");
+ }
+
+ /**
+ * Gets the number of available providers.
+ * @return the number of matching service providers.
+ * @see java.util.Collection#size()
+ */
+ public int size() {
+ return m_dependency.getSize();
+ }
+
+ /**
+ * Returns an array containing available service objects.
+ * This method executed on timeout policies if no matching
+ * providers when the timeout is reached.
+ * @return a array containing available service objects.
+ * @see java.util.Collection#toArray()
+ */
+ public Object[] toArray() {
+ return toArray(new Object[0]);
+ }
+
+ /**
+ * Returns an array containing available service objects.
+ * This method executed on timeout policies if no matching
+ * providers when the timeout is reached.
+ * @param a the array into which the elements of this collection
+ * are to be stored, if it is big enough; otherwise, a new array
+ * of the same runtime type is allocated for this purpose.
+ * @return a array containing available service objects.
+ * @see java.util.Collection#toArray(java.lang.Object[])
+ */
+ public Object[] toArray(Object[] a) {
+ List list = (List) m_dependency.getService();
+ return list.toArray(a);
+ }
+
+ /**
+ * Gets the object stored at the given index.
+ * @param index the index
+ * @return the service object
+ * @see java.util.List#get(int)
+ */
+ public Object get(int index) {
+ List list = (List) m_dependency.getService();
+ return list.get(index);
+ }
+
+ /**
+ * Gets the index of the given object in the current
+ * collection.
+ * @param o the object
+ * @return the index of the object of <code>-1</code>
+ * if not found.
+ * @see java.util.List#indexOf(java.lang.Object)
+ */
+ public int indexOf(Object o) {
+ List list = (List) m_dependency.getService();
+ return list.indexOf(o);
+ }
+
+ /**
+ * Gets the last index of the given object in the current
+ * collection.
+ * @param o the object
+ * @return the index of the object of <code>-1</code>
+ * if not found.
+ * @see java.util.List#lastIndexOf(java.lang.Object)
+ */
+ public int lastIndexOf(Object o) {
+ List list = (List) m_dependency.getService();
+ return list.lastIndexOf(o);
+ }
+
+ /**
+ * Gets a list iterator on the current list of available service objects.
+ * The returned iterator iterates on a cached copy of the service
+ * objects.
+ * @return a iterator giving access to service objects.
+ * @see java.util.List#listIterator()
+ */
+ public ListIterator listIterator() {
+ List obj = (List) m_dependency.getService();
+ return new ServiceIterator(obj); // Create the service iterator with the service reference list.
+ }
+
+ /**
+ * Unsupported Method.
+ * @param index an index
+ * @return N/A
+ * @see java.util.List#listIterator(int)
+ */
+ public ListIterator listIterator(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Unsupported Method.
+ * @param arg0 an index
+ * @param arg1 an object
+ * @return N/A
+ * @see java.util.List#set(int, E)
+ */
+ public Object set(int arg0, Object arg1) {
+ throw new UnsupportedOperationException("Cannot add elements inside this collection");
+ }
+
+ /**
+ * Returns a sublist from the current list.
+ * @param fromIndex the index of the list beginning
+ * @param toIndex the index of the list end
+ * @return the sub-list
+ * @see java.util.List#subList(int, int)
+ */
+ public List subList(int fromIndex, int toIndex) {
+ List list = (List) m_dependency.getService();
+ return list.subList(fromIndex, toIndex);
+ }
+
+ /**
+ * Iterator on a set of service objects.
+ * This iterator iterates on a cached copy of service objects.
+ */
+ private final class ServiceIterator implements ListIterator {
+
+ /**
+ * Underlying iterator.
+ */
+ private ListIterator m_iterator;
+
+ /**
+ * Creates a Service Iterator iterating
+ * on the given set of providers.
+ * @param list the list of service object.
+ */
+ private ServiceIterator(List list) {
+ m_iterator = list.listIterator();
+ }
+
+ /**
+ * Returns <code>true</code> if the iteration has
+ * more service objects.
+ * @return <code>true</code> if the iterator has more elements.
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ return m_iterator.hasNext();
+ }
+
+ /**
+ * Returns the next service objects in the iteration.
+ * @return the next service object in the iteration.
+ * @see java.util.Iterator#next()
+ */
+ public Object next() {
+ return m_iterator.next();
+ }
+
+ /**
+ * Unsupported operation.
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Unsupported operation.
+ * @param obj an object
+ * @see java.util.ListIterator#add(E)
+ */
+ public void add(Object obj) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Checks if the is an element before the currently
+ * pointed one.
+ * @return true if there is an element before the
+ * current one.
+ * @see java.util.ListIterator#hasPrevious()
+ */
+ public boolean hasPrevious() {
+ return m_iterator.hasPrevious();
+ }
+
+ /**
+ * Gets the index of the next element.
+ * @return the index of the next element.
+ * @see java.util.ListIterator#nextIndex()
+ */
+ public int nextIndex() {
+ return m_iterator.nextIndex();
+ }
+
+
+ /**
+ * Gets the previous elements.
+ * @return the previous element
+ * @see java.util.ListIterator#previous()
+ */
+ public Object previous() {
+ return m_iterator.previous();
+ }
+
+ /**
+ * Gets the index of the previous element.
+ * @return the index of the previous element.
+ * @see java.util.ListIterator#previousIndex()
+ */
+ public int previousIndex() {
+ return m_iterator.previousIndex();
+ }
+
+ /**
+ * Unsupported operation.
+ * @param obj an object
+ * @see java.util.ListIterator#set(E)
+ */
+ public void set(Object obj) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceUsage.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceUsage.java
new file mode 100644
index 0000000..3757e77
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/ServiceUsage.java
@@ -0,0 +1,100 @@
+/*
+ * 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.felix.ipojo.handlers.dependency;
+
+
+/**
+ * Object managing thread local copy of required services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUsage extends ThreadLocal {
+
+ /**
+ * Structure contained in the Thread Local.
+ */
+ public static class Usage {
+
+ /**
+ * Stack Size.
+ */
+ int m_stack = 0;
+ /**
+ * Object to inject.
+ */
+ Object m_object;
+
+ /**
+ * Tracks the number of component method called
+ * in the current thread.
+ */
+ int m_componentStack = 0;
+
+ /**
+ * Increment the stack level from the first
+ * service get.
+ */
+ public void inc() {
+ m_stack++;
+ }
+
+ /**
+ * Increment the component stack level.
+ */
+ public void incComponentStack() {
+ m_componentStack++;
+ }
+
+ /**
+ * Decrement the stack level.
+ * @return true if the stack is 0 after the decrement.
+ */
+ public boolean dec() {
+ m_stack--;
+ return m_stack == 0;
+ }
+
+ /**
+ * Decrement the component stack level.
+ * @return true if the stack is 0 after the decrement.
+ */
+ public boolean decComponentStack() {
+ m_componentStack--;
+ return m_componentStack == 0;
+ }
+
+ /**
+ * Clear the service object array.
+ */
+ public void clear() {
+ m_object = null;
+ }
+
+ }
+
+ /**
+ * Initialize the cached object.
+ * @return an empty Usage object.
+ * @see java.lang.ThreadLocal#initialValue()
+ */
+ public Object initialValue() {
+ return new Usage();
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallback.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallback.java
new file mode 100644
index 0000000..cd86798
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallback.java
@@ -0,0 +1,101 @@
+/*
+ * 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.felix.ipojo.handlers.lifecycle.callback;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.util.Callback;
+
+/**
+ * This class is the implementation of callback on lifecycle transition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class LifecycleCallback {
+
+ /**
+ * Invalid to Valid transition.
+ */
+ protected static final int VALIDATE = 1;
+
+ /**
+ * Valid to Invalid transition.
+ */
+ protected static final int INVALIDATE = 0;
+
+ /**
+ * Transition on which calling the callback.
+ */
+ private int m_transition;
+
+ /**
+ * Callback object.
+ */
+ private Callback m_callback;
+
+ /**
+ * LifecycleCallback constructor.
+ *
+ * @param handler : the callback handler calling the callback
+ * @param transition : transition on which calling the callback
+ * @param method : method metadata to invoke
+ */
+ public LifecycleCallback(LifecycleCallbackHandler handler, int transition, MethodMetadata method) {
+ m_transition = transition;
+ m_callback = new Callback(method, handler.getInstanceManager());
+ }
+
+ /**
+ * LifecycleCallback constructor.
+ *
+ * @param handler : the callback handler calling the callback
+ * @param transition : transition on which calling the callback
+ * @param method : method name to invoke
+ */
+ public LifecycleCallback(LifecycleCallbackHandler handler, int transition, String method) {
+ m_transition = transition;
+ m_callback = new Callback(method, new String[0], false, handler.getInstanceManager());
+ }
+
+ /**
+ * Call the callback method when the transition from inital tostate is
+ * detected.
+ *
+ * @throws NoSuchMethodException : Method is not found in the class
+ * @throws InvocationTargetException : The method is not static
+ * @throws IllegalAccessException : The method can not be invoked
+ */
+ protected void call() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ m_callback.call();
+ }
+
+ protected int getTransition() {
+ return m_transition;
+ }
+
+ /**
+ * Get the method name of the callback.
+ * @return the method name
+ */
+ protected String getMethod() {
+ return m_callback.getMethod();
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallbackHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallbackHandler.java
new file mode 100644
index 0000000..16f8e09
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/callback/LifecycleCallbackHandler.java
@@ -0,0 +1,178 @@
+/*
+ * 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.felix.ipojo.handlers.lifecycle.callback;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+
+/**
+ * Lifecycle callback handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class LifecycleCallbackHandler extends PrimitiveHandler {
+
+ /**
+ * The list of the callback of the component.
+ */
+ private LifecycleCallback[] m_callbacks = new LifecycleCallback[0];
+
+ /**
+ * State of the instance manager (unresolved at the beginning).
+ */
+ private int m_state = InstanceManager.INVALID;
+ /**
+ * Does a POJO object be created at starting.
+ */
+ private boolean m_immediate = false;
+
+ /**
+ * Add the given callback to the callback list.
+ *
+ * @param callback : the element to add
+ */
+ private void addCallback(LifecycleCallback callback) {
+ for (int i = 0; (m_callbacks != null) && (i < m_callbacks.length); i++) {
+ if (m_callbacks[i] == callback) {
+ return;
+ }
+ }
+
+ if (m_callbacks != null && m_callbacks.length > 0) { //TODO check here if we can improve the test
+ LifecycleCallback[] newHk = new LifecycleCallback[m_callbacks.length + 1];
+ System.arraycopy(m_callbacks, 0, newHk, 0, m_callbacks.length);
+ newHk[m_callbacks.length] = callback;
+ m_callbacks = newHk;
+ } else {
+ m_callbacks = new LifecycleCallback[] { callback };
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param metadata : the component type metadata
+ * @param configuration : the instance configuration
+ * @throws ConfigurationException : one callback metadata is not correct (either the transition or the method are not correct).
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.InstanceManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_callbacks = new LifecycleCallback[0];
+
+ String imm = metadata.getAttribute("immediate");
+ m_immediate = imm != null && imm.equalsIgnoreCase("true");
+
+ PojoMetadata meta = getFactory().getPojoMetadata();
+
+ Element[] hooksMetadata = metadata.getElements("callback");
+ for (int i = 0; hooksMetadata != null && i < hooksMetadata.length; i++) {
+ String method = hooksMetadata[i].getAttribute("method");
+ if (method == null) {
+ throw new ConfigurationException("Lifecycle callback : A callback needs to contain a method attribute");
+ }
+
+ MethodMetadata met = meta.getMethod(method, new String[0]);
+
+ int transition = -1;
+ String trans = hooksMetadata[i].getAttribute("transition");
+ if (trans == null) {
+ throw new ConfigurationException("Lifecycle callback : the transition attribute is missing");
+ } else {
+ if (trans.equalsIgnoreCase("validate")) {
+ transition = LifecycleCallback.VALIDATE;
+ } else if (trans.equalsIgnoreCase("invalidate")) {
+ transition = LifecycleCallback.INVALIDATE;
+ } else {
+ throw new ConfigurationException("Lifecycle callback : Unknown or malformed transition : " + trans);
+ }
+ }
+
+ LifecycleCallback callback = null;
+ if (met == null) {
+ callback = new LifecycleCallback(this, transition, method);
+ } else {
+ callback = new LifecycleCallback(this, transition, met);
+ }
+ addCallback(callback);
+ }
+ }
+
+ /**
+ * Start the handler.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ // Do nothing during the start
+ }
+
+ /**
+ * Stop the handler.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ m_state = InstanceManager.INVALID;
+ }
+
+ /**
+ * When the state change call the associated callback.
+ *
+ * @param state : the new instance state.
+ * @see org.apache.felix.ipojo.Handler#stateChanged(int)
+ */
+ public void stateChanged(int state) {
+ int transition = -1;
+ if (m_state == ComponentInstance.INVALID && state == ComponentInstance.VALID) {
+ transition = LifecycleCallback.VALIDATE;
+ }
+ if (m_state == ComponentInstance.VALID && state == ComponentInstance.INVALID) {
+ transition = LifecycleCallback.INVALIDATE;
+ }
+
+ // Manage immediate component
+ if (m_immediate && transition == LifecycleCallback.VALIDATE && getInstanceManager().getPojoObjects() == null) {
+ getInstanceManager().getPojoObject();
+ }
+
+ for (int i = 0; i < m_callbacks.length; i++) {
+ if (m_callbacks[i].getTransition() == transition) {
+ try {
+ m_callbacks[i].call();
+ } catch (NoSuchMethodException e) {
+ error("[" + getInstanceManager().getInstanceName() + "] The callback method " + m_callbacks[i].getMethod() + " is not found");
+ throw new IllegalStateException(e.getMessage());
+ } catch (IllegalAccessException e) {
+ error("[" + getInstanceManager().getInstanceName() + "] The callback method " + m_callbacks[i].getMethod() + " is not accessible");
+ throw new IllegalStateException(e.getMessage());
+ } catch (InvocationTargetException e) {
+ error("[" + getInstanceManager().getInstanceName() + "] The callback method " + m_callbacks[i].getMethod() + " has thrown an exception : " + e.getTargetException().getMessage(), e.getTargetException());
+ throw new IllegalStateException(e.getTargetException().getMessage());
+ }
+ }
+ }
+ // Update to internal state
+ m_state = state;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/controller/ControllerHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/controller/ControllerHandler.java
new file mode 100644
index 0000000..08bdbe2
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/lifecycle/controller/ControllerHandler.java
@@ -0,0 +1,162 @@
+/*
+ * 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.felix.ipojo.handlers.lifecycle.controller;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+
+/**
+ * Lifecycle Controller handler.
+ * This handler allow a POJO to vote for the instance state. By setting a boolean field to true or false, the handler state changed.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ControllerHandler extends PrimitiveHandler {
+
+ /**
+ * Actual handler (i.e. field value) state
+ */
+ private boolean m_state;
+ /**
+ * The controller field name.
+ */
+ private String m_field;
+
+ /**
+ * Configure method.
+ * Look for the first 'controller' element.
+ * @param metadata : metadata
+ * @param configuration : configuration
+ * @throws ConfigurationException : the field attribute is missing or does not exist in the class.
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.InstanceManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ Element[] controller = metadata.getElements("controller");
+ m_field = controller[0].getAttribute("field");
+ getInstanceManager().register(new FieldMetadata(m_field, "boolean"), this);
+ }
+
+ /**
+ * Start method.
+ * This methods initializes the controller value.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ // On start we should ask for the value
+ Object o = getInstanceManager().getFieldValue(m_field);
+ if (o == null || ! (o instanceof Boolean)) {
+ m_state = true;
+ } else {
+ m_state = ((Boolean) o).booleanValue();
+ }
+
+ if ( ! m_state) {
+ setValidity(false);
+ }
+ }
+
+ /**
+ * Stop method.
+ * Nothing to do.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ // Nothing to do.
+ }
+
+
+
+ /**
+ * GetterCallback.
+ * @param pojo : the pojo object on which the field is accessed
+ * Return the stored value.
+ * @param field : field name.
+ * @param value : value given by the previous handler.
+ * @return : the handler state.
+ */
+ public Object onGet(Object pojo, String field, Object value) {
+ if (m_state) {
+ return Boolean.TRUE;
+ } else {
+ return Boolean.FALSE;
+ }
+ }
+
+ /**
+ * SetterCallback.
+ * @param pojo : the pojo object on which the field is accessed
+ * Store the new field value & invalidate / validate the handler is required.
+ * @param field : field name.
+ * @param value : new value.
+ */
+ public void onSet(Object pojo, String field, Object value) {
+ if (value instanceof Boolean) {
+ boolean newValue = ((Boolean) value).booleanValue();
+ if (newValue != m_state) {
+ m_state = newValue;
+ if (m_state) {
+ ((InstanceManager) getHandlerManager()).setState(ComponentInstance.VALID);
+ setValidity(true);
+ } else {
+ ((InstanceManager) getHandlerManager()).setState(ComponentInstance.INVALID);
+ setValidity(false);
+ }
+ }
+ } else {
+ error("Boolean expected for the lifecycle controller");
+ getInstanceManager().stop();
+ }
+ }
+
+ /**
+ * Initialize the component factory.
+ * The controller field is checked to avoid configure check.
+ * @param desc : component description
+ * @param metadata : component type metadata
+ * @throws ConfigurationException : occurs if the controller field is not in the POJO class or is not a boolean.
+ * @see org.apache.felix.ipojo.Handler#initializeComponentFactory(org.apache.felix.ipojo.architecture.ComponentTypeDescription, org.apache.felix.ipojo.metadata.Element)
+ */
+ public void initializeComponentFactory(ComponentTypeDescription desc, Element metadata) throws ConfigurationException {
+ String field = null;
+ Element[] controller = metadata.getElements("controller");
+ // Use only the first controller
+ field = controller[0].getAttribute("field");
+ if (field == null) {
+ throw new ConfigurationException("Lifecycle controller : the controller element needs to contain a field attribute");
+ }
+
+ PojoMetadata method = getFactory().getPojoMetadata();
+ FieldMetadata fieldMetadata = method.getField(field);
+ if (fieldMetadata == null) {
+ throw new ConfigurationException("Lifecycle controller : The field " + field + " does not exist in the implementation class");
+ }
+
+ if (!fieldMetadata.getFieldType().equalsIgnoreCase("boolean")) {
+ throw new ConfigurationException("Lifecycle controller : The field " + field + " must be a boolean (" + fieldMetadata.getFieldType() + " found)");
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/CreationStrategy.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/CreationStrategy.java
new file mode 100644
index 0000000..d0f1ae8
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/CreationStrategy.java
@@ -0,0 +1,76 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice;
+
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.InstanceManager;
+import org.osgi.framework.ServiceFactory;
+
+/**
+ * Creation strategy to creation service object.
+ * This class is extended by all service object
+ * creation policy.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class CreationStrategy implements ServiceFactory {
+ /**
+ * Method called when the service is registered.
+ * @param instance the instance registering the service.
+ * @param interfaces the exposed service specification interfaces
+ * @param props the published properties.
+ */
+ public abstract void onPublication(InstanceManager instance, String[] interfaces, Properties props);
+ /**
+ * Method called when the service in unregistered.
+ */
+ public abstract void onUnpublication();
+
+ /**
+ * Checks if the given method object is the
+ * {@link IPOJOServiceFactory#getService(ComponentInstance)}
+ * method.
+ * @param method the method to check
+ * @return <code>true</code> if the method is the getService method
+ * <code>false</code> otherwise.
+ */
+ public static boolean isGetServiceMethod(Method method) {
+ Class[] params = method.getParameterTypes();
+ return method.getName().equals("getService")
+ && params.length == 1
+ && params[0].getName().equals(ComponentInstance.class.getName());
+ }
+
+ /**
+ * Checks if the given method object is the
+ * {@link IPOJOServiceFactory#ungetService(ComponentInstance, Object)}
+ * method.
+ * @param method the method to check
+ * @return <code>true</code> if the method is the ungetService method
+ * <code>false</code> otherwise.
+ */
+ public static boolean isUngetServiceMethod(Method method) {
+ Class[] params = method.getParameterTypes();
+ return method.getName().equals("ungetService")
+ && params.length == 2
+ && params[0].getName().equals(ComponentInstance.class.getName());
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
new file mode 100644
index 0000000..a539984
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
@@ -0,0 +1,1005 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+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.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.IPOJOServiceFactory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.util.Callback;
+import org.apache.felix.ipojo.util.Property;
+import org.apache.felix.ipojo.util.SecurityHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Provided Service represent a provided service by the component.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedService implements ServiceFactory {
+
+ /**
+ * Service State : REGISTRED.
+ */
+ public static final int REGISTERED = 1;
+
+ /**
+ * Service State : UNREGISTRED.
+ */
+ public static final int UNREGISTERED = 0;
+
+ /**
+ * Factory Policy : SINGLETON_FACTORY.
+ */
+ public static final int SINGLETON_STRATEGY = 0;
+
+ /**
+ * Factory policy : SERVICE_FACTORY.
+ */
+ public static final int SERVICE_STRATEGY = 1;
+
+ /**
+ * Factory policy : STATIC_FACTORY.
+ */
+ public static final int STATIC_STRATEGY = 2;
+
+ /**
+ * Factory policy : INSTANCE.
+ * Creates one service object per instance consuming the service.
+ */
+ public static final int INSTANCE_STRATEGY = 3;
+
+ /**
+ * At this time, it is only the java interface full name.
+ */
+ private String[] m_serviceSpecifications = new String[0];
+
+ /**
+ * The service registration. is null when the service is not registered.
+ * m_serviceRegistration : ServiceRegistration
+ */
+ private ServiceRegistration m_serviceRegistration;
+
+ /**
+ * Link to the owner handler. m_handler : Provided Service Handler
+ */
+ private ProvidedServiceHandler m_handler;
+
+ /**
+ * Properties Array.
+ */
+ private Property[] m_properties;
+
+ /**
+ * Service Object creation policy.
+ */
+ private CreationStrategy m_strategy;
+
+ /**
+ * Were the properties updated during the processing.
+ */
+ private volatile boolean m_wasUpdated;
+
+ /**
+ * Service Controller.
+ */
+ private Map /*<Specification, ServiceController>*/ m_controllers = new HashMap/*<Specification, ServiceController>*/();
+
+ /**
+ * Post-Registration callback.
+ */
+ private Callback m_postRegistration;
+
+ /**
+ * Post-Unregistration callback.
+ */
+ private Callback m_postUnregistration;
+
+ /**
+ * The published properties.
+ */
+ private Dictionary m_publishedProperties = new Properties();
+
+ /**
+ * Creates a provided service object.
+ *
+ * @param handler the the provided service handler.
+ * @param specification the specifications provided by this provided service
+ * @param factoryPolicy the service providing policy
+ * @param creationStrategyClass the customized service object creation strategy.
+ * @param conf the instance configuration.
+ */
+ public ProvidedService(ProvidedServiceHandler handler, String[] specification, int factoryPolicy, Class creationStrategyClass, Dictionary conf) {
+ m_handler = handler;
+
+ m_serviceSpecifications = specification;
+
+ // Add instance name, factory name and factory version is set.
+ try {
+ addProperty(new Property("instance.name", null, null, handler.getInstanceManager().getInstanceName(), String.class.getName(), handler.getInstanceManager(), handler));
+ addProperty(new Property("factory.name", null, null, handler.getInstanceManager().getFactory().getFactoryName(), String.class.getName(), handler.getInstanceManager(), handler));
+
+ if (handler.getInstanceManager().getFactory().getVersion() != null) {
+ addProperty(new Property("factory.version", null, null, handler.getInstanceManager().getFactory().getVersion(), String.class.getName(), handler.getInstanceManager(), handler));
+ }
+
+ // Add the service.* if defined
+ if (conf.get(Constants.SERVICE_PID) != null) {
+ addProperty(new Property(Constants.SERVICE_PID, null, null, (String) conf.get(Constants.SERVICE_PID), String.class.getName(), handler.getInstanceManager(), handler));
+ }
+ if (conf.get(Constants.SERVICE_RANKING) != null) {
+ addProperty(new Property(Constants.SERVICE_RANKING, null, null, (String) conf.get(Constants.SERVICE_RANKING), "int", handler.getInstanceManager(), handler));
+ }
+ if (conf.get(Constants.SERVICE_VENDOR) != null) {
+ addProperty(new Property(Constants.SERVICE_VENDOR, null, null, (String) conf.get(Constants.SERVICE_VENDOR), String.class.getName(), handler.getInstanceManager(), handler));
+ }
+ if (conf.get(Constants.SERVICE_DESCRIPTION) != null) {
+ addProperty(new Property(Constants.SERVICE_DESCRIPTION, null, null, (String) conf.get(Constants.SERVICE_DESCRIPTION), String.class.getName(), handler.getInstanceManager(), handler));
+ }
+
+ } catch (ConfigurationException e) {
+ m_handler.error("An exception occurs when adding instance.name and factory.name property : " + e.getMessage());
+ }
+
+ if (creationStrategyClass != null) {
+ try {
+ m_strategy = (CreationStrategy) creationStrategyClass.newInstance();
+ } catch (IllegalAccessException e) {
+ m_handler.error("["
+ + m_handler.getInstanceManager().getInstanceName()
+ + "] The customized service object creation policy "
+ + "(" + creationStrategyClass.getName() + ") is not accessible: "
+ + e.getMessage(), e);
+ getInstanceManager().stop();
+ return;
+ } catch (InstantiationException e) {
+ m_handler.error("["
+ + m_handler.getInstanceManager().getInstanceName()
+ + "] The customized service object creation policy "
+ + "(" + creationStrategyClass.getName() + ") cannot be instantiated: "
+ + e.getMessage(), e);
+ getInstanceManager().stop();
+ return;
+ }
+ } else {
+ switch (factoryPolicy) {
+ case SINGLETON_STRATEGY:
+ m_strategy = new SingletonStrategy();
+ break;
+ case SERVICE_STRATEGY:
+ case STATIC_STRATEGY:
+ // In this case, we need to try to create a new pojo object,
+ // the factory method will handle the creation.
+ m_strategy = new FactoryStrategy();
+ break;
+ case INSTANCE_STRATEGY:
+ m_strategy = new PerInstanceStrategy();
+ break;
+ // Other policies:
+ // Thread : one service object per asking thread
+ // Consumer : one service object per consumer
+ default:
+ List specs = Arrays.asList(m_serviceSpecifications);
+ m_handler.error("["
+ + m_handler.getInstanceManager().getInstanceName()
+ + "] Unknown creation policy for " + specs + " : "
+ + factoryPolicy);
+ getInstanceManager().stop();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Add properties to the provided service.
+ * @param props : the properties to attached to the service registration
+ */
+ protected void setProperties(Property[] props) {
+ for (int i = 0; i < props.length; i++) {
+ addProperty(props[i]);
+ }
+ }
+
+ /**
+ * Add the given property to the property list.
+ *
+ * @param prop : the element to add
+ */
+ private synchronized void addProperty(Property prop) {
+ for (int i = 0; (m_properties != null) && (i < m_properties.length); i++) {
+ if (m_properties[i] == prop) {
+ return;
+ }
+ }
+
+ if (m_properties == null) {
+ m_properties = new Property[] { prop };
+ } else {
+ Property[] newProp = new Property[m_properties.length + 1];
+ System.arraycopy(m_properties, 0, newProp, 0, m_properties.length);
+ newProp[m_properties.length] = prop;
+ m_properties = newProp;
+ }
+ }
+
+ /**
+ * Remove a property.
+ *
+ * @param name : the property to remove
+ * @return <code>true</code> if the property was removed,
+ * <code>false</code> otherwise.
+ */
+ private synchronized boolean removeProperty(String name) {
+ int idx = -1;
+ for (int i = 0; i < m_properties.length; i++) {
+ if (m_properties[i].getName().equals(name)) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx >= 0) {
+ if ((m_properties.length - 1) == 0) {
+ m_properties = null;
+ } else {
+ Property[] newPropertiesList = new Property[m_properties.length - 1];
+ System.arraycopy(m_properties, 0, newPropertiesList, 0, idx);
+ if (idx < newPropertiesList.length) {
+ System.arraycopy(m_properties, idx + 1, newPropertiesList, idx, newPropertiesList.length - idx);
+ }
+ m_properties = newPropertiesList;
+
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the service reference of the service registration.
+ * @return the service reference of the provided service (null if the
+ * service is not published).
+ */
+ public ServiceReference getServiceReference() {
+ if (m_serviceRegistration == null) {
+ return null;
+ } else {
+ return m_serviceRegistration.getReference();
+ }
+ }
+
+ /**
+ * Returns a service object for the dependency.
+ * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)
+ * @param bundle : the bundle
+ * @param registration : the service registration of the registered service
+ * @return a new service object or a already created service object (in the case of singleton) or <code>null</code>
+ * if the instance is no more valid.
+ */
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+ if (getInstanceManager().getState() == InstanceManager.VALID) {
+ return m_strategy.getService(bundle, registration);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * The unget method.
+ *
+ * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle,
+ * org.osgi.framework.ServiceRegistration, java.lang.Object)
+ * @param bundle : bundle
+ * @param registration : service registration
+ * @param service : service object
+ */
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ m_strategy.ungetService(bundle, registration, service);
+ }
+
+ /**
+ * Registers the service. The service object must be able to serve this
+ * service.
+ * This method also notifies the creation strategy of the publication.
+ */
+ protected void registerService() {
+ ServiceRegistration reg = null;
+ Properties serviceProperties = null;
+ synchronized (this) {
+ if (m_serviceRegistration != null) {
+ return;
+ } else {
+ if (m_handler.getInstanceManager().getState() == ComponentInstance.VALID
+ && m_serviceRegistration == null
+ && isAtLeastAServiceControllerValid()) {
+ // Build the service properties list
+
+ BundleContext bc = m_handler.getInstanceManager().getContext();
+ // Security check
+ if (SecurityHelper.hasPermissionToRegisterServices(
+ m_serviceSpecifications, bc)) {
+ serviceProperties = getServiceProperties();
+ m_strategy.onPublication(getInstanceManager(),
+ getServiceSpecificationsToRegister(),
+ serviceProperties);
+ m_serviceRegistration = bc.registerService(
+ getServiceSpecificationsToRegister(), this,
+ (Dictionary) serviceProperties);
+ reg = m_serviceRegistration; // Stack confinement
+ } else {
+ throw new SecurityException("The bundle "
+ + bc.getBundle().getBundleId()
+ + " does not have the"
+ + " permission to register the services "
+ + Arrays.asList(m_serviceSpecifications));
+ }
+ }
+ }
+ }
+
+ // An update may happen during the registration, re-check and apply.
+ // This must be call outside the synchronized block.
+ if (reg != null && m_wasUpdated) {
+ Properties updated = getServiceProperties();
+ reg.setProperties((Dictionary) updated);
+ m_publishedProperties = updated;
+ m_wasUpdated = false;
+ }
+
+ synchronized (this) {
+ // Call the post-registration callback in the same thread holding
+ // the monitor lock.
+ // This allows to be sure that the callback is called once per
+ // registration.
+ // But the callback must take care to not create a deadlock
+ if (m_postRegistration != null) {
+ try {
+ m_postRegistration
+ .call(new Object[] { m_serviceRegistration
+ .getReference() });
+ } catch (Exception e) {
+ m_handler.error(
+ "Cannot invoke the post-registration callback "
+ + m_postRegistration.getMethod(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregisters the service.
+ */
+ protected synchronized void unregisterService() {
+ // Create a copy of the service reference in the case we need
+ // to inject it to the post-unregistration callback.
+ ServiceReference ref = null;
+ if (m_serviceRegistration != null) {
+ ref = m_serviceRegistration.getReference();
+ m_serviceRegistration.unregister();
+ m_serviceRegistration = null;
+ }
+
+ m_strategy.onUnpublication();
+
+ // Call the post-unregistration callback in the same thread holding the monitor lock.
+ // This allows to be sure that the callback is called once per unregistration.
+ // But the callback must take care to not create a deadlock
+ if (m_postUnregistration != null && ref != null) {
+ try {
+ m_postUnregistration.call(new Object[] { ref });
+ } catch (Exception e) {
+ m_handler.error("Cannot invoke the post-unregistration callback " + m_postUnregistration.getMethod(), e);
+ }
+ }
+
+ }
+
+ /**
+ * Get the current provided service state.
+ * @return The state of the provided service.
+ */
+ public int getState() {
+ if (m_serviceRegistration == null) {
+ return UNREGISTERED;
+ } else {
+ return REGISTERED;
+ }
+ }
+
+ protected InstanceManager getInstanceManager() {
+ return m_handler.getInstanceManager();
+ }
+
+ /**
+ * Return the list of properties attached to this service. This list
+ * contains only property where a value are assigned.
+ *
+ * @return the properties attached to the provided service.
+ */
+ private Properties getServiceProperties() {
+ // Build the service properties list
+ Properties serviceProperties = new Properties();
+ for (int i = 0; i < m_properties.length; i++) {
+ Object value = m_properties[i].getValue();
+ if (value != null && value != Property.NO_VALUE) {
+ serviceProperties.put(m_properties[i].getName(), value);
+ }
+ }
+ return serviceProperties;
+ }
+
+ /**
+ * Get the list of properties attached to the service registration.
+ * @return the properties attached to the provided service.
+ */
+ public Property[] getProperties() {
+ return m_properties;
+ }
+
+ /**
+ * Update the service properties. The new list of properties is sent to
+ * the service registry.
+ */
+ public synchronized void update() {
+ // Update the service registration
+ if (m_serviceRegistration != null) {
+ Properties updated = getServiceProperties();
+ Dictionary oldProps = (Dictionary) ((Properties) m_publishedProperties).clone();
+ Dictionary newProps = (Dictionary) (updated.clone());
+
+ // Remove keys that must not be compared
+ newProps.remove("instance.name");
+ oldProps.remove("instance.name");
+ newProps.remove(Constants.SERVICE_ID);
+ oldProps.remove(Constants.SERVICE_ID);
+ newProps.remove(Constants.SERVICE_PID);
+ oldProps.remove(Constants.SERVICE_PID);
+ newProps.remove("factory.name");
+ oldProps.remove("factory.name");
+ newProps.remove(ConfigurationAdmin.SERVICE_FACTORYPID);
+ oldProps.remove(ConfigurationAdmin.SERVICE_FACTORYPID);
+
+ // Trigger the update only if the properties have changed.
+
+ // First check, are the size equals
+ if (oldProps.size() != newProps.size()) {
+ m_handler.info("Updating Registration : " + oldProps.size() + " / " + newProps.size());
+ m_publishedProperties = updated;
+ m_serviceRegistration.setProperties((Dictionary) updated);
+ } else {
+ // Check changes
+ Enumeration keys = oldProps.keys();
+ while (keys.hasMoreElements()) {
+ String k = (String) keys.nextElement();
+ Object val = oldProps.get(k);
+ if (! val.equals(updated.get(k))) {
+ m_handler.info("Updating Registration : " + k);
+ m_publishedProperties = updated;
+ m_serviceRegistration.setProperties((Dictionary) updated);
+ return;
+ }
+ }
+ }
+ } else {
+ // Need to be updated later.
+ m_wasUpdated = true;
+ }
+ }
+
+ /**
+ * Add properties to the list.
+ * @param props : properties to add
+ */
+ protected void addProperties(Dictionary props) {
+ Enumeration keys = props.keys();
+ boolean updated = false;
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ Object value = props.get(key);
+
+ boolean alreadyExisting = false;
+ for (int i = 0; i < m_properties.length; i++) {
+ if (key.equals(m_properties[i].getName())) {
+ alreadyExisting = true;
+ // Check whether the value changed.
+ if (m_properties[i].getValue() == null
+ || ! value.equals(m_properties[i].getValue())) {
+ m_properties[i].setValue(value);
+ updated = true;
+ }
+ }
+ }
+
+ if (! alreadyExisting) {
+ try {
+ Property prop = new Property(key, null, null, value, getInstanceManager(), m_handler);
+ addProperty(prop);
+ updated = true;
+ } catch (ConfigurationException e) {
+ m_handler.error("The propagated property " + key + " cannot be created correctly : " + e.getMessage());
+ }
+ }
+ }
+
+ if (updated) {
+ m_handler.info("Update triggered by adding properties " + props);
+ update();
+ }
+ }
+
+ /**
+ * Remove properties from the list.
+ * @param props : properties to remove
+ */
+ protected void deleteProperties(Dictionary props) {
+ Enumeration keys = props.keys();
+ boolean mustUpdate = false;
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ mustUpdate = mustUpdate || removeProperty(key);
+ }
+
+ if (mustUpdate) {
+ m_handler.info("Update triggered when removing properties : " + props);
+ update();
+ }
+ }
+
+ /**
+ * Get the published service specifications.
+ * @return the list of provided service specifications (i.e. java
+ * interface).
+ */
+ public String[] getServiceSpecifications() {
+ return m_serviceSpecifications;
+ }
+
+ /**
+ * Get the service registration.
+ * @return the service registration of this service.
+ */
+ public ServiceRegistration getServiceRegistration() {
+ return m_serviceRegistration;
+ }
+
+ /**
+ * Sets the service controller on this provided service.
+ * @param field the field attached to this controller
+ * @param value the value the initial value
+ * @param specification the target specification, if <code>null</code>
+ * affect all specifications.
+ */
+ public void setController(String field, boolean value, String specification) {
+ if (specification == null) {
+ m_controllers.put("ALL", new ServiceController(field, value));
+ } else {
+ m_controllers.put(specification, new ServiceController(field, value));
+
+ }
+ }
+
+ public ServiceController getController(String field) {
+ Collection controllers = m_controllers.values();
+ Iterator iterator = controllers.iterator();
+ while (iterator.hasNext()) {
+ ServiceController controller = (ServiceController) iterator.next();
+ if (field.equals(controller.m_field)) {
+ return controller;
+ }
+ }
+ return null;
+ }
+
+ public ServiceController getControllerBySpecification(String spec) {
+ return (ServiceController) m_controllers.get(spec);
+ }
+
+ /**
+ * Checks if at least one service controller is valid.
+ * @return <code>true</code> if one service controller at least
+ * is valid.
+ */
+ private boolean isAtLeastAServiceControllerValid() {
+ Collection controllers = m_controllers.values();
+
+ // No controller
+ if (controllers.isEmpty()) {
+ return true;
+ }
+
+ Iterator iterator = controllers.iterator();
+ while (iterator.hasNext()) {
+ ServiceController controller = (ServiceController) iterator.next();
+ if (controller.getValue()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String[] getServiceSpecificationsToRegister() {
+ if (m_controllers.isEmpty()) {
+ return m_serviceSpecifications;
+ }
+
+ ArrayList l = new ArrayList();
+ if (m_controllers.containsKey("ALL")) {
+ ServiceController ctrl = (ServiceController) m_controllers.get("ALL");
+ if (ctrl.m_value) {
+ l.addAll(Arrays.asList(m_serviceSpecifications));
+ }
+ }
+
+ Iterator iterator = m_controllers.keySet().iterator();
+ while (iterator.hasNext()) {
+ String spec = (String) iterator.next();
+ ServiceController ctrl = (ServiceController) m_controllers.get(spec);
+ if (ctrl.m_value) {
+ if (! "ALL".equals(spec)) { // Already added.
+ if (! l.contains(spec)) {
+ l.add(spec);
+ }
+ }
+ } else {
+ l.remove(spec);
+ }
+ }
+
+ return (String[]) l.toArray(new String[l.size()]);
+
+ }
+
+ public void setPostRegistrationCallback(Callback cb) {
+ m_postRegistration = cb;
+ }
+
+ public void setPostUnregistrationCallback(Callback cb) {
+ m_postUnregistration = cb;
+ }
+
+ /**
+ * Service Controller.
+ */
+ class ServiceController {
+ /**
+ * The controller value.
+ */
+ private boolean m_value;
+ /**
+ * The field attached to this controller.
+ */
+ private final String m_field;
+
+ /**
+ * Creates a ServiceController.
+ * @param field the field
+ * @param value the initial value
+ */
+ public ServiceController(String field, boolean value) {
+ m_field = field;
+ m_value = value;
+ }
+
+ public String getField() {
+ return m_field;
+ }
+
+ /**
+ * Gets the value.
+ * @return the value
+ */
+ public boolean getValue() {
+ synchronized (ProvidedService.this) {
+ return m_value;
+ }
+ }
+
+ /**
+ * Sets the value.
+ * @param value the value
+ */
+ public void setValue(Boolean value) {
+ synchronized (ProvidedService.this) {
+ if (value.booleanValue() != m_value) {
+ // If there is a change to the ServiceController value then
+ // we will
+ // need to modify the registrations.
+ m_value = value.booleanValue();
+ unregisterService();
+ if (getServiceSpecificationsToRegister().length != 0) {
+ registerService();
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Singleton creation strategy.
+ * This strategy just creates one service object and
+ * returns always the same.
+ */
+ private class SingletonStrategy extends CreationStrategy {
+
+ /**
+ * The service is going to be registered.
+ * @param instance the instance manager
+ * @param interfaces the published interfaces
+ * @param props the properties
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties)
+ */
+ public void onPublication(InstanceManager instance, String[] interfaces,
+ Properties props) { }
+
+ /**
+ * The service was unpublished.
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication()
+ */
+ public void onUnpublication() { }
+
+ /**
+ * A service object is required.
+ * @param arg0 the bundle requiring the service object.
+ * @param arg1 the service registration.
+ * @return the first pojo object.
+ * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)
+ */
+ public Object getService(Bundle arg0, ServiceRegistration arg1) {
+ return m_handler.getInstanceManager().getPojoObject();
+ }
+
+ /**
+ * A service object is released.
+ * @param arg0 the bundle
+ * @param arg1 the service registration
+ * @param arg2 the get service object.
+ * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object)
+ */
+ public void ungetService(Bundle arg0, ServiceRegistration arg1,
+ Object arg2) {
+ }
+
+ }
+
+ /**
+ * Service object creation policy following the OSGi Service Factory
+ * policy {@link ServiceFactory}.
+ */
+ private class FactoryStrategy extends CreationStrategy {
+
+ /**
+ * The service is going to be registered.
+ * @param instance the instance manager
+ * @param interfaces the published interfaces
+ * @param props the properties
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties)
+ */
+ public void onPublication(InstanceManager instance, String[] interfaces,
+ Properties props) { }
+
+ /**
+ * The service is unpublished.
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication()
+ */
+ public void onUnpublication() { }
+
+ /**
+ * OSGi Service Factory getService method.
+ * Returns a new service object per asking bundle.
+ * This object is then cached by the framework.
+ * @param arg0 the bundle requiring the service
+ * @param arg1 the service registration
+ * @return the service object for the asking bundle
+ * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)
+ */
+ public Object getService(Bundle arg0, ServiceRegistration arg1) {
+ return m_handler.getInstanceManager().createPojoObject();
+ }
+
+ /**
+ * OSGi Service Factory unget method.
+ * Deletes the created object for the asking bundle.
+ * @param arg0 the asking bundle
+ * @param arg1 the service registration
+ * @param arg2 the created service object returned for this bundle
+ * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object)
+ */
+ public void ungetService(Bundle arg0, ServiceRegistration arg1,
+ Object arg2) {
+ m_handler.getInstanceManager().deletePojoObject(arg2);
+ }
+ }
+
+
+ /**
+ * Service object creation policy creating a service object per asking iPOJO component
+ * instance. This creation policy follows the iPOJO Service Factory interaction pattern
+ * and does no support 'direct' invocation.
+ */
+ private class PerInstanceStrategy extends CreationStrategy implements IPOJOServiceFactory, InvocationHandler {
+ /**
+ * Map [ComponentInstance->ServiceObject] storing created service objects.
+ */
+ private Map/*<ComponentInstance, ServiceObject>*/ m_instances = new HashMap();
+
+ /**
+ * A method is invoked on the proxy object.
+ * If the method is the {@link IPOJOServiceFactory#getService(ComponentInstance)}
+ * method, this method creates a service object if not already created for the asking
+ * component instance.
+ * If the method is {@link IPOJOServiceFactory#ungetService(ComponentInstance, Object)}
+ * the service object is unget (i.e. removed from the map and deleted).
+ * In all other cases, a {@link UnsupportedOperationException} is thrown as this policy
+ * requires to use the {@link IPOJOServiceFactory} interaction pattern.
+ * @param arg0 the proxy object
+ * @param arg1 the called method
+ * @param arg2 the arguments
+ * @return the service object attached to the asking instance for 'get',
+ * <code>null</code> for 'unget',
+ * a {@link UnsupportedOperationException} for all other methods.
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(Object arg0, Method arg1, Object[] arg2) {
+ if (isGetServiceMethod(arg1)) {
+ return getService((ComponentInstance) arg2[0]);
+ }
+
+ if (isUngetServiceMethod(arg1)) {
+ ungetService((ComponentInstance) arg2[0], arg2[1]);
+ return null;
+ }
+
+ // Regular methods from java.lang.Object : equals and hashCode
+ if (arg1.getName().equals("equals") && arg2 != null && arg2.length == 1) {
+ return this.equals(arg2[0]);
+ }
+
+ if (arg1.getName().equals("hashCode")) {
+ return this.hashCode();
+ }
+
+ throw new UnsupportedOperationException("This service requires an advanced creation policy. "
+ + "Before calling the service, call the getService(ComponentInstance) method to get "
+ + "the service object. - Method called: " + arg1.getName());
+ }
+
+ /**
+ * A service object is required.
+ * This policy returns a service object per asking instance.
+ * @param instance the instance requiring the service object
+ * @return the service object for this instance
+ * @see org.apache.felix.ipojo.IPOJOServiceFactory#getService(org.apache.felix.ipojo.ComponentInstance)
+ */
+ public Object getService(ComponentInstance instance) {
+ Object obj = m_instances.get(instance);
+ if (obj == null) {
+ obj = m_handler.getInstanceManager().createPojoObject();
+ m_instances.put(instance, obj);
+ }
+ return obj;
+ }
+
+ /**
+ * A service object is unget.
+ * The service object is removed from the map and deleted.
+ * @param instance the instance releasing the service
+ * @param svcObject the service object
+ * @see org.apache.felix.ipojo.IPOJOServiceFactory#ungetService(org.apache.felix.ipojo.ComponentInstance, java.lang.Object)
+ */
+ public void ungetService(ComponentInstance instance, Object svcObject) {
+ Object pojo = m_instances.remove(instance);
+ m_handler.getInstanceManager().deletePojoObject(pojo);
+ }
+
+ /**
+ * The service is going to be registered.
+ * @param instance the instance manager
+ * @param interfaces the published interfaces
+ * @param props the properties
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties)
+ */
+ public void onPublication(InstanceManager instance, String[] interfaces,
+ Properties props) { }
+
+ /**
+ * The service is going to be unregistered.
+ * The instance map is cleared. Created object are disposed.
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication()
+ */
+ public void onUnpublication() {
+ Collection col = m_instances.values();
+ Iterator it = col.iterator();
+ while (it.hasNext()) {
+ m_handler.getInstanceManager().deletePojoObject(it.next());
+ }
+ m_instances.clear();
+ }
+
+ /**
+ * OSGi Service Factory getService method.
+ * @param arg0 the asking bundle
+ * @param arg1 the service registration
+ * @return a proxy implementing the {@link IPOJOServiceFactory}
+ * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)
+ */
+ public Object getService(Bundle arg0, ServiceRegistration arg1) {
+ Object proxy = Proxy.newProxyInstance(getInstanceManager().getClazz().getClassLoader(),
+ getSpecificationsWithIPOJOServiceFactory(m_serviceSpecifications, m_handler.getInstanceManager().getContext()), this);
+ return proxy;
+ }
+
+ /**
+ * OSGi Service factory unget method.
+ * Does nothing.
+ * @param arg0 the asking bundle
+ * @param arg1 the service registration
+ * @param arg2 the service object created for this bundle.
+ * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object)
+ */
+ public void ungetService(Bundle arg0, ServiceRegistration arg1,
+ Object arg2) { }
+
+ /**
+ * Utility method returning the class array of provided service
+ * specification and the {@link IPOJOServiceFactory} interface.
+ * @param specs the published service interface
+ * @param bc the bundle context, used to load classes
+ * @return the class array containing provided service specification and
+ * the {@link IPOJOServiceFactory} class.
+ */
+ private Class[] getSpecificationsWithIPOJOServiceFactory(String[] specs, BundleContext bc) {
+ Class[] classes = new Class[specs.length + 1];
+ int i = 0;
+ for (i = 0; i < specs.length; i++) {
+ try {
+ classes[i] = bc.getBundle().loadClass(specs[i]);
+ } catch (ClassNotFoundException e) {
+ // Should not happen.
+ }
+ }
+ classes[i] = IPOJOServiceFactory.class;
+ return classes;
+ }
+
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java
new file mode 100644
index 0000000..2973b41
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java
@@ -0,0 +1,159 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice;
+
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.util.Property;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Provided Service Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceDescription {
+
+ /**
+ * State : the service is unregistered.
+ */
+ public static final int UNREGISTERED = ProvidedService.UNREGISTERED;
+
+ /**
+ * State : the service is registered.
+ */
+ public static final int REGISTERED = ProvidedService.REGISTERED;
+
+ /**
+ * The describe provided service.
+ */
+ private ProvidedService m_ps;
+
+ /**
+ * Constructor.
+ * @param ps the described provided service.
+ */
+ public ProvidedServiceDescription(ProvidedService ps) {
+ m_ps = ps;
+ }
+
+ /**
+ * Gets the list of provided service specifications.
+ * @return the provided contract name.
+ */
+ public String[] getServiceSpecifications() {
+ return m_ps.getServiceSpecifications();
+ }
+
+ /**
+ * Gets the list of properties.
+ * A copy of the actual property set is returned.
+ * @return the properties.
+ */
+ public Properties getProperties() {
+ Properties props = new Properties();
+ org.apache.felix.ipojo.util.Property[] ps = m_ps.getProperties();
+ for (int i = 0; i < ps.length; i++) {
+ if (ps[i].getValue() != Property.NO_VALUE) {
+ props.put(ps[i].getName(), ps[i].getValue());
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Adds and Updates service properties.
+ * Existing properties are updated.
+ * New ones are added.
+ * @param props the new properties
+ */
+ public void addProperties(Dictionary props) {
+ m_ps.addProperties(props);
+ }
+
+ /**
+ * Removes service properties.
+ * @param props the properties to remove
+ */
+ public void removeProperties(Dictionary props) {
+ m_ps.deleteProperties(props);
+ }
+
+ /**
+ * Gets provided service state.
+ * @return the state of the provided service (UNREGISTERED | REGISTRED).
+ */
+ public int getState() {
+ return m_ps.getState();
+ }
+
+ /**
+ * Gets the controller value as a String.
+ * @return the value
+ */
+ public String getController() {
+ if (m_ps.getControllerBySpecification("ALL") == null) {
+ return null;
+ } else {
+ return String.valueOf(m_ps.getControllerBySpecification("ALL").getValue());
+ }
+ }
+
+ /**
+ * Gets the controller value as a String.
+ * @param specification
+ * @return the value
+ */
+ public String getController(String specification) {
+ if (m_ps.getControllerBySpecification(specification) == null) {
+ return null;
+ } else {
+ return String.valueOf(m_ps.getControllerBySpecification(specification).getValue());
+ }
+ }
+
+ /**
+ * Gets the service reference.
+ * @return the service reference (null if the service is unregistered).
+ */
+ public ServiceReference getServiceReference() {
+ return m_ps.getServiceReference();
+ }
+
+ /**
+ * Gets the 'main' service object.
+ * @return the 'main' service object or <code>null</code>
+ * if no service object are created.
+ */
+ public Object getService() {
+ Object[] objs = m_ps.getInstanceManager().getPojoObjects();
+ if (objs == null) {
+ return null;
+ } else {
+ return objs[0];
+ }
+ }
+
+ public Object[] getServices() {
+ return m_ps.getInstanceManager().getPojoObjects();
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
new file mode 100644
index 0000000..170275a
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
@@ -0,0 +1,684 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice;
+
+import java.lang.reflect.Field;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.Pojo;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.handlers.dependency.Dependency;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandler;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedService.ServiceController;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.util.Callback;
+import org.apache.felix.ipojo.util.Logger;
+import org.apache.felix.ipojo.util.Property;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Composite Provided Service Handler.
+ * This handler manage the service providing for a composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandler extends PrimitiveHandler {
+
+ /**
+ * The list of the provided service.
+ */
+ private ProvidedService[] m_providedServices = new ProvidedService[0];
+
+ /**
+ * The handler description.
+ */
+ private ProvidedServiceHandlerDescription m_description;
+
+ /**
+ * Add a provided service to the list .
+ *
+ * @param svc : the provided service to add
+ */
+ private void addProvidedService(ProvidedService svc) {
+ // Verify that the provided service is not already in the array.
+ for (int i = 0; i < m_providedServices.length; i++) {
+ if (m_providedServices[i] == svc) { return; }
+ }
+
+ if (m_providedServices.length > 0) {
+ ProvidedService[] newPS = new ProvidedService[m_providedServices.length + 1];
+ System.arraycopy(m_providedServices, 0, newPS, 0, m_providedServices.length);
+ newPS[m_providedServices.length] = svc;
+ m_providedServices = newPS;
+ } else {
+ m_providedServices = new ProvidedService[] { svc };
+ }
+ }
+
+ /**
+ * Get the array of provided service.
+ * @return the list of the provided service.
+ */
+ public ProvidedService[] getProvidedServices() {
+ return m_providedServices;
+ }
+
+ /**
+ * Configure the handler.
+ * @param componentMetadata : the component type metadata
+ * @param configuration : the instance configuration
+ * @throws ConfigurationException : the metadata are not correct.
+ * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.InstanceManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element componentMetadata, Dictionary configuration) throws ConfigurationException {
+ m_providedServices = new ProvidedService[0];
+ // Create the dependency according to the component metadata
+ Element[] providedServices = componentMetadata.getElements("Provides");
+ for (int i = 0; i < providedServices.length; i++) {
+ String[] serviceSpecifications = ParseUtils.parseArrays(providedServices[i].getAttribute("specifications")); // Set by the initialize component factory.
+
+ // Get the factory policy
+ int factory = ProvidedService.SINGLETON_STRATEGY;
+ Class custom = null;
+ String strategy = providedServices[i].getAttribute("strategy");
+ if (strategy == null) {
+ strategy = providedServices[i].getAttribute("factory");
+ }
+ if (strategy != null) {
+ if ("singleton".equalsIgnoreCase(strategy)) {
+ factory = ProvidedService.SINGLETON_STRATEGY;
+ } else if ("service".equalsIgnoreCase(strategy)) {
+ factory = ProvidedService.SERVICE_STRATEGY;
+ } else if ("method".equalsIgnoreCase(strategy)) {
+ factory = ProvidedService.STATIC_STRATEGY;
+ } else if ("instance".equalsIgnoreCase(strategy)) {
+ factory = ProvidedService.INSTANCE_STRATEGY;
+ } else {
+ // Customized policy
+ try {
+ custom = getInstanceManager().getContext().getBundle().loadClass(strategy);
+ if (! CreationStrategy.class.isAssignableFrom(custom)) {
+ throw new ConfigurationException("The custom creation policy class " + custom.getName() + " does not implement " + CreationStrategy.class.getName());
+ }
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("The custom creation policy class " + strategy + " cannot be loaded " + e.getMessage());
+
+ }
+
+ }
+ }
+
+
+ // Then create the provided service
+ ProvidedService svc = new ProvidedService(this, serviceSpecifications, factory, custom, configuration);
+
+ // Post-Registration callback
+ String post = providedServices[i].getAttribute("post-registration");
+ if (post != null) {
+ Callback cb = new Callback(post, new Class[] {ServiceReference.class}, false, getInstanceManager());
+ svc.setPostRegistrationCallback(cb);
+ }
+
+ post = providedServices[i].getAttribute("post-unregistration");
+ if (post != null) {
+ // TODO Can we really send the service reference here ?
+ Callback cb = new Callback(post, new Class[] {ServiceReference.class}, false, getInstanceManager());
+ svc.setPostUnregistrationCallback(cb);
+ }
+
+ Element[] props = providedServices[i].getElements("Property");
+ if (props != null) {
+ //Property[] properties = new Property[props.length];
+ Property[] properties = new Property[props.length];
+ for (int j = 0; j < props.length; j++) {
+ String name = props[j].getAttribute("name");
+ String value = props[j].getAttribute("value");
+ String type = props[j].getAttribute("type");
+ String field = props[j].getAttribute("field");
+
+ Property prop = new Property(name, field, null, value, type, getInstanceManager(), this);
+ properties[j] = prop;
+
+ // Check if the instance configuration has a value for this property
+ Object object = configuration.get(prop.getName());
+ if (object != null) {
+ prop.setValue(object);
+ }
+
+ if (field != null) {
+ getInstanceManager().register(new FieldMetadata(field, type), this);
+ // Cannot register the property as the interception is necessary
+ // to deal with registration update.
+ }
+ }
+
+ // Attach to properties to the provided service
+ svc.setProperties(properties);
+ }
+
+ Element[] controllers = providedServices[i].getElements("Controller");
+ if (controllers != null) {
+ for (int k = 0; k < controllers.length; k++) {
+ String field = controllers[k].getAttribute("field");
+ if (field == null) {
+ throw new ConfigurationException("The field attribute of a controller is mandatory");
+ }
+
+ String v = controllers[k].getAttribute("value");
+ boolean value = ! (v != null && v.equalsIgnoreCase("false"));
+ String s = controllers[k].getAttribute("specification");
+ if (s == null) {
+ s ="ALL";
+ }
+ svc.setController(field, value, s);
+
+ getInstanceManager().register(new FieldMetadata(field, "boolean"), this);
+ }
+ }
+
+ if (checkProvidedService(svc)) {
+ addProvidedService(svc);
+ } else {
+ StringBuffer itfs = new StringBuffer();
+ for (int j = 0; j < serviceSpecifications.length; j++) {
+ itfs.append(' ');
+ itfs.append(serviceSpecifications[j]);
+ }
+ throw new ConfigurationException("The provided service" + itfs + " is not valid");
+ }
+
+ // Initialize the description.
+ m_description = new ProvidedServiceHandlerDescription(this, m_providedServices);
+
+ }
+ }
+
+ /**
+ * Collect interfaces implemented by the POJO.
+ * @param specs : implemented interfaces.
+ * @param parent : parent class.
+ * @param bundle : Bundle object.
+ * @param interfaces : the set of implemented interfaces
+ * @param classes : the set of extended classes
+ * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
+ */
+ private void computeInterfacesAndSuperClasses(String[] specs, String parent, Bundle bundle, Set interfaces, Set classes) throws ClassNotFoundException {
+ // First iterate on found specification in manipulation metadata
+ for (int i = 0; i < specs.length; i++) {
+ interfaces.add(specs[i]);
+ // Iterate on interfaces implemented by the current interface
+ Class clazz = bundle.loadClass(specs[i]);
+ collectInterfaces(clazz, interfaces, bundle);
+ }
+
+ // Look for parent class.
+ if (parent != null) {
+ Class clazz = bundle.loadClass(parent);
+ collectInterfacesFromClass(clazz, interfaces, bundle);
+ classes.add(parent);
+ collectParentClassesFromClass(clazz, classes, bundle);
+ }
+ }
+
+ /**
+ * Look for inherited interfaces.
+ * @param clazz : interface name to explore (class object)
+ * @param acc : set (accumulator)
+ * @param bundle : bundle
+ * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
+ */
+ private void collectInterfaces(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class[] clazzes = clazz.getInterfaces();
+ for (int i = 0; i < clazzes.length; i++) {
+ acc.add(clazzes[i].getName());
+ collectInterfaces(clazzes[i], acc, bundle);
+ }
+ }
+
+ /**
+ * Collect interfaces for the given class.
+ * This method explores super class to.
+ * @param clazz : class object.
+ * @param acc : set of implemented interface (accumulator)
+ * @param bundle : bundle.
+ * @throws ClassNotFoundException : occurs if an interface cannot be load.
+ */
+ private void collectInterfacesFromClass(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class[] clazzes = clazz.getInterfaces();
+ for (int i = 0; i < clazzes.length; i++) {
+ acc.add(clazzes[i].getName());
+ collectInterfaces(clazzes[i], acc, bundle);
+ }
+ // Iterate on parent classes
+ Class sup = clazz.getSuperclass();
+ if (sup != null) {
+ collectInterfacesFromClass(sup, acc, bundle);
+ }
+ }
+
+ /**
+ * Collect parent classes for the given class.
+ * @param clazz : class object.
+ * @param acc : set of extended classes (accumulator)
+ * @param bundle : bundle.
+ * @throws ClassNotFoundException : occurs if an interface cannot be load.
+ */
+ private void collectParentClassesFromClass(Class clazz, Set acc, Bundle bundle) throws ClassNotFoundException {
+ Class parent = clazz.getSuperclass();
+ if (parent != null) {
+ acc.add(parent.getName());
+ collectParentClassesFromClass(parent, acc, bundle);
+ }
+ }
+
+ /**
+ * Check the provided service given in argument in the sense that the metadata are consistent.
+ * @param svc : the provided service to check.
+ * @return true if the provided service is correct
+ * @throws ConfigurationException : the checked provided service is not correct.
+ */
+ private boolean checkProvidedService(ProvidedService svc) throws ConfigurationException {
+ for (int i = 0; i < svc.getServiceSpecifications().length; i++) {
+ String specName = svc.getServiceSpecifications()[i];
+
+ // Check service level dependencies
+ try {
+ Class spec = getInstanceManager().getFactory().loadClass(specName);
+ Field specField = spec.getField("specification");
+ Object specif = specField.get(null);
+ if (specif instanceof String) {
+ Element specification = ManifestMetadataParser.parse((String) specif);
+ Element[] deps = specification.getElements("requires");
+ for (int j = 0; deps != null && j < deps.length; j++) {
+ Dependency dep = getAttachedDependency(deps[j]);
+ if (dep != null) {
+ // Fix service-level dependency flag
+ dep.setServiceLevelDependency();
+ }
+ isDependencyCorrect(dep, deps[j]);
+ }
+ } else {
+ throw new ConfigurationException("Service Providing: The specification field of the service specification " + svc.getServiceSpecifications()[i] + " needs to be a String");
+ }
+ } catch (NoSuchFieldException e) {
+ return true; // No specification field
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Service Providing: The service specification " + svc.getServiceSpecifications()[i] + " cannot be load");
+ } catch (IllegalArgumentException e) {
+ throw new ConfigurationException("Service Providing: The field 'specification' of the service specification " + svc.getServiceSpecifications()[i] + " is not accessible : " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Service Providing: The field 'specification' of the service specification " + svc.getServiceSpecifications()[i] + " is not accessible : " + e.getMessage());
+ } catch (ParseException e) {
+ throw new ConfigurationException("Service Providing: The field 'specification' of the service specification " + svc.getServiceSpecifications()[i] + " does not contain a valid String : " + e.getMessage());
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Look for the implementation (i.e. component) dependency for the given service-level requirement metadata.
+ * @param element : the service-level requirement metadata
+ * @return the Dependency object, null if not found or if the DependencyHandler is not plugged to the instance
+ */
+ private Dependency getAttachedDependency(Element element) {
+ DependencyHandler handler = (DependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":requires");
+ if (handler == null) { return null; }
+
+ String identity = element.getAttribute("id");
+ if (identity != null) {
+ // Look for dependency Id
+ for (int i = 0; i < handler.getDependencies().length; i++) {
+ if (handler.getDependencies()[i].getId().equals(identity)) { return handler.getDependencies()[i]; }
+ }
+ }
+ // If not found or no id, look for a dependency with the same specification
+ String requirement = element.getAttribute("specification");
+ for (int i = 0; i < handler.getDependencies().length; i++) {
+ if ((handler.getDependencies()[i].getSpecification().getName()).equals(requirement)) { return handler.getDependencies()[i]; }
+ }
+
+ return null;
+ }
+
+ /**
+ * Check the correctness of the implementation dependency against the service level dependency.
+ * @param dep : dependency to check
+ * @param elem : service-level dependency metadata
+ * @throws ConfigurationException : the service level dependency and the implementation dependency does not match.
+ */
+ private void isDependencyCorrect(Dependency dep, Element elem) throws ConfigurationException {
+ String optional = elem.getAttribute("optional");
+ boolean opt = optional != null && optional.equalsIgnoreCase("true");
+
+ String aggregate = elem.getAttribute("aggregate");
+ boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
+
+ if (dep == null && !opt) {
+ throw new ConfigurationException("Service Providing: The requirement " + elem.getAttribute("specification") + " is not present in the implementation and is declared as a mandatory service-level requirement");
+ }
+
+ if (dep != null && dep.isAggregate() && !agg) {
+ throw new ConfigurationException("Service Providing: The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ }
+
+ String filter = elem.getAttribute("filter");
+ if (dep != null && filter != null) {
+ String filter2 = dep.getFilter();
+ if (filter2 == null || !filter2.equalsIgnoreCase(filter)) {
+ throw new ConfigurationException("Service Providing: The specification requirement " + elem.getAttribute("specification") + " has not the same filter as declared in the service-level requirement");
+ }
+ }
+ }
+
+ /**
+ * Stop the provided service handler.
+ *
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ //Nothing to do.
+ }
+
+ /**
+ * Start the provided service handler.
+ *
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ // Nothing to do.
+ }
+
+ /**
+ * Setter Callback Method.
+ * Check if the modified field is a property to update the value.
+ * @param pojo : the pojo object on which the field is accessed
+ * @param fieldName : field name
+ * @param value : new value
+ * @see org.apache.felix.ipojo.Handler#onSet(Object,
+ * java.lang.String, java.lang.Object)
+ */
+ public void onSet(Object pojo, String fieldName, Object value) {
+ // Verify that the field name correspond to a dependency
+ for (int i = 0; i < m_providedServices.length; i++) {
+ ProvidedService svc = m_providedServices[i];
+ boolean update = false;
+ for (int j = 0; j < svc.getProperties().length; j++) {
+ Property prop = svc.getProperties()[j];
+ if (fieldName.equals(prop.getField()) && ! prop.getValue().equals(value)) {
+ // it is the associated property
+ prop.setValue(value);
+ update = true;
+ }
+ }
+ if (update) {
+ svc.update();
+ }
+ ServiceController ctrl = svc.getController(fieldName);
+ if (ctrl != null) {
+ if (value instanceof Boolean) {
+ ctrl.setValue((Boolean) value);
+ } else {
+ warn("Boolean value expected for the service controler " + fieldName);
+ }
+ }
+ }
+ // Else do nothing
+ }
+
+ /**
+ * Getter Callback Method.
+ * Check if the field is a property to push the stored value.
+ * @param pojo : the pojo object on which the field is accessed
+ * @param fieldName : field name
+ * @param value : value pushed by the previous handler
+ * @return the stored value or the previous value.
+ * @see org.apache.felix.ipojo.Handler#onGet(Object,
+ * java.lang.String, java.lang.Object)
+ */
+ public Object onGet(Object pojo, String fieldName, Object value) {
+ for (int i = 0; i < m_providedServices.length; i++) {
+ ProvidedService svc = m_providedServices[i];
+ for (int j = 0; j < svc.getProperties().length; j++) {
+ Property prop = svc.getProperties()[j];
+ if (fieldName.equals(prop.getField())) {
+ // Manage the No Value case.
+ return prop.onGet(pojo, fieldName, value);
+ }
+ }
+ ServiceController ctrl = svc.getController(fieldName);
+ if (ctrl != null) {
+ return new Boolean(ctrl.getValue());
+ }
+ }
+ // Else it is not a property
+ return value;
+ }
+
+ /**
+ * Register the services if the new state is VALID. Unregister the services
+ * if the new state is UNRESOLVED.
+ *
+ * @param state : the new instance state.
+ * @see org.apache.felix.ipojo.Handler#stateChanged(int)
+ */
+ public void stateChanged(int state) {
+ // If the new state is INVALID => unregister all the services
+ if (state == InstanceManager.INVALID) {
+ for (int i = 0; i < m_providedServices.length; i++) {
+ m_providedServices[i].unregisterService();
+ }
+ return;
+ }
+
+ // If the new state is VALID => register all the services
+ if (state == InstanceManager.VALID) {
+ for (int i = 0; i < m_providedServices.length; i++) {
+ m_providedServices[i].registerService();
+ }
+ return;
+ }
+ }
+
+ /**
+ * Adds properties to all provided services.
+ * @param dict : dictionary of properties to add
+ */
+ public void addProperties(Dictionary dict) {
+ for (int i = 0; i < m_providedServices.length; i++) {
+ System.out.println("adding properties " + dict);
+ m_providedServices[i].addProperties(dict);
+ m_providedServices[i].update();
+ }
+ }
+
+ /**
+ * Remove properties form all provided services.
+ *
+ * @param dict : dictionary of properties to delete.
+ */
+ public void removeProperties(Dictionary dict) {
+ for (int i = 0; i < m_providedServices.length; i++) {
+ m_providedServices[i].deleteProperties(dict);
+ m_providedServices[i].update();
+ }
+ }
+
+ /**
+ * Build the provided service description.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.Handler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return m_description;
+ }
+
+ /**
+ * Reconfigure provided service.
+ * @param dict : the new instance configuration.
+ * @see org.apache.felix.ipojo.Handler#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary dict) {
+ for (int j = 0; j < getProvidedServices().length; j++) {
+ ProvidedService svc = getProvidedServices()[j];
+ Property[] props = svc.getProperties();
+ boolean update = false;
+ for (int k = 0; k < props.length; k++) {
+ if (dict.get(props[k].getName()) != null) {
+ update = true;
+ props[k].setValue(dict.get(props[k].getName()));
+ }
+ }
+ if (update) {
+ svc.update();
+ }
+ }
+ }
+
+ /**
+ * Initialize the component type.
+ * @param desc : component type description to populate.
+ * @param metadata : component type metadata.
+ * @throws ConfigurationException : occurs when the POJO does not implement any interfaces.
+ * @see org.apache.felix.ipojo.Handler#initializeComponentFactory(org.apache.felix.ipojo.architecture.ComponentTypeDescription, org.apache.felix.ipojo.metadata.Element)
+ */
+ public void initializeComponentFactory(ComponentTypeDescription desc, Element metadata) throws ConfigurationException {
+ // Change ComponentInfo
+ Element[] provides = metadata.getElements("provides");
+ PojoMetadata manipulation = getFactory().getPojoMetadata();
+
+ for (int i = 0; i < provides.length; i++) {
+ // First : create the serviceSpecification list
+ String[] serviceSpecification = manipulation.getInterfaces();
+ String parent = manipulation.getSuperClass();
+ Set interfaces = new HashSet();
+ Set parentClasses = new HashSet();
+ try {
+ computeInterfacesAndSuperClasses(serviceSpecification, parent, desc.getBundleContext().getBundle(), interfaces, parentClasses);
+ getLogger().log(Logger.INFO, "Collected interfaces from " + metadata.getAttribute("classname") + " : " + interfaces);
+ getLogger().log(Logger.INFO, "Collected super classes from " + metadata.getAttribute("classname") + " : " + parentClasses);
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("An interface or parent class cannot be loaded : " + e.getMessage());
+ }
+
+ String serviceSpecificationStr = provides[i].getAttribute("specifications");
+ if (serviceSpecificationStr == null) {
+ serviceSpecificationStr = provides[i].getAttribute("interface");
+ if (serviceSpecificationStr != null) {
+ warn("The 'interface' attribute is deprecated, use the 'specifications' attribute instead of 'interface'");
+ }
+ }
+
+ if (serviceSpecificationStr != null) {
+ List itfs = ParseUtils.parseArraysAsList(serviceSpecificationStr);
+ for (int j = 0; j < itfs.size(); j++) {
+ if (! interfaces.contains(itfs.get(j))
+ && ! parentClasses.contains(itfs.get(j))
+ && ! desc.getFactory().getClassName().equals(itfs.get(j))) {
+ desc.getFactory().getLogger().log(Logger.ERROR, "The specification " + itfs.get(j) + " is not implemented by " + metadata.getAttribute("classname"));
+ }
+ }
+ interfaces = new HashSet(itfs);
+ }
+
+ if (interfaces.isEmpty()) {
+ warn("No service interface found in the class hierarchy, use the implementation class");
+ interfaces.add(desc.getFactory().getClassName());
+ }
+
+ StringBuffer specs = null;
+ Set set = new HashSet(interfaces);
+ set.remove(Pojo.class.getName()); // Remove POJO.
+ Iterator iterator = set.iterator();
+ while (iterator.hasNext()) {
+ String spec = (String) iterator.next();
+ desc.addProvidedServiceSpecification(spec);
+ if (specs == null) {
+ specs = new StringBuffer("{");
+ specs.append(spec);
+ } else {
+ specs.append(',');
+ specs.append(spec);
+ }
+ }
+
+ specs.append('}');
+ provides[i].addAttribute(new Attribute("specifications", specs.toString())); // Add interface attribute to avoid checking in the configure method
+
+ Element[] props = provides[i].getElements("property");
+ for (int j = 0; props != null && j < props.length; j++) {
+ String name = props[j].getAttribute("name");
+ String value = props[j].getAttribute("value");
+ String type = props[j].getAttribute("type");
+ String field = props[j].getAttribute("field");
+
+
+ // Get property name :
+ if (field != null && name == null) {
+ name = field;
+ }
+
+ // Check type if not already set
+ if (type == null) {
+ if (field == null) {
+ throw new ConfigurationException("The property " + name + " has neither type nor field.");
+ }
+ FieldMetadata fieldMeta = manipulation.getField(field);
+ if (fieldMeta == null) {
+ throw new ConfigurationException("A declared property was not found in the implementation class : " + field);
+ }
+ type = fieldMeta.getFieldType();
+ props[j].addAttribute(new Attribute("type", type));
+ }
+
+ // Is the property set to immutable
+ boolean immutable = false;
+ String imm = props[j].getAttribute("immutable");
+ if (imm != null && imm.equalsIgnoreCase("true")) {
+ immutable = true;
+ }
+
+ PropertyDescription pd = new PropertyDescription(name, type, value, immutable);
+ desc.addProperty(pd);
+
+ String man = props[j].getAttribute("mandatory");
+ if (man != null && man.equalsIgnoreCase("true")) {
+ pd.setMandatory();
+ }
+ }
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java
new file mode 100644
index 0000000..fa080f9
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java
@@ -0,0 +1,119 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.Constants;
+
+/**
+ * Provided Service Handler Description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandlerDescription extends HandlerDescription {
+
+ /**
+ * Provided Service Description list.
+ */
+ private ProvidedServiceDescription[] m_providedServices = new ProvidedServiceDescription[0];
+
+ /**
+ * Constructor.
+ * @param handler the handler.
+ * @param pss the list of provided service
+ */
+ public ProvidedServiceHandlerDescription(ProvidedServiceHandler handler, ProvidedService[] pss) {
+ super(handler);
+ m_providedServices = new ProvidedServiceDescription[pss.length];
+ for (int i = 0; i < pss.length; i++) {
+ m_providedServices[i] = new ProvidedServiceDescription(pss[i]);
+ }
+ }
+
+ /**
+ * Get the provided service descriptions.
+ * @return the provided service description list.
+ */
+ public ProvidedServiceDescription[] getProvidedServices() {
+ return m_providedServices;
+ }
+
+ /**
+ * Build the provided service handler description.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element services = super.getHandlerInfo();
+ for (int i = 0; i < m_providedServices.length; i++) {
+ Element service = new Element("provides", null);
+ StringBuffer spec = new StringBuffer("[");
+ for (int j = 0; j < m_providedServices[i].getServiceSpecifications().length; j++) {
+ if (j == 0) {
+ spec.append(m_providedServices[i].getServiceSpecifications()[j]);
+ } else {
+ spec.append(',');
+ spec.append(m_providedServices[i].getServiceSpecifications()[j]);
+ }
+ }
+ spec.append(']');
+ service.addAttribute(new Attribute("specifications", spec.toString()));
+
+ if (m_providedServices[i].getState() == ProvidedService.REGISTERED) {
+ service.addAttribute(new Attribute("state", "registered"));
+ service.addAttribute(new Attribute("service.id", m_providedServices[i].getServiceReference()
+ .getProperty(Constants.SERVICE_ID).toString()));
+ } else {
+ service.addAttribute(new Attribute("state", "unregistered"));
+ }
+
+ // Service Properties.
+ Properties props = m_providedServices[i].getProperties();
+ Iterator iterator = props.keySet().iterator();
+ while (iterator.hasNext()) {
+ Element prop = new Element("property", null);
+ String name = (String) iterator.next();
+ prop.addAttribute(new Attribute("name", name));
+ Object obj = props.get(name);
+ if (obj != null) {
+ prop.addAttribute(new Attribute("value", obj.toString()));
+ } else {
+ prop.addAttribute(new Attribute("value", "null"));
+ }
+ service.addElement(prop);
+ }
+
+ // Service Controller.
+ if (m_providedServices[i].getController() != null) {
+ Element controller = new Element("controller", null);
+ controller.addAttribute(new Attribute("value", m_providedServices[i].getController()));
+ service.addElement(controller);
+ }
+
+ services.addElement(service);
+ }
+ return services;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ConfigurableCreationStrategy.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ConfigurableCreationStrategy.java
new file mode 100644
index 0000000..5c5ed35
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ConfigurableCreationStrategy.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.felix.ipojo.handlers.providedservice.strategy;
+
+import java.lang.reflect.Proxy;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.IPOJOServiceFactory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.handlers.providedservice.CreationStrategy;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * This {@link CreationStrategy} is here to ease customization of the strategy
+ * by hiding all the reflection stuff.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class ConfigurableCreationStrategy extends CreationStrategy {
+
+ /**
+ * The instance manager passed to the iPOJO ServiceFactory to manage
+ * instances.
+ */
+ private InstanceManager m_manager;
+
+ /**
+ * The lists of interfaces provided by this service.
+ */
+ private String[] m_specs;
+
+ /**
+ * The iPOJO ServiceFactory.
+ */
+ private IPOJOServiceFactory m_factory;
+
+ /**
+ * Method called when the underlying iPOJO Service factory
+ * is published.
+ * @param manager the instance manager
+ * @param specifications the provided specifications
+ * @param props the service properties
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(org.apache.felix.ipojo.InstanceManager, java.lang.String[], java.util.Properties)
+ */
+ public void onPublication(final InstanceManager manager,
+ final String[] specifications, final Properties props) {
+ // Store specifications (aka interfaces)
+ this.m_specs = specifications;
+ this.m_manager = manager;
+ }
+
+ /**
+ * Method called when the underlying iPOJO Service factory is
+ * un-published.
+ * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication()
+ */
+ public void onUnpublication() {
+ // Try to close the factory
+ if (m_factory instanceof ServiceObjectFactory) {
+ ((ServiceObjectFactory) m_factory).close();
+ }
+ }
+
+ /**
+ * Method called when a bundle want to access a service. This method is called once per
+ * asking bundle. To turn around this limitation, a proxy is registered.
+ * @param bundle the asking bundle
+ * @param registration the service registration
+ * @return the service object
+ * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)
+ */
+ public Object getService(final Bundle bundle,
+ final ServiceRegistration registration) {
+ // Init the factory if needed
+ if (m_factory == null) {
+ m_factory = getServiceFactory(m_manager);
+ }
+ // Return a proxy wrapping the real iPOJO ServiceFactory
+ return Proxy.newProxyInstance(m_manager.getClazz().getClassLoader(),
+ getModifiedSpecifications(m_manager.getContext()),
+ new ErrorPrintingServiceFactoryProxy(m_factory));
+ }
+
+ /**
+ * Method called when a bundle release a service.
+ * @param bundle the bundle
+ * @param registration the service registration
+ * @param service the service object
+ * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object)
+ */
+ public void ungetService(final Bundle bundle,
+ final ServiceRegistration registration, final Object service) {
+ // Nothing to do
+ }
+
+ /**
+ * Utility method that transform the specifications names into a Class
+ * array, appending the IPOJOServiceFactory interface to it.
+ * @param context used for class loading
+ * @return a array of Class
+ */
+ private Class[] getModifiedSpecifications(final BundleContext context) {
+ Class[] classes = new Class[m_specs.length + 1];
+ int i = 0;
+ for (i = 0; i < m_specs.length; i++) {
+ try {
+ classes[i] = context.getBundle().loadClass(m_specs[i]);
+ } catch (ClassNotFoundException e) {
+ // Should not happen.
+ }
+ }
+ classes[i] = IPOJOServiceFactory.class;
+ return classes;
+ }
+
+ /**
+ * User provided {@link CreationStrategy} MUST implement this method to
+ * provide the real iPOJO ServiceFactory instance.
+ * @param manager {@link InstanceManager} that the factory could use
+ * @return an instance of {@link IPOJOServiceFactory}
+ */
+ protected abstract IPOJOServiceFactory getServiceFactory(
+ final InstanceManager manager);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ErrorPrintingServiceFactoryProxy.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ErrorPrintingServiceFactoryProxy.java
new file mode 100644
index 0000000..ec51005
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ErrorPrintingServiceFactoryProxy.java
@@ -0,0 +1,104 @@
+/*
+ * 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.felix.ipojo.handlers.providedservice.strategy;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.IPOJOServiceFactory;
+
+/**
+ * This proxy class is here to wrap an iPOJO ServiceFactory.
+ * If the consumer of this service do not call the getService or ungetService
+ * methods, it will get an Exception with an explicit error message telling
+ * him that this service is only usable through iPOJO.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ErrorPrintingServiceFactoryProxy implements InvocationHandler {
+
+ /**
+ * getService(ComponentInstance) method.
+ */
+ private static Method GET_METHOD;
+
+ /**
+ * ungetService(ComponentInstance, Object) method.
+ */
+ private static Method UNGET_METHOD;
+
+ /**
+ * Wrapped factory.
+ */
+ private final IPOJOServiceFactory m_factory;
+
+ static {
+ // Static initialization of theses constants.
+ try {
+ GET_METHOD = IPOJOServiceFactory.class.getMethod("getService",
+ new Class[]{ComponentInstance.class});
+ UNGET_METHOD = IPOJOServiceFactory.class.getMethod("ungetService",
+ new Class[]{ComponentInstance.class, Object.class});
+ } catch (Exception e) {
+ // Should never happen
+ }
+ }
+
+ /**
+ * Wraps a ServiceFactory in an InvocationHandler that will delegate only
+ * get/ungetService methods to the factory. All other methods will be
+ * rejected with a meaningful error message.
+ *
+ * @param factory delegating iPOJO ServiceFactory
+ */
+ public ErrorPrintingServiceFactoryProxy(final IPOJOServiceFactory factory) {
+ this.m_factory = factory;
+ }
+
+ /**
+ * 'Invoke' methods called when a method is called on the proxy.
+ * @param proxy the proxy
+ * @param method the method
+ * @param args the arguments
+ * @return the result
+ * @throws Exception if something wrong happens
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(final Object proxy,
+ final Method method,
+ final Object[] args) throws Exception {
+
+ // Handle get/unget operations
+ if (GET_METHOD.equals(method)) {
+ return m_factory.getService((ComponentInstance) args[0]);
+ }
+ if (UNGET_METHOD.equals(method)) {
+ m_factory.ungetService((ComponentInstance) args[0], args[1]);
+ return null;
+ }
+
+ // All other methods are rejected
+ throw new UnsupportedOperationException("This service requires an advanced creation policy. "
+ + "Before calling the service, call the IPOJOServiceFactory.getService(ComponentInstance) "
+ + "method to get the service object. ");
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ServiceObjectFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ServiceObjectFactory.java
new file mode 100644
index 0000000..26c4fc7
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/strategy/ServiceObjectFactory.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 org.apache.felix.ipojo.handlers.providedservice.strategy;
+
+import org.apache.felix.ipojo.IPOJOServiceFactory;
+
+/**
+ * Extended iPOJOServiceFactory that supports a close() operation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceObjectFactory extends IPOJOServiceFactory {
+
+ /**
+ * Called when the supporting CreationStrategy is unpublished
+ * from the service registry. This allows to do an explicit
+ * cleanup.
+ */
+ void close();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/FieldMetadata.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/FieldMetadata.java
new file mode 100644
index 0000000..db8bc98
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/FieldMetadata.java
@@ -0,0 +1,159 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A Field Metadata represents a field of the implementation class.
+ * This class avoids using reflection to get the type and the name of a field.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FieldMetadata {
+
+ /**
+ * The name of the field.
+ */
+ private String m_name;
+
+ /**
+ * The yype of the field.
+ */
+ private String m_type;
+
+ /**
+ * Creates a field metadata.
+ * This constructor is used when creating the {@link PojoMetadata}
+ * object.
+ * @param metadata the field manipulation element from Manipulation
+ * Metadata.
+ */
+ FieldMetadata(Element metadata) {
+ m_name = metadata.getAttribute("name");
+ m_type = metadata.getAttribute("type");
+ }
+
+ /**
+ * Creates a field metadata.
+ * This constructor can be used to avoid using {@link PojoMetadata}.
+ * Be care that creating such {@link FieldMetadata} does not assert its
+ * presence in the implementation class.
+ * @param field the field name.
+ * @param type the type of the field.
+ */
+ public FieldMetadata(String field, String type) {
+ m_name = field;
+ m_type = type;
+ }
+
+ public String getFieldName() { return m_name; }
+
+ public String getFieldType() { return m_type; }
+
+ /**
+ * Gets the 'reflective' type of the given type.
+ * The reflective type is the type used by the Java Reflection API.
+ * More precisely this method handles the array cases
+ * @param type the type to analyze to find the Java reflective type.
+ * @return the reflective type corresponding to this field.
+ */
+ public static String getReflectionType(String type) {
+ // Primitive Array
+ if (type.endsWith("[]") && type.indexOf('.') == -1) {
+ int index = type.indexOf('[');
+ return '[' + getInternalPrimitiveType(type.substring(0, index));
+ }
+ // Non-Primitive Array
+ if (type.endsWith("[]") && type.indexOf('.') != -1) {
+ int index = type.indexOf('[');
+ return "[L" + type.substring(0, index) + ";";
+ }
+ // The type is not an array.
+ return type;
+ }
+
+ /**
+ * Gets the internal notation for primitive type.
+ * @param string the String form of the type
+ * @return the internal notation or <code>null</code> if not found
+ */
+ public static String getInternalPrimitiveType(String string) {
+ if (string.equalsIgnoreCase("boolean")) {
+ return "Z";
+ }
+ if (string.equalsIgnoreCase("char")) {
+ return "C";
+ }
+ if (string.equalsIgnoreCase("byte")) {
+ return "B";
+ }
+ if (string.equalsIgnoreCase("short")) {
+ return "S";
+ }
+ if (string.equalsIgnoreCase("int")) {
+ return "I";
+ }
+ if (string.equalsIgnoreCase("float")) {
+ return "F";
+ }
+ if (string.equalsIgnoreCase("long")) {
+ return "J";
+ }
+ if (string.equalsIgnoreCase("double")) {
+ return "D";
+ }
+ return null;
+ }
+
+ /**
+ * Gets the iPOJO primitive type from the given primitive class.
+ * @param clazz the class of the primitive type
+ * @return the iPOJO primitive type name or <code>null</code> if
+ * not found.
+ */
+ public static String getPrimitiveTypeByClass(Class clazz) {
+ if (clazz.equals(Boolean.TYPE)) {
+ return "boolean";
+ }
+ if (clazz.equals(Character.TYPE)) {
+ return "char";
+ }
+ if (clazz.equals(Byte.TYPE)) {
+ return "byte";
+ }
+ if (clazz.equals(Short.TYPE)) {
+ return "short";
+ }
+ if (clazz.equals(Integer.TYPE)) {
+ return "int";
+ }
+ if (clazz.equals(Float.TYPE)) {
+ return "float";
+ }
+ if (clazz.equals(Long.TYPE)) {
+ return "long";
+ }
+ if (clazz.equals(Double.TYPE)) {
+ return "double";
+ }
+ return null;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
new file mode 100644
index 0000000..1acbfe1
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
@@ -0,0 +1,560 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * The Manifest Metadata parser reads a manifest file and builds
+ * the iPOJO metadata ({@link Element} / {@link Attribute} ) structure.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ManifestMetadataParser {
+
+ /**
+ * The element list.
+ * Contains the element found in the parsed header.
+ */
+ private Element[] m_elements = new Element[0];
+
+ /**
+ * Gets the array of component type metadata.
+ * @return the component metadata (composite & component).
+ * An empty array is returned if no component type declaration.
+ * @throws ParseException if a parsing error occurs
+ */
+ public Element[] getComponentsMetadata() throws ParseException {
+ Element[] elems = m_elements[0].getElements();
+ List list = new ArrayList();
+ for (int i = 0; i < elems.length; i++) {
+ if (!"instance".equals(elems[i].getName())) {
+ list.add(elems[i]);
+ }
+ }
+ return (Element[]) list.toArray(new Element[list.size()]);
+ }
+
+ /**
+ * Gets the array of instance configuration described in the metadata.
+ * @return the instances list or <code>null</code> if no instance configuration.
+ * @throws ParseException if the metadata cannot be parsed successfully
+ */
+ public Dictionary[] getInstances() throws ParseException {
+ Element[] configs = m_elements[0].getElements("instance");
+ if (configs == null) {
+ return null;
+ }
+ Dictionary[] dicts = new Dictionary[configs.length];
+ for (int i = 0; i < configs.length; i++) {
+ dicts[i] = parseInstance(configs[i]);
+ }
+ return dicts;
+ }
+
+ /**
+ * Parses an Element to create an instance configuration dictionary.
+ * The 'name' attribute of the instance declaration is mapped
+ * to the 'instance.name' property.
+ * @param instance the Element describing an instance.
+ * @return the resulting dictionary
+ * @throws ParseException if a configuration cannot be parse correctly.
+ */
+ private Dictionary parseInstance(Element instance) throws ParseException {
+ Dictionary dict = new Properties();
+ String name = instance.getAttribute("name");
+ String comp = instance.getAttribute("component");
+ String version = instance.getAttribute("version");
+
+ if (name != null) {
+ dict.put("instance.name", instance.getAttribute("name"));
+ }
+
+ if (comp == null) {
+ throw new ParseException("An instance does not have the 'component' attribute");
+ }
+
+ dict.put("component", comp);
+
+ if (version != null) {
+ dict.put("factory.version", version);
+ }
+
+ Element[] props = instance.getElements("property");
+
+ for (int i = 0; props != null && i < props.length; i++) {
+ parseProperty(props[i], dict);
+ }
+
+ return dict;
+ }
+
+ /**
+ * Parses an instance property.
+ * This method is recursive to handle complex properties.
+ * @param prop the current element to parse
+ * @param dict the dictionary to populate (the future instance configuration)
+ * @throws ParseException if the property cannot be parsed correctly
+ */
+ private void parseProperty(Element prop, Dictionary dict) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name == null) {
+ throw new ParseException("A property does not have the 'name' attribute: " + prop);
+ }
+
+ //case : the property element has no 'value' attribute
+ if (value == null) {
+ // Recursive case
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ dict.put(name, parseDictionary(prop));
+ } else if (type.equalsIgnoreCase("map")) {
+ dict.put(name, parseMap(prop));
+ } else if (type.equalsIgnoreCase("list")) {
+ dict.put(name, parseList(prop));
+ } else if (type.equalsIgnoreCase("array")) {
+ List list = parseList(prop);
+ boolean isString = true;
+ for (int i = 0; isString && i < list.size(); i++) {
+ isString = list.get(i) instanceof String;
+ }
+ Object[] obj = null;
+ if (isString) {
+ obj = new String[list.size()];
+ } else {
+ obj = new Object[list.size()];
+ }
+ // Transform the list to array
+ dict.put(name, list.toArray(obj));
+ }
+ } else {
+ dict.put(prop.getAttribute("name"), prop.getAttribute("value"));
+ }
+ }
+
+ /**
+ * Parses a complex property.
+ * This property will be built as a {@link Dictionary}.
+ * @param prop the Element to parse.
+ * @return the resulting dictionary
+ * @throws ParseException if an internal property is incorrect.
+ */
+ private Dictionary parseDictionary(Element prop) throws ParseException {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ }
+ return dict2;
+ } else {
+ // If the no sub-properties, inject an empty dictionary.
+ return new Properties();
+ }
+ }
+
+ /**
+ * Parses a complex property.
+ * This property will be built as a {@link Map}.
+ * The used {@link Map} implementation is {@link HashMap}.
+ * @param prop the property to parse
+ * @return the resulting Map
+ * @throws ParseException if an internal property is incorrect.
+ */
+ private Map parseMap(Element prop) throws ParseException {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Map map = new HashMap(); // Create an hashmap to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], map);
+ }
+ return map;
+ } else { // if not inject an empty map
+ return new HashMap(0);
+ }
+ }
+
+ /**
+ * Parses a complex property. This property will be built as a {@link List}.
+ * The used {@link List} implementation is {@link ArrayList}.
+ * The order of elements is kept.
+ * @param prop the property to parse
+ * @return the resulting List
+ * @throws ParseException if an internal property is incorrect.
+ */
+ private List parseList(Element prop) throws ParseException {
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ List list = new ArrayList(subProps.length); // Create a list to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list); // Anonymous properties.
+ }
+ return list;
+ } else {
+ // If no sub-properties, inject an empty list.
+ return new ArrayList(0);
+ }
+ }
+
+ /**
+ * Parse a property.
+ * This methods handles complex properties.
+ * @param prop the current element to parse
+ * @param map the map to populate
+ * @throws ParseException if the property cannot be parsed correctly
+ */
+ private void parseProperty(Element prop, Map map) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name == null) {
+ throw new ParseException(
+ "A property does not have the 'name' attribute");
+ }
+ // case : the property element has no 'value' attribute
+ if (value == null) {
+ // Recursive case
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ map.put(name, parseDictionary(prop));
+ } else if (type.equalsIgnoreCase("map")) {
+ map.put(name, parseMap(prop));
+ } else if (type.equalsIgnoreCase("list")) {
+ map.put(name, parseList(prop));
+ } else if (type.equalsIgnoreCase("array")) {
+ List list = parseList(prop);
+ boolean isString = true;
+ for (int i = 0; isString && i < list.size(); i++) {
+ isString = list.get(i) instanceof String;
+ }
+ Object[] obj = null;
+ if (isString) {
+ obj = new String[list.size()];
+ } else {
+ obj = new Object[list.size()];
+ }
+ map.put(name, list.toArray(obj)); // Transform the list to array
+ }
+ } else {
+ map.put(prop.getAttribute("name"), prop.getAttribute("value"));
+ }
+ }
+
+ /**
+ * Parse an anonymous property.
+ * An anonymous property is a property with no name.
+ * An anonymous property can be simple (just a value) or complex (i.e. a map, a dictionary
+ * a list or an array).
+ * @param prop the property to parse
+ * @param list the list to populate with the resulting property
+ * @throws ParseException if an internal property cannot be parse correctly
+ */
+ private void parseAnonymousProperty(Element prop, List list) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name != null) {
+ throw new ParseException("Anonymous property expected in a list or an array");
+ }
+ //case : the property element has no 'value' attribute
+ if (value == null) {
+ // Recursive case
+
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ }
+ list.add(dict2);
+ } else {
+ // If the no sub-properties, inject an empty dictionary.
+ list.add(new Properties());
+ }
+ } else if (type.equalsIgnoreCase("map")) {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Map map2 = new HashMap(); // Create an hashmap to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], map2);
+ }
+ list.add(map2);
+ } else { // if not inject an empty map
+ list.add(new HashMap(0));
+ }
+ } else if (type.equalsIgnoreCase("list")) {
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ // Create a list to store elements.
+ List list2 = new ArrayList(subProps.length);
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list2); // Anonymous properties
+ }
+ list.add(list2);
+ } else {
+ // If no sub-properties, inject an empty list.
+ list.add(new ArrayList(0));
+ }
+ } else if (type.equalsIgnoreCase("array")) {
+ // Check sub-props.
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ List list2 = new ArrayList(subProps.length); // Use list as
+ // pivot type
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list2);
+ }
+ list.add(list.toArray(new Object[list.size()])); // Transform
+ // the list
+ // to array
+ } else {
+ list.add(new Element[0]); // Insert an empty Element array.
+ }
+ }
+ } else {
+ list.add(prop.getAttribute("value"));
+ }
+
+ }
+
+ /**
+ * Adds an element to the {@link ManifestMetadataParser#m_elements} list.
+ * @param elem the the element to add
+ */
+ private void addElement(Element elem) {
+ if (m_elements == null) {
+ m_elements = new Element[] { elem };
+ } else {
+ Element[] newElementsList = new Element[m_elements.length + 1];
+ System.arraycopy(m_elements, 0, newElementsList, 0, m_elements.length);
+ newElementsList[m_elements.length] = elem;
+ m_elements = newElementsList;
+ }
+ }
+
+ /**
+ * Removes an element from the {@link ManifestMetadataParser#m_elements} list.
+ * @return an element to remove
+ */
+ private Element removeLastElement() {
+ int idx = -1;
+ idx = m_elements.length - 1;
+ Element last = m_elements[idx];
+ if (idx >= 0) {
+ if ((m_elements.length - 1) == 0) {
+ // It is the last element of the list;
+ m_elements = new Element[0];
+ } else {
+ // Remove the last element of the list :
+ Element[] newElementsList = new Element[m_elements.length - 1];
+ System.arraycopy(m_elements, 0, newElementsList, 0, idx);
+ m_elements = newElementsList;
+ }
+ }
+ return last;
+ }
+
+ /**
+ * Looks for the <code>iPOJO-Components</code> header
+ * in the given dictionary. Then, initializes the
+ * {@link ManifestMetadataParser#m_elements} list (adds the
+ * <code>iPOJO</code> root element) and parses the contained
+ * component type declarations and instance configurations.
+ * @param dict the given headers of the manifest file
+ * @throws ParseException if any error occurs
+ */
+ public void parse(Dictionary dict) throws ParseException {
+ String componentClassesStr = (String) dict.get("iPOJO-Components");
+ // Add the ipojo element inside the element list
+ addElement(new Element("iPOJO", ""));
+ parseElements(componentClassesStr.trim());
+ }
+
+ /**
+ * Parses the given header, initialized the
+ * {@link ManifestMetadataParser#m_elements} list
+ * (adds the <code>iPOJO</code> element) and parses
+ * contained component type declarations and instance configurations.
+ * @param header the given header of the manifest file
+ * @throws ParseException if any error occurs
+ */
+ public void parseHeader(String header) throws ParseException {
+ // Add the ipojo element inside the element list
+ addElement(new Element("iPOJO", ""));
+ parseElements(header.trim());
+ }
+
+ /**
+ * Parses the metadata from the string given in argument.
+ * This methods creates a new {@link ManifestMetadataParser} object
+ * and calls the {@link ManifestMetadataParser#parseElements(String)}
+ * method. The parsing must result as a tree (only one root element).
+ * @param metadata the metadata to parse
+ * @return Element the root element resulting of the parsing
+ * @throws ParseException if any error occurs
+ */
+ public static Element parse(String metadata) throws ParseException {
+ ManifestMetadataParser parser = new ManifestMetadataParser();
+ parser.parseElements(metadata);
+ if (parser.m_elements.length != 1) {
+ throw new ParseException("Error in parsing, root element not found : " + metadata);
+ }
+ return parser.m_elements[0];
+ }
+
+ /**
+ * Parses the metadata from the given header string.
+ * This method creates a new {@link ManifestMetadataParser} object and then
+ * creates the <code>iPOJO</code> root element, parses content elements
+ * (component types and instances declarations), and returns the resulting
+ * {@link Element} / {@link Attribute} structure. The parsed string
+ * must be a tree (only one root element).
+ * @param header the header to parse
+ * @return Element the root element resulting of the parsing
+ * @throws ParseException if any error occurs
+ */
+ public static Element parseHeaderMetadata(String header) throws ParseException {
+ ManifestMetadataParser parser = new ManifestMetadataParser();
+ parser.addElement(new Element("iPOJO", ""));
+ parser.parseElements(header);
+ if (parser.m_elements.length != 1) {
+ throw new ParseException("Error in parsing, root element not found : " + header);
+ }
+ return parser.m_elements[0];
+ }
+
+ /**
+ * Parses the given string.
+ * This methods populates the {@link ManifestMetadataParser#m_elements}
+ * list.
+ * @param elems the string to parse
+ */
+ private void parseElements(String elems) {
+ char[] string = elems.toCharArray();
+
+ for (int i = 0; i < string.length; i++) {
+ char current = string[i];
+ switch (current) { //NOPMD
+ // Beginning of an attribute.
+ case '$':
+ StringBuffer attName = new StringBuffer();
+ StringBuffer attValue = new StringBuffer();
+ StringBuffer attNs = null;
+ i++;
+ current = string[i]; // Increment and get the new current char.
+ while (current != '=') {
+ if (current == ':') {
+ attNs = attName;
+ attName = new StringBuffer();
+ } else {
+ attName.append(current);
+ }
+ i++;
+ current = string[i];
+ }
+ i = i + 2; // skip ="
+ current = string[i];
+ while (current != '"') {
+ attValue.append(current);
+ i++;
+ current = string[i]; // Increment and get the new current char.
+ }
+ i++; // skip "
+ current = string[i];
+
+ Attribute att = null;
+ if (attNs == null) {
+ att = new Attribute(attName.toString(), attValue.toString());
+ } else {
+ att = new Attribute(attName.toString(), attNs.toString(), attValue.toString());
+ }
+ m_elements[m_elements.length - 1].addAttribute(att);
+ break;
+
+ // End of an element
+ case '}':
+ Element lastElement = removeLastElement();
+ if (m_elements.length == 0) {
+ addElement(lastElement);
+ } else {
+ Element newQueue = m_elements[m_elements.length - 1];
+ newQueue.addElement(lastElement);
+ }
+ break;
+
+ // Space
+ case ' ':
+ break; // do nothing;
+
+ // Default case
+ default:
+ StringBuffer qname = new StringBuffer();
+ current = string[i];
+ while (current != ' ') {
+ qname.append(current);
+ i++;
+ current = string[i]; // Increment and get the new current char.
+ }
+ // Skip spaces
+ while (string[i] == ' ') {
+ i = i + 1;
+ }
+ i = i + 1; // skip {
+
+ Element elem = null;
+ // Parse the qname
+ String n = qname.toString();
+ if (n.indexOf(':') == -1) {
+ // No namespace
+ elem = new Element(n, null);
+ } else {
+ // The namespace ends on the first ':'
+ int last = n.lastIndexOf(':');
+ String ns = n.substring(0, last);
+ String name = n.substring(last + 1);
+ elem = new Element(name.toString(), ns.toString());
+ }
+
+ addElement(elem);
+
+ break;
+ }
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/MethodMetadata.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/MethodMetadata.java
new file mode 100644
index 0000000..1de846a
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/MethodMetadata.java
@@ -0,0 +1,176 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A Method Metadata represents a method from the implementation class.
+ * This class allows getting information about a method : name, arguments, return type...
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MethodMetadata {
+
+ /**
+ * Empty Constructor Method Id.
+ */
+ public static final String EMPTY_CONSTRUCTOR_ID = "$init";
+
+ /**
+ * Bundle Context Constructor Method Id.
+ */
+ public static final String BC_CONSTRUCTOR_ID = "$init$org_osgi_framework_BundleContext";
+
+ /**
+ * Constructor Prefix.
+ */
+ public static final String CONSTRUCTOR_PREFIX = "$init";
+
+ /**
+ * The name of the method.
+ */
+ private String m_name;
+
+ /**
+ * The argument type array.
+ */
+ private String[] m_arguments = new String[0];
+
+ /**
+ * The returned type.
+ */
+ private String m_return = "void";
+
+ /**
+ * Creates a Method Metadata.
+ * @param metadata the method manipulation element.
+ */
+ MethodMetadata(Element metadata) {
+ m_name = metadata.getAttribute("name");
+ String arg = metadata.getAttribute("arguments");
+ String result = metadata.getAttribute("return");
+ if (arg != null) {
+ m_arguments = ParseUtils.parseArrays(arg);
+ }
+ if (result != null) {
+ m_return = result;
+ }
+ }
+
+ public String getMethodName() {
+ return m_name;
+ }
+
+ public String[] getMethodArguments() {
+ return m_arguments;
+ }
+
+ public String getMethodReturn() {
+ return m_return;
+ }
+
+ /**
+ * Gets the method unique identifier. For internal usage only.
+ * A method identifier is a unique string that can be a java field
+ * that identify the method.
+ * @return the method identifier.
+ */
+ public String getMethodIdentifier() {
+ StringBuffer identifier = new StringBuffer(m_name);
+ for (int i = 0; i < m_arguments.length; i++) {
+ String arg = m_arguments[i];
+ identifier.append('$');
+ if (arg.endsWith("[]")) {
+ arg = arg.substring(0, arg.length() - 2);
+ identifier.append(arg.replace('.', '_'));
+ identifier.append("__"); // Replace [] by __
+ } else {
+ identifier.append(arg.replace('.', '_'));
+ }
+ }
+ return identifier.toString();
+ }
+
+ /**
+ * Computes the method id for the given Method object.
+ * @param method the Method object.
+ * @return the method id.
+ */
+ public static String computeMethodId(Method method) {
+ StringBuffer identifier = new StringBuffer(method.getName());
+ Class[] args = method.getParameterTypes();
+ for (int i = 0; i < args.length; i++) {
+ identifier.append('$'); // Argument separator.
+ if (args[i].isArray()) {
+ if (args[i].getComponentType().isPrimitive()) {
+ // Primitive array
+ identifier.append(FieldMetadata.getPrimitiveTypeByClass(args[i].getComponentType()));
+ } else {
+ // Object array
+ identifier.append(args[i].getComponentType().getName().replace('.', '_')); // Replace '.' by '_'
+ }
+ identifier.append("__"); // Add __ (array)
+ } else {
+ if (args[i].isPrimitive()) {
+ // Primitive type
+ identifier.append(FieldMetadata.getPrimitiveTypeByClass(args[i]));
+ } else {
+ // Object type
+ identifier.append(args[i].getName().replace('.', '_')); // Replace '.' by '_'
+ }
+ }
+ }
+ return identifier.toString();
+ }
+
+ /**
+ * Computes the method id for the given Constructor object.
+ * @param method the Method object.
+ * @return the method id.
+ */
+ public static String computeMethodId(Constructor method) {
+ StringBuffer identifier = new StringBuffer("$init");
+ Class[] args = method.getParameterTypes();
+ for (int i = 0; i < args.length; i++) {
+ identifier.append('$'); // Argument separator.
+ if (args[i].isArray()) {
+ if (args[i].getComponentType().isPrimitive()) {
+ // Primitive array
+ identifier.append(FieldMetadata.getPrimitiveTypeByClass(args[i].getComponentType()));
+ } else {
+ // Object array
+ identifier.append(args[i].getComponentType().getName().replace('.', '_')); // Replace '.' by '_'
+ }
+ identifier.append("__"); // Add __ (array)
+ } else {
+ if (args[i].isPrimitive()) {
+ // Primitive type
+ identifier.append(FieldMetadata.getPrimitiveTypeByClass(args[i]));
+ } else {
+ // Object type
+ identifier.append(args[i].getName().replace('.', '_')); // Replace '.' by '_'
+ }
+ }
+ }
+ return identifier.toString();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseException.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseException.java
new file mode 100644
index 0000000..1a1db2d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.felix.ipojo.parser;
+
+/**
+ * Exception thrown by parsers.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ParseException extends Exception {
+
+ /**
+ * serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Parsing error.
+ * @param msg : the error emssage.
+ */
+ public ParseException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
new file mode 100644
index 0000000..a2227e5
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Parser Utility Methods.
+ * This class contains helper method to parse iPOJO metadata.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class ParseUtils {
+
+ /**
+ * Parses the iPOJO string form of an array as {a, b, c}
+ * or [a, b, c].
+ * @param str the string form
+ * @return the resulting string array
+ */
+ public static String[] parseArrays(String str) {
+ if (str.length() == 0) {
+ return new String[0];
+ }
+
+ // Remove { and } or [ and ]
+ if ((str.charAt(0) == '{' && str.charAt(str.length() - 1) == '}')
+ || (str.charAt(0) == '[' && str.charAt(str.length() - 1) == ']')) {
+ String internal = (str.substring(1, str.length() - 1)).trim();
+ // Check empty array
+ if (internal.length() == 0) {
+ return new String[0];
+ }
+ return split(internal, ",");
+ } else {
+ return new String[] { str };
+ }
+ }
+
+ /**
+ * Parses the string form of an array as {a, b, c}
+ * or [a, b, c] as a list.
+ * @param str the string form
+ * @return the resulting list
+ */
+ public static List parseArraysAsList(String str) {
+ return Arrays.asList(parseArrays(str));
+ }
+
+ /**
+ * Split method.
+ * This method is equivalent of the String.split in java 1.4
+ * The result array contains 'trimmed' String
+ * @param toSplit the String to split
+ * @param separator the separator
+ * @return the split array
+ */
+ public static String[] split(String toSplit, String separator) {
+ StringTokenizer tokenizer = new StringTokenizer(toSplit, separator);
+ String[] result = new String[tokenizer.countTokens()];
+ int index = 0;
+ while (tokenizer.hasMoreElements()) {
+ result[index] = tokenizer.nextToken().trim();
+ index++;
+ }
+ return result;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java
new file mode 100644
index 0000000..8b06832
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java
@@ -0,0 +1,267 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Manipulation Metadata allows getting information about the implementation class
+ * without using reflection such as implemented interfaces, super class,
+ * methods and fields.
+ * This method allows getting object to register {@link org.apache.felix.ipojo.FieldInterceptor} and
+ * {@link org.apache.felix.ipojo.MethodInterceptor}.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PojoMetadata {
+
+ /**
+ * The list of implemented interfaces.
+ */
+ private String[] m_interfaces = new String[0];
+
+ /**
+ * The list of fields.
+ */
+ private FieldMetadata[] m_fields = new FieldMetadata[0];
+
+ /**
+ * The list of methods.
+ */
+ private MethodMetadata[] m_methods = new MethodMetadata[0];
+
+ /**
+ * The Super class (if <code>null</code> for {@link Object}).
+ */
+ private String m_super;
+
+
+ /**
+ * Creates Pojo metadata.
+ * Manipulation Metadata object are created from component type metadata by
+ * parsing manipulation metadata.
+ * @param metadata the component type metadata
+ * @throws ConfigurationException if the manipulation metadata cannot be found
+ */
+ public PojoMetadata(Element metadata) throws ConfigurationException {
+ Element[] elems = metadata.getElements("manipulation", "");
+ if (elems == null) {
+ throw new ConfigurationException("The component " + metadata.getAttribute("classname") + " has no manipulation metadata");
+ }
+ Element manip = elems[0];
+ m_super = manip.getAttribute("super");
+ Element[] fields = manip.getElements("field");
+ for (int i = 0; fields != null && i < fields.length; i++) {
+ FieldMetadata field = new FieldMetadata(fields[i]);
+ addField(field);
+ }
+ Element[] methods = manip.getElements("method");
+ for (int i = 0; methods != null && i < methods.length; i++) {
+ MethodMetadata method = new MethodMetadata(methods[i]);
+ addMethod(method);
+ }
+ Element[] itfs = manip.getElements("interface");
+ for (int i = 0; itfs != null && i < itfs.length; i++) {
+ addInterface(itfs[i].getAttribute("name"));
+ }
+ }
+
+ public MethodMetadata[] getMethods() { return m_methods; }
+
+ public FieldMetadata[] getFields() { return m_fields; }
+
+ public String[] getInterfaces() { return m_interfaces; }
+
+ /**
+ * Gets the field metadata for the given name.
+ * @param name : the name of the field
+ * @return the corresponding field metadata or <code>null</code> if not found
+ */
+ public FieldMetadata getField(String name) {
+ for (int i = 0; i < m_fields.length; i++) {
+ if (m_fields[i].getFieldName().equalsIgnoreCase(name)) { return m_fields[i]; }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the field metadata for the given name and type.
+ * @param name : the name of the field
+ * @param type : the type of the field
+ * @return the corresponding field metadata or <code>null</code> if not found
+ */
+ public FieldMetadata getField(String name, String type) {
+ for (int i = 0; i < m_fields.length; i++) {
+ if (m_fields[i].getFieldName().equalsIgnoreCase(name) && m_fields[i].getFieldType().equalsIgnoreCase(type)) { return m_fields[i]; }
+ }
+ return null;
+ }
+
+ /**
+ * Checks if the given interface name is implemented.
+ * This methods checks on interface directly implemented
+ * by the implementation class.
+ * @param itf the interface to check.
+ * @return <code>true</code> if the implementation class implements
+ * the given interface.
+ */
+ public boolean isInterfaceImplemented(String itf) {
+ for (int i = 0; i < m_interfaces.length; i++) {
+ if (m_interfaces[i].equals(itf)) { return true; }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the MethodMetadata corresponding to the method
+ * (contained in the implementation class) with
+ * the given name.
+ * If several methods match, the first one is returned.
+ * @param name the name of the method to find.
+ * @return the method metadata object or <code>null</code> if not found
+ */
+ public MethodMetadata getMethod(String name) {
+ for (int i = 0; i < m_methods.length; i++) {
+ if (m_methods[i].getMethodName().equalsIgnoreCase(name)) { return m_methods[i]; }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the MethodMetadata list corresponding to the method
+ * (contained in the implementation class) to given name.
+ * All methods contained in the implementation class matching
+ * with the name are in the returned list.
+ * @param name the name of the method to look for.
+ * @return the Method Metadata array or an empty array if not found
+ */
+ public MethodMetadata[] getMethods(String name) {
+ MethodMetadata[] mms = new MethodMetadata[0];
+ for (int i = 0; i < m_methods.length; i++) {
+ if (m_methods[i].getMethodName().equalsIgnoreCase(name)) {
+ if (mms.length > 0) {
+ MethodMetadata[] newInstances = new MethodMetadata[mms.length + 1];
+ System.arraycopy(mms, 0, newInstances, 0, mms.length);
+ newInstances[mms.length] = m_methods[i];
+ mms = newInstances;
+ } else {
+ mms = new MethodMetadata[] { m_methods[i] };
+ }
+ }
+ }
+ return mms;
+ }
+
+ /**
+ * Gets the MethodMetadata list corresponding to the constructors
+ * (contained in the implementation class).
+ * @return the Method Metadata array or an empty array if not found
+ */
+ public MethodMetadata[] getConstructors() {
+ return getMethods("$init");
+ }
+
+ /**
+ * Gets the MethodMetadata corresponding to the method
+ * (contained in the implementation class) to given name
+ * and argument types.
+ * @param name the name of the method to look for.
+ * @param types the array of the argument types of the method
+ * @return the Method Metadata or <code>null</code> if not found
+ */
+ public MethodMetadata getMethod(String name, String[] types) {
+ for (int i = 0; i < m_methods.length; i++) {
+ if (m_methods[i].getMethodName().equalsIgnoreCase(name) && m_methods[i].getMethodArguments().length == types.length) {
+ int argIndex = 0;
+ for (; argIndex < types.length; argIndex++) {
+ if (! types[argIndex].equals(m_methods[i].getMethodArguments()[argIndex])) {
+ break;
+ }
+ }
+ if (argIndex == types.length) { return m_methods[i]; } // No mismatch detected.
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the constructor corresponding to the given argument types.
+ * @param types the argument types
+ * @return the matching constructor or <code>null</code> if not found.
+ */
+ public MethodMetadata getConstructor(String[] types) {
+ return getMethod("$init", types); // Constructors are named $init in the manipulation metadata
+ }
+
+ /**
+ * Adds a method to the list.
+ * This method is used during the creation of the {@link PojoMetadata}
+ * object.
+ * @param method the Method Metadata to add.
+ */
+ private void addMethod(MethodMetadata method) {
+ if (m_methods.length > 0) {
+ MethodMetadata[] newInstances = new MethodMetadata[m_methods.length + 1];
+ System.arraycopy(m_methods, 0, newInstances, 0, m_methods.length);
+ newInstances[m_methods.length] = method;
+ m_methods = newInstances;
+ } else {
+ m_methods = new MethodMetadata[] { method };
+ }
+ }
+
+ /**
+ * Adds a field to the list.
+ * This method is used during the creation of the {@link PojoMetadata}
+ * object.
+ * @param field the Field Metadata to add.
+ */
+ private void addField(FieldMetadata field) {
+ if (m_fields.length > 0) {
+ FieldMetadata[] newInstances = new FieldMetadata[m_fields.length + 1];
+ System.arraycopy(m_fields, 0, newInstances, 0, m_fields.length);
+ newInstances[m_fields.length] = field;
+ m_fields = newInstances;
+ } else {
+ m_fields = new FieldMetadata[] { field };
+ }
+ }
+
+ /**
+ * Adds the interface to the list.
+ * This method is used during the creation of the {@link PojoMetadata}
+ * object.
+ * @param itf the interface name to add.
+ */
+ private void addInterface(String itf) {
+ if (m_interfaces.length > 0) {
+ String[] newInstances = new String[m_interfaces.length + 1];
+ System.arraycopy(m_interfaces, 0, newInstances, 0, m_interfaces.length);
+ newInstances[m_interfaces.length] = itf;
+ m_interfaces = newInstances;
+ } else {
+ m_interfaces = new String[] { itf };
+ }
+ }
+
+ public String getSuperClass() {
+ return m_super;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Callback.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Callback.java
new file mode 100644
index 0000000..fe32379
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Callback.java
@@ -0,0 +1,278 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+
+/**
+ * A callback allows invoking a method on a POJO.
+ * This class supports both public, protected and private methods of the
+ * implementation class. This class also supports public method from super class.
+ * The {@link Method} object is computed once and this computation is delayed
+ * to the first invocation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Callback {
+
+ /**
+ * The method object.
+ * Computed at the first call.
+ */
+ protected Method m_methodObj;
+
+ /**
+ * The name of the method to call.
+ */
+ private String m_method;
+
+ /**
+ * Is the method a static method ?
+ * This implies calling the method on <code>null</code>
+ */
+ private boolean m_isStatic;
+
+ /**
+ * The reference on the instance manager.
+ * Used to get POJO objects.
+ */
+ private InstanceManager m_manager;
+
+ /**
+ * The argument classes.
+ * This array contains the list of argument class names.
+ */
+ private String[] m_args;
+
+ /**
+ * Creates a Callback.
+ * If the argument array is not null the reflection type are computed.
+ * @see {@link Callback#computeArguments(String[])}
+ * @param method the name of the method to call
+ * @param args the argument type name, or <code>null</code> if no arguments
+ * @param isStatic is the method a static method
+ * @param manager the instance manager of the component containing the method
+ */
+ public Callback(String method, String[] args, boolean isStatic, InstanceManager manager) {
+ m_method = method;
+ m_isStatic = isStatic;
+ m_manager = manager;
+ if (args != null) {
+ computeArguments(args);
+ } else {
+ m_args = new String[0];
+ }
+ }
+
+ /**
+ * Creates a Callback.
+ * @param method the the name of the method to call
+ * @param args the argument classes
+ * @param isStatic the is the method a static method
+ * @param manager the the instance manager of the component containing the method
+ */
+ public Callback(String method, Class[] args, boolean isStatic, InstanceManager manager) {
+ m_method = method;
+ m_isStatic = isStatic;
+ m_manager = manager;
+ m_args = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ m_args[i] = args[i].getName();
+ }
+ }
+
+ /**
+ * Creates a Callback.
+ * @param method the {@link MethodMetadata} obtained from manipulation
+ * metadata ({@link PojoMetadata}).
+ * @param manager the instance manager.
+ */
+ public Callback(MethodMetadata method, InstanceManager manager) {
+ m_isStatic = false;
+ m_method = method.getMethodName();
+ m_manager = manager;
+ computeArguments(method.getMethodArguments());
+ }
+
+ /**
+ * Computes arguments of the method.
+ * This method computes "reflection type" from given argument.
+ * @see FieldMetadata#getReflectionType(String)
+ * @param args the arguments of the method.
+ */
+ private void computeArguments(String[] args) {
+ m_args = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ m_args[i] = FieldMetadata.getReflectionType(args[i]);
+ }
+ }
+
+ /**
+ * Searches the {@link Method} in the given method arrays.
+ * @param methods the method array in which the method should be found
+ * @return the method object or <code>null</code> if not found
+ */
+ private Method searchMethodInMethodArray(Method[] methods) {
+ for (int i = 0; i < methods.length; i++) {
+ // First check the method name
+ if (methods[i].getName().equals(m_method)) {
+ // Check arguments
+ Class[] clazzes = methods[i].getParameterTypes();
+ if (clazzes.length == m_args.length) { // Test size to avoid useless loop
+ int argIndex = 0;
+ for (; argIndex < m_args.length; argIndex++) {
+ if (!m_args[argIndex].equals(clazzes[argIndex].getName())) {
+ break;
+ }
+ }
+ if (argIndex == m_args.length) { // No mismatch detected.
+ return methods[i]; // It is the looked method.
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Searches the {@link Method} object in the POJO by analyzing implementation
+ * class methods. The name of the method and the argument type are checked.
+ * @throws NoSuchMethodException if the method cannot be found either in the
+ * implementation class or in parent classes.
+ */
+ protected void searchMethod() throws NoSuchMethodException {
+ Method[] methods = m_manager.getClazz().getDeclaredMethods();
+ m_methodObj = searchMethodInMethodArray(methods);
+
+ if (m_methodObj == null) { // look at parent classes
+ methods = m_manager.getClazz().getMethods();
+ m_methodObj = searchMethodInMethodArray(methods);
+ }
+
+ if (m_methodObj == null) {
+ throw new NoSuchMethodException(m_method);
+ } else {
+ if (! m_methodObj.isAccessible()) {
+ // If not accessible, try to set the accessibility.
+ m_methodObj.setAccessible(true);
+ }
+ }
+ }
+
+ /**
+ * Invokes the method without arguments.
+ * If several the instance contains several objects, the method is invoked
+ * on every objects.
+ * @return the result of the invocation, <code>null</code> for <code>void</code>
+ * method, the last result for multi-object instance
+ * @throws NoSuchMethodException if Method is not found in the class
+ * @throws InvocationTargetException if the method throws an exception
+ * @throws IllegalAccessException if the method can not be invoked
+ */
+ public Object call() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ return call(new Object[0]);
+ }
+
+ /**
+ * Invokes the method without arguments.
+ * The method is invokes on the specified object.
+ * @param instance the instance on which call the callback
+ * @return the result of the invocation, <code>null</code> for
+ * <code>void</code> method
+ * @throws NoSuchMethodException if the method was not found
+ * @throws IllegalAccessException if the method cannot be called
+ * @throws InvocationTargetException if an error happens in the method
+ */
+ public Object call(Object instance) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ return call(instance, new Object[0]);
+ }
+
+ /**
+ * Invokes the method on every created objects with the specified
+ * arguments.
+ * @param arg the method arguments
+ * @return the result of the invocation, <code>null</code> for
+ * <code>void</code> method, the last result for instance containing
+ * several objects.
+ * @throws NoSuchMethodException if the callback method is not found
+ * @throws IllegalAccessException if the callback method cannot be called
+ * @throws InvocationTargetException if an error is thrown by the called method
+ */
+ public Object call(Object[] arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ if (m_methodObj == null) {
+ searchMethod();
+ }
+
+ if (m_isStatic) {
+ return m_methodObj.invoke(null, arg);
+ } else {
+ // Two cases :
+ // - if instances already exists : call on each instances
+ // - if no instance exists : create an instance
+ if (m_manager.getPojoObjects() == null) {
+ return m_methodObj.invoke(m_manager.getPojoObject(), arg);
+ } else {
+ Object newObject = null;
+ for (int i = 0; i < m_manager.getPojoObjects().length; i++) {
+ newObject = m_methodObj.invoke(m_manager.getPojoObjects()[i], arg);
+ }
+ return newObject;
+ }
+ }
+ }
+
+ /**
+ * Invokes the method on the given object with the specified
+ * arguments.
+ * @param instance the instance on which call the method
+ * @param arg the argument array
+ * @return the result of the invocation, <code>null</code> for
+ * <code>void</code> method
+ * @throws NoSuchMethodException if the callback method is not found
+ * @throws IllegalAccessException if the callback method cannot be called
+ * @throws InvocationTargetException if an error is thrown by the called method
+ */
+ public Object call(Object instance, Object[] arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ if (m_methodObj == null) {
+ searchMethod();
+ }
+
+ return m_methodObj.invoke(instance, arg);
+ }
+
+ /**
+ * Gets the method name.
+ * @return the method name
+ */
+ public String getMethod() {
+ return m_method;
+ }
+
+ /**
+ * Gets the method arguments.
+ * @return the arguments.
+ */
+ public String[] getArguments() {
+ return m_args;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
new file mode 100644
index 0000000..52c11c2
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
@@ -0,0 +1,1007 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.IPOJOServiceFactory;
+import org.apache.felix.ipojo.context.ServiceReferenceImpl;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Abstract dependency model.
+ * This class is the parent class of every service dependency. It manages the most
+ * part of dependency management. This class creates an interface between the service
+ * tracker and the concrete dependency.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class DependencyModel implements TrackerCustomizer {
+
+ /**
+ * Dependency state : BROKEN.
+ * A broken dependency cannot be fulfilled anymore. The dependency becomes
+ * broken when a used service disappears in the static binding policy.
+ */
+ public static final int BROKEN = -1;
+
+ /**
+ * Dependency state : UNRESOLVED.
+ * A dependency is unresolved if the dependency is not valid and no service
+ * providers are available.
+ */
+ public static final int UNRESOLVED = 0;
+
+ /**
+ * Dependency state : RESOLVED.
+ * A dependency is resolved if the dependency is optional or at least one
+ * provider is available.
+ */
+ public static final int RESOLVED = 1;
+
+ /**
+ * Binding policy : Dynamic.
+ * In this policy, services can appears and departs without special treatment.
+ */
+ public static final int DYNAMIC_BINDING_POLICY = 0;
+
+ /**
+ * Binding policy : Static.
+ * Once a service is used, if this service disappears the dependency becomes
+ * {@link DependencyModel#BROKEN}. The instance needs to be recreated.
+ */
+ public static final int STATIC_BINDING_POLICY = 1;
+
+ /**
+ * Binding policy : Dynamic-Priority.
+ * In this policy, services can appears and departs. However, once a service
+ * with a highest ranking (according to the used comparator) appears, this
+ * new service is re-injected.
+ */
+ public static final int DYNAMIC_PRIORITY_BINDING_POLICY = 2;
+
+ /**
+ * Does the dependency bind several providers ?
+ */
+ private boolean m_aggregate;
+
+ /**
+ * Is the dependency optional ?
+ */
+ private boolean m_optional;
+
+ /**
+ * The required specification.
+ * Cannot change once set.
+ */
+ private Class m_specification;
+
+ /**
+ * The comparator to sort service references.
+ */
+ private Comparator m_comparator;
+
+ /**
+ * The LDAP filter object selecting service references
+ * from the set of providers providing the required specification.
+ */
+ private Filter m_filter;
+
+ /**
+ * Bundle context used by the dependency.
+ * (may be a {@link ServiceContext}).
+ */
+ private BundleContext m_context;
+
+ /**
+ * Listener object on which invoking the {@link DependencyStateListener#validate(DependencyModel)}
+ * and {@link DependencyStateListener#invalidate(DependencyModel)} methods.
+ */
+ private final DependencyStateListener m_listener;
+
+ /**
+ * The actual state of the dependency.
+ * {@link DependencyModel#UNRESOLVED} at the beginning.
+ */
+ private int m_state;
+
+ /**
+ * The Binding policy of the dependency.
+ */
+ private int m_policy = DYNAMIC_BINDING_POLICY;
+
+ /**
+ * The tracker used by this dependency to track providers.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * The list of matching service references. This list is a
+ * subset of tracked references. This set is computed according
+ * to the filter and the {@link DependencyModel#match(ServiceReference)} method.
+ */
+ private final List m_matchingRefs = new ArrayList();
+
+ /**
+ * The instance requiring the service.
+ */
+ private final ComponentInstance m_instance;
+
+ /**
+ * Map {@link ServiceReference} -> Service Object.
+ * This map stores service object, and so is able to handle
+ * iPOJO custom policies.
+ */
+ private Map/*<ServiceReference, Object>*/ m_serviceObjects = new HashMap();
+
+ /**
+ * Creates a DependencyModel.
+ * If the dependency has no comparator and follows the
+ * {@link DependencyModel#DYNAMIC_PRIORITY_BINDING_POLICY} policy
+ * the OSGi Service Reference Comparator is used.
+ * @param specification the required specification
+ * @param aggregate is the dependency aggregate ?
+ * @param optional is the dependency optional ?
+ * @param filter the LDAP filter
+ * @param comparator the comparator object to sort references
+ * @param policy the binding policy
+ * @param context the bundle context (or service context)
+ * @param listener the dependency lifecycle listener to notify from dependency
+ * @param ci instance managing the dependency
+ * state changes.
+ */
+ public DependencyModel(Class specification, boolean aggregate, boolean optional, Filter filter, Comparator comparator, int policy,
+ BundleContext context, DependencyStateListener listener, ComponentInstance ci) {
+ m_specification = specification;
+ m_aggregate = aggregate;
+ m_optional = optional;
+ m_filter = filter;
+ m_comparator = comparator;
+ m_context = context;
+ m_policy = policy;
+ // If the dynamic priority policy is chosen, and we have no comparator, fix it to OSGi standard service reference comparator.
+ if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY && m_comparator == null) {
+ m_comparator = new ServiceReferenceRankingComparator();
+ }
+ m_state = UNRESOLVED;
+ m_listener = listener;
+ m_instance = ci;
+ }
+
+ /**
+ * Opens the tracking.
+ * This method computes the dependency state
+ * @see DependencyModel#computeDependencyState()
+ */
+ public void start() {
+ m_state = UNRESOLVED;
+ m_tracker = new Tracker(m_context, m_specification.getName(), this);
+ m_tracker.open();
+ computeDependencyState();
+ }
+
+ /**
+ * Closes the tracking.
+ * The dependency becomes {@link DependencyModel#UNRESOLVED}
+ * at the end of this method.
+ */
+ public void stop() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ m_tracker = null;
+ }
+ m_matchingRefs.clear();
+ ungetAllServices();
+ m_state = UNRESOLVED;
+ }
+
+ /**
+ * Ungets all 'get' service references.
+ * This also clears the service object map.
+ */
+ private void ungetAllServices() {
+ Set entries = m_serviceObjects.entrySet();
+ Iterator it = entries.iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ ServiceReference ref = (ServiceReference) entry.getKey();
+ Object svc = entry.getValue();
+ if (m_tracker != null) {
+ m_tracker.ungetService(ref);
+ }
+ if (svc instanceof IPOJOServiceFactory) {
+ ((IPOJOServiceFactory) svc).ungetService(m_instance, svc);
+ }
+ }
+ m_serviceObjects.clear();
+ }
+
+ /**
+ * Is the reference set frozen (cannot change anymore)?
+ * This method must be override by concrete dependency to support
+ * the static binding policy. In fact, this method allows optimizing
+ * the static dependencies to become frozen only when needed.
+ * This method returns <code>false</code> by default.
+ * The method must always return <code>false</code> for non-static dependencies.
+ * @return <code>true</code> if the reference set is frozen.
+ */
+ public boolean isFrozen() {
+ return false;
+ }
+
+
+ /**
+ * Unfreezes the dependency.
+ * This method must be overide by concrete dependency to support
+ * the static binding policy. This method is called after tracking restarting.
+ */
+ public void unfreeze() {
+ // nothing to do
+ }
+
+ /**
+ * Does the service reference match ? This method must be override by
+ * concrete dependencies if they need advanced testing on service reference
+ * (that cannot be expressed in the LDAP filter). By default this method
+ * returns <code>true</code>.
+ * @param ref the tested reference.
+ * @return <code>true</code> if the service reference matches.
+ */
+ public boolean match(ServiceReference ref) {
+ return true;
+ }
+
+ /**
+ * Computes the actual dependency state.
+ * This methods invokes the {@link DependencyStateListener}.
+ */
+ private void computeDependencyState() {
+ if (m_state == BROKEN) { return; } // The dependency is broken ...
+
+ boolean mustCallValidate = false;
+ boolean mustCallInvalidate = false;
+ synchronized (this) {
+ if (m_optional || !m_matchingRefs.isEmpty()) {
+ // The dependency is valid
+ if (m_state == UNRESOLVED) {
+ m_state = RESOLVED;
+ mustCallValidate = true;
+ }
+ } else {
+ // The dependency is invalid
+ if (m_state == RESOLVED) {
+ m_state = UNRESOLVED;
+ mustCallInvalidate = true;
+ }
+ }
+ }
+
+ // Invoke callback in a non-synchronized region
+ if (mustCallInvalidate) {
+ invalidate();
+ } else if (mustCallValidate) {
+ validate();
+ }
+
+ }
+
+ /**
+ * Service tracker adding service callback.
+ * It accepts the service only if the dependency isn't broken or frozen.
+ * @param ref the arriving service reference.
+ * @return <code>true</code> if the reference must be tracked.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference ref) {
+ return !((m_state == BROKEN) || isFrozen());
+ }
+
+ /**
+ * Service Tracker added service callback.
+ * If the service matches (against the filter and the {@link DependencyModel#match(ServiceReference)},
+ * manages the provider arrival.
+ * @param ref : new references.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference ref) {
+ if (matchAgainstFilter(ref) && match(ref)) {
+ manageArrival(ref);
+ }
+ // Do not store the service if it doesn't match.
+ }
+
+ /**
+ * Checks if the given service reference match the current filter.
+ * This method aims to avoid calling {@link Filter#match(ServiceReference)}
+ * method when manipulating a composite reference. In fact, this method thrown
+ * a {@link ClassCastException} on Equinox.
+ * @param ref the service reference to check.
+ * @return <code>true</code> if the service reference matches.
+ */
+ private boolean matchAgainstFilter(ServiceReference ref) {
+ boolean match = true;
+ if (m_filter != null) {
+ if (ref instanceof ServiceReferenceImpl) {
+ // Can't use the match(ref) as it throw a class cast exception on Equinox.
+ match = m_filter.match(((ServiceReferenceImpl) ref).getProperties());
+ } else { // Non composite reference.
+ match = m_filter.match(ref);
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Manages the arrival of a new service reference.
+ * The reference is valid and matches the filter and the {@link DependencyModel#match(ServiceReference)}
+ * method. This method has different behavior according to the binding policy.
+ * @param ref the new reference
+ */
+ private void manageArrival(ServiceReference ref) {
+ // Create a local copy of the state and of the list size.
+ int state = m_state;
+ int size;
+
+ synchronized (this) {
+ m_matchingRefs.add(ref);
+
+ // Sort the collection if needed, if not sort, services are append to the list.
+ if (m_comparator != null) {
+ // The collection must be sort only if:
+ // The policy is dynamic-priority
+ // No services are already used
+ // If so, sorting can imply a re-binding, and so don't follow the Dynamic Binding policy
+ if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY
+ || m_tracker.getUsedServiceReferences() == null
+ || m_tracker.getUsedServiceReferences().isEmpty()) {
+ Collections.sort(m_matchingRefs, m_comparator);
+ }
+ }
+
+ size = m_matchingRefs.size();
+ }
+
+ if (m_aggregate) {
+ onServiceArrival(ref); // Always notify the arrival for aggregate dependencies.
+ if (state == UNRESOLVED) { // If we was unresolved, try to validate the dependency.
+ computeDependencyState();
+ }
+ } else { // We are not aggregate.
+ if (size == 1) {
+ onServiceArrival(ref); // It is the first service, so notify.
+ computeDependencyState();
+ } else {
+ // In the case of a dynamic priority binding, we have to test if we have to update the bound reference
+ if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY && m_matchingRefs.get(0) == ref) {
+ // We are sure that we have at least two references, so if the highest ranked references (first one) is the new received
+ // references,
+ // we have to unbind the used one and to bind the the new one.
+ onServiceDeparture((ServiceReference) m_matchingRefs.get(1));
+ onServiceArrival(ref);
+ }
+ }
+ }
+ // Ignore others cases
+ }
+
+ /**
+ * Service tracker removed service callback.
+ * A service provider goes away. The depart needs to be managed only if the
+ * reference was used.
+ * @param ref the leaving service reference
+ * @param arg1 the service object if the service was already get
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference ref, Object arg1) {
+ if (m_matchingRefs.contains(ref)) {
+ manageDeparture(ref, arg1);
+ }
+ }
+
+ /**
+ * Manages the departure of a used service.
+ * @param ref the leaving service reference
+ * @param obj the service object if the service was get
+ */
+ private void manageDeparture(ServiceReference ref, Object obj) {
+ // Unget the service reference
+ ungetService(ref);
+
+ // If we already get this service and the binding policy is static, the dependency becomes broken
+ if (isFrozen() && obj != null) {
+ if (m_state != BROKEN) {
+ m_state = BROKEN;
+ invalidate(); // This will invalidate the instance.
+ // Reinitialize the dependency tracking
+ ComponentInstance instance = null;
+ synchronized (this) {
+ instance = m_instance;
+ }
+ instance.stop(); // Stop the instance
+ unfreeze();
+ instance.start();
+ }
+ } else {
+ synchronized (this) {
+ m_matchingRefs.remove(ref);
+ }
+ if (obj == null) {
+ computeDependencyState(); // check if the dependency stills valid.
+ } else {
+ // A used service disappears, we have to sort the available providers to choose the best one.
+ // However, the sort has to be done only for scalar dependencies following the dynamic binding
+ // policy. Static dependencies will be broken, DP dependencies are always sorted.
+ // Aggregate dependencies does not need to be sort, as it will change the array
+ // order.
+ if (m_comparator != null && m_policy == DYNAMIC_BINDING_POLICY && ! m_aggregate) {
+ Collections.sort(m_matchingRefs, m_comparator);
+ }
+ onServiceDeparture(ref);
+ ServiceReference newRef = getServiceReference();
+ if (newRef == null) { // Check if there is another provider.
+ computeDependencyState(); // no more references.
+ } else {
+ if (!m_aggregate) {
+ onServiceArrival(newRef); // Injecting the new service reference for non aggregate dependencies.
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Service tracker modified service callback.
+ * This method must handle if the modified service should be considered as
+ * a depart or an arrival.
+ * According to the dependency filter, a service can now match or can no match
+ * anymore.
+ * @param ref the modified reference
+ * @param arg1 the service object if already get.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference ref, Object arg1) {
+ if (m_matchingRefs.contains(ref)) {
+ // It's a used service. Check if the service always match.
+ if (!matchAgainstFilter(ref) && match(ref)) {
+ // The service does not match anymore. Call removedService.
+ manageDeparture(ref, arg1);
+ } else {
+ manageModification(ref);
+ }
+ } else {
+ // The service was not used. Check if it matches.
+ if (matchAgainstFilter(ref) && match(ref)) {
+ manageArrival(ref);
+ }
+ // Else, the service does not match.
+ }
+ }
+
+ /**
+ * Gets the next matching service reference.
+ * @return <code>null</code> if no more provider is available,
+ * else returns the first reference from the matching set.
+ */
+ public ServiceReference getServiceReference() {
+ synchronized (this) {
+ if (m_matchingRefs.isEmpty()) {
+ return null;
+ } else {
+ return (ServiceReference) m_matchingRefs.get(0);
+ }
+ }
+ }
+
+ /**
+ * Gets matching service references.
+ * @return the sorted (if a comparator is used) array of matching service
+ * references, <code>null</code> if no references are available.
+ */
+ public ServiceReference[] getServiceReferences() {
+ synchronized (this) {
+ if (m_matchingRefs.isEmpty()) { return null; }
+ // TODO Consider sorting the array (on a copy of matching ref) if dynamic priority used.
+ return (ServiceReference[]) m_matchingRefs.toArray(new ServiceReference[m_matchingRefs.size()]);
+ }
+ }
+
+ /**
+ * Gets the list of currently used service references.
+ * If no service references, returns <code>null</code>
+ * @return the list of used reference (according to the service tracker).
+ */
+ public List getUsedServiceReferences() {
+ synchronized (this) {
+ // The list must confront actual matching services with already get services from the tracker.
+
+ int size = m_matchingRefs.size();
+ List usedByTracker = null;
+ if (m_tracker != null) {
+ usedByTracker = m_tracker.getUsedServiceReferences();
+ }
+ if (size == 0 || usedByTracker == null) { return null; }
+
+ List list = new ArrayList(1);
+ for (int i = 0; i < size; i++) {
+ if (usedByTracker.contains(m_matchingRefs.get(i))) {
+ list.add(m_matchingRefs.get(i)); // Add the service in the list.
+ if (!isAggregate()) { // IF we are not multiple, return the list when the first element is found.
+ return list;
+ }
+ }
+ }
+
+ return list;
+ }
+ }
+
+ /**
+ * Gets the number of actual matching references.
+ * @return the number of matching references
+ */
+ public int getSize() {
+ return m_matchingRefs.size();
+ }
+
+ /**
+ * Concrete dependency callback.
+ * This method is called when a new service needs to be
+ * re-injected in the underlying concrete dependency.
+ * @param ref the service reference to inject.
+ */
+ public abstract void onServiceArrival(ServiceReference ref);
+
+ /**
+ * Concrete dependency callback.
+ * This method is called when a used service (already injected) is leaving.
+ * @param ref the leaving service reference.
+ */
+ public abstract void onServiceDeparture(ServiceReference ref);
+
+ /**
+ * Concrete dependency callback.
+ * This method is called when a used service (already injected) is modified.
+ * @param ref the modified service reference.
+ */
+ public abstract void onServiceModification(ServiceReference ref);
+
+ /**
+ * This method can be override by the concrete dependency to be notified
+ * of service modification.
+ * This modification is not an arrival or a departure.
+ * @param ref the modified service reference.
+ */
+ public void manageModification(ServiceReference ref) {
+ if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY) {
+ // Check that the order has changed or not.
+ int indexBefore = m_matchingRefs.indexOf(ref);
+ Collections.sort(m_matchingRefs, m_comparator);
+ if (indexBefore != m_matchingRefs.indexOf(ref) && ! m_aggregate) {
+ // The order has changed during the sort.
+ onServiceDeparture((ServiceReference) m_matchingRefs.get(1));
+ onServiceArrival(ref);
+ }
+
+ } else {
+ // It's a modification...
+ onServiceModification(ref);
+ }
+ }
+
+ /**
+ * Concrete dependency callback.
+ * This method is called when the dependency is reconfigured and when this
+ * reconfiguration implies changes on the matching service set ( and by the
+ * way on the injected service).
+ * @param departs the service leaving the matching set.
+ * @param arrivals the service arriving in the matching set.
+ */
+ public abstract void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals);
+
+ /**
+ * Calls the listener callback to notify the new state of the current
+ * dependency.
+ */
+ private void invalidate() {
+ m_listener.invalidate(this);
+ }
+
+ /**
+ * Calls the listener callback to notify the new state of the current
+ * dependency.
+ */
+ private void validate() {
+ m_listener.validate(this);
+ }
+
+ /**
+ * Gets the actual state of the dependency.
+ * @return the state of the dependency.
+ */
+ public int getState() {
+ return m_state;
+ }
+
+ /**
+ * Gets the tracked specification.
+ * @return the Class object tracked by the dependency.
+ */
+ public Class getSpecification() {
+ return m_specification;
+ }
+
+ /**
+ * Sets the required specification of this service dependency.
+ * This operation is not supported if the dependency tracking has already begun.
+ * @param specification the required specification.
+ */
+ public void setSpecification(Class specification) {
+ if (m_tracker == null) {
+ m_specification = specification;
+ } else {
+ throw new UnsupportedOperationException("Dynamic specification change is not yet supported");
+ }
+ }
+
+ /**
+ * Sets the filter of the dependency. This method recomputes the
+ * matching set and call the onDependencyReconfiguration callback.
+ * @param filter the new LDAP filter.
+ */
+ public void setFilter(Filter filter) { //NOPMD
+ m_filter = filter;
+ if (m_tracker != null) { // Tracking started ...
+ List toRemove = new ArrayList();
+ List toAdd = new ArrayList();
+ ServiceReference usedRef = null;
+ synchronized (this) {
+
+ // Store the used service references.
+ if (!m_aggregate && !m_matchingRefs.isEmpty()) {
+ usedRef = (ServiceReference) m_matchingRefs.get(0);
+ }
+
+ // Get actually all tracked references.
+ ServiceReference[] refs = m_tracker.getServiceReferences();
+
+ if (refs == null) {
+ for (int j = 0; j < m_matchingRefs.size(); j++) {
+ // All references need to be removed.
+ toRemove.add(m_matchingRefs.get(j));
+ }
+ // No more matching dependency. Clear the matching reference set.
+ m_matchingRefs.clear();
+ } else {
+ // Compute matching services.
+ List matching = new ArrayList();
+ for (int i = 0; i < refs.length; i++) {
+ if (matchAgainstFilter(refs[i]) && match(refs[i])) {
+ matching.add(refs[i]);
+ }
+ }
+ // Now compare with used services.
+ for (int j = 0; j < m_matchingRefs.size(); j++) {
+ ServiceReference ref = (ServiceReference) m_matchingRefs.get(j);
+ // Check if the reference is inside the matching list:
+ if (!matching.contains(ref)) {
+ // The reference should be removed
+ toRemove.add(ref);
+ }
+ }
+
+ // Then remove services which do no more match.
+ m_matchingRefs.removeAll(toRemove);
+
+ // Then, add new matching services.
+
+ for (int k = 0; k < matching.size(); k++) {
+ if (!m_matchingRefs.contains(matching.get(k))) {
+ m_matchingRefs.add(matching.get(k));
+ toAdd.add(matching.get(k));
+ }
+ }
+
+ // Sort the collections if needed.
+ if (m_comparator != null) {
+ Collections.sort(m_matchingRefs, m_comparator);
+ Collections.sort(toAdd, m_comparator);
+ Collections.sort(toRemove, m_comparator);
+ }
+
+ }
+ }
+
+ // Call the callback outside the sync bloc.
+ if (m_aggregate) {
+ ServiceReference[] rem = null;
+ ServiceReference[] add = null;
+ if (!toAdd.isEmpty()) {
+ add = (ServiceReference[]) toAdd.toArray(new ServiceReference[toAdd.size()]);
+ }
+ if (!toRemove.isEmpty()) {
+ rem = (ServiceReference[]) toRemove.toArray(new ServiceReference[toRemove.size()]);
+ }
+ if (rem != null || add != null) { // Notify the change only when a change is made on the matching reference list.
+ onDependencyReconfiguration(rem, add);
+ }
+ } else {
+ // Create a local copy to avoid un-sync reference list access.
+ int size;
+ ServiceReference newRef = null;
+ synchronized (m_matchingRefs) {
+ size = m_matchingRefs.size();
+ if (size > 0) {
+ newRef = (ServiceReference) m_matchingRefs.get(0);
+ }
+ }
+ // Non aggregate case.
+ // If the used reference was not null
+ if (usedRef == null) {
+ // The used ref was null,
+ if (size > 0) {
+ onDependencyReconfiguration(null, new ServiceReference[] { newRef });
+ } // Don't notify the change, if the set is not touched by the reconfiguration.
+ } else {
+ // If the used ref disappears, inject a new service if available, else reinject null.
+ if (toRemove.contains(usedRef)) {
+ // We have to replace the service.
+ if (size > 0) {
+ onDependencyReconfiguration(new ServiceReference[] { usedRef }, new ServiceReference[] { newRef });
+ } else {
+ onDependencyReconfiguration(new ServiceReference[] { usedRef }, null);
+ }
+ } else if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY && newRef != usedRef) { //NOPMD
+ // In the case of dynamic-priority, check if the used ref is no more the highest reference
+ onDependencyReconfiguration(new ServiceReference[] { usedRef }, new ServiceReference[] { newRef });
+ }
+ }
+ }
+ // Now, compute the new dependency state.
+ computeDependencyState();
+ }
+ }
+
+ /**
+ * Returns the dependency filter (String form).
+ * @return the String form of the LDAP filter used by this dependency,
+ * <code>null</code> if not set.
+ */
+ public String getFilter() {
+ if (m_filter == null) {
+ return null;
+ } else {
+ return m_filter.toString();
+ }
+ }
+
+ /**
+ * Sets the aggregate attribute of the current dependency.
+ * If the tracking is opened, it will call arrival and departure callbacks.
+ * @param isAggregate the new aggregate attribute value.
+ */
+ public synchronized void setAggregate(boolean isAggregate) {
+ if (m_tracker == null) { // Not started ...
+ m_aggregate = isAggregate;
+ } else {
+ // We become aggregate.
+ if (!m_aggregate && isAggregate) {
+ m_aggregate = true;
+ // Call the callback on all non already injected service.
+ if (m_state == RESOLVED) {
+
+ for (int i = 1; i < m_matchingRefs.size(); i++) { // The loop begin at 1, as the 0 is already injected.
+ onServiceArrival((ServiceReference) m_matchingRefs.get(i));
+ }
+ }
+ } else if (m_aggregate && !isAggregate) {
+ m_aggregate = false;
+ // We become non-aggregate.
+ if (m_state == RESOLVED) {
+ for (int i = 1; i < m_matchingRefs.size(); i++) { // The loop begin at 1, as the 0 stills injected.
+ onServiceDeparture((ServiceReference) m_matchingRefs.get(i));
+ }
+ }
+ }
+ // Else, do nothing.
+ }
+ }
+
+ public synchronized boolean isAggregate() {
+ return m_aggregate;
+ }
+
+ /**
+ * Sets the optionality attribute of the current dependency.
+ * @param isOptional the new optional attribute value.
+ */
+ public void setOptionality(boolean isOptional) {
+ if (m_tracker == null) { // Not started ...
+ m_optional = isOptional;
+ } else {
+ computeDependencyState();
+ }
+ }
+
+ public boolean isOptional() {
+ return m_optional;
+ }
+
+ /**
+ * Gets the used binding policy.
+ * @return the current binding policy.
+ */
+ public int getBindingPolicy() {
+ return m_policy;
+ }
+
+ /**
+ * Sets the binding policy.
+ * Not yet supported.
+ */
+ public void setBindingPolicy() {
+ throw new UnsupportedOperationException("Binding Policy change is not yet supported");
+ // TODO supporting dynamic policy change.
+ }
+
+ public void setComparator(Comparator cmp) {
+ m_comparator = cmp;
+ // NOTE: the array will be sorted at the next get.
+ }
+
+ /**
+ * Gets the used comparator name.
+ * <code>Null</code> if no comparator (i.e. the OSGi one is used).
+ * @return the comparator class name or <code>null</code> if the dependency doesn't use a comparator.
+ */
+ public synchronized String getComparator() {
+ if (m_comparator != null) {
+ return m_comparator.getClass().getName();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the bundle context used by this dependency.
+ * This operation is not supported if the tracker is already opened.
+ * @param context the bundle context or service context to use
+ */
+ public void setBundleContext(BundleContext context) {
+ if (m_tracker == null) { // Not started ...
+ m_context = context;
+ } else {
+ throw new UnsupportedOperationException("Dynamic bundle (i.e. service) context change is not supported");
+ }
+ }
+
+ /**
+ * Gets a service object for the given reference.
+ * @param ref the wanted service reference
+ * @return the service object attached to the given reference
+ */
+ public Object getService(ServiceReference ref) {
+ Object svc = m_tracker.getService(ref);
+ if (svc instanceof IPOJOServiceFactory) {
+ Object obj = ((IPOJOServiceFactory) svc).getService(m_instance);
+ m_serviceObjects.put(ref, svc); // We store the factory !
+ return obj;
+ } else {
+ m_serviceObjects.put(ref, svc);
+ return svc;
+ }
+ }
+
+ /**
+ * Ungets a used service reference.
+ * @param ref the reference to unget.
+ */
+ public void ungetService(ServiceReference ref) {
+ m_tracker.ungetService(ref);
+ Object obj = m_serviceObjects.remove(ref); // Remove the service object
+ if (obj != null && obj instanceof IPOJOServiceFactory) {
+ ((IPOJOServiceFactory) obj).ungetService(m_instance, obj);
+ }
+ }
+
+ /**
+ * Helper method parsing the comparator attribute and returning the
+ * comparator object. If the 'comparator' attribute is not set, this method
+ * returns null. If the 'comparator' attribute is set to 'osgi', this method
+ * returns the normal OSGi comparator. In other case, it tries to create
+ * an instance of the declared comparator class.
+ * @param dep the Element describing the dependency
+ * @param context the bundle context (to load the comparator class)
+ * @return the comparator object, <code>null</code> if not set.
+ * @throws ConfigurationException the comparator class cannot be load or the
+ * comparator cannot be instantiated correctly.
+ */
+ public static Comparator getComparator(Element dep, BundleContext context) throws ConfigurationException {
+ Comparator cmp = null;
+ String comp = dep.getAttribute("comparator");
+ if (comp != null) {
+ if (comp.equalsIgnoreCase("osgi")) {
+ cmp = new ServiceReferenceRankingComparator();
+ } else {
+ try {
+ Class cla = context.getBundle().loadClass(comp);
+ cmp = (Comparator) cla.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Cannot load a customized comparator : " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Cannot create a customized comparator : " + e.getMessage());
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Cannot create a customized comparator : " + e.getMessage());
+ }
+ }
+ }
+ return cmp;
+ }
+
+ /**
+ * Loads the given specification class.
+ * @param specification the specification class name to load
+ * @param context the bundle context
+ * @return the class object for the given specification
+ * @throws ConfigurationException if the class cannot be loaded correctly.
+ */
+ public static Class loadSpecification(String specification, BundleContext context) throws ConfigurationException {
+ Class spec = null;
+ try {
+ spec = context.getBundle().loadClass(specification);
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("A required specification cannot be loaded : " + specification);
+ }
+ return spec;
+ }
+
+ /**
+ * Helper method parsing the binding policy.
+ * If the 'policy' attribute is not set in the dependency, the method returns
+ * the 'DYNAMIC BINDING POLICY'. Accepted policy values are : dynamic,
+ * dynamic-priority and static.
+ * @param dep the Element describing the dependency
+ * @return the policy attached to this dependency
+ * @throws ConfigurationException if an unknown binding policy was described.
+ */
+ public static int getPolicy(Element dep) throws ConfigurationException {
+ String policy = dep.getAttribute("policy");
+ if (policy == null || policy.equalsIgnoreCase("dynamic")) {
+ return DYNAMIC_BINDING_POLICY;
+ } else if (policy.equalsIgnoreCase("dynamic-priority")) {
+ return DYNAMIC_PRIORITY_BINDING_POLICY;
+ } else if (policy.equalsIgnoreCase("static")) {
+ return STATIC_BINDING_POLICY;
+ } else {
+ throw new ConfigurationException("Binding policy unknown : " + policy);
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyStateListener.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyStateListener.java
new file mode 100644
index 0000000..a72409b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyStateListener.java
@@ -0,0 +1,39 @@
+/*
+ * 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.felix.ipojo.util;
+
+
+/**
+ * This interface allows a class to be notified of service dependency state changes.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DependencyStateListener {
+
+ /**
+ * The given dependency becomes valid.
+ * @param dependency the dependency becoming valid.
+ */
+ void validate(DependencyModel dependency);
+
+ /**
+ * The given dependency becomes invalid.
+ * @param dependency the dependency becoming invalid.
+ */
+ void invalidate(DependencyModel dependency);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
new file mode 100644
index 0000000..e28009b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
@@ -0,0 +1,383 @@
+/*
+ * 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.felix.ipojo.util;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ErrorHandler;
+import org.apache.felix.ipojo.Extender;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * iPOJO Logger.
+ * This class is an helper class implementing a simple log system.
+ * This logger sends log messages to a log service if available.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Logger {
+
+ /**
+ * The iPOJO default log level property.
+ */
+ public static final String IPOJO_LOG_LEVEL_PROP = "ipojo.log.level";
+
+ /**
+ * iPOJO log level manifest header.
+ * The uppercase 'I' is important as BND removes all headers that do not
+ * start with an uppercase are not added to the bundle.
+ * Use an upper case to support bnd.
+ */
+ public static final String IPOJO_LOG_LEVEL_HEADER = "Ipojo-log-level";
+
+ /**
+ * The Log Level ERROR.
+ */
+ public static final int ERROR = 1;
+
+ /**
+ * The Log Level WARNING.
+ */
+ public static final int WARNING = 2;
+
+ /**
+ * The Log Level INFO.
+ */
+ public static final int INFO = 3;
+
+ /**
+ * The Log Level DEBUG.
+ */
+ public static final int DEBUG = 4;
+
+ /**
+ * The Bundle Context used to get the
+ * log service.
+ */
+ private BundleContext m_context;
+
+ /**
+ * The name of the logger.
+ */
+ private String m_name;
+
+ /**
+ * The instance associated to the logger if any.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * The trace level of this logger.
+ */
+ private int m_level;
+
+ /**
+ * Creates a logger.
+ * @param context the bundle context
+ * @param name the name of the logger
+ * @param level the trace level
+ */
+ public Logger(BundleContext context, String name, int level) {
+ m_name = name;
+ m_level = level;
+ m_context = context;
+ }
+
+ /**
+ * Creates a logger.
+ * @param context the bundle context
+ * @param instance the instance
+ * @param level the trace level
+ */
+ public Logger(BundleContext context, ComponentInstance instance, int level) {
+ m_instance = instance;
+ m_name = m_instance.getInstanceName();
+ m_level = level;
+ m_context = context;
+ }
+
+ /**
+ * Create a logger.
+ * Uses the default logger level.
+ * @param context the bundle context
+ * @param name the name of the logger
+ */
+ public Logger(BundleContext context, String name) {
+ this(context, name, getDefaultLevel(context));
+ }
+
+ /**
+ * Create a logger.
+ * Uses the default logger level.
+ * @param context the bundle context
+ * @param instance the instance
+ */
+ public Logger(BundleContext context, ComponentInstance instance) {
+ this(context, instance, getDefaultLevel(context));
+ }
+
+ /**
+ * Logs a message.
+ * @param level the level of the message
+ * @param msg the the message to log
+ */
+ public void log(int level, String msg) {
+ if (m_level >= level) {
+ dispatch(level, msg);
+ }
+ invokeErrorHandler(level, msg, null);
+ }
+
+ /**
+ * Logs a message with an exception.
+ * @param level the level of the message
+ * @param msg the message to log
+ * @param exception the exception attached to the message
+ */
+ public void log(int level, String msg, Throwable exception) {
+ if (m_level >= level) {
+ dispatch(level, msg, exception);
+ }
+ invokeErrorHandler(level, msg, exception);
+ }
+
+ /**
+ * Internal log method.
+ * @param level the level of the message.
+ * @param msg the message to log
+ */
+ private void dispatch(int level, String msg) {
+ LogService log = null;
+ ServiceReference ref = null;
+ try {
+ // Security Check
+ if (SecurityHelper.hasPermissionToGetService(LogService.class.getName(), m_context)) {
+ ref = m_context.getServiceReference(LogService.class.getName());
+ } else {
+ Extender.getIPOJOBundleContext().getServiceReference(LogService.class.getName());
+ }
+
+ if (ref != null) {
+ log = (LogService) m_context.getService(ref);
+ }
+ } catch (IllegalStateException e) {
+ // Handle the case where the iPOJO bundle is stopping
+ }
+
+ String message = null;
+ String name = m_name;
+ if (name == null) {
+ name = "";
+ }
+
+ switch (level) {
+ case DEBUG:
+ message = "[DEBUG] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_DEBUG, message);
+ } else {
+ System.err.println(message);
+ }
+ break;
+ case ERROR:
+ message = "[ERROR] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_ERROR, message);
+ } else {
+ System.err.println(message);
+ }
+ break;
+ case INFO:
+ message = "[INFO] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_INFO, message);
+ } else {
+ System.err.println(message);
+ }
+ break;
+ case WARNING:
+ message = "[WARNING] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_WARNING, message);
+ } else {
+ System.err.println(message);
+ }
+ break;
+ default:
+ message = "[UNKNOWN] " + name + " : " + msg;
+ System.err.println(message);
+ break;
+ }
+
+ if (log != null) {
+ m_context.ungetService(ref);
+ }
+ }
+
+ /**
+ * Internal log method.
+ * @param level the level of the message.
+ * @param msg the message to log
+ * @param exception the exception attached to the message
+ */
+ private void dispatch(int level, String msg, Throwable exception) {
+ LogService log = null;
+ ServiceReference ref = null;
+ try {
+ // Security Check
+ if (SecurityHelper.hasPermissionToGetService(LogService.class.getName(), m_context)) {
+ ref = m_context.getServiceReference(LogService.class.getName());
+ } else {
+ Extender.getIPOJOBundleContext().getServiceReference(LogService.class.getName());
+ }
+
+ if (ref != null) {
+ log = (LogService) m_context.getService(ref);
+ }
+ } catch (IllegalStateException e) {
+ // Handle the case where the iPOJO bundle is stopping
+ }
+
+ String message = null;
+ String name = m_name;
+ if (name == null) {
+ name = "";
+ }
+
+ switch (level) {
+ case DEBUG:
+ message = "[DEBUG] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_DEBUG, message, exception);
+ } else {
+ System.err.println(message);
+ exception.printStackTrace();
+ }
+ break;
+ case ERROR:
+ message = "[ERROR] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_ERROR, message, exception);
+ } else {
+ System.err.println(message);
+ exception.printStackTrace();
+ }
+ break;
+ case INFO:
+ message = "[INFO] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_INFO, message, exception);
+ } else {
+ System.err.println(message);
+ exception.printStackTrace();
+ }
+ break;
+ case WARNING:
+ message = "[WARNING] " + name + " : " + msg;
+ if (log != null) {
+ log.log(LogService.LOG_WARNING, message, exception);
+ } else {
+ System.err.println(message);
+ exception.printStackTrace();
+ }
+ break;
+ default:
+ message = "[UNKNOWN] " + name + " : " + msg;
+ System.err.println(message);
+ exception.printStackTrace();
+ break;
+ }
+
+ if (log != null) {
+ m_context.ungetService(ref);
+ }
+ }
+
+ /**
+ * Invokes the error handler service is present.
+ * @param level the log level
+ * @param msg the message
+ * @param error the error
+ */
+ private void invokeErrorHandler(int level, String msg, Throwable error) {
+ // First check the level
+ if (level > WARNING) {
+ return; // Others levels are not supported.
+ }
+ // Try to get the error handler service
+ try {
+ ServiceReference ref = m_context.getServiceReference(ErrorHandler.class.getName());
+ if (ref != null) {
+ ErrorHandler handler = (ErrorHandler) m_context.getService(ref);
+ if (level == ERROR) {
+ handler.onError(m_instance, msg, error);
+ } else if (level == WARNING) {
+ handler.onWarning(m_instance, msg, error);
+ } // The others case are not supported
+ m_context.ungetService(ref);
+ return;
+ } // Else do nothing...
+ } catch (IllegalStateException e) {
+ // Ignore
+ // The iPOJO bundle is stopping.
+ }
+ }
+
+ /**
+ * Gets the default logger level.
+ * The property is searched inside the framework properties,
+ * the system properties, and in the manifest from the given
+ * bundle context. By default, set the level to {@link Logger#WARNING}.
+ * @param context the bundle context.
+ * @return the default log level.
+ */
+ private static int getDefaultLevel(BundleContext context) {
+ // First check in the framework and in the system properties
+ String level = context.getProperty(IPOJO_LOG_LEVEL_PROP);
+
+ // If null, look in the bundle manifest
+ if (level == null) {
+ String key = IPOJO_LOG_LEVEL_PROP.replace('.', '-');
+ level = (String) context.getBundle().getHeaders().get(key);
+ }
+
+ // if still null try the second header
+ if (level == null) {
+ level = (String) context.getBundle().getHeaders().get(IPOJO_LOG_LEVEL_HEADER);
+ }
+
+ if (level != null) {
+ if (level.equalsIgnoreCase("info")) {
+ return INFO;
+ } else if (level.equalsIgnoreCase("debug")) {
+ return DEBUG;
+ } else if (level.equalsIgnoreCase("warning")) {
+ return WARNING;
+ } else if (level.equalsIgnoreCase("error")) {
+ return ERROR;
+ }
+ }
+
+ // Either l is null, either the specified log level was unknown
+ // Set the default to WARNING
+ return WARNING;
+
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java
new file mode 100644
index 0000000..13e2282
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java
@@ -0,0 +1,683 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.ConstructorInjector;
+import org.apache.felix.ipojo.FieldInterceptor;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Property class managing a managed value.
+ * This class managed the method invocation, field injection
+ * and constructor injection.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Property implements FieldInterceptor, ConstructorInjector {
+
+ /**
+ * Object used for an unvalued property.
+ */
+ public static final Object NO_VALUE = new Object();
+
+ /**
+ * The name of the property (field name if not set).
+ * Cannot change once set.
+ */
+ private final String m_name;
+
+ /**
+ * The field of the property.
+ * Cannot change once set.
+ */
+ private final String m_field;
+
+ /**
+ * The setter method of the property.
+ * Cannot change once set.
+ */
+ private final Callback m_method;
+
+ /**
+ * The index of the parameter in case of
+ * constructor injection.
+ */
+ private int m_index = -1;
+
+ /**
+ * The value of the property.
+ */
+ private Object m_value = NO_VALUE;
+
+ /**
+ * The default value of the property.
+ */
+ private Object m_defaultValue = NO_VALUE;
+
+ /**
+ * Flag tracking is the method was
+ * already called for the current value.
+ */
+ private boolean m_invoked;
+
+ /**
+ * The type of the property.
+ */
+ private final Class m_type;
+
+ /**
+ * The handler object to get the logger.
+ */
+ private final Handler m_handler;
+
+ /**
+ * The instance manager.
+ */
+ private final InstanceManager m_manager;
+
+ /**
+ * Creates a property.
+ * At least the method or the field need
+ * to be specified.
+ * @param name the name of the property (optional)
+ * @param field the name of the field
+ * @param method the method name
+ * @param value the initial value of the property (optional)
+ * @param type the the type of the property
+ * @param manager the instance manager
+ * @param handler the handler object which manage this property.
+ * @throws ConfigurationException if the property value cannot be set.
+ */
+ public Property(String name, String field, String method, String value, String type, InstanceManager manager, Handler handler) throws ConfigurationException {
+ m_handler = handler;
+ m_manager = manager;
+ m_field = field;
+
+ if (name == null) {
+ if (m_field == null) {
+ m_name = method;
+ } else {
+ m_name = field;
+ }
+ } else {
+ m_name = name;
+ }
+
+ m_type = computeType(type, manager.getGlobalContext());
+ if (value != null) {
+ m_value = create(m_type, value);
+ m_defaultValue = m_value;
+ }
+
+ if (method != null) {
+ m_method = new Callback(method, new String[] { m_type.getName() }, false, manager);
+ } else {
+ m_method = null;
+ }
+ }
+
+ /**
+ * Creates a property.
+ * At least the method or the field need
+ * to be specified.
+ * @param name the name of the property (optional)
+ * @param field the name of the field
+ * @param method the method name
+ * @param value the initial value of the property (optional)
+ * @param manager the instance manager
+ * @param handler the handler object which manage this property.
+ * @throws ConfigurationException if the property value cannot be set.
+ */
+ public Property(String name, String field, String method, Object value, InstanceManager manager, Handler handler) throws ConfigurationException {
+ m_handler = handler;
+ m_manager = manager;
+ m_field = field;
+
+ if (value == null) {
+ throw new ConfigurationException("Cannot create properties without a value");
+ }
+
+ if (name == null) {
+ if (m_field == null) {
+ m_name = method;
+ } else {
+ m_name = field;
+ }
+ } else {
+ m_name = name;
+ }
+
+ m_type = value.getClass();
+ m_value = value;
+ m_defaultValue = m_value;
+
+ if (method != null) {
+ m_method = new Callback(method, new String[] { m_type.getName() }, false, manager);
+ } else {
+ m_method = null;
+ }
+ }
+
+ public Property(String name, String field, String method, int index,
+ String value, String type, InstanceManager manager, Handler handler) throws ConfigurationException {
+ this(name, field, method, value, type, manager, handler);
+ m_index = index;
+ }
+
+ /**
+ * Computes and returns the property type according to the given type name.
+ * @param type the the type name
+ * @param context the bundle context (used to load classes)
+ * @return the class of the given type
+ * @throws ConfigurationException if an error occurs when loading the type class for non-primitive types.
+ */
+ public static Class computeType(String type, BundleContext context) throws ConfigurationException {
+ // Array :
+ if (type.endsWith("[]")) {
+ return computeArrayType(type, context);
+ } else {
+ // Syntactic sugar to avoid writing java.lang.String
+ if ("string".equals(type) || "String".equals(type)) {
+ return java.lang.String.class;
+ } else if ("boolean".equals(type)) {
+ return Boolean.TYPE;
+ } else if ("byte".equals(type)) {
+ return Byte.TYPE;
+ } else if ("short".equals(type)) {
+ return Short.TYPE;
+ } else if ("int".equals(type)) {
+ return Integer.TYPE;
+ } else if ("long".equals(type)) {
+ return Long.TYPE;
+ } else if ("float".equals(type)) {
+ return Float.TYPE;
+ } else if ("double".equals(type)) {
+ return Double.TYPE;
+ } else if ("char".equals(type)) {
+ return Character.TYPE;
+ } else {
+ // Non array, complex type.
+ try {
+ return context.getBundle().loadClass(type);
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Class not found exception in setValue on " + type + " : " + e.getMessage());
+ } catch (SecurityException e) {
+ throw new ConfigurationException("Security execption in setValue on " + type + " : " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ throw new ConfigurationException("Argument issue when calling the constructor of the type " + type);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the Class object of a type array.
+ * @param type the string descriptor of the type (must end by [] )
+ * @param context the bundle context (used to load classes)
+ * @return the Class object of the given type array.
+ * @throws ConfigurationException if the class cannot be loaded
+ */
+ private static Class computeArrayType(String type, BundleContext context) throws ConfigurationException {
+ // Note: Harmony does't support the type[].class notation.
+ // An empty array has to be created to get the class object.
+ String internalType = type.substring(0, type.length() - 2);
+ if ("string".equals(internalType) || "String".equals(internalType)) {
+ return new String[0].getClass();
+ }
+ if ("boolean".equals(internalType)) {
+ return new boolean[0].getClass();
+ }
+ if ("byte".equals(internalType)) {
+ return new byte[0].getClass();
+ }
+ if ("short".equals(internalType)) {
+ return new short[0].getClass();
+ }
+ if ("int".equals(internalType)) {
+ return new int[0].getClass();
+ }
+ if ("long".equals(internalType)) {
+ return new long[0].getClass();
+ }
+ if ("float".equals(internalType)) {
+ return new float[0].getClass();
+ }
+ if ("double".equals(internalType)) {
+ return new double[0].getClass();
+ }
+ if ("char".equals(internalType)) {
+ return new char[0].getClass();
+ }
+
+ // Complex array type.
+ try {
+ Class clazz = context.getBundle().loadClass(internalType);
+ Object[] object = (Object[]) Array.newInstance(clazz, 0);
+ return object.getClass();
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Class not found exception in setValue on " + internalType);
+ } catch (SecurityException e) {
+ throw new ConfigurationException("Security Exception in setValue on " + internalType);
+ } catch (IllegalArgumentException e) {
+ throw new ConfigurationException("Argument issue when calling the constructor of the type " + internalType);
+ }
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public String getField() {
+ return m_field;
+ }
+
+ public String getType() {
+ return m_type.getName();
+ }
+
+ /**
+ * Gets the method name,
+ * <code>null</code> if no method.
+ * @return the method name.
+ */
+ public String getMethod() {
+ if (m_method == null) { return null; }
+ return m_method.getMethod();
+ }
+
+ /**
+ * Checks if the property has a method callback.
+ * @return <code>true</code> if the property has a method.
+ */
+ public boolean hasMethod() {
+ return m_method != null;
+ }
+
+ /**
+ * Gets the parameter index.
+ * @return the parameter index or <code>-1</code>
+ * if this property is not injected using constructor
+ * parameter.
+ */
+ public int getParameterIndex() {
+ return m_index;
+ }
+
+ /**
+ * Checks if the property has a field.
+ * @return <code>true</code> if the property has a field.
+ */
+ public boolean hasField() {
+ return m_field != null;
+ }
+
+ public synchronized Object getValue() {
+ return m_value;
+ }
+
+ /**
+ * Gets the initial value of the property.
+ * @return the default value.
+ */
+ public Object getDefaultValue() {
+ return m_defaultValue;
+ }
+
+ /**
+ * Gets the NO VALUE Object.
+ * This method returns the object to inject when the property
+ * was not assigned to a value.
+ * @param type the type of the value.
+ * @return the object to inject when the property has no value.
+ */
+ private static Object getNoValue(Class type) {
+ if (Boolean.TYPE.equals(type)) { return Boolean.FALSE; }
+ if (Byte.TYPE.equals(type)) { return new Byte((byte) 0); }
+ if (Short.TYPE.equals(type)) { return new Short((short) 0); }
+ if (Integer.TYPE.equals(type)) { return new Integer(0); }
+ if (Long.TYPE.equals(type)) { return new Long(0); }
+ if (Float.TYPE.equals(type)) { return new Float(0); }
+ if (Double.TYPE.equals(type)) { return new Double(0); }
+ if (Character.TYPE.equals(type)) { return new Character((char) 0); }
+ // If all other case, return null.
+ return null;
+ }
+
+ /**
+ * Sets the value of the property.
+ * @param value the new value.
+ */
+ public void setValue(Object value) {
+ synchronized (this) {
+ // Is the object is directly assignable to the property, affect it.
+ if (isAssignable(m_type, value)) {
+ m_value = value;
+ } else {
+ // If the object is a String, we must recreate the object from the String form
+ if (value instanceof String) {
+ try {
+ m_value = create(m_type, (String) value);
+ } catch (ConfigurationException e) {
+ throw new ClassCastException("Incompatible type for the property " + m_name + " : " + e.getMessage());
+ }
+ } else {
+ // Error, the given property cannot be injected.
+ throw new ClassCastException("Incompatible type for the property " + m_name + " " + m_type.getName() + " expected, "
+ + value.getClass() + " found");
+ }
+ }
+ m_invoked = false;
+ }
+ }
+
+ /**
+ * Checks if the given value is assignable to the given type.
+ * @param type the class of the type
+ * @param value the object to check
+ * @return <code>true</code> if the object is assignable in the property of type 'type'.
+ */
+ public static boolean isAssignable(Class type, Object value) {
+ if (value == null || type.isInstance(value) || value == Property.NO_VALUE) { // When the value is null, the assign works necessary.
+ return true;
+ } else if (type.isPrimitive()) {
+ // Manage all boxing types.
+ if (value instanceof Boolean && Boolean.TYPE.equals(type)) { return true; }
+ if (value instanceof Byte && Byte.TYPE.equals(type)) { return true; }
+ if (value instanceof Short && Short.TYPE.equals(type)) { return true; }
+ if (value instanceof Integer && Integer.TYPE.equals(type)) { return true; }
+ if (value instanceof Long && Long.TYPE.equals(type)) { return true; }
+ if (value instanceof Float && Float.TYPE.equals(type)) { return true; }
+ if (value instanceof Double && Double.TYPE.equals(type)) { return true; }
+ if (value instanceof Character && Character.TYPE.equals(type)) { return true; }
+ return false;
+ } else {
+ // Else return false.
+ return false;
+ }
+ }
+
+ /**
+ * Creates an object of the given type with the given String value.
+ * @param type the type of the returned object
+ * @param strValue the String value.
+ * @return the object of type 'type' created from the String 'value'
+ * @throws ConfigurationException if the object cannot be created.
+ */
+ public static Object create(Class type, String strValue) throws ConfigurationException {
+ if (Boolean.TYPE.equals(type)) {
+ return Boolean.valueOf(strValue);
+ }
+ if (Byte.TYPE.equals(type)) { return new Byte(strValue); }
+ if (Short.TYPE.equals(type)) { return new Short(strValue); }
+ if (Integer.TYPE.equals(type)) { return new Integer(strValue); }
+ if (Long.TYPE.equals(type)) { return new Long(strValue); }
+ if (Float.TYPE.equals(type)) { return new Float(strValue); }
+ if (Double.TYPE.equals(type)) { return new Double(strValue); }
+ if (Character.TYPE.equals(type)) { return new Character(strValue.charAt(0)); }
+
+ // Array :
+ if (type.isArray()) {
+ return createArrayObject(type.getComponentType(), ParseUtils.parseArrays(strValue));
+ }
+
+ // Enum :
+ if (type.getSuperclass() != null && type.getSuperclass().getName().equals("java.lang.Enum")) {
+ try {
+ Method valueOf = type.getMethod("valueOf", new Class[] {String.class});
+ if (! valueOf.isAccessible()) {
+ valueOf.setAccessible(true);
+ }
+ // Invoke the static method
+ return valueOf.invoke(null, new String[] {strValue});
+ } catch (InvocationTargetException e) {
+ throw new ConfigurationException("Cannot create an enumerated value for " + type
+ + " with " + strValue + " : " + e.getTargetException());
+ } catch (Exception e) {
+ throw new ConfigurationException("Cannot create an enumerated value for " + type
+ + " with " + strValue + " : " + e.getMessage());
+ }
+ }
+
+ // Else it is a neither a primitive type neither a String -> create
+ // the object by calling a constructor with a string in argument.
+ try {
+ Constructor cst = type.getConstructor(new Class[] { String.class });
+ return cst.newInstance(new Object[] { strValue });
+ } catch (SecurityException e) {
+ throw new ConfigurationException("Security exception during the creation of " + type + " : " + e.getMessage());
+ } catch (NoSuchMethodException e) {
+ throw new ConfigurationException("Constructor not found exception during the creation of " + type + " : " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ throw new ConfigurationException("Argument issue when calling the constructor of the type " + type);
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Instantiation problem " + type);
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Illegal Access " + type);
+ } catch (InvocationTargetException e) {
+ throw new ConfigurationException("Invocation problem during the creation of " + type + " : " + e.getTargetException().getMessage());
+ }
+
+ }
+
+ /**
+ * Creates an array object containing the type component type from
+ * the String array 'values'.
+ * @param interntype the internal type of the array.
+ * @param values the String array
+ * @return the array containing objects created from the 'values' array
+ * @throws ConfigurationException if the array cannot be created correctly
+ */
+ public static Object createArrayObject(Class interntype, String[] values) throws ConfigurationException {
+ if (Boolean.TYPE.equals(interntype)) {
+ boolean[] bool = new boolean[values.length];
+ for (int i = 0; i < values.length; i++) {
+ bool[i] = Boolean.valueOf(values[i]).booleanValue();
+ }
+ return bool;
+ }
+ if (Byte.TYPE.equals(interntype)) {
+ byte[] byt = new byte[values.length];
+ for (int i = 0; i < values.length; i++) {
+ byt[i] = new Byte(values[i]).byteValue();
+ }
+ return byt;
+ }
+ if (Short.TYPE.equals(interntype)) {
+ short[] shor = new short[values.length];
+ for (int i = 0; i < values.length; i++) {
+ shor[i] = new Short(values[i]).shortValue();
+ }
+ return shor;
+ }
+ if (Integer.TYPE.equals(interntype)) {
+ int[] ints = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ ints[i] = new Integer(values[i]).intValue();
+ }
+ return ints;
+ }
+ if (Long.TYPE.equals(interntype)) {
+ long[] longs = new long[values.length];
+ for (int i = 0; i < values.length; i++) {
+ longs[i] = new Long(values[i]).longValue();
+ }
+ return longs;
+ }
+ if (Float.TYPE.equals(interntype)) {
+ float[] floats = new float[values.length];
+ for (int i = 0; i < values.length; i++) {
+ floats[i] = new Float(values[i]).floatValue();
+ }
+ return floats;
+ }
+ if (Double.TYPE.equals(interntype)) {
+ double[] doubles = new double[values.length];
+ for (int i = 0; i < values.length; i++) {
+ doubles[i] = new Double(values[i]).doubleValue();
+ }
+ return doubles;
+ }
+ if (Character.TYPE.equals(interntype)) {
+ char[] chars = new char[values.length];
+ for (int i = 0; i < values.length; i++) {
+ chars[i] = values[i].toCharArray()[0];
+ }
+ return chars;
+ }
+
+ // Else it is a neither a primitive type -> create the
+ // object by calling a constructor with a string in argument.
+ try {
+ Constructor cst = interntype.getConstructor(new Class[] { String.class });
+ Object[] object = (Object[]) Array.newInstance(interntype, values.length);
+ for (int i = 0; i < values.length; i++) {
+ object[i] = cst.newInstance(new Object[] { values[i].trim() });
+ }
+ return object;
+ } catch (NoSuchMethodException e) {
+ throw new ConfigurationException("Constructor not found exception in setValue on " + interntype.getName());
+ } catch (IllegalArgumentException e) {
+ throw new ConfigurationException("Argument issue when calling the constructor of the type " + interntype.getName());
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Instantiation problem " + interntype.getName());
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Illegal Access Exception in " + interntype.getName());
+ } catch (InvocationTargetException e) {
+ throw new ConfigurationException("Invocation problem " + interntype.getName() + " : " + e.getTargetException().getMessage());
+ }
+ }
+
+ /**
+ * Clears the invoked flag.
+ * Then, despite the setter was already called,
+ * it will be invoked another times.
+ */
+ public synchronized void reset() {
+ m_invoked = false;
+ }
+
+ /**
+ * Invokes the setter method on the given pojo object.
+ * If no specified pojo object, it calls on each created pojo object.
+ * @param instance the created object (could be <code>null</code>)
+ */
+ public synchronized void invoke(Object instance) {
+ if (m_invoked) {
+ return; // Already called.
+ }
+
+ if (m_value == NO_VALUE) {
+ // Don't call method if no value
+ return;
+ }
+
+ try {
+ if (instance == null) {
+ m_method.call(new Object[] { m_value });
+ } else {
+ m_method.call(instance, new Object[] { m_value });
+ }
+ m_invoked = true;
+ } catch (NoSuchMethodException e) {
+ m_handler.error("The method " + m_method + " does not exist in the implementation class " + m_manager.getClassName(), e);
+ m_manager.stop();
+ } catch (IllegalAccessException e) {
+ m_handler.error("The method " + m_method + " is not accessible in the implementation class " + m_manager.getClassName(), e);
+ m_manager.stop();
+ } catch (InvocationTargetException e) {
+ m_handler.error("The method " + m_method + " in the implementation class " + m_manager.getClassName() + "throws an exception : " + e.getTargetException().getMessage(), e.getTargetException());
+ m_manager.setState(ComponentInstance.INVALID);
+ }
+ }
+
+ /**
+ * A field value is required by the object 'pojo'.
+ * @param pojo the POJO object
+ * @param fieldName the field
+ * @param value the last value
+ * @return the value if the handler want to inject this value.
+ * @see org.apache.felix.ipojo.FieldInterceptor#onGet(java.lang.Object, java.lang.String, java.lang.Object)
+ */
+ public synchronized Object onGet(Object pojo, String fieldName, Object value) {
+ if (m_value == NO_VALUE) {
+ return getNoValue(m_type);
+ }
+ return m_value;
+ }
+
+ /**
+ * The field 'field' receives a new value.
+ * @param pojo the pojo
+ * @param fieldName the field name
+ * @param value the new value
+ * @see org.apache.felix.ipojo.FieldInterceptor#onSet(java.lang.Object, java.lang.String, java.lang.Object)
+ */
+ public synchronized void onSet(Object pojo, String fieldName, Object value) {
+ if (m_value == null || ! m_value.equals(value)) {
+ setValue(value);
+ }
+ }
+
+ /**
+ * Gets the object to inject as constructor parameter.
+ * @param index the constructor parameter index
+ * @return the object to inject, so the property value.
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameter(int)
+ */
+ public Object getConstructorParameter(int index) {
+ if (m_index != index) {
+ return null;
+ }
+
+ if (m_value == NO_VALUE) {
+ return getNoValue(m_type);
+ }
+ return m_value;
+ }
+
+ /**
+ * Gets the type of the constructor parameter to inject.
+ * @param index the parameter index
+ * @return the Class of the property.
+ * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameterType(int)
+ */
+ public Class getConstructorParameterType(int index) {
+ if (m_index != index) {
+ return null;
+ }
+ return m_type;
+ }
+
+ /**
+ * Gets the handler managing the property.
+ * @return the configuration handler.
+ */
+ public Handler getHandler() {
+ return m_handler;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SecurityHelper.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SecurityHelper.java
new file mode 100644
index 0000000..04d576f
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SecurityHelper.java
@@ -0,0 +1,186 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.security.Permission;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServicePermission;
+
+
+/**
+ * Methods checking security permissions.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SecurityHelper {
+
+
+ /**
+ * Gets a bundle context to register the given services.
+ * This method can be used only if iPOJO is able to
+ * registers the services (so for ManagedServiceFactory,
+ * Factory and Architecture)
+ * @param itfs the service interfaces
+ * @param comp the component bundle context
+ * @param ipojo the ipojo bundle context
+ * @return <code>comp</code> if the bundle has enough permission
+ * to register the service, <code>ipojo</code> otherwise.
+ */
+ public static BundleContext selectContextToRegisterServices(String[] itfs,
+ BundleContext comp, BundleContext ipojo) {
+ if (System.getSecurityManager() != null) {
+ for (int i = 0; i < itfs.length; i++) {
+ final Permission perm = new ServicePermission(itfs[i],
+ ServicePermission.REGISTER);
+ if (!comp.getBundle().hasPermission(perm)) {
+ return ipojo;
+ }
+ }
+
+ }
+ return comp;
+ }
+
+ /**
+ * Gets a bundle context to register the given service.
+ * This method can be used only if iPOJO is able to
+ * registers the service (so for ManagedServiceFactory,
+ * Factory and Architecture)
+ * @param itf the service interface
+ * @param comp the component bundle context
+ * @param ipojo the ipojo bundle context
+ * @return <code>comp</code> if the bundle has enough permission
+ * to register the service, <code>ipojo</code> otherwise.
+ */
+ public static BundleContext selectContextToRegisterService(String itf,
+ BundleContext comp, BundleContext ipojo) {
+ if (System.getSecurityManager() != null) {
+ final Permission perm = new ServicePermission(itf,
+ ServicePermission.REGISTER);
+ if (!comp.getBundle().hasPermission(perm)) {
+ return ipojo;
+ }
+ }
+ return comp;
+ }
+
+ /**
+ * Gets a bundle context to get the given service.
+ * This method can be used only if iPOJO is able to
+ * get the service (so for ManagedServiceFactory,
+ * Factory, Architecture and LogService)
+ * @param itf the service interface
+ * @param comp the component bundle context
+ * @param ipojo the ipojo bundle context
+ * @return <code>comp</code> if the bundle has enough permission
+ * to get the service, <code>ipojo</code> otherwise.
+ */
+ public static BundleContext selectContextToGetService(String itf,
+ BundleContext comp, BundleContext ipojo) {
+ if (System.getSecurityManager() != null) {
+ final Permission perm = new ServicePermission(itf,
+ ServicePermission.GET);
+ if (!comp.getBundle().hasPermission(perm)) {
+ return ipojo;
+ }
+ }
+ return comp;
+ }
+
+ /**
+ * Checks if the component bundle context has enough permission
+ * to get the given service.
+ * @param itf the service interface
+ * @param comp the component bundle context
+ * @return <code>true</code> if the bundle has enough permission
+ * to get the service, <code>false</code> otherwise.
+ */
+ public static boolean hasPermissionToGetService(String itf,
+ BundleContext comp) {
+ if (System.getSecurityManager() != null) {
+ final Permission perm = new ServicePermission(itf,
+ ServicePermission.GET);
+ return comp.getBundle().hasPermission(perm);
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the component bundle context has enough permission
+ * to get the given services.
+ * @param itfs the service interfaces
+ * @param comp the component bundle context
+ * @return <code>true</code> if the bundle has enough permission
+ * to get the services, <code>false</code> otherwise.
+ */
+ public static boolean hasPermissionToGetServices(String[] itfs,
+ BundleContext comp) {
+ if (System.getSecurityManager() != null) {
+ for (int i = 0; i < itfs.length; i++) {
+ final Permission perm = new ServicePermission(itfs[i],
+ ServicePermission.GET);
+ if (!comp.getBundle().hasPermission(perm)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the component bundle context has enough permission
+ * to register the given service.
+ * @param itf the service interface
+ * @param comp the component bundle context
+ * @return <code>true</code> if the bundle has enough permission
+ * to register the service, <code>false</code> otherwise.
+ */
+ public static boolean hasPermissionToRegisterService(String itf,
+ BundleContext comp) {
+ if (System.getSecurityManager() != null) {
+ final Permission perm = new ServicePermission(itf,
+ ServicePermission.REGISTER);
+ return comp.getBundle().hasPermission(perm);
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the component bundle context has enough permission
+ * to register the given services.
+ * @param itfs the service interfaces
+ * @param comp the component bundle context
+ * @return <code>true</code> if the bundle has enough permission
+ * to register the services, <code>false</code> otherwise.
+ */
+ public static boolean hasPermissionToRegisterServices(String[] itfs,
+ BundleContext comp) {
+ if (System.getSecurityManager() != null) {
+ for (int i = 0; i < itfs.length; i++) {
+ final Permission perm = new ServicePermission(itfs[i],
+ ServicePermission.REGISTER);
+ if (!comp.getBundle().hasPermission(perm)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java
new file mode 100644
index 0000000..649116c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java
@@ -0,0 +1,90 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Service Reference Comparator.
+ * This comparator follows OSGi Ranking policy.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceReferenceRankingComparator implements Comparator, Serializable {
+
+ /**
+ * Id.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Compares two service reference.
+ * @param ref1 the reference 1
+ * @param ref2 the reference 2
+ * @return <code>-1</code> if the reference 1
+ * is 'higher' than the reference 2, <code>1</code> otherwise.
+ * (higher is term of ranking means a lower index)
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(Object ref1, Object ref2) {
+ if (ref1.equals(ref2)) { return 0; }
+
+ if (ref1 instanceof ServiceReference && ref2 instanceof ServiceReference) {
+ Object property1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_RANKING);
+ Object property2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_RANKING);
+
+ int rank1 = 0;
+ int rank2 = 0;
+ if (property1 instanceof Integer) {
+ rank1 = ((Integer) property1).intValue();
+ }
+ if (property2 instanceof Integer) {
+ rank2 = ((Integer) property2).intValue();
+ }
+
+ if (rank1 == rank2) {
+ // Check service.id
+ Object sid1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_ID);
+ Object sid2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_ID);
+
+ long rankId1 = ((Long) sid1).longValue();
+ long rankId2 = ((Long) sid2).longValue();
+
+ if (rankId1 == rankId2) {
+ return 0;
+ } else if (rankId1 < rankId2) {
+ return -1;
+ } else {
+ return 1;
+ }
+
+ } else if (rank1 > rank2) {
+ return -1;
+ } else {
+ return 1;
+ }
+
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
new file mode 100644
index 0000000..9f02c42
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
@@ -0,0 +1,767 @@
+/*
+ * 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.felix.ipojo.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.ipojo.context.ServiceReferenceImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Utility class close to the OSGi Service Tracker.
+ * This class is used when tracking dynamic services is required.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Tracker implements TrackerCustomizer {
+
+ /**
+ * The bundle context against which this Tracker object is tracking.
+ */
+ protected BundleContext m_context;
+
+ /**
+ * the filter specifying search criteria for the services to track.
+ */
+ protected Filter m_filter;
+
+ /**
+ * The TrackerCustomizer object for this tracker.
+ */
+ protected TrackerCustomizer m_customizer;
+
+ /**
+ * The filter string for use when adding the ServiceListener.
+ * If this field is set, then certain optimizations can be taken since we don't have a user supplied filter.
+ */
+ protected String m_listenerFilter;
+
+ /**
+ * The class name to be tracked. If this field is set, then we are
+ * tracking by class name.
+ */
+ private String m_trackClass;
+
+ /**
+ * The reference to be tracked. If this field is set, then we are
+ * tracking a single ServiceReference.
+ */
+ private ServiceReference m_trackReference;
+
+ /**
+ * The tracked services: ServiceReference object -> customized.
+ *Object and ServiceListener object
+ */
+ private Tracked m_tracked;
+
+ /**
+ * The cached ServiceReference for getServiceReference.
+ * This field is volatile since it is accessed by multiple threads.
+ */
+ private volatile ServiceReference m_cachedReference;
+
+ /**
+ * The cached service object for getService. This field is volatile
+ * since it is accessed by multiple threads.
+ */
+ private volatile Object m_cachedService;
+
+ /**
+ * Creates a Tracker object on the specified ServiceReference object.
+ * The service referenced by the specified ServiceReference object will be tracked by this Tracker.
+ * @param context The BundleContext object against which the tracking is done.
+ * @param reference The ServiceReference object for the service to be tracked.
+ * @param customizer The customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as
+ * the TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+ */
+ public Tracker(BundleContext context, ServiceReference reference, TrackerCustomizer customizer) {
+ m_context = context;
+ m_trackReference = reference;
+ m_trackClass = null;
+ if (customizer == null) {
+ m_customizer = this;
+ } else {
+ m_customizer = customizer;
+ }
+ m_listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ try {
+ this.m_filter = context.createFilter(m_listenerFilter);
+ } catch (InvalidSyntaxException e) { // we could only get this exception if the ServiceReference was invalid
+ throw new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Creates a Tracker object on the specified class name.
+ * Services registered under the specified class name will be tracked by this Tracker object.
+ * @param context the BundleContext object against which the tracking is done.
+ * @param clazz the Class name of the services to be tracked.
+ * @param customizer the customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as
+ * the TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+ */
+ public Tracker(BundleContext context, String clazz, TrackerCustomizer customizer) {
+ // Security Check
+ if (! SecurityHelper.hasPermissionToGetService(clazz, context)) {
+ throw new SecurityException("The bundle " + context.getBundle().getBundleId()
+ + " does not have the permission to get the service " + clazz);
+ }
+
+ this.m_context = context;
+ this.m_trackReference = null;
+ this.m_trackClass = clazz;
+ if (customizer == null) {
+ m_customizer = this;
+ } else {
+ m_customizer = customizer;
+ }
+ this.m_listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz + ")";
+ try {
+ this.m_filter = context.createFilter(m_listenerFilter);
+ } catch (InvalidSyntaxException e) { // we could only get this exception
+ // if the clazz argument was
+ // malformed
+ throw new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Creates a Tracker object on the specified Filter object.
+ * <p>
+ * Services which match the specified Filter object will be tracked by this Tracker object.
+ * @param context the BundleContext object against which the tracking is done.
+ * @param filter the Filter object to select the services to be tracked.
+ * @param customizer The customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as the
+ * TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+ */
+ public Tracker(BundleContext context, Filter filter, TrackerCustomizer customizer) {
+ this.m_context = context;
+ this.m_trackReference = null;
+ this.m_trackClass = null;
+ this.m_listenerFilter = null;
+ this.m_filter = filter;
+ if (customizer == null) {
+ m_customizer = this;
+ } else {
+ m_customizer = customizer;
+ }
+ if ((context == null) || (filter == null)) { // we throw a NPE here to be consistent with the other constructors
+ throw new NullPointerException(); // NOPMD by clement on 29/02/08 14:12
+ }
+ }
+
+ /**
+ * Opens this Tracker object and begin tracking services.
+ * <p>
+ * Services which match the search criteria specified when this Tracker object was created are now tracked by this Tracker object.
+ */
+ public synchronized void open() {
+ if (m_tracked != null) { return; }
+
+ m_tracked = new Tracked();
+ synchronized (m_tracked) {
+ try {
+ m_context.addServiceListener(m_tracked, m_listenerFilter);
+ ServiceReference[] references;
+ if (m_listenerFilter == null) { // user supplied filter
+ references = getInitialReferences(null, m_filter.toString());
+ } else { // constructor supplied filter
+ if (m_trackClass == null) {
+ references = new ServiceReference[] { m_trackReference };
+ } else {
+ references = getInitialReferences(m_trackClass, null);
+ }
+ }
+
+ m_tracked.setInitialServices(references); // set tracked with
+ // the initial
+ // references
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ /* Call tracked outside of synchronized region */
+ m_tracked.trackInitialServices(); // process the initial references
+ }
+
+ /**
+ * Returns the list of initial ServiceReference objects that will be tracked by this Tracker object.
+ * @param trackClass the class name with which the service was registered, or null for all services.
+ * @param filterString the filter criteria or null for all services.
+ * @return the list of initial ServiceReference objects.
+ * @throws InvalidSyntaxException if the filter uses an invalid syntax.
+ */
+ private ServiceReference[] getInitialReferences(String trackClass, String filterString) throws InvalidSyntaxException {
+ return m_context.getServiceReferences(trackClass, filterString);
+ }
+
+ /**
+ * Closes this Tracker object.
+ * <p>
+ * This method should be called when this Tracker object should end the tracking of services.
+ */
+ public synchronized void close() {
+ if (m_tracked == null) { return; }
+
+ m_tracked.close();
+ ServiceReference[] references = getServiceReferences();
+ Tracked outgoing = m_tracked;
+
+ try {
+ m_context.removeServiceListener(outgoing);
+ } catch (IllegalStateException e) { //NOPMD
+ /* In case the context was stopped. */
+ }
+ if (references != null) {
+ for (int i = 0; i < references.length; i++) {
+ outgoing.untrack(references[i]);
+ }
+ }
+ m_tracked = null;
+
+ }
+
+ /**
+ * Default implementation of the TrackerCustomizer.addingService method.
+ * <p>
+ * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation returns the result of calling getService,
+ * on the BundleContext object with which this Tracker object was created, passing the specified ServiceReference object.
+ * <p>
+ * This method can be overridden in a subclass to customize the service object to be tracked for the service being added. In that case, take care not to rely on the default implementation of removedService that will unget the service.
+ * @param reference the Reference to service being added to this Tracker object.
+ * @return The service object to be tracked for the service added to this Tracker object.
+ * @see TrackerCustomizer
+ */
+ public boolean addingService(ServiceReference reference) {
+ return true;
+ }
+
+ /**
+ * Default implementation of the TrackerCustomizer.addedService method.
+ * @param reference the added reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ // Nothing to do.
+ }
+
+ /**
+ * Default implementation of the TrackerCustomizer.modifiedService method.
+ * <p>
+ * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation does nothing.
+ * @param reference the Reference to modified service.
+ * @param service The service object for the modified service.
+ * @see TrackerCustomizer
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Nothing to do.
+ }
+
+ /**
+ * Default implementation of the TrackerCustomizer.removedService method.
+ * <p>
+ * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation calls ungetService, on the
+ * BundleContext object with which this Tracker object was created, passing the specified ServiceReference object.
+ * <p>
+ * This method can be overridden in a subclass. If the default implementation of addingService method was used, this method must unget the service.
+ * @param reference the Reference to removed service.
+ * @param service The service object for the removed service.
+ * @see TrackerCustomizer
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ m_context.ungetService(reference);
+ }
+
+ /**
+ * Waits for at least one service to be tracked by this Tracker object.
+ * <p>
+ * It is strongly recommended that waitForService is not used during the calling of the BundleActivator methods. BundleActivator methods are expected to complete in a short period of time.
+ * @param timeout the time interval in milliseconds to wait. If zero, the method will wait indefinately.
+ * @return Returns the result of getService().
+ * @throws InterruptedException If another thread has interrupted the current thread.
+ */
+ public Object waitForService(long timeout) throws InterruptedException {
+ if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); }
+ Object object = getService();
+ while (object == null) {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ if (tracked.size() == 0) {
+ tracked.wait(timeout);
+ }
+ }
+ object = getService();
+ if (timeout > 0) { return object; }
+ }
+ return object;
+ }
+
+ /**
+ * Returns an array of ServiceReference objects for all services being tracked by this Tracker object.
+ * @return Array of ServiceReference objects or <code>null</code> if no service are being tracked.
+ */
+ public ServiceReference[] getServiceReferences() {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { // if Tracker is not open
+ return null;
+ }
+ synchronized (tracked) {
+ int length = tracked.size();
+ if (length == 0) { return null; }
+ ServiceReference[] references = new ServiceReference[length];
+ Iterator keys = tracked.keySet().iterator();
+ for (int i = 0; i < length; i++) {
+ references[i] = (ServiceReference) keys.next();
+ }
+ return references;
+ }
+ }
+
+ /**
+ * Gets the list of stored service reference.
+ * @return the list containing used service reference
+ */
+ public List/*<ServiceReference>*/getServiceReferencesList() {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { // if Tracker is not open
+ return null;
+ }
+ synchronized (tracked) {
+ int length = tracked.size();
+ if (length == 0) { return null; }
+ List references = new ArrayList(length);
+ Iterator keys = tracked.keySet().iterator();
+ for (int i = 0; i < length; i++) {
+ references.add(keys.next());
+ }
+ // The resulting array is sorted by ranking.
+ return references;
+ }
+ }
+
+ /**
+ * Returns the list of references used by the tracker.
+ * A reference becomes used when the dependency has already
+ * called getService on this reference.
+ * @return the list of used references.
+ */
+ public List/*<ServiceReference>*/getUsedServiceReferences() {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null || tracked.size() == 0) { // if Tracker is not open or empty
+ return null;
+ }
+ synchronized (tracked) {
+ int length = tracked.size();
+ List references = new ArrayList();
+ Iterator keys = tracked.entrySet().iterator();
+ for (int i = 0; i < length; i++) {
+ Map.Entry entry = (Map.Entry) keys.next();
+ Object key = entry.getKey();
+ if (entry.getValue() != null) {
+ references.add(key);
+ }
+ }
+ return references;
+ }
+ }
+
+ /**
+ * Returns a ServiceReference object for one of the services being tracked by this Tracker object.
+ * If multiple services are being tracked, the service with the highest ranking (as specified in its service.ranking property) is returned.
+ * If there is a tie in ranking, the service with the lowest service ID (as specified in its service.id property); that is, the service that was registered first is returned.
+ * This is the same algorithm used by BundleContext.getServiceReference.
+ * @return ServiceReference object or null if no service is being tracked.
+ * @since 1.1
+ */
+ public ServiceReference getServiceReference() {
+ ServiceReference reference = m_cachedReference;
+ if (reference != null) { return reference; }
+
+ ServiceReference[] references = getServiceReferences();
+ if (references == null) {
+ return null;
+ } else {
+ // As the map is sorted, return the first element.
+ return m_cachedReference = references[0];
+ }
+ }
+
+ /**
+ * Returns the service object for the specified ServiceReference object if the referenced service is being tracked by this Tracker object.
+ * @param reference the Reference to the desired service.
+ * @return the Service object. Try to get the service if not yet tracked.
+ */
+ public Object getService(ServiceReference reference) {
+ // Security Check
+ if (! SecurityHelper.hasPermissionToGetServices((String[]) reference.getProperty(Constants.OBJECTCLASS),
+ m_context)) {
+ throw new SecurityException("The bundle " + m_context.getBundle().getBundleId() + " does not have"
+ + " the permission to get the services "
+ + Arrays.asList((String[]) reference.getProperty(Constants.OBJECTCLASS)));
+ }
+
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return null;
+ }
+ Object object = null;
+ synchronized (tracked) {
+ object = tracked.get(reference);
+ if (object == null) {
+ if (tracked.containsKey(reference)) { // Not already get but already tracked.
+ object = m_context.getService(reference);
+ tracked.put(reference, object);
+ return object;
+ }
+ } else { // The object was already get.
+ return object;
+ }
+
+ return m_context.getService(reference);
+ }
+ }
+
+ /**
+ * Ungets the given service reference.
+ * @param reference the service reference to unget.
+ */
+ public void ungetService(ServiceReference reference) {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return;
+ }
+ Object object = null;
+ synchronized (tracked) {
+ object = tracked.get(reference);
+ }
+ if (object != null) {
+ m_context.ungetService(reference);
+ }
+ }
+
+ /**
+ * Returns an array of service objects for all services being tracked by this Tracker object.
+ * @return Array of service objects or <code>null</code> if no service are being tracked.
+ */
+ public Object[] getServices() {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ ServiceReference[] references = getServiceReferences();
+ int length = 0;
+ if (references == null) {
+ return null;
+ } else {
+ length = references.length;
+ }
+ Object[] objects = new Object[length];
+ for (int i = 0; i < length; i++) {
+ objects[i] = getService(references[i]);
+ }
+ return objects;
+ }
+ }
+
+ /**
+ * Returns a service object for one of the services being tracked by this Tracker object.
+ * <p>
+ * If any services are being tracked, this method returns the result of calling getService(getServiceReference()).
+ * @return Service object or <code>null</code> if no service is being tracked.
+ */
+ public Object getService() {
+ Object service = m_cachedService;
+ if (service != null) { return service; }
+ ServiceReference reference = getServiceReference();
+ if (reference == null) { return null; }
+ return m_cachedService = getService(reference);
+ }
+
+ /**
+ * Removes a service from this Tracker object. The specified service will be removed from this Tracker object. If the specified service was being tracked then the
+ * TrackerCustomizer.removedService method will be called for that service.
+ * @param reference the Reference to the service to be removed.
+ */
+ public void remove(ServiceReference reference) {
+ Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return;
+ }
+ tracked.untrack(reference);
+ }
+
+ /**
+ * Returns the number of services being tracked by this Tracker object.
+ * @return the Number of services being tracked.
+ */
+ public int size() {
+ Tracked tracked = this.m_tracked; //use local var since we are not synchronized
+ if (tracked == null) { /* if Tracker is not open */
+ return 0;
+ }
+ return tracked.size();
+ }
+
+ /**
+ * Inner class to track services. If a Tracker object is reused (closed then reopened), then a new Tracked object is used. This class is a hashtable mapping ServiceReference object -> customized Object. This
+ * class is the ServiceListener object for the tracker. This class is used to synchronize access to the tracked services. This is not a public class. It is only for use by the implementation of the Tracker
+ * class.
+ */
+ class Tracked extends HashMap implements ServiceListener {
+ /**
+ * UID.
+ */
+ static final long serialVersionUID = -7420065199791006079L;
+
+ /**
+ * The list of ServiceReferences in the process of being added. This is used to deal with nesting of ServiceEvents. Since ServiceEvents are synchronously delivered, ServiceEvents can be nested. For example, when processing the adding of a service
+ * and the customizer causes the service to be unregistered, notification to the nested call to untrack that the service was unregistered can be made to the track method. Since the ArrayList implementation is not synchronized, all access to
+ * this list must be protected by the same synchronized object for thread safety.
+ */
+ private List m_adding;
+
+ /**
+ * <code>true</code> if the tracked object is closed. This field is volatile because it is set by one thread and read by another.
+ */
+ private volatile boolean m_closed;
+
+ /**
+ * The Initial list of ServiceReferences for the tracker. This is used to correctly process the initial services which could become unregistered before they are tracked. This is necessary since the initial set of tracked services are not
+ * "announced" by ServiceEvents and therefore the ServiceEvent for unregistration could be delivered before we track the service. A service must not be in both the initial and adding lists at the same time. A service must be moved from the
+ * initial list to the adding list "atomically" before we begin tracking it. Since the LinkedList implementation is not synchronized, all access to this list must be protected by the same synchronized object for thread safety.
+ */
+ private List m_initial;
+
+ /**
+ * Tracked constructor.
+ */
+ protected Tracked() {
+ super();
+ m_closed = false;
+ m_adding = new ArrayList(6);
+ m_initial = new LinkedList();
+ }
+
+ /**
+ * Sets initial list of services into tracker before ServiceEvents begin to be received. This method must be called from Tracker.open while synchronized on this object in the same synchronized block as the addServiceListener call.
+ * @param references The initial list of services to be tracked.
+ */
+ protected void setInitialServices(ServiceReference[] references) {
+ if (references == null) { return; }
+ int size = references.length;
+ for (int i = 0; i < size; i++) {
+ m_initial.add(references[i]);
+ }
+ }
+
+ /**
+ * Tracks the initial list of services. This is called after ServiceEvents can begin to be received. This method must be called from Tracker.open while not synchronized on this object after the addServiceListener call.
+ */
+ protected void trackInitialServices() {
+ while (true) {
+ ServiceReference reference;
+ synchronized (this) {
+ if (m_initial.isEmpty()) { // if there are no more inital services
+ return; // we are done
+ }
+
+ // move the first service from the initial list to the adding list within this synchronized block.
+ reference = (ServiceReference) ((LinkedList) m_initial).removeFirst();
+ if (this.containsKey(reference)) { //Check if the reference is already tracked.
+ //if we are already tracking this service
+ continue; /* skip this service */
+ }
+ if (m_adding.contains(reference)) {
+ // if this service is already in the process of being added.
+ continue; // skip this service
+ }
+ m_adding.add(reference);
+ }
+ trackAdding(reference); // Begin tracking it. We call trackAdding since we have already put the reference in the adding list.
+ }
+ }
+
+ /**
+ * Called by the owning Tracker object when it is closed.
+ */
+ protected void close() {
+ m_closed = true;
+ }
+
+ /**
+ * ServiceListener method for the Tracker class. This method must NOT be synchronized to avoid deadlock potential.
+ * @param event the ServiceEvent object from the framework.
+ */
+ public void serviceChanged(ServiceEvent event) {
+ //Check if we had a delayed call (which could happen when we close).
+ if (m_closed) { return; }
+ ServiceReference reference = event.getServiceReference();
+
+ switch (event.getType()) {
+ case ServiceEvent.REGISTERED:
+ case ServiceEvent.MODIFIED:
+ if (m_listenerFilter == null) { // user supplied filter
+ boolean match = true;
+ if (reference instanceof ServiceReferenceImpl) {
+ // Can't use the match(ref) as it throw a class cast exception on Equinox.
+ match = m_filter.match(((ServiceReferenceImpl) reference).getProperties());
+ } else { // Non compute reference.
+ match = m_filter.match(reference);
+ }
+ if (match) {
+ track(reference); // Arrival
+ } else {
+ untrack(reference); // Departure
+ }
+ } else { // constructor supplied filter
+ track(reference);
+ }
+ break;
+ case ServiceEvent.UNREGISTERING:
+ untrack(reference); // Departure
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Begins to track the referenced service.
+ * @param reference the Reference to a service to be tracked.
+ */
+ protected void track(ServiceReference reference) {
+ Object object;
+ boolean alreadyTracked;
+ synchronized (this) {
+ alreadyTracked = this.containsKey(reference);
+ object = this.get(reference);
+ }
+ if (alreadyTracked) { // we are already tracking the service
+ if (object != null) { // If already get, invalidate the cache
+ synchronized (this) {
+ modified();
+ }
+ }
+ // Call customizer outside of synchronized region
+ m_customizer.modifiedService(reference, object);
+ return;
+ }
+ synchronized (this) {
+ if (m_adding.contains(reference)) { // if this service is already in the process of being added.
+ return;
+ }
+ m_adding.add(reference); // mark this service is being added
+ }
+
+ trackAdding(reference); // call trackAdding now that we have put the reference in the adding list
+ }
+
+ /**
+ * Common logic to add a service to the tracker used by track and trackInitialServices.
+ * The specified reference must have been placed in the adding list before calling this method.
+ * @param reference the Reference to a service to be tracked.
+ */
+ private void trackAdding(ServiceReference reference) {
+ boolean mustBeTracked = false;
+ boolean becameUntracked = false;
+ boolean mustCallAdded = false;
+ //Call customizer outside of synchronized region
+ try {
+ mustBeTracked = m_customizer.addingService(reference);
+ } finally {
+ synchronized (this) {
+ if (m_adding.remove(reference)) { // if the service was not untracked during the customizer callback
+ if (mustBeTracked) {
+ this.put(reference, null);
+ modified();
+ mustCallAdded = true;
+ notifyAll(); // notify any waiters in waitForService
+ }
+ } else {
+ becameUntracked = true;
+ // If already get during the customizer callback
+ ungetService(reference);
+ modified();
+ }
+ }
+ }
+
+ // Call customizer outside of synchronized region
+ if (becameUntracked) {
+ // The service became untracked during the customizer callback.
+ m_customizer.removedService(reference, null);
+ } else {
+ if (mustCallAdded) {
+ m_customizer.addedService(reference);
+ }
+ }
+ }
+
+ /**
+ * Discontinues tracking the referenced service.
+ * @param reference the Reference to the tracked service.
+ */
+ protected void untrack(ServiceReference reference) {
+ Object object;
+ synchronized (this) {
+ if (m_initial.remove(reference)) { // if this service is already in the list of initial references to process
+ return; // we have removed it from the list and it will not be processed
+ }
+
+ if (m_adding.remove(reference)) { // if the service is in the process of being added
+ return; // in case the service is untracked while in the process of adding
+ }
+
+ boolean isTraked = this.containsKey(reference); // Check if we was tracking the reference
+ object = this.remove(reference); // must remove from tracker before calling customizer callback
+
+ if (!isTraked) { return; }
+ modified();
+ }
+ // Call customizer outside of synchronized region and only if we are not closed
+ if (! m_closed) {
+ m_customizer.removedService(reference, object);
+ }
+ // If the customizer throws an unchecked exception, it is safe to let it propagate
+ }
+
+ /**
+ * Called by the Tracked object whenever the set of tracked services is modified. Increments the tracking count and clears the cache.
+ * This method must not be synchronized since it is called by Tracked while Tracked is synchronized. We don't want synchronization interactions between the ServiceListener thread and the user thread.
+ */
+ void modified() {
+ m_cachedReference = null; /* clear cached value */
+ m_cachedService = null; /* clear cached value */
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java
new file mode 100644
index 0000000..69693cf
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java
@@ -0,0 +1,61 @@
+/*
+ * 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.felix.ipojo.util;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Tracker Customizer.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface TrackerCustomizer {
+
+ /**
+ * A service is being added to the Tracker object.
+ * This method is called before a service which matched the search parameters of the Tracker object is added to it. This method should return the service object to be tracked for this ServiceReference object.
+ * The returned service object is stored in the Tracker object and is available from the getService and getServices methods.
+ * @param reference the Reference to service being added to the Tracker object.
+ * @return The service object to be tracked for the ServiceReference object or null if the ServiceReference object should not be tracked.
+ */
+ boolean addingService(ServiceReference reference);
+
+ /**
+ * A service tracked by the Tracker object has been added in the list.
+ * This method is called when a service has been added in the managed list (after addingService) and if the service has not disappeared before during the callback.
+ * @param reference the added reference.
+ */
+ void addedService(ServiceReference reference);
+
+ /**
+ * A service tracked by the Tracker object has been modified.
+ * This method is called when a service being tracked by the Tracker object has had it properties modified.
+ * @param reference the Reference to service that has been modified.
+ * @param service The service object for the modified service.
+ */
+ void modifiedService(ServiceReference reference, Object service);
+
+ /**
+ * A service tracked by the Tracker object has been removed.
+ * This method is called after a service is no longer being tracked by the Tracker object.
+ * @param reference the Reference to service that has been removed.
+ * @param service The service object for the removed service.
+ */
+ void removedService(ServiceReference reference, Object service);
+
+}
diff --git a/ipojo/runtime/core/src/main/resources/core.xsd b/ipojo/runtime/core/src/main/resources/core.xsd
new file mode 100644
index 0000000..6046c8d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/resources/core.xsd
@@ -0,0 +1,602 @@
+<!--
+ 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.
+-->
+<xs:schema elementFormDefault="qualified" targetNamespace="org.apache.felix.ipojo"
+ xmlns="org.apache.felix.ipojo" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:annotation>
+ <xs:documentation>iPOJO Core XML-Schema. This grammars models iPOJO descriptor using core
+ features. It provides several extensibility mechanism in order to compose this schema with
+ external handlers and other component implementation type such as
+ compositions.</xs:documentation>
+ </xs:annotation>
+ <xs:element name="ipojo">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation>iPOJO top level element.</xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="handler" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>The handler declarations.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="instance" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>The instance declarations.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="component" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>The component type declarations.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"
+ > </xs:any>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="HandlerType">
+ <xs:annotation>
+ <xs:documentation>Description of the handler.</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="RootElementType">
+ <xs:sequence maxOccurs="unbounded" minOccurs="0">
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##any" processContents="skip"
+ > </xs:any>
+ </xs:sequence>
+ <xs:attribute name="classname" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>The implementation class of the handler. The specified class must
+ implement (direcly or not) the "org.apache.felix.ipojo.Handler"
+ interface.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>The name of the handler.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="namespace" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The XML namespace of the handler.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="architecture" type="xs:boolean" use="optional" fixed="false">
+ <xs:annotation>
+ <xs:documentation>Enables or disables the architecture exposition. By default, the
+ architecture is not exposed. This allows handler introspection.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="level" type="xs:int" use="optional">
+ <xs:annotation>
+ <xs:documentation>The start level of the handler.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="InstanceType">
+ <xs:annotation>
+ <xs:documentation>Describes an instance of a component.</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="RootElementType">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="InstancePropertyType">
+ <xs:annotation>
+ <xs:documentation>The instance properties.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="component" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>The name of the instance component type.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="name" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The (unique) name of the instance.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="version" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The version of the factory to use.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="InstancePropertyType">
+ <xs:annotation>
+ <xs:documentation>Defines a property of an instance configuration.</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="property" type="InstancePropertyType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Name of the property. Can be optional if a property is inside a structure.
+ The 'instance.name' property has a special semantic as it will be used as the instance
+ name.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="value" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Value of the property. Can be null for property containing other
+ properties.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Type of the property, used to create the adequate object. Supported values
+ are list, array, dictionary and map.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="RootElementType"/>
+ <xs:complexType name="ComponentType">
+ <xs:annotation>
+ <xs:documentation>Declares an atomic (i.e. primitive) component type.</xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="callback" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Describes the method(s) to invoke when the component's state
+ changes.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="provides" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Indicates the component provided service(s). By default, all implemented
+ interfaces are published.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="requires" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Indicates the service requirements of the component.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="properties" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Describes the properties of the component.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element ref="controller" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation>Lifecycle controller for this component.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"
+ > </xs:any>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Specifies the name of the component type. This name is used to identify
+ the factory attached to this type. If not specified, the factory name is the
+ implementation class name.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="public" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Determines if the component type is public or private. A public factory
+ (default) can be used from any bundles.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="classname" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>Specifies the implementation class of the component
+ type.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="architecture" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Enables or disables the architecture exposition. By default, the
+ architecture is exposed. This allows instance introspection.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="immediate" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Creates the object of the component implementation type as soon as the
+ component instance becomes valid. The default value is "true" if the component doesn't
+ provide any service, "false" otherwise.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="factory-method" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Factory method called to create POJO objects instead of the constructor.
+ The specified method must be a static method of the implementation class returning an
+ instance of this implementation class. The factory method can receive the bundle context
+ in argument.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="version" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Set the version of this component type</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="RequiresType">
+ <xs:annotation>
+ <xs:documentation>Description of component services requirements.</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="ServiceDependencyType">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="callback" type="DependencyCallbackType">
+ <xs:annotation>
+ <xs:documentation>Service requirement method invocation description. Here can be
+ specified a bind method called when a service appears and an unbind method called
+ when a service disappears.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+
+ <xs:attribute name="interface" type="xs:string" use="prohibited">
+ <xs:annotation>
+ <xs:documentation>The interface describing the required service type. This attribute is
+ needed only when using aggregate dependencies with field injection and when the type
+ of this field is a list, vector, collection and set. This attribute is deprecated, use
+ 'specification'.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="field" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The name of the field representing the service dependency in the
+ implementation class.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="constructor-parameter" type="xs:int" use="optional">
+ <xs:annotation>
+ <xs:documentation>The index of the parameter if the dependency is injected
+ using the constructor.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="nullable" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Enable or disable the Nullable pattern on optional service
+ dependencies. By default, Nullable pattern is enabled. If disabled, iPOJO will inject
+ null instead of a Nullable object.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="default-implementation" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Specifies the default implementation class for an optional service
+ dependency. If no providers are found, iPOJO creates an instance of the
+ default-implementation (nullary constructor) and injects it. The given class must
+ implement the required service interface.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="from" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Specific service provider. The dependency can only be fulfilled by the
+ component with the matching name, or by the service with a matching
+ PID.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="proxy" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Enables or Disable the proxy injection (on field
+ injection)</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="scope" use="optional">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="global"/>
+ <xs:enumeration value="composite"/>
+ <xs:enumeration value="composite+global"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="DependencyCallbackType">
+ <xs:annotation>
+ <xs:documentation>Dependency callbacks are used to receive notification when service providers
+ arrive and leave.</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="method" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>Method to call</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" use="required">
+ <xs:annotation>
+ <xs:documentation> Type of callback (bind, unbind, or updated). Bind means that the method
+ will be called when a provider arrives. Unbind means that the method will be called when a
+ provider leaves. Updated means that a service was modified but is still valid for the
+ service dependency. </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="bind"/>
+ <xs:enumeration value="unbind"/>
+ <xs:enumeration value="modified"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="CallbackType">
+ <xs:annotation>
+ <xs:documentation>Lifecycle Callback. Allows a POJO to be notified when the instance becomes
+ valid or invalid.</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="method" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>Specifies the method to call on the transition.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="transition" use="required">
+ <xs:annotation>
+ <xs:documentation>Specifies the transition when the callback needs to be
+ invoked.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:annotation>
+ <xs:documentation>Lifecycle transition state. "validate" means that the component's
+ instance was invalid and becomes valid, "invalidate" means that the component's intance
+ was valid and becomes invalid.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="validate"/>
+ <xs:enumeration value="invalidate"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:element name="provides" type="ProvidesType" id="provides"/>
+ <xs:complexType name="ProvidesType">
+ <xs:annotation>
+ <xs:documentation>Provided service(s) description.</xs:documentation>
+ </xs:annotation>
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:choice>
+ <xs:element name="property" type="PropertyType">
+ <xs:annotation>
+ <xs:documentation>List of service specific properties.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="controller" minOccurs="0" maxOccurs="1" type="ServiceControllerType">
+ <xs:annotation>
+ <xs:documentation>Service Controller impacting the current provided
+ service</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="interface" type="xs:string" use="prohibited">
+ <xs:annotation>
+ <xs:documentation>Deprecated attribute, use 'specifications' instead of
+ 'interface'</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="specifications" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The list of service specifications (i.e. interfaces) to expose. By
+ default, all interfaces implemented by the component implementation class are
+ published.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="factory" type="xs:string" use="prohibited">
+ <xs:annotation>
+ <xs:documentation>Use 'strategy' instead of 'factory'</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="strategy" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>POJO creation strategy. By default, the POJO object is created once
+ (singleton). If the factory is set to "SERVICE", the creation policy follows the OSGi
+ service factory policy (one object object per asking bundle). INSTANCE allows creating one
+ different POJO object per asking instance. Finally, a custom strategy can be used by
+ specifying the qualified name of the class extending CreationPolicy</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="post-registration" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Defines a callback called after the service registration. The callback takes a ServiceReference
+ as parameter</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="post-unregistration" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Defines a callback called after the service unregistration. The callback takes a ServiceReference
+ as parameter</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="ServiceControllerType">
+ <xs:annotation>
+ <xs:documentation> Defines a service controller. </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="field" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation> Field of the controller </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="value" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Intiail value of the controller </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="PropertyType">
+ <xs:annotation>
+ <xs:documentation> Defines a component property. </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="field" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Field of the property </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="method" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Setter method of the property. This method is called to inject property
+ value. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="constructor-parameter" type="xs:int" use="optional">
+ <xs:annotation>
+ <xs:documentation>The index of the parameter if the property is injected
+ using the constructor.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="name" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Name of the property. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="value" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Default value of the property. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation> Type of the property. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="mandatory" type="xs:boolean" use="optional" default="false">
+ <xs:annotation>
+ <xs:documentation>Set the property as mandatory. A mandatory property MUST receive a value
+ either in the component type description or in the instance configuration. Properties are
+ optional by default.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:element name="callback" type="CallbackType" id="callback"/>
+ <xs:element name="controller" type="ControllerType" id="controller">
+ <xs:annotation>
+ <xs:documentation/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="requires" type="RequiresType" id="requires"/>
+ <xs:element name="component" type="ComponentType" id="component"/>
+ <xs:element name="handler" type="HandlerType" id="handler"/>
+ <xs:element name="instance" type="InstanceType" id="instance"/>
+
+ <xs:element name="properties" type="PropertiesType" id="properties"/>
+ <xs:complexType name="PropertiesType">
+ <xs:annotation>
+ <xs:documentation>List of component, instance or service properties. This field will receive
+ the property value.</xs:documentation>
+ </xs:annotation>
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="PropertyType">
+ <xs:annotation>
+ <xs:documentation>The list of properties.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="propagation" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Propagation of the component properties to the provided services. If this
+ parameter is set to "true", each time properties are reconfigured, they are propagated to
+ each service published by the component.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="pid" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Unique identifier used to reconfigure components properties (via Managed
+ Services) with the Configuration Admin.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="updated" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>Method called when a reconfiguration is done</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:complexType name="ServiceDependencyType">
+ <xs:attribute name="specification" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The specification describing the required service type. This attribute is
+ needed only when using aggregate dependencies with field injection and when the type of
+ this field is a list, vector, collection and set.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="optional" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Sets the service dependency optionality</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="aggregate" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation>Sets the service dependency cardinality.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="policy" use="optional">
+ <xs:annotation>
+ <xs:documentation>Sets the binding policy of the dependency. Three policies are supported.
+ The dynamic policy supports service providers dynamism. The static policy freezes the
+ provider set as soon as the dependency is used. The dynamic-priority policy is an
+ extension of the dynamic policy, but providers are ranked.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="dynamic"/>
+ <xs:enumeration value="static"/>
+ <xs:enumeration value="dynamic-priority"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="comparator" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The comparator attribute allows specifying the class used to compare
+ providers. This class must implemented the java.util.Comparator class and must support the
+ comparison of service references.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="filter" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>LDAP filter used to filter providers</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="id" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>id of the service dependency. The id allows to indentify and to refert to
+ this dependency.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:complexType name="ControllerType">
+ <xs:annotation>
+ <xs:documentation>Specifies the lifecycle controller of a component, which allows to validate
+ or invalidate component instances.</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="field" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>The name of the component lifecycle controller field. The type of the
+ specified field must be boolean. Setting the value of the specified field to "true" means
+ the validation of the component instance while setting it to "false" means the
+ invalidation of the component instance.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+</xs:schema>
diff --git a/ipojo/runtime/core/src/main/resources/metadata.xml b/ipojo/runtime/core/src/main/resources/metadata.xml
new file mode 100644
index 0000000..977b083
--- /dev/null
+++ b/ipojo/runtime/core/src/main/resources/metadata.xml
@@ -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.
+-->
+<ipojo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/CURRENT/core.xsd"
+ xmlns="org.apache.felix.ipojo">
+ <!-- Primitives handler -->
+ <handler
+ classname="org.apache.felix.ipojo.handlers.lifecycle.controller.ControllerHandler"
+ name="controller" architecture="false"/>
+ <handler
+ classname="org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler"
+ name="callback" level="2" architecture="false"/>
+ <handler
+ classname="org.apache.felix.ipojo.handlers.dependency.DependencyHandler"
+ name="requires" level="0" architecture="false">
+ <!-- <controller field="m_state"/> -->
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandler"
+ name="provides" level="3" architecture="false"/>
+ <handler
+ classname="org.apache.felix.ipojo.handlers.configuration.ConfigurationHandler"
+ name="properties" level="1" architecture="false"/>
+ <handler
+ classname="org.apache.felix.ipojo.handlers.architecture.ArchitectureHandler"
+ name="architecture" architecture="false">
+ <provides specifications="org.apache.felix.ipojo.architecture.Architecture">
+ <property field="m_name" name="architecture.instance"/>
+ </provides>
+ </handler>
+ <!-- iPOJO Online Manipulator -->
+ <component classname="org.apache.felix.ipojo.IPOJOURLHandler"
+ public="false"
+ immediate="true">
+ <provides>
+ <property name="url.handler.protocol" type="java.lang.String" value="ipojo"/>
+ </provides>
+ <callback transition="invalidate" method="stop"/>
+ </component>
+ <instance component="org.apache.felix.ipojo.IPOJOURLHandler"/>
+</ipojo>
\ No newline at end of file
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/dependency/SmartProxyTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/dependency/SmartProxyTest.java
new file mode 100644
index 0000000..f1e7005
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/dependency/SmartProxyTest.java
@@ -0,0 +1,115 @@
+package org.apache.felix.ipojo.handlers.dependency;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.test.MockBundle;
+import org.apache.felix.ipojo.util.Logger;
+import org.mockito.Mockito;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class SmartProxyTest extends TestCase {
+
+ public void setUp() {
+ }
+
+
+ /**
+ * Check that we don't create smart proxies for concrete and abstract classes.
+ */
+ public void testCannotProxyAbstractAndConcreteClasses() {
+ Bundle bundle = new MockBundle(Dependency.class.getClassLoader());
+
+ BundleContext context = (BundleContext) Mockito.mock(BundleContext.class);
+ Mockito.when(context.getProperty(DependencyHandler.PROXY_TYPE_PROPERTY)).thenReturn(null);
+ Mockito.when(context.getProperty(Logger.IPOJO_LOG_LEVEL_PROP)).thenReturn(null);
+ Mockito.when(context.getBundle()).thenReturn(bundle);
+
+ ComponentFactory factory = (ComponentFactory) Mockito.mock(ComponentFactory.class);
+ Mockito.when(factory.getBundleClassLoader()).thenReturn(Dependency.class.getClassLoader());
+
+ InstanceManager im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ Mockito.when(im.getContext()).thenReturn(context);
+ Mockito.when(im.getFactory()).thenReturn(factory);
+
+ DependencyHandler handler = (DependencyHandler) Mockito.mock(DependencyHandler.class);
+ Mockito.when(handler.getInstanceManager()).thenReturn(im);
+ Logger logger = new Logger(context, "test", Logger.INFO);
+
+
+ Mockito.when(handler.getLogger()).thenReturn(logger);
+
+ Dependency dependency = new Dependency(handler, "a_field", ArrayList.class, null, false, false, false,
+ true, "dep", context, Dependency.DYNAMIC_BINDING_POLICY, null, null);
+ dependency.start();
+
+ // No service
+ Assert.assertNull(dependency.onGet(new Object(), "a_field", null));
+
+ dependency.stop();
+
+ // Try with an Object.
+ dependency = new Dependency(handler, "a_field", Object.class, null, false, false, false,
+ true, "dep", context, Dependency.DYNAMIC_BINDING_POLICY, null, null);
+ dependency.start();
+ // OK
+ Assert.assertNull(dependency.onGet(new Object(), "a_field", null));
+ }
+
+ /**
+ * Tests if we can proxies classes from java.* package.
+ * Indeed, a recent JVM bug fix introduces a bug:
+ * <code>
+ * [ERROR] test : Cannot create the proxy object
+ * java.lang.SecurityException: Prohibited package name: java.awt
+ * </code>
+ */
+ public void testProxiesOfJavaClasses() {
+ Bundle bundle = new MockBundle(Dependency.class.getClassLoader());
+
+ BundleContext context = (BundleContext) Mockito.mock(BundleContext.class);
+ Mockito.when(context.getProperty(DependencyHandler.PROXY_TYPE_PROPERTY)).thenReturn(null);
+ Mockito.when(context.getProperty(Logger.IPOJO_LOG_LEVEL_PROP)).thenReturn(null);
+ Mockito.when(context.getBundle()).thenReturn(bundle);
+
+ ComponentFactory factory = (ComponentFactory) Mockito.mock(ComponentFactory.class);
+ Mockito.when(factory.getBundleClassLoader()).thenReturn(Dependency.class.getClassLoader());
+
+ InstanceManager im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ Mockito.when(im.getContext()).thenReturn(context);
+ Mockito.when(im.getFactory()).thenReturn(factory);
+
+ DependencyHandler handler = (DependencyHandler) Mockito.mock(DependencyHandler.class);
+ Mockito.when(handler.getInstanceManager()).thenReturn(im);
+ Logger logger = new Logger(context, "test", Logger.INFO);
+
+
+ Mockito.when(handler.getLogger()).thenReturn(logger);
+
+ // Try with java.List
+ Dependency dependency = new Dependency(handler, "a_field", List.class, null, false, false, false,
+ true, "dep", context, Dependency.DYNAMIC_BINDING_POLICY, null, null);
+ dependency.start();
+
+ // OK
+ Assert.assertNotNull(dependency.onGet(new Object(), "a_field", null));
+ Assert.assertTrue(dependency.onGet(new Object(), "a_field", null) instanceof List);
+
+ dependency.stop();
+
+ // Try with javax.sql.CommonDataSource
+ dependency = new Dependency(handler, "a_field", javax.sql.CommonDataSource.class, null, false, false, false,
+ true, "dep", context, Dependency.DYNAMIC_BINDING_POLICY, null, null);
+ dependency.start();
+ // OK
+ Assert.assertNotNull(dependency.onGet(new Object(), "a_field", null));
+ Assert.assertTrue(dependency.onGet(new Object(), "a_field", null) instanceof javax.sql.CommonDataSource);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithAnotherSuperClass.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithAnotherSuperClass.java
new file mode 100644
index 0000000..7bc4996
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithAnotherSuperClass.java
@@ -0,0 +1,12 @@
+package org.apache.felix.ipojo.handlers.providedservice;
+
+import java.beans.MethodDescriptor;
+import java.lang.reflect.Method;
+
+public class ComponentTestWithAnotherSuperClass extends MethodDescriptor {
+
+ public ComponentTestWithAnotherSuperClass(Method method) {
+ super(method);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithSuperClass.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithSuperClass.java
new file mode 100644
index 0000000..ab61262
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ComponentTestWithSuperClass.java
@@ -0,0 +1,7 @@
+package org.apache.felix.ipojo.handlers.providedservice;
+
+import java.beans.SimpleBeanInfo;
+
+public class ComponentTestWithSuperClass extends SimpleBeanInfo {
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerTest.java
new file mode 100644
index 0000000..0034064
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerTest.java
@@ -0,0 +1,160 @@
+package org.apache.felix.ipojo.handlers.providedservice;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.*;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.PojoMetadata;
+import org.apache.felix.ipojo.test.MockBundle;
+import org.apache.felix.ipojo.util.Logger;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.Properties;
+
+public class ProvidedServiceHandlerTest extends TestCase {
+
+ BundleContext context;
+ ComponentFactory factory;
+ InstanceManager im;
+ ComponentTypeDescription desc;
+ ProvidedServiceHandler handler;
+ Logger logger;
+
+ public void setUp() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ context = (BundleContext) Mockito.mock(BundleContext.class);
+ Mockito.when(context.getProperty(DependencyHandler.PROXY_TYPE_PROPERTY)).thenReturn(null);
+ Mockito.when(context.getProperty(Logger.IPOJO_LOG_LEVEL_PROP)).thenReturn(null);
+ Mockito.when(context.getBundle()).thenReturn(new MockBundle(this.getClass().getClassLoader()));
+
+ factory = (ComponentFactory) Mockito.mock(ComponentFactory.class);
+ Mockito.when(factory.getBundleClassLoader()).thenReturn(ProvidedServiceHandler.class.getClassLoader());
+ logger = Mockito.spy(new Logger(context, "TEST", Logger.INFO));
+ Mockito.when(factory.getLogger()).thenReturn(logger);
+
+ im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ Mockito.when(im.getContext()).thenReturn(context);
+ Mockito.when(im.getGlobalContext()).thenReturn(context);
+ Mockito.when(im.getFactory()).thenReturn(factory);
+ Mockito.when(im.getInstanceName()).thenReturn("an.instance");
+
+ desc = (ComponentTypeDescription) Mockito.mock(ComponentTypeDescription.class);
+ Mockito.when(desc.getFactory()).thenReturn(factory);
+ Mockito.when(desc.getBundleContext()).thenReturn(context);
+
+ handler = new ProvidedServiceHandler();
+ handler.setFactory(factory);
+
+ // Attach the handler
+ Method method = PrimitiveHandler.class.getDeclaredMethod("attach", new Class[]{ComponentInstance.class});
+ method.setAccessible(true);
+ method.invoke(handler, new Object[]{im});
+
+ }
+
+ public void testServiceDetectionNoInterface() throws ConfigurationException {
+ String classname = "org.apache.felix.ipojo.handlers.providedservice.ComponentTest";
+
+ Element metadata = new Element("component", "");
+ Element manipulation = new Element("manipulation", "");
+ metadata.addAttribute(new Attribute("classname", classname));
+ metadata.addElement(new Element("provides", ""));
+ metadata.addElement(manipulation);
+ manipulation.addAttribute(new Attribute("classname", classname));
+
+ Mockito.when(factory.getPojoMetadata()).thenReturn(new PojoMetadata(metadata));
+ Mockito.when(factory.getClassName()).thenReturn(classname);
+
+ handler.initializeComponentFactory(desc, metadata);
+
+ //Expected behavior: the implementation classname
+ Assert.assertEquals("{org.apache.felix.ipojo.handlers.providedservice.ComponentTest}",
+ metadata.getElements("provides")[0].getAttribute("specifications"));
+ }
+
+ public void testServiceDetectionSuperClass() throws ConfigurationException {
+ String classname = "org.apache.felix.ipojo.handlers.providedservice.ComponentTestWithSuperClass";
+
+ Element metadata = new Element("component", "");
+ Element manipulation = new Element("manipulation", "");
+ metadata.addAttribute(new Attribute("classname", classname));
+ Element provides = new Element("provides", "");
+ provides.addAttribute(new Attribute("specifications", "java.beans.SimpleBeanInfo"));
+ metadata.addElement(provides);
+ metadata.addElement(manipulation);
+ manipulation.addAttribute(new Attribute("classname", classname));
+ manipulation.addAttribute(new Attribute("super", "java.beans.SimpleBeanInfo"));
+ Mockito.when(factory.getPojoMetadata()).thenReturn(new PojoMetadata(metadata));
+ Mockito.when(factory.getClassName()).thenReturn(classname);
+
+ handler.initializeComponentFactory(desc, metadata);
+
+ System.out.println(metadata);
+
+ }
+
+ public void testServiceDetectionImplementationClass() throws ConfigurationException {
+ String classname = "org.apache.felix.ipojo.handlers.providedservice.ComponentTestWithSuperClass";
+
+ Element metadata = new Element("component", "");
+ Element manipulation = new Element("manipulation", "");
+ metadata.addAttribute(new Attribute("classname", classname));
+ Element provides = new Element("provides", "");
+ provides.addAttribute(new Attribute("specifications", classname));
+ metadata.addElement(provides);
+ metadata.addElement(manipulation);
+ manipulation.addAttribute(new Attribute("classname", classname));
+ manipulation.addAttribute(new Attribute("super", "java.beans.SimpleBeanInfo"));
+ Mockito.when(factory.getPojoMetadata()).thenReturn(new PojoMetadata(metadata));
+ Mockito.when(factory.getClassName()).thenReturn(classname);
+
+ handler.initializeComponentFactory(desc, metadata);
+
+ System.out.println(metadata);
+
+ }
+
+ public void testServiceDetectionSuperSuperClass() throws ConfigurationException {
+ String classname = "org.apache.felix.ipojo.handlers.providedservice.ComponentTestWithAnotherSuperClass";
+
+ Element metadata = new Element("component", "");
+ Element manipulation = new Element("manipulation", "");
+ metadata.addAttribute(new Attribute("classname", classname));
+ Element provides = new Element("provides", "");
+ provides.addAttribute(new Attribute("specifications", "java.beans.FeatureDescriptor"));
+ metadata.addElement(provides);
+ metadata.addElement(manipulation);
+ manipulation.addAttribute(new Attribute("classname", classname));
+ manipulation.addAttribute(new Attribute("super", "java.beans.MethodDescriptor"));
+
+ Mockito.when(factory.getPojoMetadata()).thenReturn(new PojoMetadata(metadata));
+ Mockito.when(factory.getClassName()).thenReturn(classname);
+
+ handler.initializeComponentFactory(desc, metadata);
+
+ System.out.println(metadata);
+
+ }
+
+ public void testWhenRequiresFilterIsPropagated() throws Exception {
+ Dictionary dictionary = new Properties();
+ Dictionary requiresfilter = new Properties();
+ requiresfilter.put("id1", "(filter1)");
+ dictionary.put("requires.filter", requiresfilter);
+
+
+ ProvidedService providedService = new ProvidedService(handler, new String[] {Runnable.class.getName()}, ProvidedService.SINGLETON_STRATEGY, null, new Properties());
+ Assert.assertEquals(2, providedService.getProperties().length); // instance.name, service.pid
+ providedService.addProperties(dictionary);
+
+ Assert.assertEquals(2 + 1, providedService.getProperties().length);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/ManipulationMetadataTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/ManipulationMetadataTest.java
new file mode 100644
index 0000000..7658320
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/ManipulationMetadataTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.jar.Manifest;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Check manipulation metadata written in the manifest.
+ */
+public class ManipulationMetadataTest extends TestCase {
+
+ private String header;
+
+ public void setUp() {
+ File manFile = new File("src/test/resources/manipulation/MANIFEST.MF");
+ Manifest manifest;
+ try {
+ manifest = new Manifest(new FileInputStream(manFile));
+ header = manifest.getMainAttributes().getValue("iPOJO-Components");
+ } catch (FileNotFoundException e) {
+ fail(e.getMessage());
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testGetMetadata() {
+ Element elem = null;
+ try {
+ elem = ManifestMetadataParser.parseHeaderMetadata(header);
+ } catch (ParseException e) {
+ fail("Parse Exception when parsing iPOJO-Component");
+ }
+
+ assertNotNull("Check elem not null", elem);
+
+ Element manip = getManipulationForComponent(elem, "ManipulationMetadata-FooProviderType-1");
+ assertNotNull("Check manipulation metadata not null for " + "FooProviderType-1", manip);
+ }
+
+ public void testInterface() {
+ String comp_name = "ManipulationMetadata-FooProviderType-1";
+ Element manip = getManipulationForComponent(comp_name);
+ Element[] itf = manip.getElements("Interface");
+ assertEquals("Check interfaces number", itf.length, 1);
+ assertEquals("Check itf name", itf[0].getAttribute("name"),
+ "org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService");
+ }
+
+ public void testInterfaces() {
+ String comp_name = "ManipulationMetadata-FooBarProviderType-1";
+ Element manip = getManipulationForComponent(comp_name);
+ Element[] itf = manip.getElements("Interface");
+ assertEquals("Check interfaces number", itf.length, 2);
+ assertEquals("Check itf name", itf[0].getAttribute("name"),
+ "org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService");
+ assertEquals("Check itf name", itf[1].getAttribute("name"),
+ "org.apache.felix.ipojo.test.scenarios.manipulation.service.BarService");
+ }
+
+ public void testFields() {
+ String comp_name = "ManipulationMetadata-FooProviderType-Dyn";
+ Element manip = getManipulationForComponent(comp_name);
+ Element[] fields = manip.getElements("field");
+ assertEquals("Check field count " + fields.length, fields.length, 5);
+ /*
+ private int intProp;
+ private String strProp;
+ private String[] strAProp;
+ private int[] intAProp;
+ private boolean boolProp;
+ */
+
+ Element field;
+
+ field = getFieldFromName(manip, "intProp");
+ assertEquals("Check field name : " + field.getAttribute("name"), field.getAttribute("name"), "intProp");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "int");
+
+ field = getFieldFromName(manip, "strProp");
+ assertEquals("Check field name : " + field.getAttribute("name"), field.getAttribute("name"), "strProp");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "java.lang.String");
+
+ field = getFieldFromName(manip, "strAProp");
+ assertEquals("Check field name : " + field.getAttribute("name"), field.getAttribute("name"), "strAProp");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "java.lang.String[]");
+
+ field = getFieldFromName(manip, "intAProp");
+ assertEquals("Check field name : " + field.getAttribute("name"), field.getAttribute("name"), "intAProp");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "int[]");
+
+ field = getFieldFromName(manip, "boolProp");
+ assertEquals("Check field name : " + field.getAttribute("name"), field.getAttribute("name"), "boolProp");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "boolean");
+ }
+
+ public void testPrimitivesFields() {
+ String comp_name = "ManipulationMetadata-PrimitiveManipulationTester";
+ Element manip = getManipulationForComponent(comp_name);
+ Element[] fields = manip.getElements("Field");
+ assertEquals("Check field count", fields.length, 16);
+ /*
+ byte b = 1;
+ short s = 1;
+ int i = 1;
+ long l = 1;
+ double d = 1.1;
+ float f = 1.1f;
+ char c = 'a';
+ boolean bool = false;
+ byte[] bs = new byte[] {0,1,2};
+ short[] ss = new short[] {0,1,2};
+ int[] is = new int[] {0,1,2};
+ long[] ls = new long[] {0,1,2};
+ double[] ds = new double[] {0.0, 1.1, 2.2};
+ float[] fs = new float[] {0.0f, 1.1f, 2.2f};
+ char[] cs = new char[] {'a', 'b', 'c'};
+ boolean[] bools = new boolean[] {false, true, false};
+ */
+ Element field;
+
+ field = getFieldFromName(manip, "b");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "byte");
+ field = getFieldFromName(manip, "s");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "short");
+ field = getFieldFromName(manip, "i");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "int");
+ field = getFieldFromName(manip, "l");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "long");
+ field = getFieldFromName(manip, "d");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "double");
+ field = getFieldFromName(manip, "f");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "float");
+ field = getFieldFromName(manip, "c");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "char");
+ field = getFieldFromName(manip, "bool");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "boolean");
+
+ field = getFieldFromName(manip, "bs");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "byte[]");
+ field = getFieldFromName(manip, "ss");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "short[]");
+ field = getFieldFromName(manip, "is");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "int[]");
+ field = getFieldFromName(manip, "ls");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "long[]");
+ field = getFieldFromName(manip, "ds");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "double[]");
+ field = getFieldFromName(manip, "fs");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "float[]");
+ field = getFieldFromName(manip, "cs");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "char[]");
+ field = getFieldFromName(manip, "bools");
+ assertEquals("Check field type : " + field.getAttribute("name"), field.getAttribute("type"), "boolean[]");
+ }
+
+ public void testNoArgMethod() {
+ String comp_name = "ManipulationMetadata-SimpleMultipleCheckServiceProvider";
+ Element manip = getManipulationForComponent(comp_name);
+ Element method = getMethodFromName(manip, "check");
+ assertFalse("Check no args", method.containsAttribute("arguments"));
+ assertEquals("Check return", method.getAttribute("return"), "boolean");
+ }
+
+ public void testOneArgsMethod() {
+ String comp_name = "ManipulationMetadata-SimpleMultipleCheckServiceProvider";
+ Element manip = getManipulationForComponent(comp_name);
+ Element method = getMethodFromName(manip, "refBind");
+ assertEquals("Check args", method.getAttribute("arguments"), "{org.osgi.framework.ServiceReference}");
+ assertEquals("Check args count", 1, ParseUtils.parseArrays("{org.osgi.framework.ServiceReference}").length);
+ assertFalse("Check return", method.containsAttribute("return"));
+ }
+
+ public void testTwoArgsMethod() {
+ String comp_name = "ManipulationMetadata-SimpleMultipleCheckServiceProvider";
+ Element manip = getManipulationForComponent(comp_name);
+ Element method = getMethodFromName(manip, "doNothing");
+ assertEquals("Check args", method.getAttribute("arguments"), "{java.lang.Object,java.lang.String}");
+ assertEquals("Check args count", 2, ParseUtils.parseArrays("{java.lang.Object,java.lang.String}").length);
+ assertEquals("Check return", method.getAttribute("return"), "java.lang.Object");
+ }
+
+ private Element getManipulationForComponent(Element metadata, String comp_name) {
+ Element[] comps = metadata.getElements("component");
+ for(int i = 0; i < comps.length; i++) {
+ if(comps[i].containsAttribute("factory") && comps[i].getAttribute("factory").equals(comp_name)) {
+ return comps[i].getElements("manipulation")[0];
+ }
+ if(comps[i].containsAttribute("name") && comps[i].getAttribute("name").equals(comp_name)) {
+ return comps[i].getElements("manipulation")[0];
+ }
+ }
+ return null;
+ }
+
+ private Element getManipulationForComponent(String comp_name) {
+ Element elem = null;
+ try {
+ elem = ManifestMetadataParser.parseHeaderMetadata(header);
+ } catch (ParseException e) {
+ fail("Parse Exception when parsing iPOJO-Component");
+ }
+
+ assertNotNull("Check elem not null", elem);
+ Element manip = getManipulationForComponent(elem, comp_name);
+ assertNotNull("Check manipulation metadata not null for " + comp_name, manip);
+ return manip;
+ }
+
+ private Element getMethodFromName(Element manip, String name) {
+ Element methods[] = manip.getElements("Method");
+ for(int i = 0; i < methods.length; i++) {
+ if(methods[i].containsAttribute("name") && methods[i].getAttribute("name").equals(name)) {
+ return methods[i];
+ }
+ }
+ fail("Method " + name + " not found");
+ return null;
+ }
+
+ private Element getFieldFromName(Element manip, String name) {
+ Element fields[] = manip.getElements("Field");
+ for(int i = 0; i < fields.length; i++) {
+ if(fields[i].containsAttribute("name") && fields[i].getAttribute("name").equals(name)) {
+ return fields[i];
+ }
+ }
+ fail("Field " + name + " not found");
+ return null;
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/PojoMetadataTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/PojoMetadataTest.java
new file mode 100644
index 0000000..050590f
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/parser/PojoMetadataTest.java
@@ -0,0 +1,322 @@
+/*
+ * 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.felix.ipojo.parser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.jar.Manifest;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.metadata.Element;
+
+public class PojoMetadataTest extends TestCase {
+
+ PojoMetadata FooProviderType1, FooBarProviderType1, FooProviderTypeDyn, PrimitiveManipulationTester, SimpleMultipleCheckServiceProvider;
+
+ private String header;
+
+ public void setUp() {
+ File manFile = new File("src/test/resources/manipulation/MANIFEST.MF");
+ Manifest manifest;
+ try {
+ manifest = new Manifest(new FileInputStream(manFile));
+ header = manifest.getMainAttributes().getValue("iPOJO-Components");
+ } catch (FileNotFoundException e) {
+ fail(e.getMessage());
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+
+ String comp_name = "ManipulationMetadata-FooProviderType-1";
+ FooProviderType1 = getManipulationMetadataForComponent(comp_name);
+
+ comp_name = "ManipulationMetadata-FooBarProviderType-1";
+ FooBarProviderType1 = getManipulationMetadataForComponent(comp_name);
+
+ comp_name = "ManipulationMetadata-FooProviderType-Dyn";
+ FooProviderTypeDyn = getManipulationMetadataForComponent(comp_name);
+
+ comp_name = "ManipulationMetadata-PrimitiveManipulationTester";
+ PrimitiveManipulationTester = getManipulationMetadataForComponent(comp_name);
+
+ comp_name = "ManipulationMetadata-SimpleMultipleCheckServiceProvider";
+ SimpleMultipleCheckServiceProvider = getManipulationMetadataForComponent(comp_name);
+ }
+
+ public void testGetMetadata() {
+ Element elem = null;
+ try {
+ elem = ManifestMetadataParser.parseHeaderMetadata(header);
+ } catch (ParseException e) {
+ fail("Parse Exception when parsing iPOJO-Component");
+ }
+
+ assertNotNull("Check elem not null", elem);
+
+ Element manip = getMetadataForComponent(elem, "ManipulationMetadata-FooProviderType-1");
+ assertNotNull("Check manipulation metadata not null for " + "Manipulation-FooProviderType-1", manip);
+ PojoMetadata mm;
+ try {
+ mm = new PojoMetadata(manip);
+ assertNotNull("Check mm not null", mm);
+ } catch (ConfigurationException e) {
+ fail("The creation of pojo metadata has failed");
+ }
+ }
+
+ public void testInterface() {
+ PojoMetadata manip = FooProviderType1;
+
+ String[] itf = manip.getInterfaces();
+ assertEquals("Check interfaces number", itf.length, 1);
+ assertEquals("Check itf name", itf[0], "org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService");
+
+ assertTrue("Check Foo Service implementation", manip.isInterfaceImplemented("org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService"));
+ assertFalse("Check Bar Service implementation", manip.isInterfaceImplemented("org.apache.felix.ipojo.test.scenarios.manipulation.service.BarService"));
+ }
+
+ public void testInterfaces() {
+ PojoMetadata manip = FooBarProviderType1;
+ String[] itf = manip.getInterfaces();
+ assertEquals("Check interfaces number", itf.length, 2);
+ assertEquals("Check itf name", itf[0], "org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService");
+ assertEquals("Check itf name", itf[1], "org.apache.felix.ipojo.test.scenarios.manipulation.service.BarService");
+
+ assertTrue("Check Foo Service implementation", manip.isInterfaceImplemented("org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService"));
+ assertTrue("Check Bar Service implementation", manip.isInterfaceImplemented("org.apache.felix.ipojo.test.scenarios.manipulation.service.BarService"));
+ }
+
+ public void testFields() {
+ PojoMetadata manip = FooProviderTypeDyn;
+
+ FieldMetadata[] fields = manip.getFields();
+ assertEquals("Check field count + " + fields.length, fields.length, 5);
+ /*
+ private int intProp;
+ private String strProp;
+ private String[] strAProp;
+ private int[] intAProp;
+ private boolean boolProp;
+ */
+
+ FieldMetadata field;
+
+ field = manip.getField("intProp");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "intProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int");
+ assertEquals("Check field reflective type : " + field.getFieldName(), FieldMetadata.getReflectionType(field.getFieldType()), "int");
+
+ field = manip.getField("intProp", "int");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "intProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int");
+
+ field = manip.getField("intProp", "long");
+ assertNull("Check bad field", field);
+
+ field = manip.getField("strProp");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "strProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "java.lang.String");
+ assertEquals("Check field reflective type : " + field.getFieldName(), FieldMetadata.getReflectionType(field.getFieldType()), "java.lang.String");
+
+ field = manip.getField("strProp", "String");
+ assertNull("Check bad field", field);
+
+ field = manip.getField("strProp", "java.lang.String");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "strProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "java.lang.String");
+
+ field = manip.getField("strAProp");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "strAProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "java.lang.String[]");
+ assertEquals("Check field reflective type : " + field.getFieldName() + " -> " + FieldMetadata.getReflectionType(field.getFieldType()), FieldMetadata.getReflectionType(field.getFieldType()), "[Ljava.lang.String;");
+
+ field = manip.getField("strAProp", "java.lang.String[]");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "strAProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "java.lang.String[]");
+
+ field = manip.getField("strAProp", "String[]");
+ assertNull("Check bad field", field);
+
+ field = manip.getField("intAProp");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "intAProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int[]");
+ assertEquals("Check field reflective type : " + field.getFieldName() + " -> " + FieldMetadata.getReflectionType(field.getFieldType()), FieldMetadata.getReflectionType(field.getFieldType()), "[I");
+
+ field = manip.getField("intAProp", "int[]");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "intAProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int[]");
+
+ field = manip.getField("intAProp", "String[]");
+ assertNull("Check bad field", field);
+
+ field = manip.getField("boolProp");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "boolProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "boolean");
+ assertEquals("Check field reflective type : " + field.getFieldName(), FieldMetadata.getReflectionType(field.getFieldType()), "boolean");
+
+ field = manip.getField("boolProp", "boolean");
+ assertEquals("Check field name : " + field.getFieldName(), field.getFieldName(), "boolProp");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "boolean");
+
+ field = manip.getField("boolProp", "bool");
+ assertNull("Check bad field", field);
+ }
+
+ public void testPrimitivesFields() {
+ PojoMetadata manip = PrimitiveManipulationTester;
+ FieldMetadata[] fields = manip.getFields();
+ assertEquals("Check field count", fields.length, 16);
+
+ FieldMetadata field;
+
+ field = manip.getField("b");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "byte");
+ field = manip.getField("s");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "short");
+ field = manip.getField("i");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int");
+ field = manip.getField("l");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "long");
+ field = manip.getField("d");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "double");
+ field = manip.getField("f");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "float");
+ field = manip.getField("c");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "char");
+ field = manip.getField("bool");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "boolean");
+
+ field = manip.getField("bs");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "byte[]");
+ field = manip.getField("ss");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "short[]");
+ field = manip.getField("is");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "int[]");
+ field = manip.getField("ls");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "long[]");
+ field = manip.getField("ds");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "double[]");
+ field = manip.getField("fs");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "float[]");
+ field = manip.getField("cs");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "char[]");
+ field = manip.getField("bools");
+ assertEquals("Check field type : " + field.getFieldName(), field.getFieldType(), "boolean[]");
+ }
+
+ public void testNoArgMethod() {
+ PojoMetadata manip = SimpleMultipleCheckServiceProvider;
+ MethodMetadata method = manip.getMethod("check");
+ assertEquals("Check no args", method.getMethodArguments().length, 0);
+ assertEquals("Check return", method.getMethodReturn(), "boolean");
+
+ method = manip.getMethod("check", new String[0]);
+ assertEquals("Check no args", method.getMethodArguments().length, 0);
+ assertEquals("Check return", method.getMethodReturn(), "boolean");
+ }
+
+ public void testOneArgsMethod() {
+ PojoMetadata manip = SimpleMultipleCheckServiceProvider;
+ MethodMetadata method = manip.getMethods("refBind")[0];
+ assertEquals("Check args count", method.getMethodArguments().length, 1);
+ assertEquals("Check args", method.getMethodArguments()[0], "org.osgi.framework.ServiceReference");
+ assertEquals("Check return", method.getMethodReturn(), "void");
+
+ method = manip.getMethod("refBind", new String[] {"org.osgi.framework.ServiceReference"});
+ assertEquals("Check args count", method.getMethodArguments().length, 1);
+ assertEquals("Check args", method.getMethodArguments()[0], "org.osgi.framework.ServiceReference");
+ assertEquals("Check return", method.getMethodReturn(), "void");
+ }
+
+ public void testTwoArgsMethod() {
+ PojoMetadata manip = SimpleMultipleCheckServiceProvider;
+ MethodMetadata method = manip.getMethods("doNothing")[0];
+ assertEquals("Check args count", 2, method.getMethodArguments().length);
+ assertEquals("Check args - 1", method.getMethodArguments()[0], "java.lang.Object");
+ assertEquals("Check args - 2", method.getMethodArguments()[1], "java.lang.String");
+ assertEquals("Check return", method.getMethodReturn(), "java.lang.Object");
+
+ method = manip.getMethod("doNothing", new String[] {"java.lang.Object", "java.lang.String"});
+ assertEquals("Check args count", 2, method.getMethodArguments().length);
+ assertEquals("Check args - 1", method.getMethodArguments()[0], "java.lang.Object");
+ assertEquals("Check args - 2", method.getMethodArguments()[1], "java.lang.String");
+ assertEquals("Check return", method.getMethodReturn(), "java.lang.Object");
+ }
+
+ public void testSuper() {
+ String comp_name = "org.apache.felix.ipojo.test.scenarios.component.Child";
+ PojoMetadata manip = getManipulationMetadataForComponent(comp_name);
+ assertEquals("org.apache.felix.ipojo.test.scenarios.component.Parent", manip.getSuperClass());
+ assertEquals(1, manip.getConstructors().length);
+ }
+
+ public void testConstructors() {
+ String comp_name = "org.apache.felix.ipojo.test.scenarios.component.Multiconstructor";
+ PojoMetadata manip = getManipulationMetadataForComponent(comp_name);
+ assertEquals(3, manip.getConstructors().length);
+ assertNotNull(manip.getConstructor(new String[] {String.class.getName(), String.class.getName()}));
+ assertNotNull(manip.getConstructor(new String[] {String.class.getName(), String.class.getName(), Integer.TYPE.getName()}));
+ assertNotNull(manip.getConstructor(new String[] {String.class.getName(), Integer.TYPE.getName()}));
+ assertNull(manip.getConstructor(new String[] {String.class.getName()}));
+ }
+
+
+ private Element getMetadataForComponent(Element metadata, String comp_name) {
+ Element[] comps = metadata.getElements("component");
+ for(int i = 0; i < comps.length; i++) {
+ if(comps[i].containsAttribute("factory") && comps[i].getAttribute("factory").equals(comp_name)) {
+ return comps[i];
+ }
+ if(comps[i].containsAttribute("name") && comps[i].getAttribute("name").equals(comp_name)) {
+ return comps[i];
+ }
+ if(comps[i].containsAttribute("classname") && comps[i].getAttribute("classname").equals(comp_name)) {
+ return comps[i];
+ }
+ }
+ return null;
+ }
+
+
+ private PojoMetadata getManipulationMetadataForComponent(String comp_name) {
+ Element elem = null;
+ try {
+ elem = ManifestMetadataParser.parseHeaderMetadata(header);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ fail("Parse Exception when parsing iPOJO-Component " + e);
+ }
+
+ assertNotNull("Check elem not null", elem);
+
+ Element manip = getMetadataForComponent(elem, comp_name);
+ assertNotNull("Check manipulation metadata not null for " + comp_name, manip);
+ try {
+ return new PojoMetadata(manip);
+ } catch (ConfigurationException e) {
+ fail("The creation of pojo metadata for " + comp_name + " has failed");
+ return null;
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/test/MockBundle.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/test/MockBundle.java
new file mode 100644
index 0000000..ddca7e6
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/test/MockBundle.java
@@ -0,0 +1,172 @@
+package org.apache.felix.ipojo.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.*;
+
+public class MockBundle implements Bundle {
+
+ private final ClassLoader m_classloader;
+
+ /**
+ * @param cl the Classloader to load classes and resources.
+ */
+ public MockBundle(ClassLoader cl) {
+ m_classloader = cl;
+ }
+
+ public int getState() {
+ return 0;
+ }
+
+ public void start(int i) throws BundleException {
+ }
+
+ public void start() throws BundleException {
+ }
+
+ public void stop(int i) throws BundleException {
+ }
+
+ public void stop() throws BundleException {
+ }
+
+ public void update() throws BundleException {
+ }
+
+ public void update(InputStream in) throws BundleException {
+ }
+
+ public void uninstall() throws BundleException {
+ }
+
+ public Dictionary getHeaders() {
+ return null;
+ }
+
+ public long getBundleId() {
+ return 1; // 0 is the system bundle
+ }
+
+ public String getLocation() {
+ return null;
+ }
+
+ public ServiceReference[] getRegisteredServices() {
+ return null;
+ }
+
+ public ServiceReference[] getServicesInUse() {
+ return null;
+ }
+
+ public boolean hasPermission(Object permission) {
+ return false;
+ }
+
+ public URL getResource(String name) {
+ return m_classloader.getResource(name);
+ }
+
+ public Dictionary getHeaders(String locale) {
+ return null;
+ }
+
+ public String getSymbolicName() {
+ return null;
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return m_classloader.loadClass(name);
+ }
+
+ public Enumeration getResources(String name) throws IOException {
+ return m_classloader.getResources(name);
+ }
+
+ public Enumeration getEntryPaths(String path) {
+ return null;
+ }
+
+ public URL getEntry(String name) {
+ return null;
+ }
+
+ public long getLastModified() {
+ return 0;
+ }
+
+ public Enumeration findEntries(String path, String filePattern,
+ boolean recurse) {
+ return null;
+ }
+
+ public BundleContext getBundleContext() {
+ return null;
+ }
+
+ public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int i) {
+ return null;
+ }
+
+ public Version getVersion() {
+ return null;
+ }
+
+ public <A> A adapt(Class<A> aClass) {
+ return null;
+ }
+
+ public File getDataFile(String s) {
+ return null;
+ }
+
+ /**
+ * Compares this object with the specified object for order. Returns a
+ * negative integer, zero, or a positive integer as this object is less
+ * than, equal to, or greater than the specified object.
+ * <p/>
+ * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
+ * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
+ * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
+ * <tt>y.compareTo(x)</tt> throws an exception.)
+ * <p/>
+ * <p>The implementor must also ensure that the relation is transitive:
+ * <tt>(x.compareTo(y)>0 && y.compareTo(z)>0)</tt> implies
+ * <tt>x.compareTo(z)>0</tt>.
+ * <p/>
+ * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
+ * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
+ * all <tt>z</tt>.
+ * <p/>
+ * <p>It is strongly recommended, but <i>not</i> strictly required that
+ * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
+ * class that implements the <tt>Comparable</tt> interface and violates
+ * this condition should clearly indicate this fact. The recommended
+ * language is "Note: this class has a natural ordering that is
+ * inconsistent with equals."
+ * <p/>
+ * <p>In the foregoing description, the notation
+ * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
+ * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
+ * <tt>0</tt>, or <tt>1</tt> according to whether the value of
+ * <i>expression</i> is negative, zero or positive.
+ *
+ * @param o the object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object
+ * is less than, equal to, or greater than the specified object.
+ * @throws ClassCastException if the specified object's type prevents it
+ * from being compared to this object.
+ */
+ public int compareTo(Bundle o) {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ManifestMetadataParserTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ManifestMetadataParserTest.java
new file mode 100644
index 0000000..eaaae70
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ManifestMetadataParserTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.felix.ipojo.util;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
+import org.apache.felix.ipojo.parser.ParseException;
+
+/**
+ * Test the {@link ManifestMetadataParser}
+ */
+public class ManifestMetadataParserTest extends TestCase {
+
+ /**
+ * Test FELIX-2685.
+ * The element name contains ':' as in:
+ * "//jasmine.ow2.org/rules/1.0.0:configuration"
+ * @throws ParseException
+ */
+ public void testNameWithColumn() throws ParseException {
+ // Create a test element
+ String header = "http://jasmine.ow2.org/rules/1.0.0:configuration {}";
+ Element elem = ManifestMetadataParser.parse(header);
+
+ Assert.assertEquals("http://jasmine.ow2.org/rules/1.0.0", elem.getNameSpace());
+ Assert.assertEquals("configuration", elem.getName());
+ }
+
+ /**
+ * Check the parsing of handler element using {@link ManifestMetadataParser#parseHeader(String)}
+ * @throws ParseException
+ */
+ public void testHandlerHeader() throws ParseException {
+ String header = "handler { $name=\"wbp\" $classname=\"org.apache.felix.ipojo.handler.wbp.WhiteBoardPatternHandler\"" +
+ " $namespace=\"org.apache.felix.ipojo.whiteboard\" manipulation { $super=\"org.apache.felix.ipojo.PrimitiveHandler\"" +
+ " field { $name=\"m_managers\" $type=\"java.util.List\" }method { $name=\"$init\" }method { $arguments=" +
+ "\"{org.apache.felix.ipojo.metadata.Element,java.util.Dictionary}\" $name=\"configure\" }method { $name=\"start\"" +
+ " }method { $arguments=\"{int}\" $name=\"stateChanged\" }method { $name=\"stop\" }}}";
+
+ ManifestMetadataParser parser = new ManifestMetadataParser();
+ parser.parseHeader(header);
+
+ Element[] elems = parser.getComponentsMetadata();
+ Assert.assertEquals(1, elems.length);
+
+ Element element = elems[0];
+ Assert.assertEquals("handler", element.getName());
+ Assert.assertNull(element.getNameSpace());
+
+ Assert.assertEquals("wbp", element.getAttribute("name"));
+ Assert.assertEquals("org.apache.felix.ipojo.handler.wbp.WhiteBoardPatternHandler", element.getAttribute("classname"));
+ Assert.assertEquals("org.apache.felix.ipojo.whiteboard", element.getAttribute("namespace"));
+
+ // Check the manipulation element
+ Element[] manip = element.getElements("manipulation");
+ Assert.assertNotNull(manip[0]);
+
+ Element[] methods = manip[0].getElements("method");
+ Assert.assertEquals(5, methods.length);
+ }
+
+ /**
+ * Check the parsing of handler element using {@link ManifestMetadataParser#parseHeaderMetadata(String)}
+ * @throws ParseException
+ */
+ public void testHandlerHeader2() throws ParseException {
+ String header = "handler { $name=\"wbp\" $classname=\"org.apache.felix.ipojo.handler.wbp.WhiteBoardPatternHandler\"" +
+ " $namespace=\"org.apache.felix.ipojo.whiteboard\" manipulation { $super=\"org.apache.felix.ipojo.PrimitiveHandler\"" +
+ " field { $name=\"m_managers\" $type=\"java.util.List\" }method { $name=\"$init\" }method { $arguments=" +
+ "\"{org.apache.felix.ipojo.metadata.Element,java.util.Dictionary}\" $name=\"configure\" }method { $name=\"start\"" +
+ " }method { $arguments=\"{int}\" $name=\"stateChanged\" }method { $name=\"stop\" }}}";
+
+ // This method returns an iPOJO root element
+ Element elem = ManifestMetadataParser.parseHeaderMetadata(header);
+ Element element = elem.getElements("handler")[0];
+
+ Assert.assertEquals("handler", element.getName());
+ Assert.assertNull(element.getNameSpace());
+
+ Assert.assertEquals("wbp", element.getAttribute("name"));
+ Assert.assertEquals("org.apache.felix.ipojo.handler.wbp.WhiteBoardPatternHandler", element.getAttribute("classname"));
+ Assert.assertEquals("org.apache.felix.ipojo.whiteboard", element.getAttribute("namespace"));
+
+ // Check the manipulation element
+ Element[] manip = element.getElements("manipulation");
+ Assert.assertNotNull(manip[0]);
+
+ Element[] methods = manip[0].getElements("method");
+ Assert.assertEquals(5, methods.length);
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/resources/manipulation/MANIFEST.MF b/ipojo/runtime/core/src/test/resources/manipulation/MANIFEST.MF
new file mode 100644
index 0000000..4becd87
--- /dev/null
+++ b/ipojo/runtime/core/src/test/resources/manipulation/MANIFEST.MF
@@ -0,0 +1,142 @@
+Manifest-Version: 1.0
+Export-Package: org.apache.felix.ipojo.test.scenarios.manipulation.ser
+ vice
+iPOJO-Components: component { $name="ManipulationMetadata-FooProviderT
+ ype-1" $classname="org.apache.felix.ipojo.test.scenarios.component.Fo
+ oProviderType1" $architecture="true" provides { }manipulation { field
+ { $name="m_context" $type="org.osgi.framework.BundleContext" }field
+ { $name="m_foo" $type="java.lang.String" }field { $name="m_bar" $type
+ ="int" }method { $arguments="{org.osgi.framework.BundleContext}" $nam
+ e="singleton" $return="org.apache.felix.ipojo.test.scenarios.componen
+ t.FooProviderType1" }method { $arguments="{org.osgi.framework.BundleC
+ ontext}" $name="several" $return="org.apache.felix.ipojo.test.scenari
+ os.component.FooProviderType1" }method { $arguments="{org.osgi.framew
+ ork.BundleContext}" $name="$init" }method { $name="foo" $return="bool
+ ean" }method { $name="fooProps" $return="java.util.Properties" }metho
+ d { $name="testException" }method { $name="testTry" }method { $argume
+ nts="{java.lang.String}" $name="testTry2" }method { $arguments="{java
+ .lang.String}" $name="nexttry" }method { $name="getBoolean" $return="
+ boolean" }method { $name="getDouble" $return="double" }method { $name
+ ="getInt" $return="int" }method { $name="getLong" $return="long" }met
+ hod { $name="getObject" $return="java.lang.Boolean" }method { $argume
+ nts="{int,java.lang.String,org.osgi.framework.BundleContext}" $name="
+ $init" }interface { $name="org.apache.felix.ipojo.test.scenarios.mani
+ pulation.service.FooService" }}}component { $name="ManipulationMetada
+ ta-FooBarProviderType-1" $classname="org.apache.felix.ipojo.test.scen
+ arios.component.FooBarProviderType1" $architecture="true" provides {
+ }manipulation { method { $name="$init" }method { $name="foo" $return=
+ "boolean" }method { $name="fooProps" $return="java.util.Properties" }
+ method { $name="bar" $return="boolean" }method { $name="getProps" $re
+ turn="java.util.Properties" }method { $name="getBoolean" $return="boo
+ lean" }method { $name="getDouble" $return="double" }method { $name="g
+ etInt" $return="int" }method { $name="getLong" $return="long" }method
+ { $name="getObject" $return="java.lang.Boolean" }interface { $name="
+ org.apache.felix.ipojo.test.scenarios.manipulation.service.FooService
+ " }interface { $name="org.apache.felix.ipojo.test.scenarios.manipulat
+ ion.service.BarService" }}}component { $name="ManipulationMetadata-Fo
+ oProviderType-Dyn" $classname="org.apache.felix.ipojo.test.scenarios.
+ component.FooProviderTypeDyn" $architecture="true" provides { propert
+ y { $field="intProp" $mandatory="false" $name="int" $value="2" }prope
+ rty { $field="boolProp" $mandatory="false" $name="boolean" $value="fa
+ lse" }property { $field="strProp" $mandatory="false" $name="string" $
+ value="foo" }property { $field="strAProp" $mandatory="false" $name="s
+ trAProp" $value="[foo, bar]" }property { $field="intAProp" $mandatory
+ ="false" $name="intAProp" $value="[ 1,2,3]" }}manipulation { field {
+ $name="boolProp" $type="boolean" }field { $name="strProp" $type="java
+ .lang.String" }field { $name="strAProp" $type="java.lang.String[]" }f
+ ield { $name="intProp" $type="int" }field { $name="intAProp" $type="i
+ nt[]" }method { $name="$init" }method { $name="foo" $return="boolean"
+ }method { $name="fooProps" $return="java.util.Properties" }method {
+ $name="getBoolean" $return="boolean" }method { $name="getDouble" $ret
+ urn="double" }method { $name="getInt" $return="int" }method { $name="
+ getLong" $return="long" }method { $name="getObject" $return="java.lan
+ g.Boolean" }interface { $name="org.apache.felix.ipojo.test.scenarios.
+ manipulation.service.FooService" }}}component { $name="ManipulationMe
+ tadata-PrimitiveManipulationTester" $classname="org.apache.felix.ipoj
+ o.test.scenarios.component.Manipulation23Tester" $architecture="true"
+ provides { }manipulation { field { $name="f" $type="float" }field {
+ $name="d" $type="double" }field { $name="is" $type="int[]" }field { $
+ name="bools" $type="boolean[]" }field { $name="b" $type="byte" }field
+ { $name="ls" $type="long[]" }field { $name="c" $type="char" }field {
+ $name="l" $type="long" }field { $name="bs" $type="byte[]" }field { $
+ name="cs" $type="char[]" }field { $name="i" $type="int" }field { $nam
+ e="s" $type="short" }field { $name="fs" $type="float[]" }field { $nam
+ e="ss" $type="short[]" }field { $name="bool" $type="boolean" }field {
+ $name="ds" $type="double[]" }method { $name="$init" }method { $name=
+ "getBoolean" $return="boolean" }method { $name="getBooleans" $return=
+ "boolean[]" }method { $name="getByte" $return="byte" }method { $name=
+ "getBytes" $return="byte[]" }method { $name="getChar" $return="char"
+ }method { $name="getChars" $return="char[]" }method { $name="getDoubl
+ e" $return="double" }method { $name="getDoubles" $return="double[]" }
+ method { $name="getFloat" $return="float" }method { $name="getFloats"
+ $return="float[]" }method { $name="getInt" $return="int" }method { $
+ name="getInts" $return="int[]" }method { $name="getLong" $return="lon
+ g" }method { $name="getLongs" $return="long[]" }method { $name="getSh
+ ort" $return="short" }method { $name="getShorts" $return="short[]" }m
+ ethod { $arguments="{boolean}" $name="setBoolean" }method { $argument
+ s="{boolean[]}" $name="setBooleans" }method { $arguments="{byte}" $na
+ me="setByte" }method { $arguments="{byte[]}" $name="setBytes" }method
+ { $arguments="{char}" $name="setChar" }method { $arguments="{char[]}
+ " $name="setChars" }method { $arguments="{double}" $name="setDouble"
+ }method { $arguments="{double[]}" $name="setDoubles" }method { $argum
+ ents="{float}" $name="setFloat" }method { $arguments="{float[]}" $nam
+ e="setFloats" }method { $arguments="{int}" $name="setInt" }method { $
+ arguments="{int[]}" $name="setInts" }method { $arguments="{long}" $na
+ me="setLong" }method { $arguments="{long[]}" $name="setLongs" }method
+ { $arguments="{short}" $name="setShort" }method { $arguments="{short
+ []}" $name="setShorts" }method { $arguments="{long,java.lang.String}"
+ $name="setLong" }interface { $name="org.apache.felix.ipojo.test.scen
+ arios.manipulation.service.PrimitiveManipulationTestService" }}}compo
+ nent { $name="ManipulationMetadata-SimpleMultipleCheckServiceProvider
+ " $classname="org.apache.felix.ipojo.test.scenarios.component.Multipl
+ eCheckService" $architecture="true" provides { }manipulation { field
+ { $name="refU" $type="int" }field { $name="objectU" $type="int" }fiel
+ d { $name="bothB" $type="int" }field { $name="objectB" $type="int" }f
+ ield { $name="bothU" $type="int" }field { $name="fs" $type="org.apach
+ e.felix.ipojo.test.scenarios.manipulation.service.FooService[]" }fiel
+ d { $name="simpleU" $type="int" }field { $name="simpleB" $type="int"
+ }field { $name="refB" $type="int" }method { $name="$init" }method { $
+ name="check" $return="boolean" }method { $name="getBoolean" $return="
+ boolean" }method { $name="getInt" $return="int" }method { $name="getL
+ ong" $return="long" }method { $name="getDouble" $return="double" }met
+ hod { $arguments="{java.lang.Object,java.lang.String}" $name="doNothi
+ ng" $return="java.lang.Object" }method { $name="getProps" $return="ja
+ va.util.Properties" }method { $name="voidBind" }method { $name="voidU
+ nbind" }method { $arguments="{org.apache.felix.ipojo.test.scenarios.m
+ anipulation.service.FooService}" $name="objectBind" }method { $argume
+ nts="{org.apache.felix.ipojo.test.scenarios.manipulation.service.FooS
+ ervice}" $name="objectUnbind" }method { $arguments="{org.osgi.framewo
+ rk.ServiceReference}" $name="refBind" }method { $arguments="{org.osgi
+ .framework.ServiceReference}" $name="refUnbind" }method { $arguments=
+ "{org.apache.felix.ipojo.test.scenarios.manipulation.service.FooServi
+ ce,org.osgi.framework.ServiceReference}" $name="bothBind" }method { $
+ arguments="{org.apache.felix.ipojo.test.scenarios.manipulation.servic
+ e.FooService,org.osgi.framework.ServiceReference}" $name="bothUnbind"
+ }interface { $name="org.apache.felix.ipojo.test.scenarios.manipulati
+ on.service.CheckService" }}requires { $field="fs" }}component { $clas
+ sname="org.apache.felix.ipojo.test.scenarios.component.Child" manipul
+ ation { $super="org.apache.felix.ipojo.test.scenarios.component.Paren
+ t" method { $arguments="{java.lang.String}" $name="$init" }}}componen
+ t { $classname="org.apache.felix.ipojo.test.scenarios.component.Multi
+ constructor" manipulation { method { $arguments="{java.lang.String,ja
+ va.lang.String}" $name="$init" }method { $arguments="{java.lang.Strin
+ g,int}" $name="$init" }method { $arguments="{java.lang.String,java.la
+ ng.String,int}" $name="$init" }}}
+Test-Suite: org.apache.felix.ipojo.test.scenarios.manipulation.Manipul
+ ationTestSuite
+Built-By: clement
+Tool: Bnd-0.0.357
+Bundle-Name: iPOJO Manipulation Metadata Test Suite
+Created-By: Apache Maven Bundle Plugin & iPOJO 1.6.0
+Build-Jdk: 1.6.0_22
+Bundle-Version: 1.5.0.SNAPSHOT
+Bnd-LastModified: 1292762858403
+Bundle-ManifestVersion: 2
+Import-Package: org.osgi.service.log;version=1.3, org.apache.felix.ipo
+ jo.test.scenarios.manipulation.service, org.apache.felix.ipojo.junit4
+ osgi, junit.framework, org.apache.felix.ipojo.architecture;version=1.
+ 6, org.apache.felix.ipojo;version=1.6, org.osgi.framework;version=1.5
+ , org.osgi.service.cm;version=1.2, org.apache.felix.ipojo.metadata, o
+ rg.apache.felix.ipojo.parser;version=1.6
+Bundle-SymbolicName: tests.manipulation.metadata
+
diff --git a/ipojo/runtime/org.apache.felix.ipojo.runtime-project.iml b/ipojo/runtime/org.apache.felix.ipojo.runtime-project.iml
new file mode 100644
index 0000000..b4692a3
--- /dev/null
+++ b/ipojo/runtime/org.apache.felix.ipojo.runtime-project.iml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.0" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.easymock:easymock:2.4" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-all:1.7" level="project" />
+ </component>
+</module>
+
diff --git a/ipojo/runtime/pom.xml b/ipojo/runtime/pom.xml
new file mode 100644
index 0000000..07c3eca
--- /dev/null
+++ b/ipojo/runtime/pom.xml
@@ -0,0 +1,120 @@
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.1</version>
+ <relativePath>../../pom/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>org.apache.felix.ipojo.runtime-project</artifactId>
+ <version>1.9.0-SNAPSHOT</version>
+ <name>Apache Felix iPOJO Runtime Project</name>
+ <packaging>pom</packaging>
+
+ <description>
+ The iPOJO runtime project contains the core iPOJO bundle, the composite support and the annotations.
+ </description>
+
+ <modules>
+ <module>core</module>
+ <module>composite</module>
+ <module>annotations</module>
+ </modules>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ <resource>
+ <directory>.</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ <include>DEPENDENCIES*</include>
+ </includes>
+ </resource>
+ </resources>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>project</descriptorRef>
+ </descriptorRefs>
+ <!-- we don't want to attach all the assemblies, such as bz2 -->
+ <attach>false</attach>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <!-- only attach the project and bin assemblies, in tar.gz and zip flavors -->
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-assemblies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>
+ ${project.build.directory}/${project.artifactId}-${project.version}-project.tar.gz
+ </file>
+ <classifier>project</classifier>
+ <type>tar.gz</type>
+ </artifact>
+ <artifact>
+ <file>
+ ${project.build.directory}/${project.artifactId}-${project.version}-project.zip
+ </file>
+ <classifier>project</classifier>
+ <type>zip</type>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>