Move ServiceMix Kernel trunk into Felix
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@768912 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/BUILDING.txt b/karaf/BUILDING.txt
new file mode 100644
index 0000000..7a7963e
--- /dev/null
+++ b/karaf/BUILDING.txt
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+Building Apache ServiceMix Kernel
+===================================
+
+Initial Setup
+-------------
+
+1) Install J2SE 5.0 SDK, which can be downloaded from
+ http://java.sun.com/j2se/1.5.0/download.jsp. Use version of 1.5.0_06 JDK.
+
+2) Make sure that your JAVA_HOME environment variable is set to the newly installed
+ JDK location, and that your PATH includes %JAVA_HOME%\bin (windows) or
+ $JAVA_HOME$/bin (unix).
+
+3) Install Maven 2.0.7, which can be downloaded from
+ http://maven.apache.org/download.html. Make sure that your PATH includes
+ the MVN_HOME/bin directory.
+
+
+Building
+--------
+
+1) Change to the top level directory of Apache ServiceMix Kernel source distribution.
+2) Run
+ $> mvn
+ This will compile Apache ServiceMix Kernel and run all of the tests in the
+ Apache ServiceMix Kernel source distribution. Alternatively, you can run
+ $> mvn -Pfastinstall
+ This will compile Apache ServiceMix Kernel without running the tests and takes less
+ time to build.
+ Depending on the load of remote Maven 2.0 repositories, you may have
+ to run "mvn" several times until the required dependencies are
+ all located in your local maven repository. It usually takes some time for
+ maven to download required dependencies in the first build.
+3) The distributions will be available under "assembly/target" directory.
+
diff --git a/karaf/LICENSE.txt b/karaf/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/karaf/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ 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/karaf/NOTICE.txt b/karaf/NOTICE.txt
new file mode 100644
index 0000000..15b77cc
--- /dev/null
+++ b/karaf/NOTICE.txt
@@ -0,0 +1,9 @@
+ =========================================================================
+ == NOTICE file for use with the Apache License, Version 2.0, ==
+ == in this case for the Apache ServiceMix distribution. ==
+ =========================================================================
+
+ This product contains software developed by
+ The Apache Software Foundation (http://www.apache.org/).
+
+
diff --git a/karaf/README.txt b/karaf/README.txt
new file mode 100644
index 0000000..07740ef
--- /dev/null
+++ b/karaf/README.txt
@@ -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.
+ */
+
+Welcome to Apache ServiceMix Kernel
+====================================
+Apache ServiceMix Kernel is a small OSGi based kernel which provides a
+lightweight container onto which various bundles can be deployed.
+
+The following features are included:
+ * Hot deployment: ServiceMix Kernel supports hot deployment of OSGi bundles by monitoring
+ jar files inside the [home]/deploy directory. Each time a jar is copied in this folder,
+ it will be installed inside the runtime. You can then update or delete it and changes will
+ be handled automatically. In addition, the Kernel also supports exploded bundles and custom
+ deployers (a spring one is included by default).
+ * Dynamic configuration: Services are usually configured through the ConfigurationAdmin OSGi
+ service. Such configuration can be defined in ServiceMix Kernel using property files inside
+ the [home]/etc directory. These configurations are monitored and changes on the properties
+ files will be propagated to the services.
+ * Logging System: using a centralized logging back end supported by Log4J, ServiceMix Kernel
+ supports a number of different APIs (JDK 1.4, JCL, SLF4J, Avalon, Tomcat, OSGi)
+ * Provisioning: Provisioning of libraries or applications can be done through a number of
+ different ways, by which they will be downloaded locally, installed and started.
+ * Native OS integration: ServiceMix Kernel can be integrated into your own Operating System as
+ a service so that the lifecycle will be bound to your Operating System.
+ * Extensible Shell console: ServiceMix features a nice text console where you can manage the
+ services, install new applications or libraries and manage their state. This shell is easily
+ extensible by deploying new commands dynamically along with new features or applications.
+ * Remote access: use any SSH client to connect to the kernel and issue commands in the console
+ * Security framework based on JAAS
+ * Managing instances: ServiceMix Kernel provides simple commands for managing instances of
+ ServiceMix Kernel. You can easily create, delete, start and stop instances of ServiceMix
+ Kernel through the console.
+
+Getting Started
+===============
+For an Apache ServiceMix Kernel source distribution, please read
+BUILDING.txt for instructions on building Apache ServiceMix Kernel.
+
+For an Apache ServiceMix Kernel binary distribution, please read
+RELEASE-NOTES.txt for installation instructions and list of supported
+and unsupported features.
+
+Alternatively, you can also find out how to get started here:
+ http://servicemix.apache.org/kernel/
+
+If you need more help try talking to us on our mailing lists
+ http://servicemix.apache.org/mailing-lists.html
+
+If you find any issues with ServiceMix Kernel, please submit reports
+with JIRA here:
+ http://issues.apache.org/activemq/browse/SMX4KNL
+
+We welcome contributions, and encourage you to get involved in the
+ServiceMix community. If you'd like to learn more about how you can
+contribute, please see:
+ http://servicemix.apache.org/contributing.html
+
+Many thanks for using Apache ServiceMix.
+
+
+The ServiceMix Team
+http://servicemix.apache.org/team.html
diff --git a/karaf/RELEASE-NOTES.txt b/karaf/RELEASE-NOTES.txt
new file mode 100644
index 0000000..41fc751
--- /dev/null
+++ b/karaf/RELEASE-NOTES.txt
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+
+ Apache ServiceMix Kernel 1.1.0
+ ==============================
+
+ Overview
+ --------
+
+ The Apache ServiceMix Kernel 1.1.0 release brings a lot of new features enhancements and bug fixes:
+ * remote connection using SSH protocol
+ * provisioning enhancements: versioning / hot deployment of features
+ * new commands, including OSGi related commands for the Configuration Admin and Package Admin services
+ * improved spring integration: upgrade to spring 2.5.6 and spring-dm 1.2.0-m2, the osgi/list command
+ now displays spring applications status
+ * container level locking for master / slave deployments
+ * support for JAXP 1.4 on all platforms
+ * improved JMX support for managing the OSGi framework and features
+ Note that the commands syntax has changed due to the upgrade to the latest gshell version.
+
+ Known issues
+ ============
+
+ * [SMX4KNL-141] - The config/edit command changes does not takes precedence over configuration files in the etc folder at restart
+ * [SMX4KNL-183] - features/uninstall behaves different in version selection than features/install
+ * [SMX4KNL-189] - Shell completion works only in the root shell
+ * [SMX4KNL-200] - The client jar / ssh daemon should support direct commands
+ * [SMX4KNL-227] - Full support of fragment bundles
+ * [SMX4KNL-228] - Warning messages when using OpenSSH 5.1 to connect to ServiceMix Kernel
+ * [SMX4KNL-231] - Unable to log into ServiceMix Kernel using OpenSolaris SSH client
+ * [SMX4KNL-232] - Unable to log into ServiceMix Kernel using Windows putty SSH client
+
+ Changelog
+ ---------
+
+ The following list shows the bug fixes and enhancements included in this release:
+
+ ** Bug
+ * [SMX4KNL-72] - If there is no etc/startup.properties, the kernel hangs without displaying any error
+ * [SMX4KNL-79] - When starting the kernel, a log entry states "Can not install feature ''"
+ * [SMX4KNL-86] - When running integration tests, the container classpath contains all the test bundles which may cause problems
+ * [SMX4KNL-87] - Problems with batch files on windows
+ * [SMX4KNL-95] - kernel build failed in testing/itests
+ * [SMX4KNL-96] - package missing for felix.config.properties
+ * [SMX4KNL-97] - Dissociate the name and the location of ServiceMix Kernel instances managed by the admin shell
+ * [SMX4KNL-100] - The client does not close when running "osgi shutdown" command
+ * [SMX4KNL-102] - The file monitor should use ${servicemix.base} for default polling locations
+ * [SMX4KNL-104] - When using a remote connection, errors are not reported correctly
+ * [SMX4KNL-105] - Features are reinstalled on restart of servicemix
+ * [SMX4KNL-112] - gshell pom needs to include gshell-config and gshell-packages modules
+ * [SMX4KNL-115] - config.properties for SMX-Kernel needs to be updated
+ * [SMX4KNL-117] - The Smx testing platform should take into account the boot delegation packages specified in the configuration file
+ * [SMX4KNL-118] - The configuration file for felix comes from 3 different places in the svn tree, only one copy should be used
+ * [SMX4KNL-122] - When using the client and trying to connect to another instance using the "admin connect" command, the console hangs
+ * [SMX4KNL-123] - XML parsing does not work on non Sun JVMs and Sun's SAAJ bundle requires Sun's JAXP implementation
+ * [SMX4KNL-124] - Issuing command "log d" fails with NullPointerException on AIX.
+ * [SMX4KNL-126] - The user is no more authenticated with the new gshell integration
+ * [SMX4KNL-128] - Remove OBR support from default distribution
+ * [SMX4KNL-132] - osgi/list command should display the spring application state
+ * [SMX4KNL-133] - jaxp-ri bundle miss javax.xml.datatype.DatatypeFactory factoryId under META-INF/services
+ * [SMX4KNL-134] - XPathFactoryFinder in jaxp api should use factoryId to load the spi class in osgi enviroment
+ * [SMX4KNL-135] - Remote shell missing help resources
+ * [SMX4KNL-138] - No command history with GShell console
+ * [SMX4KNL-140] - GShell commands references not cleaned up when bundle uninstalled
+ * [SMX4KNL-142] - Starting a new instance that was created with admin on windows fails to start
+ * [SMX4KNL-145] - java.lang.IllegalStateException: Streams not registered for thread
+ * [SMX4KNL-148] - OPS4J maven repo missing form default search list
+ * [SMX4KNL-149] - Build failure due to new sshd snapshot
+ * [SMX4KNL-150] - GShell group name variable doesn't resolve
+ * [SMX4KNL-151] - Update displayed version in GShell to be 1.1.0-SNAPSHOT for trunk
+ * [SMX4KNL-153] - Stopping one of the "core" bundles of the servicemix engine block the server
+ * [SMX4KNL-155] - Error changing manifest while bundle is deployed
+ * [SMX4KNL-162] - The default jaas realm uses the config admin to store clear password
+ * [SMX4KNL-164] - features/removeUrl does not remove the repository
+ * [SMX4KNL-165] - Initial provisioning using features is very slow especially when using snapshots
+ * [SMX4KNL-170] - Unknown protocol: org.apache.servicemix.kernel.gshell
+ * [SMX4KNL-172] - the new command optional/cat from gshell can't support mvn/http protocol
+ * [SMX4KNL-174] - NoClassDefFoundError jline/ConsoleReader when invoking servicemix-client
+ * [SMX4KNL-176] - 'features/install xxx' tries to install version 0.0.0 instead of the latest one
+ * [SMX4KNL-177] - Some of the Optional commands have moved to different packages in the latest snapshot of geronimo gshell
+ * [SMX4KNL-178] - Remove debug log statements when booting
+ * [SMX4KNL-180] - features/install xxx barfs when using non osgi compliant versions
+ * [SMX4KNL-181] - osgi/install -s throws a NPE if the bundle can not be resolved
+ * [SMX4KNL-182] - Upon restart, the list of previously installed features is not available anymore
+ * [SMX4KNL-185] - Features JMX view does not reload persistent state on restart
+ * [SMX4KNL-187] - Features JMX view making too much log noise
+ * [SMX4KNL-188] - Gshell itests fail on windows
+ * [SMX4KNL-190] - Deployment / Startup of spring xml configurations doesn't work correctly
+ * [SMX4KNL-192] - ServiceMix Kernel startup blocks
+ * [SMX4KNL-198] - The console / server / client arguments to servicemix shell script are broken
+ * [SMX4KNL-199] - Missing resource bundle for obr shell
+ * [SMX4KNL-201] - The client jar does not exit when running with --help argument
+ * [SMX4KNL-202] - Startup of a pending to deploy Spring config file fails when all required bundles got installed
+ * [SMX4KNL-204] - OsgiConfiguration.unregister() throw NPE when exit smx
+ * [SMX4KNL-205] - "ld | grep WARN" does not filter while "log/display | grep WARN" does
+ * [SMX4KNL-207] - features/install should install latest feature version for any dependency that does not explicitly specify a dependency
+ * [SMX4KNL-208] - Bundle#loadClass sometimes return null instead of throwing a CNFE
+ * [SMX4KNL-212] - Exceptions are displayed in the console when trying to load default xml catalogs
+ * [SMX4KNL-215] - Don't attempt to stop fragments in filemonitor
+ * [SMX4KNL-218] - Can't find bundle for base name org.apache.servicemix.kernel.gshell.wrapper.InstallCommand, locale fr_BE
+ * [SMX4KNL-220] - The org.apache.servicemix.kernel.management bundle should not use DynamicImport-Package=*
+
+ ** Improvement
+ * [SMX4KNL-36] - Investigate having a different bootstrap mechanism for a remote client to not start all the bundles installed (maybe by not using osgi)
+ * [SMX4KNL-62] - Enhance admin shell create command to allocate unique ports to new instances.
+ * [SMX4KNL-74] - When using the spring deployer, allow the customization of the OSGi manifest entries
+ * [SMX4KNL-78] - Small refactoring of the Feature interface
+ * [SMX4KNL-85] - Extend GShell#waitForFrameworkToStart to wait 60 seconds instead of 5 seconds.
+ * [SMX4KNL-101] - Reduce the amount of logging issued by mina
+ * [SMX4KNL-106] - Support for container level lock to support master/slave deployments
+ * [SMX4KNL-114] - prompt should print username@instance-name when using remote shell
+ * [SMX4KNL-119] - Use boot delegation instead of listing sun packages for jaxp / url handlers
+ * [SMX4KNL-120] - The ServiceMix Kernel testing platform should use the servicemix kernel main jar instead of the felix one
+ * [SMX4KNL-121] - Improve the Main class to easily embed the whole ServiceMix Kernel distribution in another web application
+ * [SMX4KNL-125] - Show command environment in kernel shell
+ * [SMX4KNL-127] - Add the update command in the osgi shell
+ * [SMX4KNL-130] - Support configurable lock directory for container level locking (for master/slave deployments).
+ * [SMX4KNL-136] - Upgrade to pax logging 1.2.0
+ * [SMX4KNL-154] - some Action moved from to another package cause kernel start up failed
+ * [SMX4KNL-158] - Move jline into its own bundle instead of using a private package in gshell-core
+ * [SMX4KNL-169] - Use the start level to implement the container level locking
+ * [SMX4KNL-184] - Align the commands name with the gshell ones
+ * [SMX4KNL-186] - features/uninstall should select the right version if only one version has been installed
+ * [SMX4KNL-191] - Upgrade to pax-logging 1.3.0
+ * [SMX4KNL-203] - When installing a feature, the service should first install all bundles, then start them all in order
+ * [SMX4KNL-206] - Felix version in servicemix-kernel-1.1.0-snapshot doesn't seem to support fragment bundles.
+ * [SMX4KNL-216] - Display fragments informations in the osgi list command
+ * [SMX4KNL-230] - Log statements using java.util.logging are logged into both stderr and data/log/servicemix.log file
+
+ ** New Feature
+ * [SMX4KNL-28] - Create a config command to administer the ConfigAdmin service
+ * [SMX4KNL-33] - Enhance admin shell to provide commands to start / stop / list / connect to known ServiceMix instances
+ * [SMX4KNL-60] - Versionning of features
+ * [SMX4KNL-76] - support to install/uninstall different version of features
+ * [SMX4KNL-90] - Create new commands to leverage the PackageAdmin OSGi service
+ * [SMX4KNL-94] - Provide a JMX frontend on top of the features service
+ * [SMX4KNL-107] - Create a new "admin connect" command to easily connect to a named instance of servicemix
+ * [SMX4KNL-108] - Add a new 'log de' command that displays the last exception from the log
+ * [SMX4KNL-109] - Include the pax wrapper url handler in the kernel for ease of use of non osgi third party dependencies
+ * [SMX4KNL-111] - Add a "starting" state to the admin list command
+ * [SMX4KNL-113] - Upgrade to Felix 1.2.1
+ * [SMX4KNL-116] - Allow repository descriptors to reference other repository descriptors
+ * [SMX4KNL-131] - Include JAXP 1.4 API
+ * [SMX4KNL-143] - Provide a way to make BouncyCastle work in ServiceMix Kernel
+ * [SMX4KNL-144] - Use SSH as a remoting protocol to connect to a servicemix kernel
+ * [SMX4KNL-147] - Upgrade to spring-DM 1.2.0-m2
+ * [SMX4KNL-152] - The version number displayed in the console should be extract from resources (using maven filtering + properties file) but the branding should be easy to change so that people embedding the Kernel can define their own
+ * [SMX4KNL-156] - Move core management feature to the kernel
+ * [SMX4KNL-157] - Add a JAAS realm for authenticating users from SSH and JMX
+ * [SMX4KNL-163] - Provide default jdbc lock impl for master/slave deployments
+ * [SMX4KNL-167] - Upgrade to spring 2.5.6
+ * [SMX4KNL-171] - Include RFC0139 (JMX management of OSGi)
+ * [SMX4KNL-179] - Upgrade to felix 1.5.0-SNAPSHOT
+ * [SMX4KNL-214] - Hot deployment of features through the deploy folder
+
+ ** Task
+ * [SMX4KNL-43] - Upgrade to latest gshell
+ * [SMX4KNL-93] - Extract the admin commands in their own module
+ * [SMX4KNL-98] - Use dependency management section for all dependencies
+ * [SMX4KNL-99] - Remove ant from the kernel distribution and replace its use in the filemonitor
+ * [SMX4KNL-129] - Make sure aliases and links commands work with the new gshell integration
+ * [SMX4KNL-166] - Move the embedded smx demo into trunk
+ * [SMX4KNL-168] - Downgrade to junit 3.8
+ * [SMX4KNL-196] - gshell-core source jar should use the shade plugin to include the gshell sources
+ * [SMX4KNL-197] - The client source jar should include the slf4j sources using the shade plugin
+ * [SMX4KNL-211] - Remove one of jmock or easymock from the kernel
+
+
+
+
+ Apache ServiceMix Kernel 1.0.0
+ ==============================
+
+ Overview
+ --------
+
+ This release is the first GA release of ServiceMix Kernel. A few bugs and enhancements have been done
+ to the Kernel since the second release candidate. The most two important fixes from a user point of view
+ are SMX4KNL-70 which makes sure the bundles provided in the distribution are used first instead of the ones
+ from the user's local maven repository and SMx4KNL-77 which allows artifacts in the /deploy folder to be
+ started when the needed dependencies have been installed successfully (the previous behavior was to fail
+ if there were missing bundles when the artifact is processed).
+ See the change log below for more informations.
+
+ Changelog
+ ---------
+
+ The following list shows the bug fixes and enhancements included in this release:
+
+ ** Bug
+ * [SMX4KNL-70] - Bundles in /system folder should take precedence over local Maven repository
+ * [SMX4KNL-79] - When starting the kernel, a log entry states "Can not install feature ''"
+ * [SMX4KNL-80] - "features list" output has a "version" column but no infos
+ * [SMX4KNL-81] - The wrapper feature can not be installed out of the box
+ * [SMX4KNL-82] - The installation state of a feature is not displayed correctly after a restart when using the "features list" command
+ * [SMX4KNL-83] - When using the wrapper command, ServiceMix Kernel does not start correctly
+
+ ** Improvement
+ * [SMX4KNL-69] - Add installation status for the 'features list' result.
+ * [SMX4KNL-71] - Export the gshell CommandExecutor in the OSGi registry
+ * [SMX4KNL-73] - Require work around for blocking in.read() call in ProxyIO for automated testing.
+ * [SMX4KNL-75] - Modify GShell argument processing to execute multiple commands.
+ * [SMX4KNL-77] - The deployer should keep a list of installed bundles that failed to start and retry those when new bundles have been resolved
+ * [SMX4KNL-78] - Small refactoring of the Feature interface
+
+
+
+ Apache ServiceMix Kernel 1.0.0-rc2
+ ==================================
+
+Overview
+--------
+
+This second Release Candidate of Apache ServiceMix Kernel includes a few bug fixes and a few new features.
+The main change is that the system folder that contains the OSGi bundles to install in the runtime is now
+organized as a maven 2 repository, though it should not directly affect users. A more detailed changelog
+is following.
+
+Changelog
+---------
+
+The following list shows the bug fixes and enhancements included in this release:
+
+** Bug
+ * [SMX4KNL-34] - snapshot jar name mismatch in startup.properties and in the system folder
+ * [SMX4KNL-52] - Deployment of pain spring files does not work when using xml extensions because these are not available in the classpath
+ * [SMX4KNL-53] - During starting, files are redeployed several times, one each time a new Deployer is registered in OSGi
+ * [SMX4KNL-54] - Kernel does not start if system property "felix.cache.profiledir" is not defined
+ * [SMX4KNL-56] - Kernel does not start on Windows if classpath contains spaces
+ * [SMX4KNL-58] - Hot-deploy capability of FileMonitor does not update bundles properly on Windows.
+ * [SMX4KNL-63] - Possible deadlock at startup caused by the filedeployer not running in its own thread the first time
+ * [SMX4KNL-64] - The list of repositories for the features shell is not persisted across restart
+ * [SMX4KNL-65] - After copying or moving the kernel, it does not start in a very clean way anymore
+ * [SMX4KNL-68] - failed to start smx4
+
+** Improvement
+ * [SMX4KNL-51] - Add a configuration file for the maven url handler so that adding default repositories is easy enough
+ * [SMX4KNL-55] - Use maven shade plugin on the main module so that sources jar is correct
+ * [SMX4KNL-66] - When the file monitor loads configuration for the config admin, allow the use of system properties using ${xxx}
+
+** New Feature
+ * [SMX4KNL-61] - Organize the system folder as a maven 2 repository
+ * [SMX4KNL-67] - Allow the features service to obtain a list of features to install at startup
+
+** Task
+ * [SMX4KNL-57] - Extract bundles from the svn tree
+ * [SMX4KNL-59] - Upgrade to spring 2.5.5 / spring-dm 1.1.0
+
+
+
+ Apache ServiceMix Kernel 1.0-rc1
+ ================================
+
+This first Release Candidate of Apache ServiceMix Kernel includes a security framework based on
+JAAS allowing authentication using JAAS realms deployed at runtime and also allow keystores and
+trustores to be deployed and accessed through OSGi. This allow the remote console to be secured
+correctly (if the console is activated in a production environment, we strongly recommand to
+deploy a custom JAAS realm and new keystores when using the SSL connection). A few bugs have been
+fixed too.
+
+A release candidate (RC) is a distribution that has not been proven completely stable, but the
+feature set is the one that will be available in the final release. The number of RCs has not
+been fixed and it will depend on the feedback we obtain from this release, so please give it a try
+and report any problems you can find.
+
+** Bug
+ * [SMX4KNL-38] - Fix NullPointerException instead of Command not found in the console
+ * [SMX4KNL-40] - Remote console does not display output
+ * [SMX4KNL-41] - When using the remote command on the client from the command line, the process does not exit correctly
+ * [SMX4KNL-44] - Changed bundles restarted twice
+ * [SMX4KNL-45] - The "log display" command does not display anything when running from an instance that has been created using the "admin create" command
+ * [SMX4KNL-46] - After using the 'admin create' command, cpu goes to 100% until another command is entered
+ * [SMX4KNL-48] - ulimit error on MacOS X Leopard 10.5
+ * [SMX4KNL-50] - Kernel does not start on Windows
+
+** Improvement
+ * [SMX4KNL-39] - Make bundle locations for SMX configurable
+ * [SMX4KNL-49] - Upgrade to Spring 2.5.4, Spring-DM 1.1.0-m2, PaxLogging 1.1.1
+
+** New Feature
+ * [SMX4KNL-35] - Secure the connection between the client and the kernel in some way
+ * [SMX4KNL-37] - JAAS support
+
+
+ Apache ServiceMix Kernel 1.0-m3
+ ================================
+
+This third milestone of Apache ServiceMix Kernel brings a lot of new features:
+ * a provisioning system to install applications easily
+ * a log shell to visualize the logging output and change log levels
+ * pipe support on commands
+ * a bunch of new commands: history, grep, cat, ava, exec, sleep
+
+Note that this milestone does not include the latest spring version because
+the latest ones have wrong OSGi manifest headers and do not work.
+
+** Bug
+ * [SMX4KNL-22] - The console can not be reloaded at runtime
+ * [SMX4KNL-29] - Shell commands are not unregistered automatically when the last command is unregistered
+ * [SMX4KNL-31] - Aliases do not work when inside a subshell
+
+** Improvement
+ * [SMX4KNL-5] - Show gshell sub-shell name in prompt
+ * [SMX4KNL-6] - Error Message improvement on Gshell console.
+ * [SMX4KNL-10] - Handle dependant features correctly
+ * [SMX4KNL-21] - Allow pipes in command syntax
+ * [SMX4KNL-25] - Feature commands enhancement.
+ * [SMX4KNL-32] - Lazy load feature repositories to avoid delay when the url can not be accessed or things like that
+
+** New Feature
+ * [SMX4KNL-1] - Implements a log command
+ * [SMX4KNL-11] - Provisioning system
+ * [SMX4KNL-20] - Adding a new OBR command to reload the repository url's
+ * [SMX4KNL-23] - Add a grep command
+ * [SMX4KNL-24] - Add a few utils commands: cat, java, exec, sleep
+ * [SMX4KNL-26] - Spring URL handler to wrap xml spring files into bundles for direct installation.
+ * [SMX4KNL-27] - Include an implementation of the Preferences service
+ * [SMX4KNL-30] - Create an history command to be able to browse previous commands easily
+
+
+ Apache ServiceMix Kernel 1.0-m2
+ ================================
+
+This is the second milestone of ServiceMix Kernel, but mostly a bug fix release
+compared to the first milestone.
+
+** Bug
+ * [SMX4KNL-13] - startup issue in recognizing new files in deploy folder
+ * [SMX4KNL-15] - Display of memory size using the info command on Windows is ugly
+ * [SMX4KNL-16] - Bad error message when startup.properties lists a unknown file (the file name is not displayed, but 'null' instead)
+ * [SMX4KNL-18] - No need for DynamicImport-Package=* in filemonitor
+ * [SMX4KNL-19] - Performance problems when using the OSGi classloaders in a certain way
+
+** Improvement
+ * [SMX4KNL-4] - Add a start option on the 'osgi install' command so that the bundle is automatically started
+
+** Task
+ * [SMX4KNL-17] - Remove all the warnings when building the distribution tar.gz
+
+See the README.txt file for more general informations about ServiceMix Kernel.
+
+
+ Apache ServiceMix Kernel 1.0-m1
+ ================================
+
+See the README.txt file for more general informations about ServiceMix Kernel.
+
+This is the first milestone of ServiceMix Kernel. All the features are not
+fully implemented yet and things may be changed until the final 1.0 release.
+Please refer to the web site for more informations and feel free to ask any
+questions on the ServiceMix mailing lists or simply to provide feedback
+(we appreciate it very much).
+
+Thanks you for downloading ServiceMix Kernel. Enjoy !
+
+The ServiceMix Team
+http://servicemix.apache.org/team.html
+
diff --git a/karaf/assembly/pom.xml b/karaf/assembly/pom.xml
new file mode 100644
index 0000000..936d36a
--- /dev/null
+++ b/karaf/assembly/pom.xml
@@ -0,0 +1,409 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>apache-servicemix-kernel</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Assembly</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.spring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.modules</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.management</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-jexl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-vfs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.oro</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jline</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.prefs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-mvn</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-wrap</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>jmx</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.oracle.osgi</groupId>
+ <artifactId>jmx-impl</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ <outputDirectory>target/dependencies</outputDirectory>
+ <destFileName>servicemix.jar</destFileName>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.client</artifactId>
+ <outputDirectory>target/dependencies</outputDirectory>
+ <destFileName>servicemix-client.jar</destFileName>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <outputDirectory>target/dependencies</outputDirectory>
+ <destFileName>servicemix-jaas-boot.jar</destFileName>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>target/classes/features.xml</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unix-bin</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/unix-bin.xml</descriptor>
+ </descriptors>
+ <finalName>${pom.artifactId}-${pom.version}</finalName>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </execution>
+ <execution>
+ <id>windows-bin</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/windows-bin.xml</descriptor>
+ </descriptors>
+ <finalName>${pom.artifactId}-${pom.version}</finalName>
+ <appendAssemblyId>true</appendAssemblyId>
+ <assemblyId/>
+ </configuration>
+ </execution>
+ <execution>
+ <id>unix-src</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/unix-src.xml</descriptor>
+ </descriptors>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </execution>
+ <execution>
+ <id>windows-src</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/windows-src.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>assembly-src</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unix-src</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attached</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/unix-src.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ <execution>
+ <id>windows-src</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attached</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/descriptors/windows-src.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/karaf/assembly/src/main/descriptors/unix-bin.xml b/karaf/assembly/src/main/descriptors/unix-bin.xml
new file mode 100644
index 0000000..3dd8246
--- /dev/null
+++ b/karaf/assembly/src/main/descriptors/unix-bin.xml
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+ <id></id> <!-- intentionally left blank -> http://jira.codehaus.org/browse/MASSEMBLY-301 -->
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <fileSets>
+
+ <!-- Copy license and other files from root -->
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>/</outputDirectory>
+ <includes>
+ <include>README.txt</include>
+ <include>RELEASE*.txt</include>
+ </includes>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+
+ <!-- Copy over everything that needs to get unix line endings -->
+ <fileSet>
+ <directory>src/main/distribution/text</directory>
+ <outputDirectory>/</outputDirectory>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/distribution/unix-text</directory>
+ <outputDirectory>/</outputDirectory>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+ <fileSet>
+ <directory>target/classes/etc</directory>
+ <outputDirectory>/etc/</outputDirectory>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+
+ <!-- Copy over the examples -->
+ <fileSet>
+ <directory>../demos</directory>
+ <outputDirectory>/demos/</outputDirectory>
+ <lineEnding>unix</lineEnding>
+ <excludes>
+ <exclude>**/target/**</exclude>
+ </excludes>
+ </fileSet>
+
+ <!-- Copy over files that should not get the line endings converted -->
+ <fileSet>
+ <directory>src/main/distribution/binary</directory>
+ <outputDirectory>/</outputDirectory>
+ </fileSet>
+
+ <!-- Copy over the files that should not get the line endings converted but need to be chmod to 755 -->
+ <fileSet>
+ <directory>src/main/distribution/unix-binary</directory>
+ <outputDirectory>/</outputDirectory>
+ <fileMode>0755</fileMode>
+ </fileSet>
+
+ <!-- Copy over the files that need unix line endings and also chmod to 755 -->
+ <fileSet>
+ <directory>src/main/distribution/unix-shell</directory>
+ <outputDirectory>/</outputDirectory>
+ <lineEnding>unix</lineEnding>
+ <fileMode>0755</fileMode>
+ </fileSet>
+
+ <!-- Copy over jar files -->
+ <fileSet>
+ <directory>target/dependencies</directory>
+ <outputDirectory>/lib/</outputDirectory>
+ </fileSet>
+
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${basedir}/../etc/config.properties</source>
+ <outputDirectory>/etc/</outputDirectory>
+ <destName>config.properties</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/LICENSE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>LICENSE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/NOTICE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>NOTICE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/classes/features.xml</source>
+ <outputDirectory>/system/org/apache/servicemix/kernel/apache-servicemix-kernel/${version}</outputDirectory>
+ <destName>apache-servicemix-kernel-${version}-features.xml</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ </files>
+
+ <dependencySets>
+
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/felix/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.felix:org.osgi.compendium</include>
+ <include>org.apache.felix:org.apache.felix.configadmin</include>
+ <include>org.apache.felix:org.apache.felix.prefs</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/ops4j/pax/logging/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.ops4j.pax.logging:pax-logging-api</include>
+ <include>org.ops4j.pax.logging:pax-logging-service</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/ops4j/pax/url/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.ops4j.pax.url:pax-url-mvn</include>
+ <include>org.ops4j.pax.url:pax-url-wrap</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/springframework/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.springframework:spring-aop</include>
+ <include>org.springframework:spring-beans</include>
+ <include>org.springframework:spring-context</include>
+ <include>org.springframework:spring-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/springframework/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.springframework.osgi:spring-osgi-core</include>
+ <include>org.springframework.osgi:spring-osgi-extender</include>
+ <include>org.springframework.osgi:spring-osgi-io</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/geronimo/specs/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.geronimo.specs:geronimo-servlet_2.5_spec</include>
+ <include>org.apache.geronimo.specs:geronimo-annotation_1.0_spec</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/bundles/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.aopalliance</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.cglib</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxp-ri</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.oro</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-jexl</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-vfs</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-codec</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-httpclient</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.jline</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/specs/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.specs:org.apache.servicemix.specs.jaxp-api-1.4</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.filemonitor</include>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.spring</include>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.management</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/gshell/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.core</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.admin</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.osgi</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.log</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.features</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.config</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.packages</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/jaas/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel.jaas:org.apache.servicemix.kernel.jaas.config</include>
+ <include>org.apache.servicemix.kernel.jaas:org.apache.servicemix.kernel.jaas.modules</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/sshd/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.sshd:sshd-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/mina/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.mina:mina-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.osgi:jmx</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>com/oracle/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>com.oracle.osgi:jmx-impl</include>
+ </includes>
+ </dependencySet>
+
+ </dependencySets>
+
+</assembly>
diff --git a/karaf/assembly/src/main/descriptors/unix-src.xml b/karaf/assembly/src/main/descriptors/unix-src.xml
new file mode 100644
index 0000000..9227d56
--- /dev/null
+++ b/karaf/assembly/src/main/descriptors/unix-src.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+ <id>src</id>
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <fileSets>
+ <!-- Copy license and other files from root -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>/</outputDirectory>
+ <includes>
+ <include>BUILDING.txt</include>
+ <include>README.txt</include>
+ <include>RELEASE*.txt</include>
+ </includes>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+
+ <!-- Binary Files -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>src</outputDirectory>
+ <includes>
+ <include>**/*.jpeg</include>
+ <include>**/*.jpg</include>
+ <include>**/*.gif</include>
+ <include>**/*.png</include>
+ <include>**/*.exe</include>
+ <include>**/*.dll</include>
+ <include>**/*.jar</include>
+ <include>**/*.so</include>
+ <include>**/*.ks</include>
+ <include>**/*.ts</include>
+ <include>**/*.keystore</include>
+ <include>**/*.bin</include>
+ <include>**/*.jnilib</include>
+ <include>**/*.cert</include>
+ <include>apache-servicemix/src/main/release/bin/*/wrapper</include>
+ </includes>
+ <excludes>
+ <exclude>**/eclipse-classes/**</exclude>
+ <exclude>**/target/**</exclude>
+ </excludes>
+ </fileSet>
+
+ <!-- Text Files -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>src</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <excludes>
+ <exclude>**/*.jpeg</exclude>
+ <exclude>**/*.jpg</exclude>
+ <exclude>**/*.gif</exclude>
+ <exclude>**/*.png</exclude>
+ <exclude>**/*.exe</exclude>
+ <exclude>**/*.dll</exclude>
+ <exclude>**/*.jar</exclude>
+ <exclude>**/*.so</exclude>
+ <exclude>**/*.ks</exclude>
+ <exclude>**/*.ts</exclude>
+ <exclude>**/*.keystore</exclude>
+ <exclude>**/*.bin</exclude>
+ <exclude>**/*.jnilib</exclude>
+ <exclude>**/*.cert</exclude>
+ <exclude>apache-servicemix/src/main/release/bin/*/wrapper</exclude>
+ <exclude>**/target/**</exclude>
+ <exclude>**/build/**</exclude>
+ <exclude>activemq-data/**</exclude>
+ <exclude>*/activemq-data/**</exclude>
+ <exclude>**/eclipse-classes/**</exclude>
+ <exclude>**/.*</exclude>
+ <exclude>**/.*/**</exclude>
+
+ <exclude>**/surefire*</exclude>
+ <exclude>**/svn-commit*</exclude>
+
+ <exclude>**/*.iml</exclude>
+ <exclude>**/*.ipr</exclude>
+ <exclude>**/*.iws</exclude>
+
+ <exclude>**/cobertura.ser</exclude>
+
+ </excludes>
+ <lineEnding>unix</lineEnding>
+ </fileSet>
+ </fileSets>
+ <files>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/LICENSE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>LICENSE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/NOTICE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>NOTICE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ </files>
+</assembly>
diff --git a/karaf/assembly/src/main/descriptors/windows-bin.xml b/karaf/assembly/src/main/descriptors/windows-bin.xml
new file mode 100644
index 0000000..0901795
--- /dev/null
+++ b/karaf/assembly/src/main/descriptors/windows-bin.xml
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+ <id></id> <!-- intentionally left blank -> http://jira.codehaus.org/browse/MASSEMBLY-301 -->
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+
+
+ <!-- Copy license and other files from root -->
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>/</outputDirectory>
+ <includes>
+ <include>README.txt</include>
+ <include>RELEASE*.txt</include>
+ </includes>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+
+ <!-- Copy over everything that needs to get dos line endings -->
+ <fileSet>
+ <directory>src/main/distribution/text</directory>
+ <outputDirectory>/</outputDirectory>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/distribution/windows-text</directory>
+ <outputDirectory>/</outputDirectory>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+ <fileSet>
+ <directory>target/classes/etc</directory>
+ <outputDirectory>/etc/</outputDirectory>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+
+ <!-- Copy over the examples -->
+ <fileSet>
+ <directory>../demos</directory>
+ <outputDirectory>/demos/</outputDirectory>
+ <lineEnding>dos</lineEnding>
+ <excludes>
+ <exclude>**/target/**</exclude>
+ </excludes>
+ </fileSet>
+
+ <!-- Copy over files that should not get the line endings converted -->
+ <fileSet>
+ <directory>src/main/distribution/binary</directory>
+ <outputDirectory>/</outputDirectory>
+ </fileSet>
+
+ <!-- Copy over the files that should not get the line endings converted but need to be chmod to 755 -->
+ <fileSet>
+ <directory>src/main/distribution/windows-binary</directory>
+ <outputDirectory>/</outputDirectory>
+ </fileSet>
+
+ <!-- Copy over jar files -->
+ <fileSet>
+ <directory>target/dependencies</directory>
+ <outputDirectory>/lib/</outputDirectory>
+ </fileSet>
+
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${basedir}/../etc/config.properties</source>
+ <outputDirectory>/etc/</outputDirectory>
+ <destName>config.properties</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>dos</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/LICENSE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>LICENSE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>dos</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/NOTICE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>NOTICE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>dos</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/classes/features.xml</source>
+ <outputDirectory>/system/org/apache/servicemix/kernel/apache-servicemix-kernel/${version}</outputDirectory>
+ <destName>apache-servicemix-kernel-${version}-features.xml</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>dos</lineEnding>
+ </file>
+ </files>
+
+ <dependencySets>
+
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/felix/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.felix:org.osgi.compendium</include>
+ <include>org.apache.felix:org.apache.felix.configadmin</include>
+ <include>org.apache.felix:org.apache.felix.prefs</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/ops4j/pax/logging/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.ops4j.pax.logging:pax-logging-api</include>
+ <include>org.ops4j.pax.logging:pax-logging-service</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/ops4j/pax/url/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.ops4j.pax.url:pax-url-mvn</include>
+ <include>org.ops4j.pax.url:pax-url-wrap</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/springframework/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.springframework:spring-aop</include>
+ <include>org.springframework:spring-beans</include>
+ <include>org.springframework:spring-context</include>
+ <include>org.springframework:spring-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/springframework/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.springframework.osgi:spring-osgi-core</include>
+ <include>org.springframework.osgi:spring-osgi-extender</include>
+ <include>org.springframework.osgi:spring-osgi-io</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/geronimo/specs/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.geronimo.specs:geronimo-servlet_2.5_spec</include>
+ <include>org.apache.geronimo.specs:geronimo-annotation_1.0_spec</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/bundles/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.aopalliance</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.cglib</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxp-ri</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.oro</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-jexl</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-vfs</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-codec</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-httpclient</include>
+ <include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.jline</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/specs/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.specs:org.apache.servicemix.specs.jaxp-api-1.4</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.filemonitor</include>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.spring</include>
+ <include>org.apache.servicemix.kernel:org.apache.servicemix.kernel.management</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/gshell/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.core</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.admin</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.osgi</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.log</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.features</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.config</include>
+ <include>org.apache.servicemix.kernel.gshell:org.apache.servicemix.kernel.gshell.packages</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/servicemix/kernel/jaas/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.servicemix.kernel.jaas:org.apache.servicemix.kernel.jaas.config</include>
+ <include>org.apache.servicemix.kernel.jaas:org.apache.servicemix.kernel.jaas.modules</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/sshd/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.sshd:sshd-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/apache/mina/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.apache.mina:mina-core</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>org/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>org.osgi:jmx</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/system</outputDirectory>
+ <unpack>false</unpack>
+ <useProjectArtifact>false</useProjectArtifact>
+ <outputFileNameMapping>com/oracle/osgi/${artifact.artifactId}/${artifact.baseVersion}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+ <includes>
+ <include>com.oracle.osgi:jmx-impl</include>
+ </includes>
+ </dependencySet>
+
+ </dependencySets>
+
+</assembly>
diff --git a/karaf/assembly/src/main/descriptors/windows-src.xml b/karaf/assembly/src/main/descriptors/windows-src.xml
new file mode 100644
index 0000000..4d0c6ae
--- /dev/null
+++ b/karaf/assembly/src/main/descriptors/windows-src.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+ <id>src</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <!-- Copy license and other files from root -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>/</outputDirectory>
+ <includes>
+ <include>BUILDING.txt</include>
+ <include>README.txt</include>
+ <include>RELEASE*.txt</include>
+ </includes>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+
+ <!-- Binary Files -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>src</outputDirectory>
+ <includes>
+ <include>**/*.jpeg</include>
+ <include>**/*.jpg</include>
+ <include>**/*.gif</include>
+ <include>**/*.png</include>
+ <include>**/*.exe</include>
+ <include>**/*.dll</include>
+ <include>**/*.jar</include>
+ <include>**/*.so</include>
+ <include>**/*.ks</include>
+ <include>**/*.ts</include>
+ <include>**/*.keystore</include>
+ <include>**/*.bin</include>
+ <include>**/*.jnilib</include>
+ <include>**/*.cert</include>
+ <include>apache-servicemix/src/main/release/bin/*/wrapper</include>
+ </includes>
+ <excludes>
+ <exclude>**/eclipse-classes/**</exclude>
+ <exclude>**/target/**</exclude>
+ </excludes>
+ </fileSet>
+
+ <!-- Text Files -->
+ <fileSet>
+ <directory>${basedir}/..</directory>
+ <outputDirectory>src</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <excludes>
+ <exclude>**/*.jpeg</exclude>
+ <exclude>**/*.jpg</exclude>
+ <exclude>**/*.gif</exclude>
+ <exclude>**/*.png</exclude>
+ <exclude>**/*.exe</exclude>
+ <exclude>**/*.dll</exclude>
+ <exclude>**/*.jar</exclude>
+ <exclude>**/*.so</exclude>
+ <exclude>**/*.ks</exclude>
+ <exclude>**/*.ts</exclude>
+ <exclude>**/*.keystore</exclude>
+ <exclude>**/*.bin</exclude>
+ <exclude>**/*.jnilib</exclude>
+ <exclude>**/*.cert</exclude>
+ <exclude>apache-servicemix/src/main/release/bin/*/wrapper</exclude>
+
+ <exclude>**/target/**</exclude>
+ <exclude>**/build/**</exclude>
+ <exclude>activemq-data/**</exclude>
+ <exclude>*/activemq-data/**</exclude>
+ <exclude>**/eclipse-classes/**</exclude>
+ <exclude>**/.*</exclude>
+ <exclude>**/.*/**</exclude>
+
+ <exclude>**/surefire*</exclude>
+ <exclude>**/svn-commit*</exclude>
+
+ <exclude>**/*.iml</exclude>
+ <exclude>**/*.ipr</exclude>
+ <exclude>**/*.iws</exclude>
+
+ <exclude>**/cobertura.ser</exclude>
+
+ </excludes>
+ <lineEnding>dos</lineEnding>
+ </fileSet>
+ </fileSets>
+ <files>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/LICENSE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>LICENSE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ <file>
+ <source>${basedir}/target/maven-shared-archive-resources/META-INF/NOTICE</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>NOTICE.txt</destName>
+ <fileMode>0644</fileMode>
+ <lineEnding>unix</lineEnding>
+ </file>
+ </files>
+</assembly>
diff --git a/karaf/assembly/src/main/distribution/text/etc/java.util.logging.properties b/karaf/assembly/src/main/distribution/text/etc/java.util.logging.properties
new file mode 100644
index 0000000..b7b50c5
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/java.util.logging.properties
@@ -0,0 +1,23 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Empty java.util.logging.properties to prevent the log to stderr, so that
+# all logs will be delegated to pax logging JUL handler only
+
+
diff --git a/karaf/assembly/src/main/distribution/text/etc/org.apache.servicemix.shell.cfg b/karaf/assembly/src/main/distribution/text/etc/org.apache.servicemix.shell.cfg
new file mode 100644
index 0000000..98bbd57
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/org.apache.servicemix.shell.cfg
@@ -0,0 +1,23 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+sshPort=8101
+sshRealm=servicemix
+hostKey=${servicemix.base}/etc/host.key
diff --git a/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.logging.cfg b/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..24ddea5
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.logging.cfg
@@ -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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, osgi:VmLogAppender
+
+# Logger infos
+log4j.logger.org.apache.geronimo.gshell.remote=WARN
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.url.mvn.cfg b/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.url.mvn.cfg
new file mode 100644
index 0000000..6e50c3e
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/org.ops4j.pax.url.mvn.cfg
@@ -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.
+#
+################################################################################
+
+#
+# If set to true, the following property will not allow any certificate to be used
+# when accessing maven repositories through SSL
+#
+#org.ops4j.pax.url.mvn.certificateCheck=
+
+#
+# Path to the local maven settings file.
+# The repositories defined in this file will be automatically added to the list
+# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
+# below is not set.
+# The following locations are checked for the existence of the settings.xml file
+# * 1. looks for the specified url
+# * 2. if not found looks for ${user.home}/.m2/settings.xml
+# * 3. if not found looks for ${maven.home}/conf/settings.xml
+# * 4. if not found looks for ${M2_HOME}/conf/settings.xml
+#
+#org.ops4j.pax.url.mvn.settings=
+
+#
+# Path to the local maven repository which is used to avoid downloading
+# artifacts when they already exist locally.
+# The value of this property will be extracted from the settings.xml file
+# above, or defaulted to:
+# System.getProperty( "user.home" ) + "/.m2/repository"
+#
+#org.ops4j.pax.url.mvn.localRepository=
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# Those repositories will be checked before iterating through the
+ below list of repositories and even before the local repository
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folder as a repo.
+#
+org.ops4j.pax.url.mvn.defaultRepositories=file:${servicemix.home}/system@snapshots
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# The default list includes the following repositories:
+# http://repo1.maven.org/maven2
+# http://repository.ops4j.org/maven2
+# To add repositories to the default ones, prepend '+' to the list of repositories
+# to add.
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folder as a repo.
+#
+org.ops4j.pax.url.mvn.repositories=http://repo1.maven.org/maven2,http://people.apache.org/repo/m2-snapshot-repository@snapshots@noreleases,http://repository.ops4j.org/maven2,http://svn.apache.org/repos/asf/servicemix/m2-repo
diff --git a/karaf/assembly/src/main/distribution/text/etc/system.properties b/karaf/assembly/src/main/distribution/text/etc/system.properties
new file mode 100644
index 0000000..d82ce7c
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/system.properties
@@ -0,0 +1,22 @@
+################################################################################
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
+servicemix.name=root
+xml.catalog.files=
diff --git a/karaf/assembly/src/main/distribution/text/etc/users.properties b/karaf/assembly/src/main/distribution/text/etc/users.properties
new file mode 100644
index 0000000..0348fae
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/text/etc/users.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+smx=smx,admin
diff --git a/karaf/assembly/src/main/distribution/unix-shell/bin/servicemix b/karaf/assembly/src/main/distribution/unix-shell/bin/servicemix
new file mode 100755
index 0000000..6f5d30d
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/unix-shell/bin/servicemix
@@ -0,0 +1,292 @@
+#!/bin/sh
+#
+# 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.
+#
+# $Id: servicemix 979 2005-11-30 22:50:55Z bsnyder $
+#
+
+DIRNAME=`dirname $0`
+PROGNAME=`basename $0`
+
+#
+# Check/Set up some easily accessible MIN/MAX params for JVM mem usage
+#
+
+if [ "x$JAVA_MIN_MEM" = "x" ]; then
+ JAVA_MIN_MEM=128M
+ export JAVA_MIN_MEM
+fi
+
+if [ "x$JAVA_MAX_MEM" = "x" ]; then
+ JAVA_MAX_MEM=512M
+ export JAVA_MAX_MEM
+fi
+
+warn() {
+ echo "${PROGNAME}: $*"
+}
+
+die() {
+ warn "$*"
+ exit 1
+}
+
+maybeSource() {
+ file="$1"
+ if [ -f "$file" ] ; then
+ . $file
+ fi
+}
+
+detectOS() {
+ # OS specific support (must be 'true' or 'false').
+ cygwin=false;
+ darwin=false;
+ aix=false;
+ os400=false;
+ case "`uname`" in
+ CYGWIN*)
+ cygwin=true
+ ;;
+ Darwin*)
+ darwin=true
+ ;;
+ AIX*)
+ aix=true
+ ;;
+ OS400*)
+ os400=true
+ ;;
+ esac
+ # For AIX, set an environment variable
+ if $aix; then
+ export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+ export IBM_JAVA_HEAPDUMP_TEXT=true
+ echo $LDR_CNTRL
+ fi
+}
+
+unlimitFD() {
+ # Use the maximum available, or set MAX_FD != -1 to use that
+ if [ "x$MAX_FD" = "x" ]; then
+ MAX_FD="maximum"
+ fi
+
+ # Increase the maximum file descriptors if we can
+ if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then
+ if [ $? -eq 0 ]; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+ # use the system max
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+
+ ulimit -n $MAX_FD > /dev/null
+ # echo "ulimit -n" `ulimit -n`
+ if [ $? -ne 0 ]; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+ fi
+ fi
+}
+
+locateHome() {
+ if [ "x$SERVICEMIX_HOME" != "x" ]; then
+ warn "Ignoring predefined value for SERVICEMIX_HOME"
+ fi
+
+ SERVICEMIX_HOME=`cd $DIRNAME/..; pwd`
+ if [ ! -d "$SERVICEMIX_HOME" ]; then
+ die "SERVICEMIX_HOME is not valid: $SERVICEMIX_HOME"
+ fi
+}
+
+locateBase() {
+ if [ "x$SERVICEMIX_BASE" != "x" ]; then
+ if [ ! -d "$SERVICEMIX_BASE" ]; then
+ die "SERVICEMIX_BASE is not valid: $SERVICEMIX_BASE"
+ fi
+ else
+ SERVICEMIX_BASE=$SERVICEMIX_HOME
+ fi
+}
+
+setupNativePath() {
+ # Support for loading native libraries
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$SERVICEMIX_BASE/lib:$SERVICEMIX_HOME/lib"
+
+ # For Cygwin, set PATH from LD_LIBRARY_PATH
+ if $cygwin; then
+ LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"`
+ PATH="$PATH;$LD_LIBRARY_PATH"
+ export PATH
+ fi
+ export LD_LIBRARY_PATH
+}
+
+locateJava() {
+ # Setup the Java Virtual Machine
+ if $cygwin ; then
+ [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"`
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ fi
+
+ if [ "x$JAVA" = "x" ]; then
+ if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then
+ JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
+ fi
+ if [ "x$JAVA_HOME" != "x" ]; then
+ if [ ! -d "$JAVA_HOME" ]; then
+ die "JAVA_HOME is not valid: $JAVA_HOME"
+ fi
+ JAVA="$JAVA_HOME/bin/java"
+ else
+ warn "JAVA_HOME not set; results may vary"
+ JAVA="java"
+ fi
+ fi
+}
+
+detectJVM() {
+ #echo "`$JAVA -version`"
+ # This service should call `java -version`,
+ # read stdout, and look for hints
+ if $JAVA -version 2>&1 | grep "^IBM" ; then
+ JVM_VENDOR="IBM"
+ # on OS/400, java -version does not contain IBM explicitly
+ elif $os400; then
+ JVM_VENDOR="IBM"
+ else
+ JVM_VENDOR="SUN"
+ fi
+ # echo "JVM vendor is $JVM_VENDOR"
+}
+
+setupDebugOptions() {
+ if [ "x$JAVA_OPTS" = "x" ]; then
+ JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ fi
+ export JAVA_OPTS
+
+ # Set Debug options if enabled
+ if [ "x$SERVICEMIX_DEBUG" != "x" ]; then
+ # Use the defaults if JAVA_DEBUG_OPTS was not set
+ if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then
+ JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS"
+ fi
+
+ JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS"
+ warn "Enabling Java debug options: $JAVA_DEBUG_OPTS"
+ fi
+}
+
+setupDefaults() {
+ DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM "
+
+ #Set the JVM_VENDOR specific JVM flags
+ if [ "$JVM_VENDOR" = "SUN" ]; then
+ DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS -Dcom.sun.management.jmxremote"
+ elif [ "$JVM_VENDOR" = "IBM" ]; then
+ if $os400; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ elif $aix; then
+ DEFAULT_JAVA_OPTS="-Xverify:none -Xlp $DEFAULT_JAVA_OPTS"
+ else
+ DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS"
+ fi
+ fi
+
+ # Add the jars in the lib dir
+ for file in $SERVICEMIX_HOME/lib/*.jar
+ do
+ if [ -z "$CLASSPATH" ]; then
+ CLASSPATH="$file"
+ else
+ CLASSPATH="$CLASSPATH:$file"
+ fi
+ done
+ DEFAULT_JAVA_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
+
+ ##
+ ## TODO: Move to conf/profiler/yourkit.{sh|cmd}
+ ##
+ # Uncomment to enable YourKit profiling
+ #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent"
+}
+
+init() {
+ # Determine if there is special OS handling we must perform
+ detectOS
+
+ # Unlimit the number of file descriptors if possible
+ unlimitFD
+
+ # Locate the ServiceMix home directory
+ locateHome
+
+ # Locate the ServiceMix base directory
+ locateBase
+
+ # Setup the native library path
+ setupNativePath
+
+ # Locate the Java VM to execute
+ locateJava
+
+ # Determine the JVM vendor
+ detectJVM
+
+ # Setup default options
+ setupDefaults
+
+ # Install debug options
+ setupDebugOptions
+
+}
+
+run() {
+ OPTS="-Dservicemix.startLocalConsole=true -Dservicemix.startRemoteShell=true"
+ case "$1" in
+ 'console')
+ shift
+ ;;
+ 'server')
+ OPTS="-Dservicemix.startLocalConsole=false -Dservicemix.startRemoteShell=true"
+ shift
+ ;;
+ 'client')
+ OPTS="-Dservicemix.startLocalConsole=true -Dservicemix.startRemoteShell=false"
+ shift
+ ;;
+ esac
+
+ if $cygwin; then
+ SERVICEMIX_HOME=`cygpath --path --windows "$SERVICEMIX_HOME"`
+ SERVICEMIX_BASE=`cygpath --path --windows "$SERVICEMIX_BASE"`
+ fi
+ exec $JAVA $JAVA_OPTS -Dservicemix.home="$SERVICEMIX_HOME" -Dservicemix.base="$SERVICEMIX_BASE" -Djava.util.logging.config.file=$SERVICEMIX_BASE/etc/java.util.logging.properties $OPTS -classpath "$CLASSPATH" org.apache.servicemix.kernel.main.Main $*
+}
+
+main() {
+ init
+ run $@
+}
+
+main $@
diff --git a/karaf/assembly/src/main/distribution/windows-text/bin/servicemix.bat b/karaf/assembly/src/main/distribution/windows-text/bin/servicemix.bat
new file mode 100755
index 0000000..f7aa2d1
--- /dev/null
+++ b/karaf/assembly/src/main/distribution/windows-text/bin/servicemix.bat
@@ -0,0 +1,166 @@
+@echo off
+rem
+rem
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+rem
+rem
+rem $Id: servicemix.bat 979 2005-11-30 22:50:55Z bsnyder $
+rem
+
+if not "%ECHO%" == "" echo %ECHO%
+
+setlocal
+set DIRNAME=%~dp0%
+set PROGNAME=%~nx0%
+set ARGS=%*
+
+title ServiceMix
+
+goto BEGIN
+
+:warn
+ echo %PROGNAME%: %*
+goto :EOF
+
+:BEGIN
+
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+if not "%SERVICEMIX_HOME%" == "" (
+ call :warn Ignoring predefined value for SERVICEMIX_HOME
+)
+set SERVICEMIX_HOME=%DIRNAME%..
+if not exist "%SERVICEMIX_HOME%" (
+ call :warn SERVICEMIX_HOME is not valid: %SERVICEMIX_HOME%
+ goto END
+)
+
+if not "%SERVICEMIX_BASE%" == "" (
+ if not exist "%SERVICEMIX_BASE%" (
+ call :warn SERVICEMIX_BASE is not valid: %SERVICEMIX_BASE%
+ goto END
+ )
+)
+if "%SERVICEMIX_BASE%" == "" (
+ set SERVICEMIX_BASE=%SERVICEMIX_HOME%
+)
+
+set LOCAL_CLASSPATH=%CLASSPATH%
+set DEFAULT_JAVA_OPTS=-server -Xmx512M -Dderby.system.home="%SERVICEMIX_BASE%\data\derby" -Dderby.storage.fileSyncTransactionLog=true -Dcom.sun.management.jmxremote
+set CLASSPATH=%LOCAL_CLASSPATH%;%SERVICEMIX_BASE%\conf
+set DEFAULT_JAVA_DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+
+if "%LOCAL_CLASSPATH%" == "" goto :SERVICEMIX_CLASSPATH_EMPTY
+ set CLASSPATH=%LOCAL_CLASSPATH%;%SERVICEMIX_BASE%\conf
+ goto :SERVICEMIX_CLASSPATH_END
+:SERVICEMIX_CLASSPATH_EMPTY
+ set CLASSPATH=%SERVICEMIX_BASE%\conf
+:SERVICEMIX_CLASSPATH_END
+
+rem Setup Servicemix Home
+if exist "%SERVICEMIX_HOME%\conf\servicemix-rc.cmd" call %SERVICEMIX_HOME%\conf\servicemix-rc.cmd
+if exist "%HOME%\servicemix-rc.cmd" call %HOME%\servicemix-rc.cmd
+
+rem Support for loading native libraries
+set PATH=%PATH%;%SERVICEMIX_BASE%\lib;%SERVICEMIX_HOME%\lib
+
+rem Setup the Java Virtual Machine
+if not "%JAVA%" == "" goto :Check_JAVA_END
+ set JAVA=java
+ if "%JAVA_HOME%" == "" call :warn JAVA_HOME not set; results may vary
+ if not "%JAVA_HOME%" == "" set JAVA=%JAVA_HOME%\bin\java
+ if not exist "%JAVA_HOME%" (
+ call :warn JAVA_HOME is not valid: "%JAVA_HOME%"
+ goto END
+ )
+:Check_JAVA_END
+
+if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS%
+
+if "%SERVICEMIX_DEBUG%" == "" goto :SERVICEMIX_DEBUG_END
+ rem Use the defaults if JAVA_DEBUG_OPTS was not set
+ if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS%
+
+ set "JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS%"
+ call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS%
+:SERVICEMIX_DEBUG_END
+
+if "%SERVICEMIX_PROFILER%" == "" goto :SERVICEMIX_PROFILER_END
+ set SERVICEMIX_PROFILER_SCRIPT=%SERVICEMIX_HOME%\conf\profiler\%SERVICEMIX_PROFILER%.cmd
+
+ if exist "%SERVICEMIX_PROFILER_SCRIPT%" goto :SERVICEMIX_PROFILER_END
+ call :warn Missing configuration for profiler '%SERVICEMIX_PROFILER%': %SERVICEMIX_PROFILER_SCRIPT%
+ goto END
+:SERVICEMIX_PROFILER_END
+
+rem Setup the classpath
+pushd "%SERVICEMIX_HOME%\lib"
+for %%G in (*.*) do call:APPEND_TO_CLASSPATH %%G
+popd
+goto CLASSPATH_END
+
+: APPEND_TO_CLASSPATH
+set filename=%~1
+set suffix=%filename:~-4%
+if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%SERVICEMIX_HOME%\lib\%filename%
+goto :EOF
+
+:CLASSPATH_END
+
+rem Execute the JVM or the load the profiler
+if "%SERVICEMIX_PROFILER%" == "" goto :RUN
+ rem Execute the profiler if it has been configured
+ call :warn Loading profiler script: %SERVICEMIX_PROFILER_SCRIPT%
+ call %SERVICEMIX_PROFILER_SCRIPT%
+
+:RUN
+ SET OPTS=-Dservicemix.startLocalConsole=true -Dservicemix.startRemoteShell=true
+ SET SHIFT=false
+ if "%1" == "console" goto :EXECUTE_CONSOLE
+ if "%1" == "server" goto :EXECUTE_SERVER
+ if "%1" == "client" goto :EXECUTE_CLIENT
+ goto :EXECUTE
+
+:EXECUTE_CONSOLE
+ SET SHIFT=true
+ goto :EXECUTE
+
+:EXECUTE_SERVER
+ SET OPTS="-Dservicemix.startLocalConsole=false -Dservicemix.startRemoteShell=true"
+ SET SHIFT=true
+ goto :EXECUTE
+
+:EXECUTE_CLIENT
+ SET OPTS="-Dservicemix.startLocalConsole=true -Dservicemix.startRemoteShell=false"
+ SET SHIFT=true
+ goto :EXECUTE
+
+:EXECUTE
+ if "%SHIFT%" == "true" SET ARGS=%2 %3 %4 %5 %6 %7 %8
+ if not "%SHIFT%" == "true" SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8
+ rem Execute the Java Virtual Machine
+ "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Dservicemix.home="%SERVICEMIX_HOME%" -Dservicemix.base="%SERVICEMIX_BASE%" -Djava.util.logging.config.file=%SERVICEMIX_BASE%\etc\java.util.logging.properties org.apache.servicemix.kernel.main.Main %ARGS%
+
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+:END
+
+endlocal
+
+if not "%PAUSE%" == "" pause
+
+:END_NO_PAUSE
+
diff --git a/karaf/assembly/src/main/filtered-resources/etc/org.apache.servicemix.features.cfg b/karaf/assembly/src/main/filtered-resources/etc/org.apache.servicemix.features.cfg
new file mode 100644
index 0000000..2e22aec
--- /dev/null
+++ b/karaf/assembly/src/main/filtered-resources/etc/org.apache.servicemix.features.cfg
@@ -0,0 +1,28 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+# Comma separated list of features repositories to register by default
+#
+featuresRepositories=mvn:org.apache.servicemix.kernel/apache-servicemix-kernel/${version}/xml/features
+
+#
+# Comma separated list of features to install at startup
+#
+featuresBoot=
diff --git a/karaf/assembly/src/main/filtered-resources/etc/startup.properties b/karaf/assembly/src/main/filtered-resources/etc/startup.properties
new file mode 100644
index 0000000..06519d6
--- /dev/null
+++ b/karaf/assembly/src/main/filtered-resources/etc/startup.properties
@@ -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.
+#
+################################################################################
+
+# This file allows you to control the start level of each bundle.
+#
+
+#
+# Startup core services like logging
+#
+org/ops4j/pax/url/pax-url-mvn/${pax.url.version}/pax-url-mvn-${pax.url.version}.jar=5
+org/ops4j/pax/url/pax-url-wrap/${pax.url.version}/pax-url-wrap-${pax.url.version}.jar=5
+org/ops4j/pax/logging/pax-logging-api/${pax.logging.version}/pax-logging-api-${pax.logging.version}.jar=8
+org/ops4j/pax/logging/pax-logging-service/${pax.logging.version}/pax-logging-service-${pax.logging.version}.jar=8
+org/apache/geronimo/specs/geronimo-servlet_2.5_spec/${geronimo.servlet.version}/geronimo-servlet_2.5_spec-${geronimo.servlet.version}.jar=10
+org/apache/servicemix/specs/org.apache.servicemix.specs.jaxp-api-1.4/${servicemix.specs.version}/org.apache.servicemix.specs.jaxp-api-1.4-${servicemix.specs.version}.jar=10
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxp-ri/${jaxp.ri.version}/org.apache.servicemix.bundles.jaxp-ri-${jaxp.ri.version}.jar=10
+org/apache/felix/org.osgi.compendium/${felix.compendium.version}/org.osgi.compendium-${felix.compendium.version}.jar=10
+org/apache/felix/org.apache.felix.configadmin/${felix.configadmin.version}/org.apache.felix.configadmin-${felix.configadmin.version}.jar=10
+org/apache/geronimo/specs/geronimo-annotation_1.0_spec/${geronimo.annotation.version}/geronimo-annotation_1.0_spec-${geronimo.annotation.version}.jar=10
+org/apache/felix/org.apache.felix.prefs/${felix.prefs.version}/org.apache.felix.prefs-${felix.prefs.version}.jar=10
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.filemonitor/${pom.version}/org.apache.servicemix.kernel.filemonitor-${pom.version}.jar=15
+
+#
+# The rest of the services..
+#
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jline/${jline.version}/org.apache.servicemix.bundles.jline-${jline.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.version}/org.apache.servicemix.bundles.aopalliance-${aopalliance.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.cglib/${cglib.version}/org.apache.servicemix.bundles.cglib-${cglib.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.oro/${oro.version}/org.apache.servicemix.bundles.oro-${oro.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-codec/${commons.codec.version}/org.apache.servicemix.bundles.commons-codec-${commons.codec.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-httpclient/${commons.httpclient.version}/org.apache.servicemix.bundles.commons-httpclient-${commons.httpclient.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-jexl/${commons.jexl.version}/org.apache.servicemix.bundles.commons-jexl-${commons.jexl.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-vfs/${commons.vfs.version}/org.apache.servicemix.bundles.commons-vfs-${commons.vfs.version}.jar=30
+org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar=30
+org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar=30
+org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar=30
+org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar=30
+org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.spring/${pom.version}/org.apache.servicemix.kernel.spring-${pom.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.management/${pom.version}/org.apache.servicemix.kernel.management-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.admin/${pom.version}/org.apache.servicemix.kernel.gshell.admin-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.osgi/${pom.version}/org.apache.servicemix.kernel.gshell.osgi-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.features/${pom.version}/org.apache.servicemix.kernel.gshell.features-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.log/${pom.version}/org.apache.servicemix.kernel.gshell.log-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.config/${pom.version}/org.apache.servicemix.kernel.gshell.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.packages/${pom.version}/org.apache.servicemix.kernel.gshell.packages-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.config/${pom.version}/org.apache.servicemix.kernel.jaas.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.modules/${pom.version}/org.apache.servicemix.kernel.jaas.modules-${pom.version}.jar=30
+org/apache/mina/mina-core/${mina.version}/mina-core-${mina.version}.jar=30
+org/apache/sshd/sshd-core/${sshd.version}/sshd-core-${sshd.version}.jar=30
+org/osgi/jmx/${osgi.jmx.version}/jmx-${osgi.jmx.version}.jar=30
+com/oracle/osgi/jmx-impl/${osgi.jmx.version}/jmx-impl-${osgi.jmx.version}.jar=30
+
+#
+# Start console last
+#
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.core/${pom.version}/org.apache.servicemix.kernel.gshell.core-${pom.version}.jar=40
+
diff --git a/karaf/assembly/src/main/filtered-resources/features.xml b/karaf/assembly/src/main/filtered-resources/features.xml
new file mode 100644
index 0000000..8316b3f
--- /dev/null
+++ b/karaf/assembly/src/main/filtered-resources/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<features>
+ <feature name="wrapper" version="${version}">
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.wrapper/${version}</bundle>
+ </feature>
+ <feature name="obr" version="${version}">
+ <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/${felix.bundlerepository.version}</bundle>
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.obr/${version}</bundle>
+ </feature>
+</features>
diff --git a/karaf/client/pom.xml b/karaf/client/pom.xml
new file mode 100644
index 0000000..13534f1
--- /dev/null
+++ b/karaf/client/pom.xml
@@ -0,0 +1,136 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.client</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Client</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-nop</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <_donotcopy>(CVS|.svn|config.properties)</_donotcopy>
+ <Main-Class>org.apache.servicemix.kernel.client.Main</Main-Class>
+ <Class-Path>
+ ../system/com/google/code/sshd/sshd/${sshd.version}/sshd-${sshd.version}.jar
+ ../system/org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.core/${pom.version}/org.apache.servicemix.kernel.gshell.core-${pom.version}.jar
+ ../system/org/apache/servicemix/bundles/org.apache.servicemix.bundles.mina/${mina.version}/org.apache.servicemix.bundles.mina-${mina.version}.jar
+ ../system/org/apache/servicemix/bundles/org.apache.servicemix.bundles.jline/${jline.version}/org.apache.servicemix.bundles.jline-${jline.version}.jar
+ </Class-Path>
+ <Bundle-Name>Apache ServiceMix Shell Client</Bundle-Name>
+ <Bundle-Description>Shell client bundle for Apache ServiceMix Kernel.</Bundle-Description>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.client</Export-Package>
+ <Private-Package>
+ org.apache.servicemix.kernel.client;-split-package:=merge-first,
+ org.slf4j;-split-package:=merge-first,
+ org.slf4j.spi;-split-package:=merge-first,
+ org.slf4j.helpers;-split-package:=merge-first,
+ org.slf4j.impl;-split-package:=merge-first,
+ META-INF;-split-package:=merge-first
+ </Private-Package>
+ <Import-Package>!*</Import-Package>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>${project.groupId}:${project.artifactId}</include>
+ <include>org.slf4j:slf4j-api</include>
+ <include>org.slf4j:slf4j-nop</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+ <artifact>org.slf4j:slf4j-api</artifact>
+ <excludes>
+ <exclude>org/slf4j/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.slf4j:slf4j-nop</artifact>
+ <excludes>
+ <exclude>org/slf4j/**</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <createSourcesJar>${createSourcesJar}</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>true</createDependencyReducedPom>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+
+
+</project>
diff --git a/karaf/client/src/main/java/org/apache/servicemix/kernel/client/Main.java b/karaf/client/src/main/java/org/apache/servicemix/kernel/client/Main.java
new file mode 100644
index 0000000..886acaf
--- /dev/null
+++ b/karaf/client/src/main/java/org/apache/servicemix/kernel/client/Main.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.servicemix.kernel.client;
+
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+
+import jline.ConsoleReader;
+
+/**
+ * A very simple
+ */
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ String host = "localhost";
+ int port = 8101;
+ String user = "smx";
+ String password = "smx";
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].charAt(0) == '-') {
+ if (args[i].equals("-a")) {
+ port = Integer.parseInt(args[++i]);
+ } else if (args[i].equals("-h")) {
+ host = args[++i];
+ } else if (args[i].equals("-u")) {
+ user = args[++i];
+ } else if (args[i].equals("-p")) {
+ password = args[++i];
+ } else if (args[i].equals("--help")) {
+ System.out.println("Apache ServiceMix Kernel client");
+ System.out.println(" -a [port] specify the port to connect to");
+ System.out.println(" -h [host] specify the host to connect to");
+ System.out.println(" -u [user] specify the user name");
+ System.out.println(" -p [password] specify the password");
+ System.out.println(" --help shows this help message");
+ System.out.println(" [commands] commands to run");
+ System.out.println("If no commands are specified, the client will be put in an interactive mode");
+ System.exit(0);
+ } else {
+ System.err.println("Unknown option: " + args[i]);
+ System.err.println("Run with --help for usage");
+ System.exit(1);
+ }
+ } else {
+ sb.append(args[i]);
+ sb.append(' ');
+ }
+ }
+
+ // TODO: implement sending a direct command
+
+ SshClient client = null;
+ try {
+ client = SshClient.setUpDefaultClient();
+ client.start();
+ ConnectFuture future = client.connect(host, port);
+ future.await();
+ ClientSession session = future.getSession();
+ session.authPassword(user, password);
+ ClientChannel channel = session.createChannel("shell");
+ channel.setIn(new ConsoleReader().getInput());
+ channel.setOut(System.out);
+ channel.setErr(System.err);
+ channel.open();
+ channel.waitFor(ClientChannel.CLOSED, 0);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.exit(1);
+ } finally {
+ try {
+ client.stop();
+ } catch (Throwable t) { }
+ }
+ System.exit(0);
+ }
+
+}
diff --git a/karaf/client/src/main/resources/client.login.conf b/karaf/client/src/main/resources/client.login.conf
new file mode 100644
index 0000000..dc2b0ba
--- /dev/null
+++ b/karaf/client/src/main/resources/client.login.conf
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+//
+// $Rev: 580717 $ $Date: 2007-09-30 14:47:55 +0200 (Sun, 30 Sep 2007) $
+//
+
+RshClient {
+ org.apache.geronimo.gshell.remote.client.auth.RemoteLoginModule required;
+};
diff --git a/karaf/demos/pom.xml b/karaf/demos/pom.xml
new file mode 100644
index 0000000..9665c6c
--- /dev/null
+++ b/karaf/demos/pom.xml
@@ -0,0 +1,40 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.demos</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Demos</name>
+
+ <modules>
+ <module>smx4web</module>
+ </modules>
+
+</project>
diff --git a/karaf/demos/smx4web/README.txt b/karaf/demos/smx4web/README.txt
new file mode 100644
index 0000000..a41aad9
--- /dev/null
+++ b/karaf/demos/smx4web/README.txt
@@ -0,0 +1,28 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+This is an example showing how to embed ServiceMix Kernel in a web application.
+You can either:
+ * build the web app using "mvn package" and deploy the web application to your favorite web container
+ * run the web app from the command line using the "mvn package jetty:run" command
+
+Once the web application is started, you can use the Apache ServiceMix Kernel client to connect to the running server:
+ cd [smx-kernel-install-dir]
+ java -jar lib/servicemix-client.jar
+
diff --git a/karaf/demos/smx4web/pom.xml b/karaf/demos/smx4web/pom.xml
new file mode 100644
index 0000000..ae8a45a
--- /dev/null
+++ b/karaf/demos/smx4web/pom.xml
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+
+<!--
+
+ 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.
+-->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.demos</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.demos</groupId>
+ <artifactId>smx4web</artifactId>
+ <packaging>war</packaging>
+ <name>Apache ServiceMix Kernel :: Demos :: smx4web</name>
+
+ <properties>
+ <jetty.port>8080</jetty.port>
+ <jetty.version>6.1.12rc1</jetty.version>
+ <geronimo.servlet.version>1.1.2</geronimo.servlet.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>apache-servicemix-kernel</artifactId>
+ <type>zip</type>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.client</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.spring</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.keystore</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-io</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.prefs</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.mina</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.ant</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-mvn</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <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.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.1-beta-1</version>
+ <configuration>
+ <warSourceDirectory>src/main/webapp/</warSourceDirectory>
+ <webResources>
+ <resource>
+ <directory>target/kernel</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>maven-jetty-plugin</artifactId>
+ <version>${jetty.version}</version>
+ <configuration>
+ <connectors>
+ <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
+ <port>${jetty.port}</port>
+ <maxIdleTime>60000</maxIdleTime>
+ </connector>
+ </connectors>
+ <systemProperties>
+ <!-- enable easy connection to JConsole -->
+ <systemProperty>
+ <name>com.sun.management.jmxremote</name>
+ <value></value>
+ </systemProperty>
+ </systemProperties>
+ <scanIntervalSeconds>10</scanIntervalSeconds>
+ <webAppSourceDirectory>${project.build.directory}/${project.build.finalName}</webAppSourceDirectory>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.0</version>
+ <executions>
+ <execution>
+ <id>unpack-unix</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>apache-servicemix-kernel</artifactId>
+ <type>zip</type>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/kernel/WEB-INF</outputDirectory>
+ <excludes>**/lib/*,**/*.txt,**/bin/*,**/demos/**,**/lib,**/bin,**/demos</excludes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.3</version>
+ <executions>
+ <execution>
+ <id>copy-kernel</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <move todir="${project.build.directory}/kernel/WEB-INF/servicemix">
+ <fileset dir="${project.build.directory}/kernel/WEB-INF/apache-servicemix-kernel-${pom.version}"/>
+ </move>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/karaf/demos/smx4web/src/main/java/org/apache/servicemix/smx4webex/WebAppListener.java b/karaf/demos/smx4web/src/main/java/org/apache/servicemix/smx4webex/WebAppListener.java
new file mode 100644
index 0000000..11e9bbb
--- /dev/null
+++ b/karaf/demos/smx4web/src/main/java/org/apache/servicemix/smx4webex/WebAppListener.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.servicemix.smx4webex;
+
+import java.io.File;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.servicemix.kernel.main.Main;
+
+public class WebAppListener implements ServletContextListener {
+
+ private Main main;
+
+ public void contextInitialized(ServletContextEvent sce) {
+ try {
+ System.err.println("contextInitialized");
+ String root = new File(sce.getServletContext().getRealPath("/") + "WEB-INF/servicemix").getAbsolutePath();
+ System.err.println("Root: " + root);
+ System.setProperty("servicemix.home", root);
+ System.setProperty("servicemix.base", root);
+ System.setProperty("servicemix.startLocalConsole", "false");
+ System.setProperty("servicemix.startRemoteShell", "true");
+ main = new Main(new String[0]);
+ main.launch();
+ } catch (Exception e) {
+ main = null;
+ e.printStackTrace();
+ }
+ }
+
+ public void contextDestroyed(ServletContextEvent sce) {
+ try {
+ System.err.println("contextDestroyed");
+ if (main != null) {
+ main.destroy(false);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/karaf/demos/smx4web/src/main/webapp/WEB-INF/web.xml b/karaf/demos/smx4web/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..30f7072
--- /dev/null
+++ b/karaf/demos/smx4web/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+ http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <description>ServiceMix 4 Embedded Example</description>
+ <display-name>ServiceMix 4 Embedded Example</display-name>
+
+ <listener>
+ <listener-class>org.apache.servicemix.smx4webex.WebAppListener</listener-class>
+ </listener>
+
+ <welcome-file-list>
+ <welcome-file>index.jsp</welcome-file>
+ </welcome-file-list>
+
+</web-app>
diff --git a/karaf/demos/smx4web/src/main/webapp/index.jsp b/karaf/demos/smx4web/src/main/webapp/index.jsp
new file mode 100644
index 0000000..3734ee9
--- /dev/null
+++ b/karaf/demos/smx4web/src/main/webapp/index.jsp
@@ -0,0 +1,34 @@
+<%--
+ 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.
+--%>
+<html>
+<head>
+<title>Apache ServiceMix Kernel</title>
+</head>
+<body>
+
+<h2>Welcome!</h2>
+
+<p>
+Welcome to Apache ServiceMix Kernel web application demo
+</p>
+
+<p>
+You can find more information about ServiceMix Kernel on the <a href="http://servicemix.apache.org/kernel">Apache ServiceMix Kernel Site</a>
+</p>
+</body>
+</html>
+
diff --git a/karaf/etc/config.properties b/karaf/etc/config.properties
new file mode 100644
index 0000000..3146515
--- /dev/null
+++ b/karaf/etc/config.properties
@@ -0,0 +1,281 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+# Framework config properties.
+#
+org.osgi.framework.system.packages=org.osgi.framework; version=1.4.0, \
+ org.osgi.framework.hooks.service; version=1.4.0, \
+ org.osgi.service.packageadmin; version=1.2.0, \
+ org.osgi.service.startlevel; version=1.1.0, \
+ org.osgi.service.url; version=1.0.0, \
+ org.apache.servicemix.kernel.main.spi; version=1.0.0, \
+ org.apache.servicemix.kernel.jaas.boot, \
+ org.osgi.util.tracker; version=1.3.3, \
+ org.apache.servicemix.kernel.version, \
+ ${jre-${java.specification.version}}
+org.osgi.framework.bootdelegation=sun.*,com.sun.management*
+
+# To enable the use of the startup.properties file to control the start level:
+felix.auto.start=startup.properties
+#felix.auto.start=all
+
+org.osgi.framework.startlevel=100
+felix.startlevel.bundle=60
+#framework.service.urlhandlers=false
+
+# Only works on Felix 1.4+
+felix.fragment.validation=warning
+# felix.log.level=1
+
+#
+# Bundle config properties.
+#
+obr.repository.url=http://svn.apache.org/repos/asf/servicemix/smx4/obr-repo/repository.xml
+
+#
+# FileMonitor properties
+#
+org.apache.servicemix.filemonitor.configDir = ${servicemix.base}/etc
+org.apache.servicemix.filemonitor.monitorDir = ${servicemix.base}/deploy
+org.apache.servicemix.filemonitor.generatedJarDir = ${servicemix.base}/data/generated-bundles
+org.apache.servicemix.filemonitor.scanInterval = 500
+
+
+#
+# Java platform package export properties.
+#
+jre-1.5= \
+ javax.accessibility; \
+ javax.activity; \
+ javax.crypto; \
+ javax.crypto.interfaces; \
+ javax.crypto.spec; \
+ javax.imageio; \
+ javax.imageio.event; \
+ javax.imageio.metadata; \
+ javax.imageio.plugins.bmp; \
+ javax.imageio.plugins.jpeg; \
+ javax.imageio.spi; \
+ javax.imageio.stream; \
+ javax.management; \
+ javax.management.loading; \
+ javax.management.modelmbean; \
+ javax.management.monitor; \
+ javax.management.openmbean; \
+ javax.management.relation; \
+ javax.management.remote; \
+ javax.management.remote.rmi; \
+ javax.management.timer; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.net; \
+ javax.net.ssl; \
+ javax.print; \
+ javax.print.attribute; \
+ javax.print.attribute.standard; \
+ javax.print.event; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.rmi.ssl; \
+ javax.security.auth; \
+ javax.security.auth.callback; \
+ javax.security.auth.kerberos; \
+ javax.security.auth.login; \
+ javax.security.auth.spi; \
+ javax.security.auth.x500; \
+ javax.security.cert; \
+ javax.security.sasl; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.sql; \
+ javax.sql.rowset; \
+ javax.sql.rowset.serial; \
+ javax.sql.rowset.spi; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.multi; \
+ javax.swing.plaf.synth; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ org.ietf.jgss; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextExtPackage; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.Dynamic; \
+ org.omg.DynamicAny; \
+ org.omg.DynamicAny.DynAnyFactoryPackage; \
+ org.omg.DynamicAny.DynAnyPackage; \
+ org.omg.IOP; \
+ org.omg.IOP.CodecFactoryPackage; \
+ org.omg.IOP.CodecPackage; \
+ org.omg.Messaging; \
+ org.omg.PortableInterceptor; \
+ org.omg.PortableInterceptor.ORBInitInfoPackage; \
+ org.omg.PortableServer; \
+ org.omg.PortableServer.CurrentPackage; \
+ org.omg.PortableServer.POAManagerPackage; \
+ org.omg.PortableServer.POAPackage; \
+ org.omg.PortableServer.portable; \
+ org.omg.PortableServer.ServantLocatorPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ org.omg.stub.javax.management.remote.rmi; \
+ sun.misc; \
+ sun.reflect; \
+ version="1.5.0"
+
+jre-1.6= \
+ javax.accessibility; \
+ javax.activation; \
+ javax.activity; \
+ javax.annotation; \
+ javax.annotation.processing; \
+ javax.crypto; \
+ javax.crypto.interfaces; \
+ javax.crypto.spec; \
+ javax.imageio; \
+ javax.imageio.event; \
+ javax.imageio.metadata; \
+ javax.imageio.plugins.bmp; \
+ javax.imageio.plugins.jpeg; \
+ javax.imageio.spi; \
+ javax.imageio.stream; \
+ javax.jws; \
+ javax.jws.soap; \
+ javax.lang; \
+ javax.lang.model; \
+ javax.lang.model.element; \
+ javax.lang.model.type; \
+ javax.lang.model.util; \
+ javax.management; \
+ javax.management.loading; \
+ javax.management.modelmbean; \
+ javax.management.monitor; \
+ javax.management.openmbean; \
+ javax.management.relation; \
+ javax.management.remote; \
+ javax.management.remote.rmi; \
+ javax.management.timer; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.net; \
+ javax.net.ssl; \
+ javax.print; \
+ javax.print.attribute; \
+ javax.print.attribute.standard; \
+ javax.print.event; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.rmi.ssl; \
+ javax.script; \
+ javax.security.auth; \
+ javax.security.auth.callback; \
+ javax.security.auth.kerberos; \
+ javax.security.auth.login; \
+ javax.security.auth.spi; \
+ javax.security.auth.x500; \
+ javax.security.cert; \
+ javax.security.sasl; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.sql; \
+ javax.sql.rowset; \
+ javax.sql.rowset.serial; \
+ javax.sql.rowset.spi; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.multi; \
+ javax.swing.plaf.synth; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ javax.tools; \
+ org.ietf.jgss; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextExtPackage; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.Dynamic; \
+ org.omg.DynamicAny; \
+ org.omg.DynamicAny.DynAnyFactoryPackage; \
+ org.omg.DynamicAny.DynAnyPackage; \
+ org.omg.IOP; \
+ org.omg.IOP.CodecFactoryPackage; \
+ org.omg.IOP.CodecPackage; \
+ org.omg.Messaging; \
+ org.omg.PortableInterceptor; \
+ org.omg.PortableInterceptor.ORBInitInfoPackage; \
+ org.omg.PortableServer; \
+ org.omg.PortableServer.CurrentPackage; \
+ org.omg.PortableServer.POAManagerPackage; \
+ org.omg.PortableServer.POAPackage; \
+ org.omg.PortableServer.portable; \
+ org.omg.PortableServer.ServantLocatorPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ org.omg.stub.javax.management.remote.rmi; \
+ sun.misc; \
+ sun.reflect; \
+ version="1.6.0"
+
diff --git a/karaf/filemonitor/pom.xml b/karaf/filemonitor/pom.xml
new file mode 100644
index 0000000..58468bf
--- /dev/null
+++ b/karaf/filemonitor/pom.xml
@@ -0,0 +1,86 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: File Monitor</name>
+
+ <description>This bundle monitors the deploy directory for new OSGi bundles in jar form, expanded form or for
+ configuration file changes and undeploys/redeploys them
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Export-Package>${pom.artifactId}</Export-Package>
+ <Bundle-Activator>org.apache.servicemix.kernel.filemonitor.FileMonitorActivator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/DeploymentListener.java b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/DeploymentListener.java
new file mode 100644
index 0000000..6423561
--- /dev/null
+++ b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/DeploymentListener.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.servicemix.kernel.filemonitor;
+
+import java.io.File;
+
+
+public interface DeploymentListener {
+
+ /**
+ * Returns true if the listener can process the given file
+ */
+ boolean canHandle(File artifact);
+
+ /**
+ * Process the given file (canHandle returned true previously)
+ * Can return <null> or a pointer to a transformed file.
+ */
+ File handle(File artifact, File tmpDir);
+
+}
diff --git a/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitor.java b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitor.java
new file mode 100644
index 0000000..c0bf235
--- /dev/null
+++ b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitor.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.servicemix.kernel.filemonitor;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Watches a deploy directory for files that are added, updated or removed then
+ * processing them. Currently we support OSGi bundles, OSGi configuration files
+ * and expanded directories of OSGi bundles.
+ *
+ * @version $Revision: 1.1 $
+ */
+public class FileMonitor {
+
+ public final static String CONFIG_DIR = "org.apache.servicemix.filemonitor.configDir";
+ public final static String DEPLOY_DIR = "org.apache.servicemix.filemonitor.monitorDir";
+ public final static String GENERATED_JAR_DIR = "org.apache.servicemix.filemonitor.generatedJarDir";
+ public final static String SCAN_INTERVAL = "org.apache.servicemix.filemonitor.scanInterval";
+ public final static String PREFERENCE_KEY = "FileMonitor";
+
+ protected static final String ALIAS_KEY = "_alias_factory_pid";
+
+ private static final Log LOGGER = LogFactory.getLog(FileMonitor.class);
+
+ private FileMonitorActivator activator;
+ private File configDir;
+ private File deployDir;
+ private File generateDir;
+ private Scanner scanner = new Scanner();
+ private long scanInterval = 500L;
+ private boolean loggedConfigAdminWarning;
+ private List<Bundle> bundlesToStart = new ArrayList<Bundle>();
+ private List<Bundle> bundlesToUpdate = new ArrayList<Bundle>();
+ private Map<String, String> artifactToBundle = new HashMap<String, String>();
+ private final Set<String> pendingTransformationArtifacts = new HashSet<String>();
+ private final Set<Bundle> pendingStartBundles = new HashSet<Bundle>();
+ private ServiceListener deployerListener;
+ private BundleListener bundleListener;
+ private Executor executor;
+ private String pid;
+
+ public FileMonitor() {
+ String base = System.getProperty("servicemix.base", ".");
+ configDir = new File(base, "etc");
+ deployDir = new File(base, "deploy");
+ generateDir = new File(base, "data/generated-bundles");
+ }
+
+ @SuppressWarnings("unchecked")
+ public FileMonitor(FileMonitorActivator activator, Dictionary properties, String pid) {
+ this();
+
+ this.activator = activator;
+ this.pid = pid;
+
+ File value = getFileValue(properties, CONFIG_DIR);
+ if (value != null) {
+ configDir = value;
+ }
+ value = getFileValue(properties, DEPLOY_DIR);
+ if (value != null) {
+ deployDir = value;
+ }
+ value = getFileValue(properties, GENERATED_JAR_DIR);
+ if (value != null) {
+ generateDir = value;
+ }
+ Long i = getLongValue(properties, SCAN_INTERVAL);
+ if (i != null) {
+ scanInterval = i;
+ }
+ }
+
+ public void start() {
+ executor = Executors.newSingleThreadExecutor();
+ if (configDir != null) {
+ configDir.mkdirs();
+ }
+ deployDir.mkdirs();
+ generateDir.mkdirs();
+
+ List<File> dirs = new ArrayList<File>();
+ if (configDir != null) {
+ dirs.add(configDir);
+ }
+ dirs.add(deployDir);
+ scanner.setScanDirs(dirs);
+ scanner.setScanInterval(scanInterval);
+ // load the scan results for this monitor
+ loadScannerState();
+ scanner.addListener(new Scanner.BulkListener() {
+ public void filesChanged(List<String> filenames) throws Exception {
+ onFilesChanged(filenames);
+ }
+ });
+
+ LOGGER.info("Starting to monitor the deploy directory: " + deployDir + " every " + scanInterval
+ + " millis");
+ if (configDir != null) {
+ LOGGER.info("Config directory is at: " + configDir);
+ }
+ LOGGER.info("Will generate bundles from expanded source directories to: " + generateDir);
+
+ executor.execute(new Runnable() {
+ public void run() {
+ scanner.start();
+ }
+ });
+ }
+
+ public void stop() {
+ // first stop the scanner
+ scanner.stop();
+ // load the scan results for this monitor
+ saveScannerState();
+ }
+
+ // Properties
+ // -------------------------------------------------------------------------
+
+ public BundleContext getContext() {
+ return activator.getContext();
+ }
+
+ public FileMonitorActivator getActivator() {
+ return activator;
+ }
+
+ public void setActivator(FileMonitorActivator activator) {
+ this.activator = activator;
+ }
+
+ public File getConfigDir() {
+ return configDir;
+ }
+
+ public void setConfigDir(File configDir) {
+ this.configDir = configDir;
+ }
+
+ public File getDeployDir() {
+ return deployDir;
+ }
+
+ public void setDeployDir(File deployDir) {
+ this.deployDir = deployDir;
+ }
+
+ public File getGenerateDir() {
+ return generateDir;
+ }
+
+ public void setGenerateDir(File generateDir) {
+ this.generateDir = generateDir;
+ }
+
+ public long getScanInterval() {
+ return scanInterval;
+ }
+
+ public void setScanInterval(long scanInterval) {
+ this.scanInterval = scanInterval;
+ }
+
+ // Implementation methods
+ // -------------------------------------------------------------------------
+
+ protected synchronized void onFilesChanged(Collection<String> filenames) {
+ bundlesToStart.clear();
+ bundlesToUpdate.clear();
+ Set<File> bundleJarsCreated = new HashSet<File>();
+
+ for (String filename : filenames) {
+ File file = new File(filename);
+ try {
+ LOGGER.debug("File changed: " + filename);
+
+ // Handle config files
+ if (isValidConfigFile(file)) {
+ if (file.exists()) {
+ updateConfiguration(file);
+ } else {
+ deleteConfiguration(file);
+ }
+ continue;
+ }
+
+ // Handle exploded artifacts removal
+ if (!file.exists() && file.getName().equals("MANIFEST.MF")) {
+ File parentFile = file.getParentFile();
+ if (parentFile.getName().equals("META-INF")) {
+ File bundleDir = parentFile.getParentFile();
+ if (isValidBundleSourceDirectory(bundleDir)) {
+ undeployBundle(bundleDir);
+ continue;
+ }
+ }
+ }
+
+ // Handle exploded artifacts add
+ File jardir = getExpandedBundleRootDirectory(file);
+ if (jardir != null) {
+ if (bundleJarsCreated.contains(jardir)) {
+ continue;
+ }
+ bundleJarsCreated.add(jardir);
+ file = createBundleJar(jardir);
+ }
+
+ // Transformation step
+ if (file.exists()) {
+ File f = transformArtifact(file);
+ if (f == null) {
+ LOGGER.warn("Unsupported deployment: " + filename);
+ rescheduleTransformation(file);
+ continue;
+ }
+ file = f;
+ } else {
+ String transformedFile = artifactToBundle.get(filename);
+ if (transformedFile != null) {
+ file = new File(transformedFile);
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+ }
+
+ // Handle final bundles
+ if (isValidArtifactFile(file)) {
+ if (file.exists()) {
+ deployBundle(file);
+ } else {
+ undeployBundle(file);
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Failed to process: " + file + ". Reason: " + e, e);
+ }
+ }
+ refreshPackagesAndStartOrUpdateBundles();
+ }
+
+ private void rescheduleTransformation(File file) {
+ synchronized (pendingTransformationArtifacts) {
+ pendingTransformationArtifacts.add(file.getAbsolutePath());
+ }
+ if (deployerListener == null) {
+ try {
+ String filter = "(" + Constants.OBJECTCLASS + "=" + DeploymentListener.class.getName() + ")";
+ deployerListener = new ServiceListener() {
+ public void serviceChanged(ServiceEvent event) {
+ executor.execute(new Runnable() {
+ public void run() {
+ Set<String> files;
+ synchronized (pendingTransformationArtifacts) {
+ files = new HashSet<String>(pendingTransformationArtifacts);
+ pendingTransformationArtifacts.clear();
+ }
+ onFilesChanged(files);
+ }
+ });
+ }
+ };
+ getContext().addServiceListener(deployerListener, filter);
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+ }
+ }
+
+ private File transformArtifact(File file) throws Exception {
+ // Check registered deployers
+ ServiceReference[] srvRefs = getContext().getAllServiceReferences(DeploymentListener.class.getName(),
+ null);
+ if (srvRefs != null) {
+ for (ServiceReference sr : srvRefs) {
+ try {
+ DeploymentListener deploymentListener = (DeploymentListener)getContext().getService(sr);
+ if (deploymentListener.canHandle(file)) {
+ File transformedFile = deploymentListener.handle(file, getGenerateDir());
+ artifactToBundle.put(file.getAbsolutePath(), transformedFile.getAbsolutePath());
+ return transformedFile;
+ }
+ } finally {
+ getContext().ungetService(sr);
+ }
+ }
+ }
+ JarFile jar = null;
+ try {
+ // Handle OSGi bundles with the default deployer
+ if (file.getName().endsWith("txt") || file.getName().endsWith("xml")
+ || file.getName().endsWith("properties")) {
+ // that's file type which is not supported as bundle and avoid
+ // exception in the log
+ return null;
+ }
+ jar = new JarFile(file);
+ Manifest m = jar.getManifest();
+ if (m.getMainAttributes().getValue(new Attributes.Name("Bundle-SymbolicName")) != null
+ && m.getMainAttributes().getValue(new Attributes.Name("Bundle-Version")) != null) {
+ return file;
+ }
+ } catch (Exception e) {
+ LOGGER.debug("Error transforming artifact " + file.getName(), e);
+ } finally {
+ if (jar != null) {
+ jar.close();
+ }
+ }
+ return null;
+ }
+
+ protected void deployBundle(File file) throws IOException, BundleException {
+ LOGGER.info("Deploying: " + file.getCanonicalPath());
+
+ InputStream in = new FileInputStream(file);
+
+ try {
+ Bundle bundle = getBundleForJarFile(file);
+ if (bundle != null) {
+ bundlesToUpdate.add(bundle);
+ } else {
+ bundle = getContext().installBundle(file.getCanonicalFile().toURI().toString(), in);
+ if (!isBundleFragment(bundle)) {
+ bundlesToStart.add(bundle);
+ }
+ }
+ } finally {
+ closeQuietly(in);
+ }
+ }
+
+ protected void undeployBundle(File file) throws BundleException, IOException {
+ LOGGER.info("Undeploying: " + file.getCanonicalPath());
+ Bundle bundle = getBundleForJarFile(file);
+
+ if (bundle == null) {
+ LOGGER.warn("Could not find Bundle for file: " + file.getCanonicalPath());
+ } else {
+ if (!isBundleFragment(bundle)) {
+ bundle.stop();
+ }
+ bundle.uninstall();
+ }
+ }
+
+ protected Bundle getBundleForJarFile(File file) throws IOException {
+ String absoluteFilePath = file.getAbsoluteFile().toURI().toString();
+ Bundle bundles[] = getContext().getBundles();
+ for (Bundle bundle : bundles) {
+ String location = bundle.getLocation();
+ if (filePathsMatch(absoluteFilePath, location)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ protected static boolean filePathsMatch(String p1, String p2) {
+ p1 = normalizeFilePath(p1);
+ p2 = normalizeFilePath(p2);
+ return (p1 != null && p1.equalsIgnoreCase(p2));
+ }
+
+ protected static String normalizeFilePath(String path) {
+ if (path != null) {
+ path = path.replaceFirst("file:/*", "");
+ path = path.replaceAll("[\\\\/]+", "/");
+ }
+ return path;
+ }
+
+ protected void updateConfiguration(File file) throws IOException, InvalidSyntaxException {
+ ConfigurationAdmin configurationAdmin = activator.getConfigurationAdmin();
+ if (configurationAdmin == null) {
+ if (!loggedConfigAdminWarning) {
+ LOGGER.warn("No ConfigurationAdmin so cannot deploy configurations");
+ loggedConfigAdminWarning = true;
+ }
+ } else {
+ Properties properties = new Properties();
+ InputStream in = new FileInputStream(file);
+ try {
+ properties.load(in);
+ interpolation(properties);
+ closeQuietly(in);
+ String[] pid = parsePid(file);
+ Hashtable<Object, Object> hashtable = new Hashtable<Object, Object>();
+ hashtable.putAll(properties);
+ if (pid[1] != null) {
+ hashtable.put(ALIAS_KEY, pid[1]);
+ }
+
+ Configuration config = getConfiguration(pid[0], pid[1]);
+ if (config.getBundleLocation() != null) {
+ config.setBundleLocation(null);
+ }
+ config.update(hashtable);
+ } finally {
+ closeQuietly(in);
+ }
+ }
+ }
+
+ protected void interpolation(Properties properties) {
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
+ String key = (String)e.nextElement();
+ String val = properties.getProperty(key);
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+ while (matcher.find()) {
+ String rep = System.getProperty(matcher.group(1));
+ if (rep != null) {
+ val = val.replace(matcher.group(0), rep);
+ matcher.reset(val);
+ }
+ }
+ properties.put(key, val);
+ }
+ }
+
+ protected void deleteConfiguration(File file) throws IOException, InvalidSyntaxException {
+ String[] pid = parsePid(file);
+ Configuration config = getConfiguration(pid[0], pid[1]);
+ config.delete();
+ }
+
+ protected Configuration getConfiguration(String pid, String factoryPid) throws IOException,
+ InvalidSyntaxException {
+ ConfigurationAdmin configurationAdmin = activator.getConfigurationAdmin();
+ if (factoryPid != null) {
+ Configuration[] configs = configurationAdmin.listConfigurations("(|(" + ALIAS_KEY + "=" + pid
+ + ")(.alias_factory_pid="
+ + factoryPid + "))");
+ if (configs == null || configs.length == 0) {
+ return configurationAdmin.createFactoryConfiguration(pid, null);
+ } else {
+ return configs[0];
+ }
+ } else {
+ return configurationAdmin.getConfiguration(pid, null);
+ }
+ }
+
+ protected String[] parsePid(File file) {
+ String path = file.getName();
+ String pid = path.substring(0, path.length() - 4);
+ int n = pid.indexOf('-');
+ if (n > 0) {
+ String factoryPid = pid.substring(n + 1);
+ pid = pid.substring(0, n);
+ return new String[] {pid, factoryPid};
+ } else {
+ return new String[] {pid, null};
+ }
+ }
+
+ protected PackageAdmin getPackageAdmin() {
+ ServiceTracker packageAdminTracker = activator.getPackageAdminTracker();
+ if (packageAdminTracker != null) {
+ try {
+ return (PackageAdmin)packageAdminTracker.waitForService(5000L);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ protected boolean isBundleFragment(Bundle bundle) {
+ PackageAdmin packageAdmin = getPackageAdmin();
+ if (packageAdmin != null) {
+ return packageAdmin.getBundleType(bundle) == PackageAdmin.BUNDLE_TYPE_FRAGMENT;
+ }
+ return false;
+ }
+
+ protected void refreshPackagesAndStartOrUpdateBundles() {
+ for (Bundle bundle : bundlesToUpdate) {
+ try {
+ bundle.update();
+ LOGGER.info("Updated: " + bundle);
+ } catch (BundleException e) {
+ LOGGER.warn("Failed to update bundle: " + bundle + ". Reason: " + e, e);
+ }
+ }
+
+ for (Bundle bundle : bundlesToStart) {
+ try {
+ bundle.start();
+ LOGGER.info("Started: " + bundle);
+ } catch (BundleException e) {
+ LOGGER.warn("Failed to start bundle: " + bundle + ". Reason: " + e, e);
+ rescheduleStart(bundle);
+ }
+ }
+
+ PackageAdmin packageAdmin = getPackageAdmin();
+ if (packageAdmin != null) {
+ packageAdmin.refreshPackages(null);
+ }
+ }
+
+ private void rescheduleStart(Bundle bundle) {
+ synchronized (pendingStartBundles) {
+ pendingStartBundles.add(bundle);
+ if (bundleListener == null) {
+ bundleListener = new BundleListener() {
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.RESOLVED) {
+ executor.execute(new Runnable() {
+ public void run() {
+ retryPendingStartBundles();
+ }
+ });
+ } else if (event.getType() == BundleEvent.UNINSTALLED) {
+ // bundle was uninstalled meanwhile, so remove
+ // it from the list of pending bundles
+ pendingStartBundles.remove(event.getBundle());
+ }
+ }
+ };
+ getContext().addBundleListener(bundleListener);
+ }
+ }
+ }
+
+ protected void retryPendingStartBundles() {
+ synchronized (pendingStartBundles) {
+ Set<Bundle> bundles = new HashSet<Bundle>();
+ bundles.addAll(pendingStartBundles);
+ pendingStartBundles.clear();
+ boolean success = false;
+ for (Bundle bundle : bundles) {
+ try {
+ bundle.start();
+ LOGGER.info("Pending bundle started: " + bundle);
+ success = true;
+ } catch (BundleException e) {
+ LOGGER.info("Pending bundle not started: " + bundle);
+ pendingStartBundles.add(bundle);
+ }
+ }
+ if (success) {
+ PackageAdmin packageAdmin = getPackageAdmin();
+ if (packageAdmin != null) {
+ packageAdmin.refreshPackages(null);
+ }
+ }
+ }
+ }
+
+ protected File createBundleJar(File dir) throws BundleException, IOException {
+ File destFile = new File(generateDir, dir.getName() + ".jar");
+ if (destFile.exists()) {
+ undeployBundle(destFile);
+ destFile.delete();
+ }
+ JarUtil.jarDir(dir.getPath(), destFile.getPath());
+ return destFile;
+ }
+
+ /**
+ * Returns the root directory of the expanded OSGi bundle if the file is
+ * part of an expanded OSGi bundle or null if it is not
+ */
+ protected File getExpandedBundleRootDirectory(File file) throws IOException {
+ File parent = file.getParentFile();
+ if (file.isDirectory()) {
+ String rootPath = deployDir.getCanonicalPath();
+ if (file.getCanonicalPath().equals(rootPath)) {
+ return null;
+ }
+ if (containsManifest(file)) {
+ return file;
+ }
+ }
+ if (isValidBundleSourceDirectory(parent)) {
+ return getExpandedBundleRootDirectory(parent);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the given directory is a valid child directory within the
+ * {@link #deployDir}
+ */
+ protected boolean isValidBundleSourceDirectory(File dir) throws IOException {
+ if (dir != null) {
+ String parentPath = dir.getCanonicalPath();
+ String rootPath = deployDir.getCanonicalPath();
+ return !parentPath.equals(rootPath) && parentPath.startsWith(rootPath);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the given directory is a valid child file within the
+ * {@link #deployDir} or a child of {@link #generateDir}
+ */
+ protected boolean isValidArtifactFile(File file) throws IOException {
+ if (file != null) {
+ String filePath = file.getParentFile().getCanonicalPath();
+ String deployPath = deployDir.getCanonicalPath();
+ String generatePath = generateDir.getCanonicalPath();
+ return filePath.equals(deployPath) || filePath.startsWith(generatePath);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the given directory is a valid child file within the
+ * {@link #configDir}
+ */
+ protected boolean isValidConfigFile(File file) throws IOException {
+ if (file != null && file.getName().endsWith(".cfg")) {
+ String filePath = file.getParentFile().getCanonicalPath();
+ String configPath = configDir.getCanonicalPath();
+ return filePath.equals(configPath);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the given directory contains a valid manifest file
+ */
+ protected boolean containsManifest(File dir) {
+ File metaInfDir = new File(dir, "META-INF");
+ if (metaInfDir.exists() && metaInfDir.isDirectory()) {
+ File manifest = new File(metaInfDir, "MANIFEST.MF");
+ return manifest.exists() && !manifest.isDirectory();
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected File getFileValue(Dictionary properties, String key) {
+ Object value = properties.get(key);
+ if (value instanceof File) {
+ return (File)value;
+ } else if (value != null) {
+ return new File(value.toString());
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Long getLongValue(Dictionary properties, String key) {
+ Object value = properties.get(key);
+ if (value instanceof Long) {
+ return (Long)value;
+ } else if (value != null) {
+ return Long.parseLong(value.toString());
+ }
+ return null;
+ }
+
+ protected void closeQuietly(Closeable in) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ LOGGER.warn("Failed to close stream. " + e, e);
+ }
+ }
+
+ protected PreferencesService getPreferenceService() {
+ if (activator.getPreferenceServiceTracker() != null) {
+ try {
+ return (PreferencesService)activator.getPreferenceServiceTracker().waitForService(5000L);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ protected void saveScannerState() {
+ try {
+ Map<String, Long> results = scanner.getLastScanResults();
+ Preferences prefs = getPreferenceService().getUserPreferences(PREFERENCE_KEY).node(pid);
+ Iterator<String> it = results.keySet().iterator();
+ while (it.hasNext()) {
+ String key = it.next();
+ Long value = results.get(key);
+ prefs.putLong(key, value);
+ }
+ prefs.flush();
+ } catch (Exception e) {
+ LOGGER.error("Error persisting FileMonitor state", e);
+ }
+ }
+
+ protected void loadScannerState() {
+ try {
+ Preferences prefs = getPreferenceService().getUserPreferences(PREFERENCE_KEY).node(pid);
+ Map<String, Long> lastResult = new HashMap<String, Long>();
+ for (String key : prefs.keys()) {
+ lastResult.put(key, prefs.getLong(key, -1));
+ }
+ scanner.setLastScanResults(lastResult);
+ } catch (Exception e) {
+ LOGGER.error("Error loading FileMonitor state", e);
+ }
+ }
+}
diff --git a/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitorActivator.java b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitorActivator.java
new file mode 100644
index 0000000..82dea71
--- /dev/null
+++ b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/FileMonitorActivator.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.servicemix.kernel.filemonitor;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.prefs.PreferencesService;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Inspired by <a href="http://www.aqute.biz/Code/FileInstall">FileInstall</a>
+ * by Peter Kriens.
+ *
+ * @version $Revision: 1.1 $
+ */
+public class FileMonitorActivator implements BundleActivator, ManagedServiceFactory {
+ private static final Log LOGGER = LogFactory.getLog(FileMonitorActivator.class);
+
+ private BundleContext context;
+ private ServiceTracker packageAdminTracker;
+ private ServiceTracker configurationAdminTracker;
+ private ServiceTracker preferenceServiceTracker;
+ private Map<String, FileMonitor> fileMonitors = new HashMap<String, FileMonitor>();
+
+ // BundleActivator interface
+ // -------------------------------------------------------------------------
+ public void start(BundleContext context) throws Exception {
+ this.context = context;
+
+ Hashtable properties = new Hashtable();
+ properties.put(Constants.SERVICE_PID, getName());
+ context.registerService(ManagedServiceFactory.class.getName(), this, properties);
+
+ packageAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
+ packageAdminTracker.open();
+
+ configurationAdminTracker = new ServiceTracker(context, ConfigurationAdmin.class.getName(), null);
+ configurationAdminTracker.open();
+
+ preferenceServiceTracker = new ServiceTracker(context, PreferencesService.class.getName(), null);
+ preferenceServiceTracker.open();
+
+ Hashtable initialProperties = new Hashtable();
+ setPropertiesFromContext(initialProperties, FileMonitor.CONFIG_DIR, FileMonitor.DEPLOY_DIR,
+ FileMonitor.GENERATED_JAR_DIR, FileMonitor.SCAN_INTERVAL);
+ updated("initialPid", initialProperties);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ Collection<FileMonitor> fileMonitors = this.fileMonitors.values();
+ for (FileMonitor monitor : fileMonitors) {
+ try {
+ // stop the monitor
+ monitor.stop();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ this.fileMonitors.clear();
+
+ preferenceServiceTracker.close();
+ configurationAdminTracker.close();
+ packageAdminTracker.close();
+ }
+
+ // ManagedServiceFactory interface
+ // -------------------------------------------------------------------------
+ public String getName() {
+ return "org.apache.servicemix.kernel.filemonitor.FileMonitor";
+ }
+
+ public void updated(String pid, Dictionary properties) throws ConfigurationException {
+ deleted(pid);
+ FileMonitor monitor = new FileMonitor(this, properties, pid);
+ fileMonitors.put(pid, monitor);
+ monitor.start();
+ }
+
+ public void deleted(String pid) {
+ FileMonitor monitor = fileMonitors.remove(pid);
+ if (monitor != null) {
+ monitor.stop();
+ }
+ }
+
+ // Properties
+ // -------------------------------------------------------------------------
+ public BundleContext getContext() {
+ return context;
+ }
+
+ public ServiceTracker getConfigurationAdminTracker() {
+ return configurationAdminTracker;
+ }
+
+ public ServiceTracker getPackageAdminTracker() {
+ return packageAdminTracker;
+ }
+
+ public ConfigurationAdmin getConfigurationAdmin() {
+ return (ConfigurationAdmin)getConfigurationAdminTracker().getService();
+ }
+
+ public ServiceTracker getPreferenceServiceTracker() {
+ return preferenceServiceTracker;
+ }
+
+ // Implementation methods
+ // -------------------------------------------------------------------------
+ protected void setPropertiesFromContext(Hashtable properties, String... keys) {
+ for (String key : keys) {
+ Object value = context.getProperty(key);
+ if (value != null) {
+ properties.put(key, value);
+ }
+ }
+ }
+}
diff --git a/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/JarUtil.java b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/JarUtil.java
new file mode 100644
index 0000000..98b6036
--- /dev/null
+++ b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/JarUtil.java
@@ -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.
+ */
+package org.apache.servicemix.kernel.filemonitor;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class JarUtil {
+
+ /**
+ * Zip up a directory
+ *
+ * @param directory
+ * @param zipName
+ * @throws IOException
+ */
+ public static void zipDir(String directory, String zipName) throws IOException {
+ // create a ZipOutputStream to zip the data to
+ ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipName)));
+ String path = "";
+ zipDir(directory, zos, path, Collections.<String>emptySet());
+ // close the stream
+ zos.close();
+ }
+
+ /**
+ * Jar up a directory
+ *
+ * @param directory
+ * @param zipName
+ * @throws IOException
+ */
+ public static void jarDir(String directory, String zipName) throws IOException {
+ // create a ZipOutputStream to zip the data to
+ JarOutputStream zos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(zipName)));
+ String path = "";
+ File manFile = new File(directory, JarFile.MANIFEST_NAME);
+ if (manFile.exists()) {
+ byte[] readBuffer = new byte[8192];
+ FileInputStream fis = new FileInputStream(manFile);
+ try {
+ ZipEntry anEntry = new ZipEntry(JarFile.MANIFEST_NAME);
+ zos.putNextEntry(anEntry);
+ int bytesIn = fis.read(readBuffer);
+ while (bytesIn != -1) {
+ zos.write(readBuffer, 0, bytesIn);
+ bytesIn = fis.read(readBuffer);
+ }
+ } finally {
+ fis.close();
+ }
+ zos.closeEntry();
+ }
+ zipDir(directory, zos, path, Collections.singleton(JarFile.MANIFEST_NAME));
+ // close the stream
+ zos.close();
+ }
+
+ /**
+ * Zip up a directory path
+ * @param directory
+ * @param zos
+ * @param path
+ * @throws IOException
+ */
+ public static void zipDir(String directory, ZipOutputStream zos, String path, Set<String> exclusions) throws IOException {
+ File zipDir = new File(directory);
+ // get a listing of the directory content
+ String[] dirList = zipDir.list();
+ byte[] readBuffer = new byte[8192];
+ int bytesIn = 0;
+ // loop through dirList, and zip the files
+ for (int i = 0; i < dirList.length; i++) {
+ File f = new File(zipDir, dirList[i]);
+ if (f.isDirectory()) {
+ String filePath = f.getPath();
+ zipDir(filePath, zos, path + f.getName() + "/", exclusions);
+ continue;
+ }
+ String entry = path + f.getName();
+ if (!exclusions.contains(entry)) {
+ FileInputStream fis = new FileInputStream(f);
+ try {
+ ZipEntry anEntry = new ZipEntry(entry);
+ zos.putNextEntry(anEntry);
+ bytesIn = fis.read(readBuffer);
+ while (bytesIn != -1) {
+ zos.write(readBuffer, 0, bytesIn);
+ bytesIn = fis.read(readBuffer);
+ }
+ } finally {
+ fis.close();
+ }
+ }
+ }
+ }
+
+}
diff --git a/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/Scanner.java b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/Scanner.java
new file mode 100644
index 0000000..8b25226
--- /dev/null
+++ b/karaf/filemonitor/src/main/java/org/apache/servicemix/kernel/filemonitor/Scanner.java
@@ -0,0 +1,455 @@
+//========================================================================
+//$Id: Scanner.java 2180 2007-10-31 04:01:26Z janb $
+//Copyright 2006 Mort Bay Consulting Pty. Ltd.
+//------------------------------------------------------------------------
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//http://www.apache.org/licenses/LICENSE-2.0
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+//========================================================================
+package org.apache.servicemix.kernel.filemonitor;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Scanner
+ * <p/>
+ * Utility for scanning a directory for added, removed and changed files and
+ * reporting these events via registered Listeners.
+ * <p/>
+ * From the <a href="http://jetty.codehaus.org/">Jetty Util project</a>
+ *
+ * @version $Revision: 1.1 $
+ */
+public class Scanner {
+
+ private static Log logger = LogFactory.getLog(Scanner.class);
+ private long _scanInterval;
+ private List<Listener> _listeners = Collections.synchronizedList(new ArrayList<Listener>());
+ private Map<String, Long> _prevScan = new HashMap<String, Long>();
+ private FilenameFilter _filter;
+ private List<File> _scanDirs;
+ private volatile boolean _running = false;
+ private boolean _reportExisting = true;
+ private Timer _timer;
+ private TimerTask _task;
+
+ /**
+ * Listener
+ * <p/>
+ * Marker for notifications re file changes.
+ */
+ public interface Listener {
+ }
+
+ public interface DiscreteListener extends Listener {
+ public void fileChanged(String filename) throws Exception;
+
+ public void fileAdded(String filename) throws Exception;
+
+ public void fileRemoved(String filename) throws Exception;
+ }
+
+ public interface BulkListener extends Listener {
+ public void filesChanged(List<String> filenames) throws Exception;
+ }
+
+ /**
+ *
+ */
+ public Scanner() {
+ }
+
+ /**
+ * Get the scan interval
+ *
+ * @return interval between scans in millis
+ */
+ public long getScanInterval() {
+ return _scanInterval;
+ }
+
+ /**
+ * Set the scan interval
+ *
+ * @param scanInterval pause between scans in millis
+ */
+ public synchronized void setScanInterval(long scanInterval) {
+ this._scanInterval = scanInterval;
+
+ if (_running) {
+ stop();
+
+ _timer = newTimer();
+ _task = newTimerTask();
+
+ schedule(_timer, _task);
+ _running = true;
+ }
+ }
+
+ /**
+ * Set the location of the directory to scan.
+ *
+ * @param dir
+ * @deprecated use setScanDirs(List dirs) instead
+ */
+ public void setScanDir(File dir) {
+ _scanDirs = new ArrayList<File>();
+ _scanDirs.add(dir);
+ }
+
+ /**
+ * Get the location of the directory to scan
+ *
+ * @return
+ * @deprecated use getScanDirs() instead
+ */
+ public File getScanDir() {
+ return (_scanDirs == null ? null : (File)_scanDirs.get(0));
+ }
+
+ public void setScanDirs(List<File> dirs) {
+ _scanDirs = dirs;
+ }
+
+ public List<File> getScanDirs() {
+ return _scanDirs;
+ }
+
+ /**
+ * Apply a filter to files found in the scan directory. Only files matching
+ * the filter will be reported as added/changed/removed.
+ *
+ * @param filter
+ */
+ public void setFilenameFilter(FilenameFilter filter) {
+ this._filter = filter;
+ }
+
+ /**
+ * Get any filter applied to files in the scan dir.
+ *
+ * @return
+ */
+ public FilenameFilter getFilenameFilter() {
+ return _filter;
+ }
+
+ /**
+ * Whether or not an initial scan will report all files as being added.
+ *
+ * @param reportExisting if true, all files found on initial scan will be
+ * reported as being added, otherwise not
+ */
+ public void setReportExistingFilesOnStartup(boolean reportExisting) {
+ this._reportExisting = reportExisting;
+ }
+
+ /**
+ * Add an added/removed/changed listener
+ *
+ * @param listener
+ */
+ public synchronized void addListener(Listener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ _listeners.add(listener);
+ }
+
+ /**
+ * Remove a registered listener
+ *
+ * @param listener the Listener to be removed
+ */
+ public synchronized void removeListener(Listener listener) {
+ if (listener == null) {
+ return;
+ }
+ _listeners.remove(listener);
+ }
+
+ /**
+ * Start the scanning action.
+ */
+ public synchronized void start() {
+ if (_running) {
+ return;
+ }
+
+ _running = true;
+
+ if (_reportExisting) {
+ // if files exist at startup, report them
+ scan();
+ } else {
+ // just register the list of existing files and only report changes
+ _prevScan = scanFiles();
+ }
+
+ _timer = newTimer();
+ _task = newTimerTask();
+
+ schedule(_timer, _task);
+ }
+
+ public TimerTask newTimerTask() {
+ return new TimerTask() {
+ public void run() {
+ scan();
+ }
+ };
+ }
+
+ public Timer newTimer() {
+ return new Timer(true);
+ }
+
+ public void schedule(Timer timer, TimerTask task) {
+ if (timer == null) {
+ throw new IllegalArgumentException("Timer is null");
+ }
+ if (task == null) {
+ throw new IllegalArgumentException("TimerTask is null");
+ }
+
+ if (getScanInterval() > 0) {
+ timer.scheduleAtFixedRate(task, getScanInterval(), getScanInterval());
+ }
+ }
+
+ /**
+ * Stop the scanning.
+ */
+ public synchronized void stop() {
+ if (_running) {
+ _running = false;
+ _timer.cancel();
+ _task.cancel();
+ _task = null;
+ _timer = null;
+ }
+ }
+
+ /**
+ * Perform a pass of the scanner and report changes
+ */
+ public void scan() {
+ Map<String, Long> currentScan = scanFiles();
+ reportDifferences(currentScan, _prevScan);
+ _prevScan = currentScan;
+ }
+
+ /**
+ * Recursively scan all files in the designated directories.
+ *
+ * @return Map of name of file to last modified time
+ */
+ public Map<String, Long> scanFiles() {
+ if (_scanDirs == null) {
+ return Collections.emptyMap();
+ }
+
+ HashMap<String, Long> scanInfo = new HashMap<String, Long>();
+ Iterator<File> itor = _scanDirs.iterator();
+ while (itor.hasNext()) {
+ File dir = (File)itor.next();
+
+ if ((dir != null) && (dir.exists())) {
+ scanFile(dir, scanInfo);
+ }
+ }
+
+ return scanInfo;
+ }
+
+ /**
+ * Report the adds/changes/removes to the registered listeners
+ *
+ * @param currentScan the info from the most recent pass
+ * @param oldScan info from the previous pass
+ */
+ public void reportDifferences(Map<String, Long> currentScan, Map<String, Long> oldScan) {
+ List<String> bulkChanges = new ArrayList<String>();
+
+ Set<String> oldScanKeys = new HashSet<String>(oldScan.keySet());
+ Iterator<Map.Entry<String, Long>> itor = currentScan.entrySet().iterator();
+ while (itor.hasNext()) {
+ Map.Entry<String, Long> entry = itor.next();
+ if (!oldScanKeys.contains(entry.getKey())) {
+ logger.debug("File added: " + entry.getKey());
+ reportAddition(entry.getKey());
+ bulkChanges.add(entry.getKey());
+ } else if (!oldScan.get(entry.getKey()).equals(entry.getValue())) {
+ logger.debug("File changed: " + entry.getKey());
+ reportChange(entry.getKey());
+ oldScanKeys.remove(entry.getKey());
+ bulkChanges.add(entry.getKey());
+ } else {
+ oldScanKeys.remove(entry.getKey());
+ }
+ }
+
+ if (!oldScanKeys.isEmpty()) {
+
+ Iterator<String> keyItor = oldScanKeys.iterator();
+ while (keyItor.hasNext()) {
+ String filename = keyItor.next();
+ logger.debug("File removed: " + filename);
+ reportRemoval(filename);
+ bulkChanges.add(filename);
+ }
+ }
+
+ if (!bulkChanges.isEmpty()) {
+ reportBulkChanges(bulkChanges);
+ }
+ }
+
+ /**
+ * Get last modified time on a single file or recurse if the file is a
+ * directory.
+ *
+ * @param f file or directory
+ * @param scanInfoMap map of filenames to last modified times
+ */
+ private void scanFile(File f, Map<String, Long> scanInfoMap) {
+ try {
+ if (!f.exists()) {
+ return;
+ }
+
+ if (f.isFile()) {
+ if ((_filter == null)
+ || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName()))) {
+ String name = f.getCanonicalPath();
+ long lastModified = f.lastModified();
+ scanInfoMap.put(name, new Long(lastModified));
+ }
+ } else if (f.isDirectory()) {
+ File[] files = f.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ scanFile(files[i], scanInfoMap);
+ }
+ }
+ } catch (IOException e) {
+ logger.warn("Error scanning watched files", e);
+ }
+ }
+
+ /**
+ * Report a file addition to the registered FileAddedListeners
+ *
+ * @param filename
+ */
+ private void reportAddition(String filename) {
+ Iterator<Listener> itor = _listeners.iterator();
+ while (itor.hasNext()) {
+ try {
+ Listener l = itor.next();
+ if (l instanceof DiscreteListener) {
+ ((DiscreteListener)l).fileAdded(filename);
+ }
+ } catch (Exception e) {
+ logger.warn(e);
+ } catch (Error e) {
+ logger.warn(e);
+ }
+ }
+ }
+
+ /**
+ * Report a file removal to the FileRemovedListeners
+ *
+ * @param filename
+ */
+ private void reportRemoval(String filename) {
+ Iterator<Listener> itor = _listeners.iterator();
+ while (itor.hasNext()) {
+ try {
+ Listener l = itor.next();
+ if (l instanceof DiscreteListener) {
+ ((DiscreteListener)l).fileRemoved(filename);
+ }
+ } catch (Exception e) {
+ logger.warn(e);
+ } catch (Error e) {
+ logger.warn(e);
+ }
+ }
+ }
+
+ /**
+ * Report a file change to the FileChangedListeners
+ *
+ * @param filename
+ */
+ private void reportChange(String filename) {
+ Iterator<Listener> itor = _listeners.iterator();
+ while (itor.hasNext()) {
+ try {
+ Listener l = itor.next();
+ if (l instanceof DiscreteListener) {
+ ((DiscreteListener)l).fileChanged(filename);
+ }
+ } catch (Exception e) {
+ logger.warn(e);
+ } catch (Error e) {
+ logger.warn(e);
+ }
+ }
+ }
+
+ private void reportBulkChanges(List<String> filenames) {
+ Iterator<Listener> itor = _listeners.iterator();
+ while (itor.hasNext()) {
+ try {
+ Listener l = itor.next();
+ if (l instanceof BulkListener) {
+ ((BulkListener)l).filesChanged(filenames);
+ }
+ } catch (Exception e) {
+ logger.warn(e);
+ } catch (Error e) {
+ logger.warn(e);
+ }
+ }
+ }
+
+ protected Map<String, Long> getLastScanResults() {
+ Map<String, Long> result = new HashMap<String, Long>();
+ synchronized (_prevScan) {
+ result.putAll(_prevScan);
+ }
+ return result;
+ }
+
+ protected void setLastScanResults(Map<String, Long> results) {
+ synchronized (_prevScan) {
+ _prevScan.clear();
+ _prevScan.putAll(results);
+ }
+ }
+}
diff --git a/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/LICENSE.txt b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ 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/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/MANIFEST.MF b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..15e6aae
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/MANIFEST.MF
@@ -0,0 +1,24 @@
+Manifest-Version: 1.0
+Created-By: 1.5.0_07 (Apple Computer, Inc.)
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Import-Package: org.apache.camel.builder,org.apache.camel.example.spri
+ ng;version=1.3.0.SNAPSHOT,org.apache.camel.model,org.apache.camel.spr
+ ing
+Include-Resource: src/main/resources
+Bnd-LastModified: 1195835480265
+Export-Package: org.apache.camel.example.spring;uses:="org.apache.came
+ l.model,org.apache.camel.builder,org.apache.camel.spring";version=1.3
+ .0.SNAPSHOT
+Bundle-Version: 1.3.0.SNAPSHOT
+Bundle-Name: camel-example-spring
+Bundle-Description: An example showing how to work with Camel and Spri
+ ng
+Private-Package: META-INF.spring
+Bundle-DocURL: http://www.apache.org/
+Bundle-Vendor: Apache Software Foundation
+Bundle-ManifestVersion: 2
+Implementation-Title: Apache Camel
+Bundle-SymbolicName: org.apache.camel.camel-example-spring
+Tool: Bnd-0.0.160
+Implementation-Version: 1.3-SNAPSHOT
+
diff --git a/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/NOTICE.txt b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/NOTICE.txt
new file mode 100644
index 0000000..2e215bf
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Apache Camel distribution. ==
+ =========================================================================
+
+ This product includes software developed by
+ The Apache Software Foundation (http://www.apache.org/).
+
+ Please read the different LICENSE files present in the licenses directory of
+ this distribution.
diff --git a/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/spring/camel-context.xml b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/spring/camel-context.xml
new file mode 100644
index 0000000..eb2c42a
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/META-INF/spring/camel-context.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+
+ The default Application Context used by the org.apache.camel.spring.Main if there
+ is no /META-INF/sprint.xml
+
+ -->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+ http://activemq.apache.org/camel/schema/spring http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">
+
+ <camelContext id="camel" useJmx="true" mbeanServer="mbeanServer"
+ xmlns="http://activemq.apache.org/camel/schema/spring">
+ <package>org.apache.camel.example.spring</package>
+ </camelContext>
+
+ <!-- lets configure the default ActiveMQ broker URL -->
+ <bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
+ <property name="connectionFactory">
+ <bean class="org.apache.activemq.ActiveMQConnectionFactory">
+ <property name="brokerURL" value="vm://localhost?broker.persistent=false&broker.useJmx=false"/>
+ </bean>
+ </property>
+ </bean>
+
+ <!-- Use JMX -->
+ <bean id="agent" class="org.apache.camel.spring.SpringInstrumentationAgent">
+ <property name="MBeanServer" ref="mbeanServer"/>
+ </bean>
+
+ <!--
+ this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
+ this means that it must not be marked as lazily initialized
+ -->
+ <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
+ <property name="defaultDomain" value="org.apache.camel.test"/>
+ </bean>
+ <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
+ <property name="port" value="1099"/>
+ </bean>
+ <bean id="serverConnector"
+ class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry">
+ <property name="objectName" value="connector:name=rmi"/>
+ <property name="serviceUrl"
+ value="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"/>
+ <property name="threaded" value="true"/>
+ <property name="daemon" value="true"/>
+ </bean>
+
+</beans>
diff --git a/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder$SomeBean.class b/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder$SomeBean.class
new file mode 100644
index 0000000..158c9da
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder$SomeBean.class
Binary files differ
diff --git a/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder.class b/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder.class
new file mode 100644
index 0000000..79a433e
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/camel-example-spring/org/apache/camel/example/spring/MyRouteBuilder.class
Binary files differ
diff --git a/karaf/filemonitor/src/test/data/foo/META-INF/MANIFEST.MF b/karaf/filemonitor/src/test/data/foo/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..001dd22
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/foo/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+License-00:
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements. See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License. You may obtain a copy of the License at
+License-07:
+License-08: http://www.apache.org/licenses/LICENSE-2.0
+License-09:
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15:
diff --git a/karaf/filemonitor/src/test/data/foo/foo.txt b/karaf/filemonitor/src/test/data/foo/foo.txt
new file mode 100644
index 0000000..c094a79
--- /dev/null
+++ b/karaf/filemonitor/src/test/data/foo/foo.txt
@@ -0,0 +1,18 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+Hey!
\ No newline at end of file
diff --git a/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/BundlePackerTest.java b/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/BundlePackerTest.java
new file mode 100644
index 0000000..78a9159
--- /dev/null
+++ b/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/BundlePackerTest.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.servicemix.kernel.filemonitor;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.commons.io.FileUtils;
+
+/**
+ * @version $Revision: 1.1 $
+ */
+public class BundlePackerTest extends TestCase {
+ public void testFiles() throws Exception {
+ File bundleDir = new File("target/deploy");
+ File unpackDir = new File("target/data");
+ FileUtils.deleteDirectory(unpackDir);
+
+ bundleDir.mkdirs();
+ unpackDir.mkdirs();
+
+ FileUtils.copyDirectory(new File("src/test/data"), unpackDir);
+
+/*
+ FileMonitor packer = new FileMonitor(bundleDir, unpackDir);
+ packer.scan();
+*/
+ }
+
+}
diff --git a/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/FileMonitorTest.java b/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/FileMonitorTest.java
new file mode 100644
index 0000000..5a99c96
--- /dev/null
+++ b/karaf/filemonitor/src/test/java/org/apache/servicemix/kernel/filemonitor/FileMonitorTest.java
@@ -0,0 +1,99 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.filemonitor;
+
+import junit.framework.TestCase;
+
+/**
+ * FileMonitor Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>07/02/2008</pre>
+ */
+public class FileMonitorTest extends TestCase {
+
+ public FileMonitorTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testFilePathsMatch() throws Exception {
+ assertTrue(FileMonitor.filePathsMatch(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt"));
+
+ assertTrue(FileMonitor.filePathsMatch(
+ "C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt",
+ "C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt"));
+
+ assertTrue(FileMonitor.filePathsMatch(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ "C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt"));
+
+ assertTrue(FileMonitor.filePathsMatch(
+ "file:/C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ "C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt"));
+
+ assertTrue(FileMonitor.filePathsMatch(
+ "file://C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ "C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt"));
+ }
+
+ public void testNormalizeFilePath() throws Exception {
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("C:/Apps//apache-servicemix-kernel-1.0.0-rc1////src/////README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("C:\\Apps\\apache-servicemix-kernel-1.0.0-rc1\\src\\README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("C:\\\\Apps\\\\apache-servicemix-kernel-1.0.0-rc1\\\\src\\\\README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("C:\\Apps\\//apache-servicemix-kernel-1.0.0-rc1\\/\\src///\\\\README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("file:C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("file:/C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt"));
+
+ assertEquals(
+ "C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt",
+ FileMonitor.normalizeFilePath("file://C:/Apps/apache-servicemix-kernel-1.0.0-rc1/src/README.txt"));
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/pom.xml b/karaf/gshell/gshell-admin/pom.xml
new file mode 100644
index 0000000..0a6a2a4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/pom.xml
@@ -0,0 +1,154 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Admin</name>
+
+ <description>
+ Provides administration commands
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+ org.apache.geronimo.gshell*,
+ </gshell.osgi.import>
+ <gshell.osgi.export>
+ org.apache.servicemix.jpm
+ </gshell.osgi.export>
+ <gshell.osgi.private>
+ org.apache.servicemix.kernel.gshell.admin.*,
+ org.apache.servicemix.jpm.impl
+ </gshell.osgi.private>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/org/apache/servicemix/kernel/gshell/admin/etc</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../${config.location}</directory>
+ <includes>
+ <include>config.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.admin.*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>org.apache.servicemix.jpm.*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
new file mode 100644
index 0000000..2e22aec
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
@@ -0,0 +1,28 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+# Comma separated list of features repositories to register by default
+#
+featuresRepositories=mvn:org.apache.servicemix.kernel/apache-servicemix-kernel/${version}/xml/features
+
+#
+# Comma separated list of features to install at startup
+#
+featuresBoot=
diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
new file mode 100644
index 0000000..f9689b1
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
@@ -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.
+#
+################################################################################
+
+# This file allows you to control the start level of each bundle.
+#
+
+#
+# Startup core services like logging
+#
+org/ops4j/pax/url/pax-url-mvn/${pax.url.version}/pax-url-mvn-${pax.url.version}.jar=5
+org/ops4j/pax/url/pax-url-wrap/${pax.url.version}/pax-url-wrap-${pax.url.version}.jar=5
+org/apache/geronimo/specs/geronimo-servlet_2.5_spec/${geronimo.servlet.version}/geronimo-servlet_2.5_spec-${geronimo.servlet.version}.jar=10
+org/apache/servicemix/specs/org.apache.servicemix.specs.jaxp-api-1.4/${servicemix.specs.version}/org.apache.servicemix.specs.jaxp-api-1.4-${servicemix.specs.version}.jar=10
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxp-ri/${jaxp.ri.version}/org.apache.servicemix.bundles.jaxp-ri-${jaxp.ri.version}.jar=10
+org/apache/felix/org.osgi.compendium/${felix.compendium.version}/org.osgi.compendium-${felix.compendium.version}.jar=10
+org/apache/felix/org.apache.felix.configadmin/${felix.configadmin.version}/org.apache.felix.configadmin-${felix.configadmin.version}.jar=10
+org/apache/geronimo/specs/geronimo-annotation_1.0_spec/${geronimo.annotation.version}/geronimo-annotation_1.0_spec-${geronimo.annotation.version}.jar=10
+org/apache/felix/org.apache.felix.prefs/${felix.prefs.version}/org.apache.felix.prefs-${felix.prefs.version}.jar=10
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.filemonitor/${pom.version}/org.apache.servicemix.kernel.filemonitor-${pom.version}.jar=15
+org/ops4j/pax/logging/pax-logging-api/${pax.logging.version}/pax-logging-api-${pax.logging.version}.jar=20
+org/ops4j/pax/logging/pax-logging-service/${pax.logging.version}/pax-logging-service-${pax.logging.version}.jar=20
+
+#
+# The rest of the services..
+#
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jline/${jline.version}/org.apache.servicemix.bundles.jline-${jline.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.version}/org.apache.servicemix.bundles.aopalliance-${aopalliance.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.cglib/${cglib.version}/org.apache.servicemix.bundles.cglib-${cglib.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.mina/${mina.version}/org.apache.servicemix.bundles.mina-${mina.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.oro/${oro.version}/org.apache.servicemix.bundles.oro-${oro.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-codec/${commons.codec.version}/org.apache.servicemix.bundles.commons-codec-${commons.codec.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-httpclient/${commons.httpclient.version}/org.apache.servicemix.bundles.commons-httpclient-${commons.httpclient.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-jexl/${commons.jexl.version}/org.apache.servicemix.bundles.commons-jexl-${commons.jexl.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-vfs/${commons.vfs.version}/org.apache.servicemix.bundles.commons-vfs-${commons.vfs.version}.jar=30
+org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar=30
+org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar=30
+org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar=30
+org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar=30
+org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.spring/${pom.version}/org.apache.servicemix.kernel.spring-${pom.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.management/${pom.version}/org.apache.servicemix.kernel.management-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.admin/${pom.version}/org.apache.servicemix.kernel.gshell.admin-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.osgi/${pom.version}/org.apache.servicemix.kernel.gshell.osgi-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.features/${pom.version}/org.apache.servicemix.kernel.gshell.features-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.log/${pom.version}/org.apache.servicemix.kernel.gshell.log-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.config/${pom.version}/org.apache.servicemix.kernel.gshell.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.packages/${pom.version}/org.apache.servicemix.kernel.gshell.packages-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.config/${pom.version}/org.apache.servicemix.kernel.jaas.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.modules/${pom.version}/org.apache.servicemix.kernel.jaas.modules-${pom.version}.jar=30
+com/google/code/sshd/sshd/${sshd.version}/sshd-${sshd.version}.jar=30
+org/osgi/jmx/${osgi.jmx.version}/jmx-${osgi.jmx.version}.jar=30
+com/oracle/osgi/jmx-impl/${osgi.jmx.version}/jmx-impl-${osgi.jmx.version}.jar=30
+
+#
+# Start console last
+#
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.core/${pom.version}/org.apache.servicemix.kernel.gshell.core-${pom.version}.jar=40
+
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
new file mode 100644
index 0000000..eedb31d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Interface representing a process
+ */
+public interface Process extends Serializable {
+
+ /**
+ * Retrieves the PID of the process
+ * @return the pid
+ */
+ int getPid();
+
+ /**
+ * Check if this process is still running
+ * @return <code>true</code> if the process is running
+ * @throws IOException if an error occurs
+ */
+ boolean isRunning() throws IOException;
+
+ /**
+ * Destroy the process.
+ *
+ * @throws IOException
+ */
+ void destroy() throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
new file mode 100644
index 0000000..6862222
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Interface used to create new processes.
+ */
+public interface ProcessBuilder {
+
+ /**
+ * Specified the current directory to run the command from
+ *
+ * @param dir the directory to run the command from
+ * @return the ProcessBuilder instance
+ */
+ ProcessBuilder directory(File dir);
+
+ /**
+ * Set the command to execute
+ *
+ * @param command the command to execute
+ * @return the ProcessBuilder instance
+ */
+ ProcessBuilder command(String command);
+
+ /**
+ * Create and start the process
+ *
+ * @return the process that has been started
+ * @throws IOException if the process can not be created
+ */
+ Process start() throws IOException;
+
+ /**
+ * Attach to an existing process
+ *
+ * @return the process that has been attached
+ * @throws IOException if the process can not be attached to
+ */
+ Process attach(int pid) throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
new file mode 100644
index 0000000..433b615
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.jpm;
+
+/**
+ * Factory for process builders.
+ */
+public abstract class ProcessBuilderFactory {
+
+ public static ProcessBuilderFactory newInstance() {
+ return new org.apache.servicemix.jpm.impl.ProcessBuilderFactoryImpl();
+ }
+
+ public abstract ProcessBuilder newBuilder();
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
new file mode 100644
index 0000000..4db4657
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import org.apache.servicemix.jpm.ProcessBuilder;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+
+public class ProcessBuilderFactoryImpl extends ProcessBuilderFactory {
+
+ public ProcessBuilder newBuilder() {
+ return new ProcessBuilderImpl();
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
new file mode 100644
index 0000000..649da6b
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilder;
+
+
+public class ProcessBuilderImpl implements ProcessBuilder {
+
+ private File dir;
+ private String command;
+
+ public ProcessBuilder directory(File dir) {
+ this.dir = dir;
+ return this;
+ }
+
+ public ProcessBuilder command(String command) {
+ this.command = command;
+ return this;
+ }
+
+ public Process start() throws IOException {
+ return ProcessImpl.create(dir, command);
+ }
+
+ public Process attach(int pid) throws IOException {
+ return ProcessImpl.attach(pid);
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
new file mode 100644
index 0000000..71146fa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicemix.jpm.Process;
+
+public class ProcessImpl implements Process {
+
+ private int pid;
+ //private File input;
+ //private File output;
+ //private File error;
+
+ public ProcessImpl(int pid/*, File input, File output, File error*/) {
+ this.pid = pid;
+ //this.input = input;
+ //this.output = output;
+ //this.error = error;
+ }
+
+ public int getPid() {
+ return pid;
+ }
+
+ public boolean isRunning() throws IOException {
+ if (ScriptUtils.isWindows()) {
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("${pid}", Integer.toString(pid));
+ int ret = ScriptUtils.execute("running", props);
+ return ret == 0;
+ } else {
+ try {
+ java.lang.Process process = new java.lang.ProcessBuilder("ps", "-p", Integer.toString(pid)).start();
+ BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ r.readLine(); // skip headers
+ String s = r.readLine();
+ boolean running = s != null && s.length() > 0;
+ int ret = process.waitFor();
+ return running;
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+ }
+
+ public void destroy() throws IOException {
+ int ret;
+ if (ScriptUtils.isWindows()) {
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("${pid}", Integer.toString(pid));
+ ret = ScriptUtils.execute("destroy", props);
+ } else {
+ ret = ScriptUtils.executeProcess(new java.lang.ProcessBuilder("kill", "-9", Integer.toString(pid)));
+ }
+ if (ret != 0) {
+ throw new IOException("Unable to destroy proces, it may be already terminated");
+ }
+ }
+
+ /*
+ public OutputStream getInputStream() throws FileNotFoundException {
+ return new FileOutputStream(input);
+ }
+
+ public InputStream getOutputStream() throws FileNotFoundException {
+ return new FileInputStream(output);
+ }
+
+ public InputStream getErrorStream() throws FileNotFoundException {
+ return new FileInputStream(error);
+ }
+ */
+
+ public int waitFor() throws InterruptedException {
+ return 0;
+ }
+
+ public int exitValue() {
+ return 0;
+ }
+
+ public static Process create(File dir, String command) throws IOException {
+ //File input = File.createTempFile("jpm.", ".input");
+ //File output = File.createTempFile("jpm.", ".output");
+ //File error = File.createTempFile("jpm.", ".error");
+ File pidFile = File.createTempFile("jpm.", ".pid");
+ try {
+ Map<String, String> props = new HashMap<String, String>();
+ //props.put("${in.file}", input.getCanonicalPath());
+ //props.put("${out.file}", output.getCanonicalPath());
+ //props.put("${err.file}", error.getCanonicalPath());
+ props.put("${pid.file}", pidFile.getCanonicalPath());
+ props.put("${dir}", dir != null ? dir.getCanonicalPath() : "");
+ if (ScriptUtils.isWindows()) {
+ command = command.replaceAll("\"", "\"\"");
+ }
+ props.put("${command}", command);
+ int ret = ScriptUtils.execute("start", props);
+ if (ret != 0) {
+ throw new IOException("Unable to create process (error code: " + ret + ")");
+ }
+ int pid = readPid(pidFile);
+ return new ProcessImpl(pid/*, input, output, error*/);
+ } finally {
+ pidFile.delete();
+ }
+ }
+
+ public static Process attach(int pid) throws IOException {
+ return new ProcessImpl(pid);
+ }
+
+ private static int readPid(File pidFile) throws IOException {
+ InputStream is = new FileInputStream(pidFile);
+ try {
+ BufferedReader r = new BufferedReader(new InputStreamReader(is));
+ String pidString = r.readLine();
+ return Integer.valueOf(pidString);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {}
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
new file mode 100644
index 0000000..869c958
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.Scanner;
+
+public class ScriptUtils {
+
+ public static int execute(String name, Map<String, String> props) throws IOException {
+ File script = File.createTempFile("jpm.", ".script");
+ try {
+ if (isWindows()) {
+ String res = "windows/" + name + ".vbs";
+ ScriptUtils.copyFilteredResource(res, script, props);
+ return executeProcess(new java.lang.ProcessBuilder("cscript",
+ "/NOLOGO",
+ "//E:vbs",
+ script.getCanonicalPath()));
+ } else {
+ String res = "unix/" + name + ".sh";
+ ScriptUtils.copyFilteredResource(res, script, props);
+ return executeProcess(new java.lang.ProcessBuilder("/bin/sh",
+ script.getCanonicalPath()));
+ }
+ } finally {
+ script.delete();
+ }
+ }
+
+ public static int executeProcess(java.lang.ProcessBuilder builder) throws IOException {
+ try {
+ java.lang.Process process = builder.start();
+ return process.waitFor();
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+
+ public static void copyFilteredResource(String resource, File outFile, Map<String, String> props) throws IOException {
+ InputStream is = null;
+ try {
+ is = ScriptUtils.class.getResourceAsStream(resource);
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+
+ private static void safeClose(InputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private static void safeClose(OutputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private static String filter(String line, Map<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private static final boolean windows;
+
+ static {
+ windows = System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
+ }
+
+ public static boolean isWindows() {
+ return windows;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
new file mode 100644
index 0000000..cbf7181
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface AdminService {
+
+ Instance createInstance(String name, int port, String location) throws Exception;
+
+ Instance[] getInstances();
+
+ Instance getInstance(String name);
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java
new file mode 100644
index 0000000..8a68d2d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.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.servicemix.kernel.gshell.admin;
+
+public interface AdminServiceMBean {
+
+ void createInstance(String name, int port, String location) throws Exception;
+
+ String[] getInstances();
+
+ int getPort(String name) throws Exception;
+
+ void changePort(String name, int port) throws Exception;
+
+ String getState(String name) throws Exception;
+
+ void start(String name, String javaOpts) throws Exception;
+
+ void stop(String name) throws Exception;
+
+ void destroy(String name) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java
new file mode 100644
index 0000000..07612aa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.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.servicemix.kernel.gshell.admin;
+
+public interface Instance {
+
+ String STOPPED = "Stopped";
+ String STARTING = "Starting";
+ String STARTED = "Started";
+
+ String getName();
+
+ String getLocation();
+
+ int getPid();
+
+ int getPort() throws Exception;
+
+ void changePort(int port) throws Exception;
+
+ void start(String javaOpts) throws Exception;
+
+ void stop() throws Exception;
+
+ void destroy() throws Exception;
+
+ String getState() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
new file mode 100644
index 0000000..54283a8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
@@ -0,0 +1,299 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.beans.factory.InitializingBean;
+
+public class AdminServiceImpl implements AdminService, InitializingBean {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminServiceImpl.class);
+
+ private PreferencesService preferences;
+
+ private Map<String, Instance> instances = new HashMap<String, Instance>();
+
+ private int defaultPortStart = 8101;
+
+ public PreferencesService getPreferences() {
+ return preferences;
+ }
+
+ public void setPreferences(PreferencesService preferences) {
+ this.preferences = preferences;
+ }
+
+ public synchronized void afterPropertiesSet() throws Exception {
+ try {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ Preferences child = prefs.node("Instances");
+ int count = child.getInt("count", 0);
+ Map<String, Instance> newInstances = new HashMap<String, Instance>();
+ for (int i = 0; i < count; i++) {
+ String name = child.get("item." + i + ".name", null);
+ String loc = child.get("item." + i + ".loc", null);
+ int pid = child.getInt("item." + i + ".pid", 0);
+ if (name != null) {
+ InstanceImpl instance = new InstanceImpl(this, name, loc);
+ if (pid > 0) {
+ try {
+ instance.attach(pid);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ newInstances.put(name, instance);
+ }
+ }
+ instances = newInstances;
+ } catch (Exception e) {
+ LOGGER.warn("Unable to reload ServiceMix instance list", e);
+ }
+ }
+
+ public synchronized Instance createInstance(String name, int port, String location) throws Exception {
+ if (instances.get(name) != null) {
+ throw new IllegalArgumentException("Instance '" + name + "' already exists");
+ }
+ File serviceMixBase = new File(location != null ? location : ("instances/" + name)).getCanonicalFile();
+ int sshPort = port;
+ if (sshPort <= 0) {
+ try {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ sshPort = prefs.getInt("port", defaultPortStart + 1);
+ prefs.putInt("port", sshPort + 1);
+ prefs.flush();
+ prefs.sync();
+ } catch (Exception e) {
+ try {
+ ServerSocket ss = new ServerSocket(0);
+ sshPort = ss.getLocalPort();
+ ss.close();
+ } catch (Exception t) {
+ }
+ }
+ if (sshPort <= 0) {
+ sshPort = defaultPortStart;
+ }
+ }
+ println("Creating new instance on port " + sshPort + " at: @|bold " + serviceMixBase + "|");
+
+ mkdir(serviceMixBase, "bin");
+ mkdir(serviceMixBase, "etc");
+ mkdir(serviceMixBase, "system");
+ mkdir(serviceMixBase, "deploy");
+ mkdir(serviceMixBase, "data");
+
+ copyResourceToDir(serviceMixBase, "etc/config.properties", true);
+ copyResourceToDir(serviceMixBase, "etc/org.apache.servicemix.features.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/users.properties", true);
+ copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.logging.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.url.mvn.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/startup.properties", true);
+
+ HashMap<String, String> props = new HashMap<String, String>();
+ props.put("${servicemix.name}", name);
+ props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+ props.put("${servicemix.base}", serviceMixBase.getPath());
+ props.put("${servicemix.sshPort}", Integer.toString(sshPort));
+ copyFilteredResourceToDir(serviceMixBase, "etc/system.properties", props);
+ copyFilteredResourceToDir(serviceMixBase, "etc/org.apache.servicemix.shell.cfg", props);
+ if( System.getProperty("os.name").startsWith("Win") ) {
+ copyFilteredResourceToDir(serviceMixBase, "bin/servicemix.bat", props);
+ } else {
+ copyFilteredResourceToDir(serviceMixBase, "bin/servicemix", props);
+ chmod(new File(serviceMixBase, "bin/servicemix"), "a+x");
+ }
+ Instance instance = new InstanceImpl(this, name, serviceMixBase.toString());
+ instances.put(name, instance);
+ saveState();
+ return instance;
+ }
+
+ public synchronized Instance[] getInstances() {
+ return instances.values().toArray(new Instance[0]);
+ }
+
+ public synchronized Instance getInstance(String name) {
+ return instances.get(name);
+ }
+
+ synchronized void forget(String name) {
+ instances.remove(name);
+ }
+
+ synchronized void saveState() throws IOException, BackingStoreException {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ Preferences child = prefs.node("Instances");
+ child.clear();
+ Instance[] data = getInstances();
+ child.putInt("count", data.length);
+ for (int i = 0; i < data.length; i++) {
+ child.put("item." + i + ".name", data[i].getName());
+ child.put("item." + i + ".loc", data[i].getLocation());
+ child.putInt("item." + i + ".pid", data[i].getPid());
+ }
+ prefs.flush();
+ prefs.sync();
+ }
+
+ private void copyResourceToDir(File target, String resource, boolean text) throws Exception {
+ File outFile = new File(target, resource);
+ if( !outFile.exists() ) {
+ println("Creating file: @|bold " + outFile.getPath() + "|");
+ InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+ try {
+ if( text ) {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } else {
+ // Binary so just write it out the way it came in.
+ FileOutputStream out = new FileOutputStream(new File(target, resource));
+ try {
+ int c=0;
+ while((c=is.read())>=0) {
+ out.write(c);
+ }
+ } finally {
+ safeClose(out);
+ }
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+ }
+
+ private void println(String st) {
+ ShellContext ctx = ShellContextHolder.get(true);
+ if (ctx != null) {
+ ctx.getIo().out.println(st);
+ } else {
+ System.out.println(st);
+ }
+ }
+
+ private void copyFilteredResourceToDir(File target, String resource, HashMap<String, String> props) throws Exception {
+ File outFile = new File(target, resource);
+ if( !outFile.exists() ) {
+ println("Creating file: @|bold "+outFile.getPath()+"|");
+ InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+ try {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+ }
+
+ private void safeClose(InputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private void safeClose(OutputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private String filter(String line, HashMap<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private void mkdir(File serviceMixBase, String path) {
+ File file = new File(serviceMixBase, path);
+ if( !file.exists() ) {
+ println("Creating dir: @|bold "+file.getPath()+"|");
+ file.mkdirs();
+ }
+ }
+
+ private int chmod(File serviceFile, String mode) throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("chmod", mode, serviceFile.getCanonicalPath());
+ Process p = builder.start();
+
+ // gnodet: Fix SMX4KNL-46: cpu goes to 100% after running the 'admin create' command
+ // Not sure exactly what happens, but commenting the process io redirection seems
+ // to work around the problem.
+ //
+ //PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+ //handler.attach(p);
+ //handler.start();
+ int status = p.waitFor();
+ //handler.stop();
+ return status;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java
new file mode 100644
index 0000000..a53b663
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.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.servicemix.kernel.gshell.admin.internal;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.AdminServiceMBean;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class AdminServiceMBeanImpl implements AdminServiceMBean {
+
+ private AdminService adminService;
+
+ public AdminService getAdminService() {
+ return adminService;
+ }
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ public void createInstance(String name, int port, String location) throws Exception {
+ adminService.createInstance(name, port, location);
+ }
+
+ public String[] getInstances() {
+ Instance[] instances = adminService.getInstances();
+ String[] names = new String[instances.length];
+ for (int i = 0; i < instances.length; i++) {
+ names[i] = instances[i].getName();
+ }
+ return names;
+ }
+
+ public int getPort(String name) throws Exception {
+ return getExistingInstance(name).getPort();
+ }
+
+ public void changePort(String name, int port) throws Exception {
+ getExistingInstance(name).changePort(port);
+ }
+
+ public String getState(String name) throws Exception {
+ return getExistingInstance(name).getState();
+ }
+
+ public void start(String name, String javaOpts) throws Exception {
+ getExistingInstance(name).start(javaOpts);
+ }
+
+ public void stop(String name) throws Exception {
+ getExistingInstance(name).stop();
+ }
+
+ public void destroy(String name) throws Exception {
+ getExistingInstance(name).destroy();
+ }
+
+
+ private Instance getExistingInstance(String name) {
+ Instance i = adminService.getInstance(name);
+ if (i == null) {
+ throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+ }
+ return i;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
new file mode 100644
index 0000000..3815ca5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
@@ -0,0 +1,220 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class InstanceImpl implements Instance {
+
+ private static final Log LOG = LogFactory.getLog(InstanceImpl.class);
+
+ private AdminServiceImpl service;
+ private String name;
+ private String location;
+ private Process process;
+
+ public InstanceImpl(AdminServiceImpl service, String name, String location) {
+ this.service = service;
+ this.name = name;
+ this.location = location;
+ }
+
+ public void attach(int pid) throws IOException {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance already started");
+ }
+ this.process = ProcessBuilderFactory.newInstance().newBuilder().attach(pid);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public int getPid() {
+ checkProcess();
+ return this.process != null ? this.process.getPid() : 0;
+ }
+
+ public int getPort() throws Exception {
+ InputStream is = null;
+ try {
+ File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+ is = new FileInputStream(f);
+ Properties props = new Properties();
+ props.load(is);
+ String loc = props.getProperty("sshPort");
+ return Integer.parseInt(loc);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ public void changePort(int port) throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance not stopped");
+ }
+ Properties props = new Properties();
+ File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+ InputStream is = new FileInputStream(f);
+ try {
+ props.load(is);
+ } finally {
+ is.close();
+ }
+ props.setProperty("sshPort", Integer.toString(port));
+ OutputStream os = new FileOutputStream(f);
+ try {
+ props.store(os, null);
+ } finally {
+ os.close();
+ }
+ }
+
+ public synchronized void start(String javaOpts) throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance already started");
+ }
+ if (javaOpts == null) {
+ javaOpts = "-server -Xmx512M -Dcom.sun.management.jmxremote";
+ }
+ File libDir = new File(System.getProperty("servicemix.home"), "lib");
+ File[] jars = libDir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".jar");
+ }
+ });
+ StringBuilder classpath = new StringBuilder();
+ for (File jar : jars) {
+ if (classpath.length() > 0) {
+ classpath.append(System.getProperty("path.separator"));
+ }
+ classpath.append(jar.getCanonicalPath());
+ }
+ String command = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath()
+ + " " + javaOpts
+ + " -Dservicemix.home=\"" + System.getProperty("servicemix.home") + "\""
+ + " -Dservicemix.base=\"" + new File(location).getCanonicalPath() + "\""
+ + " -Dservicemix.startLocalConsole=false"
+ + " -Dservicemix.startRemoteShell=true"
+ + " -classpath " + classpath.toString()
+ + " org.apache.servicemix.kernel.main.Main";
+ LOG.debug("Starting instance with command: " + command);
+ this.process = ProcessBuilderFactory.newInstance().newBuilder()
+ .directory(new File(location))
+ .command(command)
+ .start();
+ this.service.saveState();
+ }
+
+ public synchronized void stop() throws Exception {
+ checkProcess();
+ if (this.process == null) {
+ throw new IllegalStateException("Instance not started");
+ }
+ this.process.destroy();
+ }
+
+ public synchronized void destroy() throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance not stopped");
+ }
+ deleteFile(new File(location));
+ this.service.forget(name);
+ this.service.saveState();
+ }
+
+
+ public synchronized String getState() {
+ checkProcess();
+ if (this.process == null) {
+ return STOPPED;
+ } else {
+ try {
+ int port = getPort();
+ Socket s = new Socket("localhost", port);
+ s.close();
+ return STARTED;
+ } catch (Exception e) {
+ // ignore
+ }
+ return STARTING;
+ }
+ }
+
+ protected void checkProcess() {
+ if (this.process != null) {
+ try {
+ if (!this.process.isRunning()) {
+ this.process = null;
+ }
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ protected static boolean deleteFile(File fileToDelete) {
+ if (fileToDelete == null || !fileToDelete.exists()) {
+ return true;
+ }
+ boolean result = true;
+ if (fileToDelete.isDirectory()) {
+ File[] files = fileToDelete.listFiles();
+ if (files == null) {
+ result = false;
+ } else {
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (file.getName().equals(".") || file.getName().equals("..")) {
+ continue;
+ }
+ if (file.isDirectory()) {
+ result &= deleteFile(file);
+ } else {
+ result &= file.delete();
+ }
+ }
+ }
+ }
+ result &= fileToDelete.delete();
+ return result;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java
new file mode 100644
index 0000000..9da97b4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+public abstract class AdminCommandSupport extends OsgiCommandSupport {
+
+ private AdminService adminService;
+
+ public AdminService getAdminService() {
+ return adminService;
+ }
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ protected Instance getExistingInstance(String name) {
+ Instance i = adminService.getInstance(name);
+ if (i == null) {
+ throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+ }
+ return i;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java
new file mode 100644
index 0000000..adbf2f6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class ChangePortCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ @Argument(index=1, required=true, description="The new port")
+ private int port = 0;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).changePort(port);
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java
new file mode 100644
index 0000000..244b9f8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ConnectCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ @Option(name="-u", aliases={"--username"}, token="USERNAME", description="Remote user name")
+ private String username = "smx";
+
+ @Option(name="-p", aliases={"--password"}, token="PASSWORD", description="Remote user password")
+ private String password = "smx";
+
+ protected Object doExecute() throws Exception {
+ int port = getExistingInstance(instance).getPort();
+ ShellContextHolder.get().getShell().execute("ssh -l " + username + " -P " + password + " -p " + port + " localhost");
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java
new file mode 100644
index 0000000..d13c381
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+/**
+ * Creates a new servicemix instance
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class CreateCommand extends AdminCommandSupport
+{
+ @Option(name = "-p", aliases = { "--port"}, description = "Port number for remote shell connection")
+ private int port = 0;
+
+ @Option(name = "-l", aliases = { "--location"}, description = "Location of the new instance on the file system")
+ private String location;
+
+ @Argument(index=0, required=true, description="Name of the new ServiceMix instance")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getAdminService().createInstance(instance, port, location);
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java
new file mode 100644
index 0000000..6084caa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+/**
+ * Creates a new servicemix instance
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class DestroyCommand extends AdminCommandSupport
+{
+ @Argument(index=0, required=true, description="The name of the ServiceMix instance to destroy")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).destroy();
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
new file mode 100644
index 0000000..61f3afb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+/**
+ * List available instances
+ */
+public class ListCommand extends AdminCommandSupport {
+
+ @Option(name = "-l", aliases = { "--location" }, description = "Display instances location")
+ boolean location;
+
+ protected Object doExecute() throws Exception {
+ Instance[] instances = getAdminService().getInstances();
+ if (location) {
+ io.out.println(" Port State Pid Location");
+ } else {
+ io.out.println(" Port State Pid Name");
+ }
+ for (Instance instance : instances) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ String s = Integer.toString(instance.getPort());
+ for (int i = s.length(); i < 5; i++) {
+ sb.append(' ');
+ }
+ sb.append(s);
+ sb.append("] [");
+ String state = instance.getState();
+ while (state.length() < "starting".length()) {
+ state += " ";
+ }
+ sb.append(state);
+ sb.append("] [");
+ s = Integer.toString(instance.getPid());
+ for (int i = s.length(); i < 5; i++) {
+ sb.append(' ');
+ }
+ sb.append(s);
+ sb.append("] ");
+ if (location) {
+ sb.append(instance.getLocation());
+ } else {
+ sb.append(instance.getName());
+ }
+ io.out.println(sb.toString());
+ }
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
new file mode 100644
index 0000000..77afa91
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
@@ -0,0 +1,34 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+public class StartCommand extends AdminCommandSupport {
+
+ @Option(name = "-o", aliases = { "--java-opts"}, description = "Java options when launching the instance")
+ private String javaOpts;
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).start(javaOpts);
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
new file mode 100644
index 0000000..c449387
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class StopCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).stop();
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
new file mode 100644
index 0000000..cfcb95a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.completers;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import jline.Completor;
+
+/**
+ * {@link jline.Completor} for server instance names.
+ *
+ * Displays a list of configured server instances for the Admin commands.
+ *
+ */
+public class InstanceCompleter implements Completor {
+ private AdminService adminService;
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ public int complete(String buffer, int cursor, List candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ for (Instance instance : adminService.getInstances()) {
+ delegate.getStrings().add(instance.getName());
+ }
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
new file mode 100644
index 0000000..5f05513
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="adminCommandBundleSupport" scope="prototype">
+ <property name="adminService" ref="adminService" />
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="admin/create">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.CreateCommand"
+ parent="adminCommandBundleSupport" />
+ </gshell:command>
+ <gshell:command name="admin/connect">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ConnectCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ListCommand"
+ parent="adminCommandBundleSupport" />
+ </gshell:command>
+ <gshell:command name="admin/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StartCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/stop">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StopCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/destroy">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.DestroyCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/change-port">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ChangePortCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+ <bean id="adminService" class="org.apache.servicemix.kernel.gshell.admin.internal.AdminServiceImpl">
+ <property name="preferences" ref="preferences" />
+ </bean>
+
+ <bean id="instanceCompleter" class="org.apache.servicemix.kernel.gshell.admin.internal.completers.InstanceCompleter">
+ <property name="adminService" ref="adminService" />
+ </bean>
+
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
new file mode 100644
index 0000000..1d1d720
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#exec 1>${out.file}
+#exec 2>${err.file}
+exec 1>/dev/null
+exec 2>/dev/null
+if [ "x${dir}" != "x" ]; then
+ cd ${dir}
+fi
+nohup ${command} &
+echo $! > ${pid.file}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
new file mode 100644
index 0000000..abd60eb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
@@ -0,0 +1,27 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+ objProcess.Terminate()
+ intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
new file mode 100644
index 0000000..32c65c5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
@@ -0,0 +1,26 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+ intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
new file mode 100644
index 0000000..6004c86
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
@@ -0,0 +1,34 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set objConfig = objWMIService.Get("Win32_ProcessStartup").SpawnInstance_
+objConfig.ShowWindow = SW_HIDE
+objConfig.CreateFlags = 8
+If Len("${dir}") > 0 Then
+ intReturn = objWMIService.Get("Win32_Process").Create("${command}", "${dir}", objConfig, intProcessID)
+Else
+ intReturn = objWMIService.Get("Win32_Process").Create("${command}", Null, objConfig, intProcessID)
+End If
+If intReturn = 0 Then
+ Set objOutputFile = CreateObject("Scripting.fileSystemObject").CreateTextFile("${pid.file}", TRUE)
+ objOutputFile.WriteLine(intProcessID)
+ objOutputFile.Close
+End If
+WScript.Quit(intReturn)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
new file mode 100644
index 0000000..20a33e6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
@@ -0,0 +1,25 @@
+#!/bin/sh
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+SERVICEMIX_HOME=${servicemix.home}
+SERVICEMIX_BASE=${servicemix.base}
+
+export SERVICEMIX_BASE
+${SERVICEMIX_HOME}/bin/servicemix "$*"
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
new file mode 100644
index 0000000..13c155a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
@@ -0,0 +1,25 @@
+@ECHO OFF
+REM =========================================================================
+REM
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements. See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License. You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM
+REM =========================================================================
+
+SET SERVICEMIX_BASE=${servicemix.base}
+SETLOCAL
+SET SERVICEMIX_HOME=${servicemix.home}
+
+%SERVICEMIX_HOME%\bin\servicemix.bat %*
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
new file mode 100644
index 0000000..e6a8fd2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
@@ -0,0 +1,23 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+startLocalConsole=${servicemix.startLocalConsole}
+startRemoteShell=${servicemix.startRemoteShell}
+sshPort=${servicemix.sshPort}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..24ddea5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
@@ -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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, osgi:VmLogAppender
+
+# Logger infos
+log4j.logger.org.apache.geronimo.gshell.remote=WARN
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
new file mode 100644
index 0000000..d0317cd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
@@ -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.
+#
+################################################################################
+
+#
+# If set to true, the following property will not allow any certificate to be used
+# when accessing maven repositories through SSL
+#
+#org.ops4j.pax.url.mvn.certificateCheck=
+
+#
+# Path to the local maven settings file.
+# The repositories defined in this file will be automatically added to the list
+# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
+# below is not set.
+# The following locations are checked for the existence of the settings.xml file
+# * 1. looks for the specified url
+# * 2. if not found looks for ${user.home}/.m2/settings.xml
+# * 3. if not found looks for ${maven.home}/conf/settings.xml
+# * 4. if not found looks for ${M2_HOME}/conf/settings.xml
+#
+#org.ops4j.pax.url.mvn.settings=
+
+#
+# Path to the local maven repository which is used to avoid downloading
+# artifacts when they already exist locally.
+# The value of this property will be extracted from the settings.xml file
+# above, or defaulted to:
+# System.getProperty( "user.home" ) + "/.m2/repository"
+#
+#org.ops4j.pax.url.mvn.localRepository=
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# Those repositories will be checked before iterating through the
+ below list of repositories and even before the local repository
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folder as a repo.
+#
+org.ops4j.pax.url.mvn.defaultRepositories=file:${servicemix.home}/system@snapshots,file:${servicemix.base}/system@snapshots
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# The default list includes the following repositories:
+# http://repo1.maven.org/maven2
+# http://repository.ops4j.org/maven2
+# To add repositories to the default ones, prepend '+' to the list of repositories
+# to add.
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folders as a repo.
+#
+org.ops4j.pax.url.mvn.repositories=file:${servicemix.base}/system@snapshots,file:${user.home}/.m2/repository@snapshots,http://repo1.maven.org/maven2,http://people.apache.org/repo/m2-snapshot-repository@snapshots@noreleases,http://svn.apache.org/repos/asf/servicemix/m2-repo
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
new file mode 100644
index 0000000..255295e
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
+servicemix.name=${servicemix.name}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
new file mode 100644
index 0000000..0348fae
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+smx=smx,admin
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
new file mode 100644
index 0000000..6a7edbf
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the port of an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
new file mode 100644
index 0000000..1c78a34
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Connect to an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
new file mode 100644
index 0000000..532ada2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create a new instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
new file mode 100644
index 0000000..cb0581c
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Destroy an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
new file mode 100644
index 0000000..c473471
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing instances.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
new file mode 100644
index 0000000..dfb4046
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
new file mode 100644
index 0000000..0cd3acd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
new file mode 100644
index 0000000..1833584
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.servicemix.jpm;
+
+public class MainTest {
+
+ public static void main(String[] args) throws Exception {
+ Thread.sleep(Long.parseLong(args[0]));
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
new file mode 100644
index 0000000..8328813
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+
+public class ProcessTest extends TestCase {
+
+ public void testCreate() throws Exception {
+ String javaPath = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath();
+ System.err.println(javaPath);
+ StringBuilder command = new StringBuilder();
+ command.append(javaPath);
+ command.append(" -Dprop=\"key\"");
+ command.append(" -classpath ");
+ String clRes = getClass().getName().replace('.', '/') + ".class";
+ String str = getClass().getClassLoader().getResource(clRes).toString();
+ str = str.substring("file:".length(), str.indexOf(clRes));
+ command.append(str);
+ command.append(" ");
+ command.append(MainTest.class.getName());
+ command.append(" ");
+ command.append(60000);
+ System.err.println("Executing: " + command.toString());
+
+ ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+ Process p = builder.command(command.toString()).start();
+ assertNotNull(p);
+ System.err.println("Process: " + p.getPid());
+ assertNotNull(p.getPid());
+ Thread.currentThread().sleep(1000);
+ System.err.println("Running: " + p.isRunning());
+ assertTrue(p.isRunning());
+ System.err.println("Destroying");
+ p.destroy();
+ Thread.currentThread().sleep(1000);
+ System.err.println("Running: " + p.isRunning());
+ assertFalse(p.isRunning());
+ }
+
+ /*
+ * When the process creation fails, no error is reported by the script
+ *
+ public void testFailure() throws Exception {
+ ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+ Process p = builder.command("ec").start();
+ fail("An exception should have been thrown");
+ }
+ */
+}
diff --git a/karaf/gshell/gshell-config/pom.xml b/karaf/gshell/gshell-config/pom.xml
new file mode 100644
index 0000000..8bab513
--- /dev/null
+++ b/karaf/gshell/gshell-config/pom.xml
@@ -0,0 +1,93 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell ConfigAdmin Commands</name>
+
+ <description>
+ Provides the ConfigAdmin GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.config*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
new file mode 100644
index 0000000..e31ccfb
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class CancelCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ this.variables.parent().unset(PROPERTY_CONFIG_PID);
+ this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
new file mode 100644
index 0000000..5a28289
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
@@ -0,0 +1,64 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Abstract class from which all commands related to the ConfigurationAdmin
+ * service should derive.
+ * This command retrieves a reference to the ConfigurationAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class ConfigCommandSupport extends OsgiCommandSupport {
+
+ public static final String PROPERTY_CONFIG_PID = "ConfigCommand.PID";
+ public static final String PROPERTY_CONFIG_PROPS = "ConfigCommand.Props";
+
+ protected Object doExecute() throws Exception {
+ // Get config admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("ConfigurationAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ ConfigurationAdmin admin = (ConfigurationAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("ConfigAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected Dictionary getEditedProps() throws Exception {
+ return (Dictionary) this.variables.parent().get(PROPERTY_CONFIG_PROPS);
+ }
+
+ protected abstract void doExecute(ConfigurationAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java
new file mode 100644
index 0000000..f041da3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class EditCommand extends ConfigCommandSupport {
+
+ @Argument(required = true, description = "PID of the configuration")
+ String pid;
+
+ @Option(name = "--force", description = "Force the edition of this config, even if another one was under edition")
+ boolean force;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ String oldPid = (String) this.variables.get(PROPERTY_CONFIG_PID);
+ if (oldPid != null && !oldPid.equals(pid) && !force) {
+ io.err.println("Another config is being edited. Cancel / update first, or use the --force option");
+ return;
+ }
+ Dictionary props = admin.getConfiguration(pid).getProperties();
+ this.variables.parent().set(PROPERTY_CONFIG_PID, pid);
+ this.variables.parent().set(PROPERTY_CONFIG_PROPS, props);
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java
new file mode 100644
index 0000000..7657d6b
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class ListCommand extends ConfigCommandSupport {
+
+ @Argument(required = false, description = "LDAP query")
+ String query;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Configuration[] configs = admin.listConfigurations(query);
+ for (Configuration config : configs) {
+ io.out.println("----------------------------------------------------------------");
+ io.out.println("Pid: " + config.getPid());
+ if (config.getFactoryPid() != null) {
+ io.out.println("FactoryPid: " + config.getFactoryPid());
+ }
+ io.out.println("BundleLocation: " + config.getBundleLocation());
+ if (config.getProperties() != null) {
+ io.out.println("Properties:");
+ Dictionary props = config.getProperties();
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ io.out.println(" " + key + " = " + props.get(key));
+ }
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java
new file mode 100644
index 0000000..8e87dc7
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropDelCommand extends ConfigCommandSupport {
+
+ @Argument(index = 0, required = true, description = "the property to delete")
+ String prop;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ props.remove(prop);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java
new file mode 100644
index 0000000..2f7a446
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropListCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ io.out.println(" " + key + " = " + props.get(key));
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java
new file mode 100644
index 0000000..a0c3112
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropSetCommand extends ConfigCommandSupport {
+
+ @Argument(index = 0, required = true, description = "the property to set")
+ String prop;
+
+ @Argument(index = 1, required = true, description = "the value of the property")
+ String value;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ props.put(prop, value);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java
new file mode 100644
index 0000000..1c6683f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class UpdateCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ String pid = (String) this.variables.parent().get(PROPERTY_CONFIG_PID);
+ Configuration cfg = admin.getConfiguration(pid, null);
+ cfg.update(props);
+ this.variables.parent().unset(PROPERTY_CONFIG_PID);
+ this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java
new file mode 100644
index 0000000..7b63351
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.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.servicemix.kernel.gshell.config.completers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+
+/**
+ * {@link jline.Completor} for Configuration Admin configurations.
+ *
+ * Displays a list of existing config admin configurations for completion.
+ *
+ */
+public class ConfigurationCompleter implements Completor, ConfigurationListener {
+
+ private final StringsCompleter delegate = new StringsCompleter();
+
+ private ConfigurationAdmin admin;
+
+ public void setAdmin(ConfigurationAdmin admin) {
+ this.admin = admin;
+ }
+
+ @PostConstruct
+ public void init() {
+ Configuration[] configs;
+ try {
+ configs = admin.listConfigurations(null);
+ } catch (Exception e) {
+ return;
+ }
+
+ Collection<String> pids = new ArrayList<String>();
+
+ for (Configuration config : configs) {
+ if (config.getFactoryPid() != null) {
+ pids.add(config.getFactoryPid());
+ } else {
+ pids.add(config.getPid());
+ }
+ }
+
+ delegate.getStrings().addAll(pids);
+
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+ public void configurationEvent(ConfigurationEvent configurationEvent) {
+ String pid = configurationEvent.getFactoryPid()!=null ? configurationEvent.getFactoryPid() : configurationEvent.getPid();
+ if (configurationEvent.getType() == ConfigurationEvent.CM_DELETED) {
+ delegate.getStrings().remove(pid);
+ } else if (configurationEvent.getType() == ConfigurationEvent.CM_UPDATED) {
+ delegate.getStrings().add(pid);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java
new file mode 100644
index 0000000..e21a8e3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.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.servicemix.kernel.gshell.config.completers;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.config.ConfigCommandSupport;
+
+/**
+ * {@link jline.Completor} for Configuration Admin properties.
+ *
+ * Displays a list of existing properties based on the current configuration being edited.
+ *
+ */
+public class ConfigurationPropertyCompleter implements Completor {
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ Variables vars = ShellContextHolder.get().getVariables();
+ if (vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PID) == null) {
+ return -1;
+ }
+
+ Dictionary props = (Dictionary) vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PROPS);
+ StringsCompleter delegate = new StringsCompleter();
+
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ delegate.getStrings().add(key);
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
new file mode 100644
index 0000000..e5c9d6d
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="config/cancel">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.CancelCommand" />
+ </gshell:command>
+ <gshell:command name="config/edit">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.EditCommand"/>
+ <gshell:completers>
+ <ref bean="configCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.ListCommand" />
+ </gshell:command>
+ <gshell:command name="config/propdel">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropDelCommand" />
+ <gshell:completers>
+ <ref bean="configPropertyCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/proplist">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropListCommand" />
+ </gshell:command>
+ <gshell:command name="config/propset">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropSetCommand" />
+ <gshell:completers>
+ <ref bean="configPropertyCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/update">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.UpdateCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="configCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationCompleter" init-method="init">
+ <property name="admin" ref="configAdmin"/>
+ </bean>
+
+ <bean id="configPropertyCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationPropertyCompleter" />
+
+ <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+ <osgi:service ref="configCompleter" interface="org.osgi.service.cm.ConfigurationListener" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
new file mode 100644
index 0000000..1bc5226
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the changes to the configuration being edited.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
new file mode 100644
index 0000000..ca2f8e6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create or edit a configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
new file mode 100644
index 0000000..a5d2c31
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing configurations.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
new file mode 100644
index 0000000..9e76af6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Delete a property from the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
new file mode 100644
index 0000000..9dab240
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List properties from the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
new file mode 100644
index 0000000..df4a2dc
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set a property on the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
new file mode 100644
index 0000000..810f08f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Save and propagate changes from the configuration being edited.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/pom.xml b/karaf/gshell/gshell-core/pom.xml
new file mode 100644
index 0000000..19e9a27
--- /dev/null
+++ b/karaf/gshell/gshell-core/pom.xml
@@ -0,0 +1,480 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Core</name>
+
+ <description>
+ Provides the OSGi GShell integration
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.springframework.aop,
+ org.springframework.aop.framework,
+ org.springframework.beans.factory.annotation,
+ org.springframework.context.annotation,
+ org.springframework.osgi.service.importer,
+ org.aopalliance.aop,
+ org.apache.commons.vfs.provider.temp,
+ org.apache.commons.vfs.provider.ram,
+ jline*,
+ org.apache.servicemix.kernel.jaas.config;resolution:=optional,
+ org.apache.servicemix.kernel.version;resolution:=optional,
+ org.apache.servicemix.kernel.main.spi;resolution:=optional;version="1.0.0",
+ org.codehaus.plexus*;resolution:=optional,
+ org.apache.sshd.server.keyprovider,
+ org.apache.sshd.server.jaas,
+ org.jsecurity*;resolution:=optional,
+ *
+ </gshell.osgi.import>
+ <!-- TODO: remove plexus util package -->
+ <gshell.osgi.export>
+ org.apache.geronimo.gshell*;version="1.0.0.alpha-2-SNAPSHOT";-split-package:=merge-first,
+ org.apache.servicemix.kernel.gshell.core*,
+ org.codehaus.plexus.interpolation*;-split-package:=merge-first,
+ org.codehaus.plexus.util;-split-package:=merge-first
+ </gshell.osgi.export>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-builtin</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>oro</groupId>
+ <artifactId>oro</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-vfs</groupId>
+ <artifactId>commons-vfs</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-file</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-network</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-ssh</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-text</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.wisdom</groupId>
+ <artifactId>gshell-wisdom-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-ivy</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-xstore</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-jexl</groupId>
+ <artifactId>commons-jexl</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-httpclient</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-jexl</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-vfs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.oro</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jline</artifactId>
+ </dependency>
+
+ <!-- jsecurity is a dependency for the ssh commands -->
+ <dependency>
+ <groupId>org.jsecurity</groupId>
+ <artifactId>jsecurity</artifactId>
+ <version>0.9.0-RC2</version>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Import-Package>${gshell.osgi.import}</Import-Package>
+ <Export-Package>${gshell.osgi.export}</Export-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>org.apache.geronimo.gshell:gshell-api</include>
+ <include>org.apache.geronimo.gshell:gshell-application</include>
+ <include>org.apache.geronimo.gshell:gshell-parser</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-builtin</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-file</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-network</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-shell</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-text</include>
+ <include>org.apache.geronimo.gshell.support:gshell-ansi</include>
+ <include>org.apache.geronimo.gshell.support:gshell-artifact</include>
+ <include>org.apache.geronimo.gshell.support:gshell-chronos</include>
+ <include>org.apache.geronimo.gshell.support:gshell-clp</include>
+ <include>org.apache.geronimo.gshell.support:gshell-console</include>
+ <include>org.apache.geronimo.gshell.support:gshell-event</include>
+ <include>org.apache.geronimo.gshell.support:gshell-i18n</include>
+ <include>org.apache.geronimo.gshell.support:gshell-interpolation</include>
+ <include>org.apache.geronimo.gshell.support:gshell-io</include>
+ <include>org.apache.geronimo.gshell.support:gshell-security</include>
+ <include>org.apache.geronimo.gshell.support:gshell-spring</include>
+ <include>org.apache.geronimo.gshell.support:gshell-terminal</include>
+ <include>org.apache.geronimo.gshell.support:gshell-vfs</include>
+ <include>org.apache.geronimo.gshell.support:gshell-vfs-meta</include>
+ <include>org.apache.geronimo.gshell.support:gshell-yarn</include>
+ <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</include>
+ <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</include>
+ <include>org.codehaus.plexus:plexus-utils</include>
+ <include>org.codehaus.plexus:plexus-interpolation</include>
+ <include>${project.groupId}:${project.artifactId}</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-api</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-application</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-parser</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-builtin</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-file</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-network</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-shell</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-ssh</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-text</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-ansi</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-artifact</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-chronos</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-clp</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-console</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-event</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-i18n</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-interpolation</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-io</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-security</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-spring</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-terminal</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-vfs</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-vfs-meta</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-yarn</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.codehaus.plexus:plexus-utils</artifact>
+ <excludes>
+ <exclude>org/codehaus/plexus/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.codehaus.plexus:plexus-interpolation</artifact>
+ <excludes>
+ <exclude>org/codehaus/plexus/**</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <createSourcesJar>${createSourcesJar}</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>true</createDependencyReducedPom>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
new file mode 100644
index 0000000..1fc0355
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
@@ -0,0 +1,20 @@
+##
+## 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.
+##
+
+version=${pom.version}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
new file mode 100644
index 0000000..203e340
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
@@ -0,0 +1,208 @@
+/*
+ * 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.geronimo.gshell.commands.shell;
+
+import java.net.URI;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.selector.AggregateFileSelector;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+import org.apache.oro.text.GlobCompiler;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+/**
+ * Find files in a hierarchy.
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class FindAction
+ extends VfsActionSupport
+{
+ private final AggregateFileSelector selector = new AggregateFileSelector();
+
+ @Option(name="-name")
+ private void addNameFilter(final String name) throws MalformedPatternException {
+ log.debug("Adding -name selector for: {}", name);
+ selector.getSelectors().add(new NameSelector(name));
+ }
+
+ @Option(name="-iname")
+ private void addiNameFilter(final String name) throws MalformedPatternException {
+ log.debug("Adding -iname selector for: {}", name);
+ selector.getSelectors().add(new NameSelector(name, true));
+ }
+
+ @Option(name="-type")
+ private void addTypeFilter(final Type type) {
+ log.debug("Adding -type selector for: {}", type);
+ selector.getSelectors().add(new TypeSelector(type));
+ }
+
+ @Argument(required=true)
+ private String path;
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+
+ FileObject root = resolveFile(context, path);
+
+ ensureFileExists(root);
+
+ find(context, root, selector);
+
+ FileObjects.close(root);
+
+ return CommandAction.Result.SUCCESS;
+ }
+
+ private void find(final CommandContext context, final FileObject file, final FileSelector selector) throws FileSystemException {
+ assert context != null;
+ assert file != null;
+ assert selector != null;
+
+ FileObject[] files = file.findFiles(selector);
+
+ if (files != null && files.length != 0) {
+ for (FileObject child : files) {
+ display(context, child, file);
+ }
+ }
+ }
+
+ private void display(final CommandContext context, final FileObject file, final FileObject root) throws FileSystemException {
+ assert context != null;
+ assert file != null;
+
+ String path;
+ try {
+ path = new URI(this.path).resolve(root.getURL().toURI().relativize(file.getURL().toURI())).toString();
+ } catch (Exception e) {
+ path = file.getName().getPath();
+ }
+ IO io = context.getIo();
+ io.info(path);
+ }
+
+ //
+ // Type & TypeSelector
+ //
+
+ private enum Type
+ {
+ F, // normal file
+ D, // directory
+ }
+
+ private class TypeSelector
+ implements FileSelector
+ {
+ private final Type type;
+
+ public TypeSelector(final Type type) {
+ assert type != null;
+
+ this.type = type;
+
+ log.trace("Type: {}", type);
+ }
+
+ public boolean includeFile(final FileSelectInfo selection) throws Exception {
+ assert selection != null;
+
+ FileType ftype = selection.getFile().getType();
+
+ switch (type) {
+ case D:
+ return ftype == FileType.FOLDER;
+
+ case F:
+ return ftype == FileType.FILE;
+
+ // TODO: Handle FileType.FILE_OR_FOLDER
+
+ default:
+ return false;
+ }
+ }
+
+ public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+ return true;
+ }
+ }
+
+ //
+ // NameSelector
+ //
+
+ private class NameSelector
+ implements FileSelector
+ {
+ private final Pattern pattern;
+
+ private final PatternMatcher matcher;
+
+ public NameSelector(final String name, final boolean ignoreCase) throws MalformedPatternException {
+ assert name != null;
+
+ PatternCompiler compiler = new GlobCompiler();
+ int options;
+ if (ignoreCase) {
+ options = GlobCompiler.CASE_INSENSITIVE_MASK;
+ }
+ else {
+ options = GlobCompiler.DEFAULT_MASK;
+ }
+ this.pattern = compiler.compile(name, options);
+ this.matcher = new Perl5Matcher();
+
+ log.trace("Pattern: {}", pattern.getPattern());
+ }
+
+ public NameSelector(final String name) throws MalformedPatternException {
+ this(name, false);
+ }
+
+ public boolean includeFile(final FileSelectInfo selection) throws Exception {
+ assert selection != null;
+ return matcher.matches(selection.getFile().getName().getBaseName(), pattern);
+ }
+
+ public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
new file mode 100644
index 0000000..d3bb6de
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.jsecurity.SecurityUtils;
+import org.jsecurity.authc.AuthenticationException;
+import org.jsecurity.authc.UsernamePasswordToken;
+import org.jsecurity.mgt.SecurityManager;
+import org.jsecurity.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <a href="http://jsecurity.org">JSecurity</a> {@link PasswordAuthenticator}.
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class JSecurityPasswordAuthenticator
+ implements PasswordAuthenticator
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final SecurityManager securityManager;
+
+ public JSecurityPasswordAuthenticator(final SecurityManager securityManager) {
+ // securityManager can be null
+ this.securityManager = securityManager;
+ }
+
+ public JSecurityPasswordAuthenticator() {
+ this(null);
+ }
+
+ public Object authenticate(final String username, final String password) {
+ assert username != null;
+ assert password != null;
+
+ log.debug("Authenticating: {}/{}", username, password);
+
+ Subject currentUser;
+
+ if (securityManager != null) {
+ currentUser = securityManager.getSubject();
+ }
+ else {
+ currentUser = SecurityUtils.getSubject();
+ }
+
+ if (currentUser.isAuthenticated()) {
+ log.debug("Logging out current user: {}", currentUser.getPrincipal());
+ currentUser.logout();
+ }
+
+ try {
+ UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+ currentUser.login(token);
+ Object principal = currentUser.getPrincipal();
+ log.info("User [{}] logged in successfully", principal);
+ return principal;
+ }
+ catch (AuthenticationException e) {
+ log.error("Authentication failed: " + e, e);
+ return null;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java
new file mode 100644
index 0000000..9795f01
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.ShellFactory;
+import jline.Completor;
+import jline.History;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.console.Console;
+import org.apache.geronimo.gshell.console.JLineConsole;
+import org.apache.geronimo.gshell.console.completer.AggregateCompleter;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.registry.CommandResolver;
+import org.apache.geronimo.gshell.application.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SSHD {@link ShellFactory} which provides access to GShell.
+ *
+ * @version $Rev: 731517 $ $Date: 2009-01-05 11:25:19 +0100 (Mon, 05 Jan 2009) $
+ */
+public class ShellFactoryImpl
+ implements ShellFactory
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Application application;
+
+ private Console.Prompter prompter;
+
+ private CommandLineExecutor executor;
+
+ private History history;
+
+ private List<Completor> completers;
+
+ private Console.ErrorHandler errorHandler;
+
+ public Console.Prompter getPrompter() {
+ return prompter;
+ }
+
+ public void setPrompter(final Console.Prompter prompter) {
+ this.prompter = prompter;
+ }
+
+ public CommandLineExecutor getExecutor() {
+ return executor;
+ }
+
+ public void setExecutor(final CommandLineExecutor executor) {
+ this.executor = executor;
+ }
+
+ public History getHistory() {
+ return history;
+ }
+
+ public void setHistory(final History history) {
+ this.history = history;
+ }
+
+ public List<Completor> getCompleters() {
+ return completers;
+ }
+
+ public void setCompleters(final List<Completor> completers) {
+ this.completers = completers;
+ }
+
+ public Console.ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorHandler(final Console.ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public void setApplication(Application application) {
+ this.application = application;
+ }
+
+ public Shell createShell() {
+ return new ShellImpl();
+ }
+
+ public class ShellImpl
+ implements ShellFactory.Shell, org.apache.geronimo.gshell.shell.Shell, ShellContext, Runnable
+ {
+ private InputStream in;
+
+ private OutputStream out;
+
+ private OutputStream err;
+
+ private ExitCallback callback;
+
+ private IO io;
+
+ private Variables variables;
+
+ private boolean closed;
+
+ public void setInputStream(final InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(final OutputStream out) {
+ this.out = out;
+ }
+
+ public void setErrorStream(final OutputStream err) {
+ this.err = err;
+ }
+
+ public void setExitCallback(ExitCallback callback) {
+ this.callback = callback;
+ }
+
+ public void start(final Map<String,String> env) throws IOException {
+ this.io = new IO(in, out, err, false);
+
+ // Create variables, inheriting the application ones
+ this.variables = new Variables(application.getVariables());
+ // Set up additional env
+ if (env != null) {
+ for (Map.Entry<String,String> entry : env.entrySet()) {
+ this.variables.set(entry.getKey(), entry.getValue());
+ }
+ }
+ this.variables.set("gshell.prompt", application.getModel().getBranding().getPrompt());
+ this.variables.set(CommandResolver.GROUP, "/");
+ this.variables.set("gshell.username", env.get("USER"));
+ this.variables.set("gshell.hostname", application.getLocalHost());
+ // HACK: Add history for the 'history' command, since its not part of the Shell intf it can't really access it
+ this.variables.set("gshell.internal.history", getHistory(), true);
+ new Thread(this).start();
+ }
+
+ public void destroy() {
+ close();
+ }
+
+ public ShellContext getContext() {
+ return this;
+ }
+
+ public Object execute(final String line) throws Exception {
+
+ return executor.execute(getContext(), line);
+ }
+
+ public Object execute(final String command, final Object[] args) throws Exception {
+ return executor.execute(getContext(), args);
+ }
+
+ public Object execute(final Object... args) throws Exception {
+ return executor.execute(getContext(), args);
+ }
+
+ public boolean isOpened() {
+ return !closed;
+ }
+
+ public void close() {
+ closed = true;
+ Closer.close(in, out, err);
+ callback.onExit(0);
+ }
+
+ public boolean isInteractive() {
+ return false;
+ }
+
+ public void run(final Object... args) throws Exception {
+ Console.Executor executor = new Console.Executor()
+ {
+ public Result execute(final String line) throws Exception {
+ assert line != null;
+ try {
+ ShellImpl.this.execute(line);
+ }
+ catch (ExitNotification n) {
+ return Result.STOP;
+ }
+ return Result.CONTINUE;
+ }
+ };
+
+ IO io = getContext().getIo();
+
+ // Setup the console runner
+ JLineConsole console = new JLineConsole(executor, io);
+ console.setPrompter(getPrompter());
+ console.setErrorHandler(getErrorHandler());
+ console.setHistory(getHistory());
+
+ if (completers != null) {
+ // Have to use aggregate here to get the completion list to update properly
+ console.addCompleter(new AggregateCompleter(completers));
+ }
+
+ console.run();
+ }
+
+ public org.apache.geronimo.gshell.shell.Shell getShell() {
+ return this;
+ }
+
+ public IO getIo() {
+ return io;
+ }
+
+ public Variables getVariables() {
+ return variables;
+ }
+
+ public void run() {
+ ShellContext ctx = ShellContextHolder.get(true);
+
+ try {
+ ShellContextHolder.set(getContext());
+ run(new Object[0]);
+ }
+ catch (Exception e) {
+ log.error("Unhandled failure: " + e, e);
+ }
+ finally {
+ ShellContextHolder.set(ctx);
+ close();
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
new file mode 100644
index 0000000..cafc437
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
@@ -0,0 +1,169 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.util.NoCloseInputStream;
+import org.apache.sshd.common.util.NoCloseOutputStream;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.io.PromptReader;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Connect to a SSH server.
+ *
+ * @version $Rev: 721244 $ $Date: 2008-11-27 18:19:56 +0100 (Thu, 27 Nov 2008) $
+ */
+public class SshAction
+ implements CommandAction, BeanContainerAware
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Option(name="-l", aliases={"--username"})
+ private String username;
+
+ @Option(name="-P", aliases={"--password"})
+ private String password;
+
+ @Argument(required=true)
+ private String hostname;
+
+ @Option(name="-p", aliases={"--port"})
+ private int port = 22;
+
+ private BeanContainer container;
+
+ private ClientSession session;
+
+ public void setBeanContainer(final BeanContainer container) {
+ assert container != null;
+ this.container = container;
+ }
+
+ /**
+ * Helper to validate that prompted username or password is not null or empty.
+ */
+ private class UsernamePasswordValidator
+ implements PromptReader.Validator
+ {
+ private String type;
+
+ private int count = 0;
+
+ private int max = 3;
+
+ public UsernamePasswordValidator(final String type) {
+ assert type != null;
+
+ this.type = type;
+ }
+
+ public boolean isValid(final String value) {
+ count++;
+
+ if (value != null && value.trim().length() > 0) {
+ return true;
+ }
+
+ if (count >= max) {
+ throw new RuntimeException("Too many attempts; failed to prompt user for " + type + " after " + max + " tries");
+ }
+
+ return false;
+ }
+ }
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+ MessageSource messages = context.getCommand().getMessages();
+
+ //
+ // TODO: Parse hostname for <username>@<hostname>
+ //
+
+ io.info(messages.format("info.connecting", hostname, port));
+
+ // If the username/password was not configured via cli, then prompt the user for the values
+ if (username == null || password == null) {
+ PromptReader prompter = new PromptReader(io);
+ String text;
+
+ log.debug("Prompting user for credentials");
+
+ if (username == null) {
+ text = messages.getMessage("prompt.username");
+ username = prompter.readLine(text + ": ", new UsernamePasswordValidator(text));
+ }
+
+ if (password == null) {
+ text = messages.getMessage("prompt.password");
+ password = prompter.readPassword(text + ": ", new UsernamePasswordValidator(text));
+ }
+ }
+
+ // Create the client from prototype
+ SshClient client = container.getBean(SshClient.class);
+ log.debug("Created client: {}", client);
+ client.start();;
+
+ try {
+ ConnectFuture future = client.connect(hostname, port);
+ future.await();
+ session = future.getSession();
+ try {
+ io.info(messages.getMessage("info.connected"));
+
+ session.authPassword(username, password);
+ int ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+ if ((ret & ClientSession.AUTHED) == 0) {
+ io.err.println("Authentication failed");
+ return Result.FAILURE;
+ }
+
+ ClientChannel channel = session.createChannel("shell");
+ channel.setIn(new NoCloseInputStream(io.inputStream));
+ channel.setOut(new NoCloseOutputStream(io.outputStream));
+ channel.setErr(new NoCloseOutputStream(io.errorStream));
+ channel.open();
+ channel.waitFor(ClientChannel.CLOSED, 0);
+ } finally {
+ session.close(false);
+ }
+ } finally {
+ client.stop();
+ }
+
+ io.verbose(messages.getMessage("verbose.disconnected"));
+
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
new file mode 100644
index 0000000..db1cfb0
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.SshServer;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Start a SSH server.
+ *
+ * @version $Rev: 720411 $ $Date: 2008-11-25 05:32:43 +0100 (Tue, 25 Nov 2008) $
+ */
+public class SshServerAction
+ implements CommandAction, BeanContainerAware
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Option(name="-p", aliases={ "--port" })
+ private int port=8101;
+
+ @Option(name="-b", aliases={ "--background"})
+ private boolean background = true;
+
+ private BeanContainer container;
+
+ public void setBeanContainer(final BeanContainer container) {
+ assert container != null;
+
+ this.container = container;
+ }
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+ MessageSource messages = context.getCommand().getMessages();
+
+ SshServer server = container.getBean("sshServer", SshServer.class);
+
+ log.debug("Created server: {}", server);
+
+ server.setPort(port);
+
+ server.start();
+
+ io.info(messages.format("info.listening", port));
+
+ if (!background) {
+ synchronized (this) {
+ log.debug("Waiting for server to shutdown");
+
+ wait();
+ }
+
+ server.stop();
+ }
+
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
new file mode 100644
index 0000000..e81b247
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
@@ -0,0 +1,391 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+
+/**
+ * Sort lines of text
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ */
+public class SortAction extends VfsActionSupport {
+
+ @Option(name = "-f")
+ private boolean caseInsensitive;
+
+ @Option(name = "-r")
+ private boolean reverse;
+
+ @Option(name = "-u")
+ private boolean unique;
+
+ @Option(name = "-t")
+ private String separator;
+
+ @Option(name = "-b")
+ private boolean ignoreBlanks;
+
+ @Option(name = "-k", argumentRequired = true, multiValued = true)
+ private List<String> sortFields;
+
+ @Option(name = "-n")
+ private boolean numeric;
+
+ @Argument(index = 0, required=false)
+ private String path;
+
+
+ public Object execute(CommandContext context) throws Exception {
+ assert context != null;
+
+ if (path != null) {
+ FileObject file = resolveFile(context, path);
+
+ try {
+ sort(context, file);
+ }
+ finally {
+ FileObjects.close(file);
+ }
+ }
+ else {
+ sort(context.getIo().inputStream, context.getIo().out);
+ }
+ return Result.SUCCESS;
+ }
+
+ protected void sort(final CommandContext context, final FileObject file) throws Exception {
+ assert context != null;
+ assert file != null;
+
+ ensureFileExists(file);
+ ensureFileHasContent(file);
+ ensureFileIsReadable(file);
+
+ BufferedInputStream input = new BufferedInputStream(file.getContent().getInputStream());
+ try {
+ sort(input, context.getIo().out);
+ }
+ finally {
+ Closer.close(input);
+ }
+ }
+
+ protected void sort(InputStream input, PrintWriter out) throws Exception {
+ BufferedReader r = new BufferedReader(new InputStreamReader(input));
+ List<String> strings = new ArrayList<String>();
+ for (String s = r.readLine(); s != null; s = r.readLine()) {
+ strings.add(s);
+ }
+ char sep = (separator == null || separator.length() == 0) ? '\0' : separator.charAt(0);
+ Collections.sort(strings, new SortComparator(caseInsensitive, reverse, ignoreBlanks, numeric, sep, sortFields));
+ String last = null;
+ for (String s : strings) {
+ if (last == null) {
+ last = s;
+ } else if (!unique || !s.equals(last)) {
+ out.println(s);
+ }
+ }
+ }
+
+ public static class SortComparator implements Comparator<String> {
+
+ private boolean caseInsensitive;
+ private boolean reverse;
+ private boolean ignoreBlanks;
+ private boolean numeric;
+ private char separator;
+ private List<Key> sortKeys;
+
+ private static Pattern fpPattern;
+ static {
+ final String Digits = "(\\p{Digit}+)";
+ final String HexDigits = "(\\p{XDigit}+)";
+ final String Exp = "[eE][+-]?" + Digits;
+ final String fpRegex = "([\\x00-\\x20]*[+-]?(NaN|Infinity|(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|(\\.(" + Digits + ")(" + Exp + ")?)|(((0[xX]" + HexDigits + "(\\.)?)|(0[xX]" + HexDigits + "?(\\.)" + HexDigits + "))[pP][+-]?" + Digits + "))" + "[fFdD]?))[\\x00-\\x20]*)(.*)";
+ fpPattern = Pattern.compile(fpRegex);
+ }
+
+ public SortComparator(boolean caseInsensitive,
+ boolean reverse,
+ boolean ignoreBlanks,
+ boolean numeric,
+ char separator,
+ List<String> sortFields) {
+ this.caseInsensitive = caseInsensitive;
+ this.reverse = reverse;
+ this.separator = separator;
+ this.ignoreBlanks = ignoreBlanks;
+ this.numeric = numeric;
+ if (sortFields == null || sortFields.size() == 0) {
+ sortFields = new ArrayList<String>();
+ sortFields.add("1");
+ }
+ sortKeys = new ArrayList<Key>();
+ for (String f : sortFields) {
+ sortKeys.add(new Key(f));
+ }
+ }
+
+ public int compare(String o1, String o2) {
+ int res = 0;
+
+ List<Integer> fi1 = getFieldIndexes(o1);
+ List<Integer> fi2 = getFieldIndexes(o2);
+ for (Key key : sortKeys) {
+ int[] k1 = getSortKey(o1, fi1, key);
+ int[] k2 = getSortKey(o2, fi2, key);
+ if (key.numeric) {
+ Double d1 = getDouble(o1, k1[0], k1[1]);
+ Double d2 = getDouble(o2, k2[0], k2[1]);
+ res = d1.compareTo(d2);
+ } else {
+ res = compareRegion(o1, k1[0], k1[1], o2, k2[0], k2[1], key.caseInsensitive);
+ }
+ if (res != 0) {
+ if (key.reverse) {
+ res = - res;
+ }
+ break;
+ }
+ }
+ return res;
+ }
+
+ protected Double getDouble(String s, int start, int end) {
+ Matcher m = fpPattern.matcher(s.substring(start, end));
+ m.find();
+ return new Double(s.substring(0, m.end(1)));
+ }
+
+ protected int compareRegion(String s1, int start1, int end1, String s2, int start2, int end2, boolean caseInsensitive) {
+ int n1 = end1, n2 = end2;
+ for (int i1 = start1, i2 = start2; i1 < end1 && i2 < n2; i1++, i2++) {
+ char c1 = s1.charAt(i1);
+ char c2 = s2.charAt(i2);
+ if (c1 != c2) {
+ if (caseInsensitive) {
+ c1 = Character.toUpperCase(c1);
+ c2 = Character.toUpperCase(c2);
+ if (c1 != c2) {
+ c1 = Character.toLowerCase(c1);
+ c2 = Character.toLowerCase(c2);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ } else {
+ return c1 - c2;
+ }
+ }
+ }
+ return n1 - n2;
+ }
+
+ protected int[] getSortKey(String str, List<Integer> fields, Key key) {
+ int start;
+ int end;
+ if (key.startField * 2 < fields.size()) {
+ start = fields.get((key.startField - 1) * 2);
+ if (key.ignoreBlanksStart) {
+ while (start < fields.get((key.startField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(start))) {
+ start++;
+ }
+ }
+ if (key.startChar > 0) {
+ start = Math.min(start + key.startChar - 1, fields.get((key.startField - 1) * 2 + 1));
+ }
+ } else {
+ start = 0;
+ }
+ if (key.endField > 0 && key.endField * 2 < fields.size()) {
+ end = fields.get((key.endField - 1) * 2);
+ if (key.ignoreBlanksEnd) {
+ while (end < fields.get((key.endField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(end))) {
+ end++;
+ }
+ }
+ if (key.endChar > 0) {
+ end = Math.min(end + key.endChar - 1, fields.get((key.endField - 1) * 2 + 1));
+ }
+ } else {
+ end = str.length();
+ }
+ return new int[] { start, end };
+ }
+
+ protected List<Integer> getFieldIndexes(String o) {
+ List<Integer> fields = new ArrayList<Integer>();
+ if (o.length() > 0) {
+ if (separator == '\0') {
+ int i = 0;
+ fields.add(0);
+ for (int idx = 1; idx < o.length(); idx++) {
+ if (Character.isWhitespace(o.charAt(idx)) && !Character.isWhitespace(o.charAt(idx - 1))) {
+ fields.add(idx - 1);
+ fields.add(idx);
+ }
+ }
+ fields.add(o.length() - 1);
+ } else {
+ int last = -1;
+ for (int idx = o.indexOf(separator); idx >= 0; idx = o.indexOf(separator, idx + 1)) {
+ if (last >= 0) {
+ fields.add(last);
+ fields.add(idx - 1);
+ } else if (idx > 0) {
+ fields.add(0);
+ fields.add(idx - 1);
+ }
+ last = idx + 1;
+ }
+ if (last < o.length()) {
+ fields.add(last < 0 ? 0 : last);
+ fields.add(o.length() - 1);
+ }
+ }
+ }
+ return fields;
+ }
+
+ public class Key {
+ int startField;
+ int startChar;
+ int endField;
+ int endChar;
+ boolean ignoreBlanksStart;
+ boolean ignoreBlanksEnd;
+ boolean caseInsensitive;
+ boolean reverse;
+ boolean numeric;
+
+ public Key(String str) {
+ boolean modifiers = false;
+ boolean startPart = true;
+ boolean inField = true;
+ boolean inChar = false;
+ for (char c : str.toCharArray()) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!inField && !inChar) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ if (startPart) {
+ if (inChar) {
+ startChar = startChar * 10 + (c - '0');
+ } else {
+ startField = startField * 10 + (c - '0');
+ }
+ } else {
+ if (inChar) {
+ endChar = endChar * 10 + (c - '0');
+ } else {
+ endField = endField * 10 + (c - '0');
+ }
+ }
+ break;
+ case '.':
+ if (!inField) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ inField = false;
+ inChar = true;
+ break;
+ case 'n':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ numeric = true;
+ break;
+ case 'f':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ caseInsensitive = true;
+ break;
+ case 'r':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ reverse = true;
+ break;
+ case 'b':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ if (startPart) {
+ ignoreBlanksStart = true;
+ } else {
+ ignoreBlanksEnd = true;
+ }
+ break;
+ case ',':
+ inField = true;
+ inChar = false;
+ startPart = false;
+ break;
+ default:
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ }
+ if (!modifiers) {
+ ignoreBlanksStart = ignoreBlanksEnd = SortComparator.this.ignoreBlanks;
+ reverse = SortComparator.this.reverse;
+ caseInsensitive = SortComparator.this.caseInsensitive;
+ numeric = SortComparator.this.numeric;
+ }
+ if (startField < 1) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
new file mode 100644
index 0000000..7e71328
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
@@ -0,0 +1,144 @@
+/*
+ * 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.geronimo.gshell.vfs.provider.meta;
+
+import org.apache.commons.vfs.FileContentInfoFactory;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.AbstractFileObject;
+import org.apache.commons.vfs.provider.DefaultFileContent;
+import org.apache.geronimo.gshell.vfs.provider.meta.data.MetaData;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Meta file object.
+ *
+ * TODO: remove this file when gshell version is upgraded
+ *
+ * @version $Rev: 706033 $ $Date: 2008-10-19 17:36:15 +0200 (Sun, 19 Oct 2008) $
+ */
+public class MetaFileObject
+ extends AbstractFileObject
+{
+ private final MetaFileSystem fileSystem;
+
+ private MetaData data;
+ private FileContent content;
+
+ public MetaFileObject(final FileName fileName, final MetaFileSystem fileSystem) {
+ super(fileName, fileSystem);
+
+ // Save for uncasted typed access
+ this.fileSystem = fileSystem;
+ }
+
+ public MetaData getData() {
+ if (data == null) {
+ throw new IllegalStateException("Meta data has not been attached");
+ }
+
+ return data;
+ }
+
+ @Override
+ protected FileType doGetType() throws Exception {
+ return getData().getType();
+ }
+
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ return getData().getLastModified();
+ }
+
+ @Override
+ protected boolean doIsReadable() throws Exception {
+ return data.getBuffer() != null;
+ }
+
+ @Override
+ protected boolean doIsWriteable() throws Exception {
+ return false;
+ }
+
+ @Override
+ protected FileContentInfoFactory getFileContentInfoFactory() {
+ return fileSystem.getFileContentInfoFactory();
+ }
+
+ @Override
+ protected long doGetContentSize() throws Exception {
+ byte[] bytes = data.getBuffer();
+ return bytes != null ? bytes.length : 0;
+ }
+
+ @Override
+ protected InputStream doGetInputStream() throws Exception {
+ byte[] bytes = data.getBuffer();
+ if (bytes != null) {
+ return new ByteArrayInputStream(bytes);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Map<String,Object> doGetAttributes() {
+ return getData().getAttributes();
+ }
+
+ @Override
+ protected void doSetAttribute(final String name, final Object value) {
+ getData().getAttributes().put(name, value);
+ }
+
+ protected void doRemoveAttribute(final String name) {
+ getData().getAttributes().remove(name);
+ }
+
+ @Override
+ protected String[] doListChildren() throws Exception {
+ return fileSystem.listChildren(getName());
+ }
+
+ @Override
+ protected void doAttach() throws Exception {
+ if (data == null) {
+ data = fileSystem.lookupData(this);
+ }
+ content = new DefaultFileContent(this, getFileContentInfoFactory());
+ }
+
+ @Override
+ protected void doDetach() throws Exception {
+ data = null;
+ content = null;
+ }
+
+ @Override
+ public FileContent getContent() throws FileSystemException {
+ super.getContent();
+ return content;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
new file mode 100644
index 0000000..350fd63
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ClassPath;
+import org.apache.geronimo.gshell.application.model.ApplicationModel;
+import org.apache.geronimo.gshell.artifact.Artifact;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.springframework.beans.factory.InitializingBean;
+
+public class ApplicationImpl implements Application, InitializingBean {
+
+ private static final String EMBEDDED_PROPS = "org/apache/servicemix/kernel/version/embedded.properties";
+ private static final String SERVICEMIX_VERSION ="org/apache/servicemix/kernel/gshell/core/servicemix-version.properties";
+ private static final String VERSION_PROPERTY = "version";
+
+ private String id;
+ private IO io;
+ private ApplicationModel model;
+ private Variables variables;
+ private InetAddress localHost;
+ private File homeDir;
+ private URL embeddedResource = null;
+
+ public ApplicationImpl() throws Exception {
+ this.localHost = InetAddress.getLocalHost();
+ this.homeDir = detectHomeDir();
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Properties props = new Properties();
+ props.load(getClass().getClassLoader().getResourceAsStream(SERVICEMIX_VERSION));
+ String kernelVersion = props.getProperty(VERSION_PROPERTY);
+ this.model.setVersion(kernelVersion);
+ ServiceMixBranding smxBranding = (ServiceMixBranding) this.model.getBranding();
+ smxBranding.setVersion(kernelVersion);
+
+ if (this.getClass().getClassLoader().getResource(EMBEDDED_PROPS) != null) {
+ embeddedResource = this.getClass().getClassLoader().getResource(EMBEDDED_PROPS);
+ smxBranding.setEmbeddedResource(embeddedResource);
+ }
+ }
+
+ public URL getEmbeddedResource() {
+ return embeddedResource;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public IO getIo() {
+ return io;
+ }
+
+ public void setIo(IO io) {
+ this.io = io;
+ }
+
+ public Variables getVariables() {
+ return variables;
+ }
+
+ public void setVariables(Variables variables) {
+ this.variables = variables;
+ }
+
+ public ApplicationModel getModel() {
+ return model;
+ }
+
+ public void setModel(ApplicationModel model) {
+ this.model = model;
+ }
+
+ public ClassPath getClassPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ public File getHomeDir() {
+ if (homeDir == null) {
+ throw new IllegalStateException();
+ }
+ return homeDir;
+ }
+
+ public InetAddress getLocalHost() {
+ return localHost;
+ }
+
+ public String getUserName() {
+ return System.getProperty("user.name");
+ }
+
+ public Artifact getArtifact() {
+ return null;
+ }
+
+ private File detectHomeDir() {
+ String homePath = System.getProperty("user.home");
+ // And now lets resolve this sucker
+ File dir;
+ try {
+ dir = new File(homePath).getCanonicalFile();
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Failed to resolve home directory: " + homePath, e);
+ }
+ // And some basic sanity too
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new RuntimeException("Home directory configured but is not a valid directory: " + dir);
+ }
+
+ return dir;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java
new file mode 100644
index 0000000..7465900
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ApplicationConfiguration;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.event.EventPublisher;
+import org.apache.geronimo.gshell.io.SystemOutputHijacker;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.wisdom.application.ShellCreatedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class ApplicationManagerImpl implements ApplicationManager, ApplicationContextAware, InitializingBean, DisposableBean {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private EventPublisher eventPublisher;
+
+ private Application application;
+
+ private ApplicationContext applicationContext;
+
+ public ApplicationManagerImpl(EventPublisher eventPublisher, Application application) {
+ this.eventPublisher = eventPublisher;
+ this.application = application;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (!SystemOutputHijacker.isInstalled()) {
+ SystemOutputHijacker.install();
+ }
+ //SystemOutputHijacker.register(application.getIo().outputStream, application.getIo().errorStream);
+ }
+
+ public void destroy() {
+ SystemOutputHijacker.uninstall();
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public void configure(ApplicationConfiguration applicationConfiguration) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public Shell create() throws Exception {
+ final Shell shell = (Shell) applicationContext.getBean("shell");
+
+ log.debug("Created shell instance: {}", shell);
+
+ eventPublisher.publish(new ShellCreatedEvent(shell));
+
+ return shell;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java
new file mode 100644
index 0000000..7deacb1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class BeanContainerAwareProcessor implements InitializingBean, BeanPostProcessor, ApplicationContextAware {
+
+ private ApplicationContext applicationContext;
+ private BeanContainer container;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ this.container = new BeanContainerWrapper(applicationContext);
+ }
+
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ assert bean != null;
+
+ if (bean instanceof BeanContainerAware) {
+ ((BeanContainerAware)bean).setBeanContainer(container);
+ }
+
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ return bean;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
new file mode 100644
index 0000000..edc15d7
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+public class BeanContainerWrapper implements BeanContainer {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private ApplicationContext context;
+
+ public BeanContainerWrapper(ApplicationContext context) {
+ this.context = context;
+ }
+
+ public BeanContainer getParent() {
+ return null;
+ }
+
+ public ClassLoader getClassLoader() {
+ return context.getClassLoader();
+ }
+
+ public void loadBeans(String[] strings) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> T getBean(Class<T> type) {
+ assert type != null;
+
+ log.trace("Getting bean of type: {}", type);
+
+ String[] names = context.getBeanNamesForType(type);
+
+ if (names.length == 0) {
+ throw new NoSuchBeanDefinitionException(type, "No bean defined for type: " + type);
+ }
+ if (names.length > 1) {
+ throw new NoSuchBeanDefinitionException(type, "No unique bean defined for type: " + type + ", found matches: " + Arrays.asList(names));
+ }
+
+ return getBean(names[0], type);
+ }
+
+ public <T> T getBean(String name, Class<T> requiredType) {
+ assert name != null;
+ assert requiredType != null;
+
+ log.trace("Getting bean named '{}' of type: {}", name, requiredType);
+
+ return (T) context.getBean(name, requiredType);
+ }
+
+ public <T> Map<String, T> getBeans(Class<T> type) {
+ assert type != null;
+
+ log.trace("Getting beans of type: {}", type);
+
+ return (Map<String,T>) context.getBeansOfType(type);
+ }
+
+ public String[] getBeanNames() {
+ log.trace("Getting bean names");
+
+ return context.getBeanDefinitionNames();
+ }
+
+ public String[] getBeanNames(Class type) {
+ assert type != null;
+
+ log.trace("Getting bean names of type: {}", type);
+
+ return context.getBeanNamesForType(type);
+ }
+
+ public BeanContainer createChild(Collection<URL> urls) {
+ throw new UnsupportedOperationException();
+ }
+
+ public BeanContainer createChild() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
new file mode 100644
index 0000000..2111072
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
@@ -0,0 +1,175 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class CommandBundle implements BundleContextAware, InitializingBean, DisposableBean, ApplicationContextAware {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private CommandRegistry commandRegistry;
+
+ private AliasRegistry aliasRegistry;
+
+ private BundleContext bundleContext;
+
+ private List<Command> commands;
+
+ private List<Link> links;
+
+ private List<Alias> aliases;
+
+ private ApplicationContext applicationContext;
+
+ private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
+
+ public CommandBundle() {
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public List<Command> getCommands() {
+ return commands;
+ }
+
+ public void setCommands(final List<Command> commands) {
+ assert commands != null;
+
+ this.commands = commands;
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ assert links != null;
+
+ this.links = links;
+ }
+
+ public List<Alias> getAliases() {
+ return aliases;
+ }
+
+ public void setAliases(List<Alias> aliases) {
+ assert aliases != null;
+
+ this.aliases = aliases;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ log.debug("Initializing command bundle");
+ if (commandRegistry == null) {
+ String[] names = applicationContext.getBeanNamesForType(CommandRegistry.class);
+ if (names.length == 1) {
+ commandRegistry = (CommandRegistry) applicationContext.getBean(names[0], CommandRegistry.class);
+ }
+ }
+ if (aliasRegistry == null) {
+ String[] names = applicationContext.getBeanNamesForType(AliasRegistry.class);
+ if (names.length == 1) {
+ aliasRegistry = (AliasRegistry) applicationContext.getBean(names[0], AliasRegistry.class);
+ }
+ }
+ if (commandRegistry != null && aliasRegistry != null) {
+ log.debug("Command bundle is using the auto wired command/alias registry");
+ if (commands != null) {
+ for (Command command : commands) {
+ log.debug("Registering command: {}", command.getLocation());
+ commandRegistry.registerCommand(command);
+ }
+ }
+ if (links != null) {
+ for (Link link : links) {
+ log.debug("Registering link: {}", link.getName());
+ LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+ cmd.setLocation(new CommandLocationImpl(link.getName()));
+ commandRegistry.registerCommand(cmd);
+ }
+ }
+ if (aliases != null) {
+ for (Alias alias : aliases) {
+ log.debug("Registering alias: {}", alias.getName());
+ aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+ }
+ }
+ } else if (bundleContext != null) {
+ log.debug("Command bundle is using the OSGi registry");
+ if (commands != null) {
+ for (Command command : commands) {
+ log.debug("Registering command: {}", command.getLocation());
+ Dictionary props = new Properties();
+ props.put(OsgiCommandRegistry.NAME, command.getLocation().getFullPath());
+ registrations.add(bundleContext.registerService(Command.class.getName(), command, props));
+ }
+ }
+ if (links != null) {
+ for (Link link : links) {
+ log.debug("Registering link: {}", link.getName());
+ registrations.add(bundleContext.registerService(Link.class.getName(), link, new Properties()));
+ }
+ }
+ if (aliases != null) {
+ for (Alias alias : aliases) {
+ log.debug("Registering alias: {}", alias.getName());
+ Dictionary props = new Properties();
+ registrations.add(bundleContext.registerService(Alias.class.getName(), alias, new Properties()));
+ }
+ }
+ } else {
+ throw new Exception("Command bundle should be wired to the command/alias registry or be used in an OSGi context");
+ }
+ }
+
+ public void destroy() {
+ log.debug("Destroying command bundle");
+ for (ServiceRegistration reg : registrations) {
+ reg.unregister();
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
new file mode 100644
index 0000000..98c7193
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
@@ -0,0 +1,172 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class LocalConsole implements Runnable, BundleContextAware {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Shell shell;
+
+ private boolean createLocalShell;
+
+ private BundleContext bundleContext;
+
+ private MainService mainService;
+
+ private CountDownLatch frameworkStarted;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public MainService getMainService() {
+ return mainService;
+ }
+
+ public void setMainService(MainService mainService) {
+ this.mainService = mainService;
+ }
+
+ public Shell getShell() {
+ return shell;
+ }
+
+ public void setShell(Shell shell) {
+ this.shell = shell;
+ }
+
+ public boolean isCreateLocalShell() {
+ return createLocalShell;
+ }
+
+ public void setCreateLocalShell(boolean createLocalShell) {
+ this.createLocalShell = createLocalShell;
+ }
+
+ public void init() {
+ shell.getContext().getVariables().set("gshell.username", "smx");
+ frameworkStarted = new CountDownLatch(1);
+ getBundleContext().addFrameworkListener(new FrameworkListener(){
+ public void frameworkEvent(FrameworkEvent event) {
+ log.debug("Got event: " + event.getType());
+ if( event.getType() == FrameworkEvent.STARTED ) {
+ frameworkStarted.countDown();
+ }
+ }
+ });
+ if (createLocalShell) {
+ new Thread(this, "localShell").start();
+ }
+ }
+
+ public void destroy() {
+ if (createLocalShell) {
+ shell.close();
+ }
+ }
+
+ public void run() {
+ try {
+ String[] args = mainService.getArgs();
+ // If a command was specified on the command line, then just execute that command.
+ if (args != null && args.length > 0) {
+ waitForFrameworkToStart();
+ log.info("Executing Shell with arguments: " + Arrays.toString(args));
+ StringBuilder sb = new StringBuilder();
+ for (String arg : args) {
+ sb.append(arg).append(" ");
+ }
+ Object value = shell.execute(sb.toString());
+ if (mainService != null) {
+ if (value instanceof Number) {
+ mainService.setExitCode(((Number) value).intValue());
+ } else {
+ mainService.setExitCode(value != null ? 1 : 0);
+ }
+ log.info("Exiting shell due to terminated command");
+ }
+ } else {
+ shell.run();
+ }
+ } catch (ExitNotification e) {
+ if (mainService != null) {
+ mainService.setExitCode(0);
+ }
+ log.info("Exiting shell due received exit notification");
+ } catch (Throwable e) {
+ if (mainService != null) {
+ mainService.setExitCode(-1);
+ }
+ log.error("Exiting shell due to caught exception " + e, e);
+ } finally {
+ try {
+ shell.close();
+ } catch (Throwable t) {}
+ asyncShutdown();
+ }
+ }
+
+ /**
+ * Blocks until the framework has finished starting. We do this so that any installed
+ * bundles for commands get fully registered.
+ *
+ * @throws InterruptedException
+ */
+ private void waitForFrameworkToStart() throws InterruptedException {
+ log.info("Waiting from framework to start.");
+ if (frameworkStarted.await(60, TimeUnit.SECONDS)) {
+ log.info("System completed startup.");
+ } else {
+ log.warn("System took too long startup... continuing");
+ }
+ }
+
+ private void asyncShutdown() {
+ new Thread() {
+ public void run() {
+ try {
+ getBundleContext().getBundle(0).stop();
+ } catch (BundleException e) {
+ log.info("Caught exception while shutting down framework: " + e, e);
+ }
+ }
+ }.start();
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java
new file mode 100644
index 0000000..a09ce0b
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.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.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+
+public class OsgiAliasRegistry {
+
+ public static final String NAME = "name";
+ public static final String ALIAS = "alias";
+
+ private AliasRegistry aliasRegistry;
+
+ public OsgiAliasRegistry(AliasRegistry aliasRegistry) {
+ this.aliasRegistry = aliasRegistry;
+ }
+
+ public void register(final Alias alias, Map<String, ?> properties) throws Exception {
+ aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+ }
+
+ public void unregister(final Alias alias, Map<String, ?> properties) throws Exception {
+ aliasRegistry.removeAlias(alias.getName());
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java
new file mode 100644
index 0000000..c6817e8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.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.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+
+public class OsgiCommandRegistry {
+
+ public static final String NAME = "name";
+ public static final String TARGET = "target";
+
+ private CommandRegistry commandRegistry;
+
+ public OsgiCommandRegistry(CommandRegistry commandRegistry) {
+ this.commandRegistry = commandRegistry;
+ }
+
+ public void register(final Command command, Map<String, ?> properties) throws Exception {
+ commandRegistry.registerCommand(command);
+ }
+
+ public void unregister(final Command command, Map<String, ?> properties) throws Exception {
+ commandRegistry.removeCommand(command);
+ }
+
+ public void register(final Link link, Map<String, ?> properties) throws Exception {
+ LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+ cmd.setLocation(new CommandLocationImpl(link.getName()));
+ commandRegistry.registerCommand(cmd);
+ }
+
+ public void unregister(final Link link, Map<String, ?> properties) throws Exception {
+ commandRegistry.removeCommand(commandRegistry.getCommand(link.getName()));
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java
new file mode 100644
index 0000000..e13940a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.springframework.osgi.context.BundleContextAware;
+
+public abstract class OsgiCommandSupport implements CommandAction, BundleContextAware {
+
+ protected Log log = LogFactory.getLog(getClass());
+ protected BundleContext bundleContext;
+ protected CommandContext commandContext;
+ protected IO io;
+ protected Variables variables;
+ protected List<ServiceReference> usedReferences;
+
+ public Object execute(CommandContext commandContext) throws Exception {
+ this.commandContext = commandContext;
+ this.io = commandContext.getIo();
+ this.variables = commandContext.getVariables();
+ try {
+ return doExecute();
+ } finally {
+ ungetServices();
+ }
+ }
+
+ protected abstract Object doExecute() throws Exception;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ protected <T> List<T> getAllServices(Class<T> clazz, String filter) throws Exception {
+ ServiceReference[] references = getBundleContext().getAllServiceReferences(clazz.getName(), filter);
+ if (references == null) {
+ return null;
+ }
+ List<T> services = new ArrayList<T>();
+ for (ServiceReference ref : references) {
+ T t = getService(clazz, ref);
+ services.add(t);
+ }
+ return services;
+ }
+
+ protected <T> T getService(Class<T> clazz, ServiceReference reference) {
+ T t = (T) getBundleContext().getService(reference);
+ if (t != null) {
+ if (usedReferences == null) {
+ usedReferences = new ArrayList<ServiceReference>();
+ }
+ usedReferences.add(reference);
+ }
+ return t;
+ }
+
+ protected void ungetServices() {
+ if (usedReferences != null) {
+ for (ServiceReference ref : usedReferences) {
+ getBundleContext().ungetService(ref);
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
new file mode 100644
index 0000000..803b511
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
@@ -0,0 +1,188 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.apache.geronimo.gshell.ansi.AnsiBuffer;
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderWriter;
+import org.apache.geronimo.gshell.application.model.Branding;
+
+public class ServiceMixBranding extends Branding {
+
+ private String prompt;
+ private String[] banner;
+ private String displayName;
+ private String displayVersion;
+ private String displayLocation;
+ private String applicationName;
+ private String applicationVersion;
+ private String applicationLocation;
+
+ private String[] kernelBanner = {
+ " ____ _ __ __ _ ",
+ "/ ___| ___ _ ____ _(_) ___ ___| \\/ (_)_ __",
+ "\\___ \\ / _ \\ '__\\ \\ / / |/ __/ _ \\ |\\/| | \\ \\/ /",
+ " ___) | __/ | \\ V /| | (_| __/ | | | |> < ",
+ "|____/ \\___|_| \\_/ |_|\\___\\___|_| |_|_/_/\\_\\",
+ };
+
+ public ServiceMixBranding() {
+ banner = kernelBanner;
+ displayName = "ServiceMix Kernel";
+ displayLocation = "http://servicemix.apache.org/kernel/";
+ }
+
+ public void setEmbeddedResource(URL embeddedResource) {
+ Properties brandProps = loadPropertiesFile(embeddedResource);
+ String brandBanner = brandProps.getProperty("banner");
+ int i = 0;
+ char quot = '"';
+
+ StringTokenizer st = new StringTokenizer(brandBanner, ",");
+ banner = new String[st.countTokens()];
+
+ while (st.hasMoreTokens()) {
+ banner[i] = st.nextToken();
+ banner[i] = banner[i].substring(1, banner[i].lastIndexOf(quot));
+ i++;
+ }
+
+ applicationName = brandProps.getProperty("application.name");
+ applicationVersion = brandProps.getProperty("application.version");
+ applicationLocation = brandProps.getProperty("application.location");
+ }
+
+ public String getName() {
+ return "servicemix";
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setVersion(String version) {
+ displayVersion = version;
+ }
+
+ public String getVersion() {
+ return displayVersion;
+ }
+
+ public String getApplicationName() {
+ return applicationName;
+ }
+
+ public String getApplicationVersion() {
+ return applicationVersion;
+ }
+
+ public String getProgramName() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ public String getAboutMessage() {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new AnsiRenderWriter(writer);
+
+ out.println("For information about @|cyan " + displayName + "|, visit:");
+ out.println(" @|bold " + displayLocation + "| ");
+ out.flush();
+
+ if (applicationName != null && applicationVersion != null) {
+ out.println();
+ out.println(applicationName + " " + applicationVersion);
+ out.println();
+ if (applicationLocation != null) {
+ out.println("For information about @|cyan " + applicationName + "|, visit:");
+ out.println(" @|bold " + applicationLocation + "| ");
+ out.flush();
+ }
+ }
+
+ return writer.toString();
+ }
+
+ public String getWelcomeMessage() {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new AnsiRenderWriter(writer);
+
+ AnsiBuffer buff = new AnsiBuffer();
+
+ for (String line : banner) {
+ buff.attrib(line, AnsiCode.CYAN);
+ out.println(buff);
+ }
+
+ out.println();
+ out.println(" @|bold " + displayName + "| (" + displayVersion + ")");
+ if (applicationName != null && applicationVersion != null) {
+ out.println(" @|bold " + applicationName + "| (" + applicationVersion + ")");
+ }
+ out.println();
+ out.println("Type '@|bold help|' for more information.");
+ out.flush();
+
+ return writer.toString();
+ }
+
+ private static Properties loadPropertiesFile(URL brandPropURL) {
+ // Read the properties file.
+ Properties brandProps = new Properties();
+ InputStream is = null;
+ try {
+ is = brandPropURL.openConnection().getInputStream();
+ brandProps.load(is);
+ is.close();
+ }
+ catch (FileNotFoundException ex) {
+ // Ignore file not found.
+ }
+ catch (Exception ex) {
+ System.err.println(
+ "Error loading embedded properties from " + brandPropURL);
+ System.err.println("ServicemixBranding: " + ex);
+ try {
+ if (is != null) is.close();
+ }
+ catch (IOException ex2) {
+ // Nothing we can do.
+ }
+ return null;
+ }
+
+ return brandProps;
+ }
+
+}
+
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java
new file mode 100644
index 0000000..ae86534
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ShellWrapper implements Shell {
+
+ private Shell delegate;
+
+ public ShellWrapper(Shell delegate) {
+ this.delegate = delegate;
+ }
+
+ public ShellContext getContext() {
+ return delegate.getContext();
+ }
+
+ public Object execute(String s) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(s);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public Object execute(String s, Object[] objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(s, objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public Object execute(Object... objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public boolean isOpened() {
+ return delegate.isOpened();
+ }
+
+ public void close() {
+ delegate.close();
+ }
+
+ public boolean isInteractive() {
+ return delegate.isInteractive();
+ }
+
+ public void run(Object... objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ delegate.run(objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
new file mode 100644
index 0000000..1e52177
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
@@ -0,0 +1,118 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.CommandResult;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.wisdom.command.AliasCommand;
+
+public class WorkAroundAliasCommand extends AliasCommand {
+
+ private final CommandLineExecutor executor;
+
+ public WorkAroundAliasCommand(CommandLineExecutor executor) {
+ super(executor);
+ this.executor = executor;
+ setAction(new AliasCommandAction());
+ }
+
+ @Override
+ protected void prepareAction(final ShellContext context, final Object[] args) {
+ // HACK: Reset state for proper appendArgs muck
+ assert context != null;
+ assert args != null;
+
+ setAction(new AliasCommandAction());
+ log.trace("Preparing action");
+
+ IO io = context.getIo();
+ CommandAction action = getAction();
+
+ // Setup the command action
+ try {
+ // Process command line options/arguments
+ processArguments(io, action, args);
+ }
+ catch (Exception e) {
+ // Abort if preparation caused a failure
+ throw new AbortExecutionNotification(new CommandResult.FailureResult(e));
+ }
+ }
+
+ private class AliasCommandAction
+ implements CommandAction
+ {
+ @Argument
+ private List<String> appendArgs = null;
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+
+ StringBuilder buff = new StringBuilder();
+ buff.append(getAlias());
+
+ // If we have args to append, then do it
+ if (appendArgs != null && !appendArgs.isEmpty()) {
+ buff.append(" ");
+
+ // Append args quoted as they have already been processed by the parser
+ Iterator iter = appendArgs.iterator();
+ while (iter.hasNext()) {
+ //
+ // HACK: Using double quote instead of single quote for now as the parser's handling of single quote is broken
+ //
+
+ buff.append('"').append(iter.next()).append('"');
+ if (iter.hasNext()) {
+ buff.append(" ");
+ }
+ }
+ }
+
+ log.debug("Executing alias: {}", buff);
+
+ final Shell shell = ShellContextHolder.get().getShell();
+ ShellContext shellContext = new ShellContext() {
+ public Shell getShell() {
+ return shell;
+ }
+ public IO getIo() {
+ return context.getIo();
+ }
+ public Variables getVariables() {
+ return context.getVariables();
+ }
+ };
+ Object result = executor.execute(shellContext, buff.toString());
+
+ log.debug("Alias result: {}", result);
+
+ return result;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java
new file mode 100644
index 0000000..8f3b580
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.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.servicemix.kernel.gshell.core.commands;
+
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderer;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.servicemix.kernel.gshell.core.ServiceMixBranding;
+import org.codehaus.plexus.util.StringUtils;
+
+public class InfoAction implements CommandAction {
+
+ private ServiceMixBranding branding;
+
+ private IO io;
+
+ private AnsiRenderer renderer = new AnsiRenderer();
+ private NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
+ private NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
+
+ public InfoAction(ServiceMixBranding branding) {
+ this.branding = branding;
+ }
+
+ public Object execute(CommandContext context) throws Exception {
+ int maxNameLen;
+
+ RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+ OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
+ ThreadMXBean threads = ManagementFactory.getThreadMXBean();
+ MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
+ ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
+ io = context.getIo();
+
+ //
+ // print ServiceMix informations
+ //
+ maxNameLen = 25;
+ io.out.println("ServiceMix");
+ printValue("ServiceMix home", maxNameLen, System.getProperty("servicemix.home"));
+ printValue("ServiceMix base", maxNameLen, System.getProperty("servicemix.base"));
+ printValue("ServiceMix Kernel version", maxNameLen, branding.getParent().getVersion());
+
+ if (branding.getApplicationName() != null && branding.getApplicationVersion() != null) {
+ printValue(branding.getApplicationName() + " version", maxNameLen, branding.getApplicationVersion());
+ }
+ io.out.println();
+
+ io.out.println("JVM");
+ printValue("Java Virtual Machine", maxNameLen, runtime.getVmName() + " version " + runtime.getVmVersion());
+ printValue("Vendor", maxNameLen, runtime.getVmVendor());
+ printValue("Uptime", maxNameLen, printDuration(runtime.getUptime()));
+ try {
+ printValue("Process CPU time", maxNameLen, printDuration(getSunOsValueAsLong(os, "getProcessCpuTime") / 1000000));
+ } catch (Throwable t) {}
+ printValue("Total compile time", maxNameLen, printDuration(ManagementFactory.getCompilationMXBean().getTotalCompilationTime()));
+
+ io.out.println("Threads");
+ printValue("Live threads", maxNameLen, Integer.toString(threads.getThreadCount()));
+ printValue("Daemon threads", maxNameLen, Integer.toString(threads.getDaemonThreadCount()));
+ printValue("Peak", maxNameLen, Integer.toString(threads.getPeakThreadCount()));
+ printValue("Total started", maxNameLen, Long.toString(threads.getTotalStartedThreadCount()));
+
+ io.out.println("Memory");
+ printValue("Current heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getUsed()));
+ printValue("Maximum heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getMax()));
+ printValue("Committed heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getCommitted()));
+ printValue("Pending objects", maxNameLen, Integer.toString(mem.getObjectPendingFinalizationCount()));
+ for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+ String val = "Name = '" + gc.getName() + "', Collections = " + gc.getCollectionCount() + ", Time = " + printDuration(gc.getCollectionTime());
+ printValue("Garbage collector", maxNameLen, val);
+ }
+
+ io.out.println("Classes");
+ printValue("Current classes loaded", maxNameLen, printLong(cl.getLoadedClassCount()));
+ printValue("Total classes loaded", maxNameLen, printLong(cl.getTotalLoadedClassCount()));
+ printValue("Total classes unloaded", maxNameLen, printLong(cl.getUnloadedClassCount()));
+
+ io.out.println("Operating system");
+ printValue("Name", maxNameLen, os.getName() + " version " + os.getVersion());
+ printValue("Architecture", maxNameLen, os.getArch());
+ printValue("Processors", maxNameLen, Integer.toString(os.getAvailableProcessors()));
+ try {
+ printValue("Total physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalPhysicalMemorySize")));
+ printValue("Free physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreePhysicalMemorySize")));
+ printValue("Committed virtual memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getCommittedVirtualMemorySize")));
+ printValue("Total swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalSwapSpaceSize")));
+ printValue("Free swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreeSwapSpaceSize")));
+ } catch (Throwable t) {}
+
+ return null;
+ }
+
+ private long getSunOsValueAsLong(OperatingSystemMXBean os, String name) throws Exception {
+ Method mth = os.getClass().getMethod(name);
+ return (Long) mth.invoke(os);
+ }
+
+ private String printLong(long i) {
+ return fmtI.format(i);
+ }
+
+ private String printSizeInKb(double size) {
+ return fmtI.format((long) (size / 1024)) + " kbytes";
+ }
+
+ private String printDuration(double uptime) {
+ uptime /= 1000;
+ if (uptime < 60) {
+ return fmtD.format(uptime) + " seconds";
+ }
+ uptime /= 60;
+ if (uptime < 60) {
+ long minutes = (long) uptime;
+ String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
+ return s;
+ }
+ uptime /= 60;
+ if (uptime < 24) {
+ long hours = (long) uptime;
+ long minutes = (long) ((uptime - hours) * 60);
+ String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
+ if (minutes != 0) {
+ s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : "minute");
+ }
+ return s;
+ }
+ uptime /= 24;
+ long days = (long) uptime;
+ long hours = (long) ((uptime - days) * 60);
+ String s = fmtI.format(days) + (days > 1 ? " days" : " day");
+ if (hours != 0) {
+ s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : "hour");
+ }
+ return s;
+ }
+
+ void printSysValue(String prop, int pad) {
+ printValue(prop, pad, System.getProperty(prop));
+ }
+
+ void printValue(String name, int pad, String value) {
+ io.out.println(" " + renderer.render(AnsiRenderer.encode(StringUtils.rightPad(name, pad), AnsiCode.BOLD)) + " " + value);
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
new file mode 100644
index 0000000..45d9d7d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
@@ -0,0 +1,474 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.wisdom.command.AliasImpl;
+import org.apache.geronimo.gshell.wisdom.command.ConfigurableCommandCompleter;
+import org.apache.geronimo.gshell.wisdom.command.LinkImpl;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.apache.servicemix.kernel.gshell.core.CommandBundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.StringUtils;
+import org.springframework.util.xml.DomUtils;
+
+public class CommandParser extends AbstractBeanDefinitionParser {
+
+ public static final String ID = ID_ATTRIBUTE;
+
+ public static final String DESCRIPTION = "description";
+
+ public static final String PLUGIN_TEMPLATE = "pluginTemplate";
+
+ public static final String ACTION = "action";
+
+ public static final String ACTION_ID = "actionId";
+
+ public static final String COMMAND_TEMPLATE_SUFFIX = "CommandTemplate";
+
+ public static final String COMMAND_BUNDLE = "command-bundle";
+
+ public static final String NAME = "name";
+
+ public static final String LOCATION = "location";
+
+ public static final String COMMANDS = "commands";
+
+ public static final String COMMAND = "command";
+
+ public static final String TYPE = "type";
+
+ public static final String DOCUMENTER = "documenter";
+
+ public static final String COMPLETER = "completer";
+
+ public static final String COMPLETERS = "completers";
+
+ public static final String BEAN = "bean";
+
+ public static final String REF = "ref";
+
+ public static final String NULL = "null";
+
+ public static final String MESSAGE_SOURCE = "message-source";
+
+ public static final String MESSAGES = "messages";
+
+ public static final String PROTOTYPE = "prototype";
+
+ public static final String ALIAS = "alias";
+
+ public static final String ALIASES = "aliases";
+
+ public static final String LINK = "link";
+
+ public static final String LINKS = "links";
+
+ public static final String TARGET = "target";
+
+ @Override
+ protected boolean shouldGenerateId() {
+ return true;
+ }
+
+ @Override
+ protected boolean shouldGenerateIdAsFallback() {
+ return true;
+ }
+
+ protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext context) {
+ assert element != null;
+ assert context != null;
+
+ Builder builder = new Builder(context);
+ BeanDefinitionBuilder plugin = builder.parseCommandBundle(element);
+ return plugin.getBeanDefinition();
+ }
+
+ /**
+ * Helper to deal with command type.
+ */
+ private enum CommandType
+ {
+ STATELESS,
+ STATEFUL;
+
+ public static CommandType parse(final String text) {
+ assert text != null;
+
+ return valueOf(text.toUpperCase());
+ }
+
+ public String getTemplateName() {
+ return name().toLowerCase() + COMMAND_TEMPLATE_SUFFIX;
+ }
+
+ public void wire(final BeanDefinitionBuilder command, final BeanDefinitionHolder action) {
+ assert command != null;
+ assert action != null;
+
+ switch (this) {
+ case STATELESS:
+ command.addPropertyReference(ACTION, action.getBeanName());
+ break;
+
+ case STATEFUL:
+ command.addPropertyValue(ACTION_ID, action.getBeanName());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Helper to build plugin related bean definitions.
+ */
+ private class Builder
+ {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private ParserContext context;
+
+ public Builder(final ParserContext context) {
+ assert context != null;
+
+ this.context = context;
+ }
+
+ private String resolveId(final Element element, final BeanDefinition def) throws BeanDefinitionStoreException {
+ assert element != null;
+ assert def != null;
+
+ if (shouldGenerateId()) {
+ return context.getReaderContext().generateBeanName(def);
+ }
+
+ String id = element.getAttribute(ID_ATTRIBUTE);
+
+ if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
+ id = context.getReaderContext().generateBeanName(def);
+ }
+
+ return id;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private List<Element> getChildElements(final Element element, final String name) {
+ assert element != null;
+ assert name != null;
+
+ return DomUtils.getChildElementsByTagName(element, name);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private List<Element> getChildElements(final Element element, final String[] names) {
+ assert element != null;
+ assert names != null;
+
+ return DomUtils.getChildElementsByTagName(element, names);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private Element getChildElement(final Element element, final String name) {
+ assert element != null;
+ assert name != null;
+
+ List<Element> elements = DomUtils.getChildElementsByTagName(element, name);
+ if (elements != null && !elements.isEmpty()) {
+ return elements.get(0);
+ }
+ return null;
+ }
+
+ private BeanDefinitionParserDelegate createBeanDefinitionParserDelegate(final Element element) {
+ assert element != null;
+
+ BeanDefinitionParserDelegate parser = new BeanDefinitionParserDelegate(context.getReaderContext());
+ parser.initDefaults(element.getOwnerDocument().getDocumentElement());
+ return parser;
+ }
+
+ private BeanDefinitionHolder parseBeanDefinitionElement(final Element element) {
+ assert element != null;
+
+ BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(element);
+ return parser.parseBeanDefinitionElement(element);
+ }
+
+ private void parseAndApplyDescription(final Element element, final BeanDefinition def) {
+ assert element != null;
+ assert def != null;
+
+ Element desc = getChildElement(element, DESCRIPTION);
+ if (desc != null) {
+ if (def instanceof AbstractBeanDefinition) {
+ ((AbstractBeanDefinition)def).setDescription(desc.getTextContent());
+ }
+ }
+ }
+
+ private void parseAndApplyDescription(final Element element, final BeanDefinitionBuilder builder) {
+ assert element != null;
+ assert builder != null;
+
+ parseAndApplyDescription(element, builder.getRawBeanDefinition());
+ }
+
+ private BeanDefinitionHolder register(final BeanDefinitionHolder holder) {
+ assert holder != null;
+
+ registerBeanDefinition(holder, context.getRegistry());
+ return holder;
+ }
+
+ private BeanDefinitionHolder register(final BeanDefinition def, final String id) {
+ assert def != null;
+ assert id != null;
+
+ BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
+ return register(holder);
+ }
+
+ //
+ // <gshell:command-bundle>
+ //
+
+ private BeanDefinitionBuilder parseCommandBundle(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command bundle; element; {}", element);
+
+ BeanDefinitionBuilder bundle = BeanDefinitionBuilder.rootBeanDefinition(CommandBundle.class);
+ parseAndApplyDescription(element, bundle);
+
+ //
+ // TODO: Figure out how we can save the order of <gshell:command> and <gshell:link> so that 'help' displays them in the order they are defined
+ //
+
+ ManagedList commands = new ManagedList();
+ commands.addAll(parseCommands(element));
+ bundle.addPropertyValue(COMMANDS, commands);
+
+ ManagedList links = new ManagedList();
+ links.addAll(parseLinks(element));
+ bundle.addPropertyValue(LINKS, links);
+
+ ManagedList aliases = new ManagedList();
+ aliases.addAll(parseAliases(element));
+ bundle.addPropertyValue(ALIASES, aliases);
+
+ return bundle;
+ }
+
+ //
+ // <gshell:command>
+ //
+
+ private List<BeanDefinition> parseCommands(final Element element) {
+ assert element != null;
+
+ log.trace("Parse commands; element; {}", element);
+
+ List<BeanDefinition> commands = new ArrayList<BeanDefinition>();
+
+ List<Element> children = getChildElements(element, COMMAND);
+
+ for (Element child : children) {
+ BeanDefinitionBuilder command = parseCommand(child);
+ commands.add(command.getBeanDefinition());
+ }
+
+ return commands;
+ }
+
+ private BeanDefinitionBuilder parseCommand(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command; element; {}", element);
+
+ CommandType type = CommandType.parse(element.getAttribute(TYPE));
+ BeanDefinitionBuilder command = BeanDefinitionBuilder.childBeanDefinition(type.getTemplateName());
+ parseAndApplyDescription(element, command);
+
+ Element child;
+
+ // Required children elements
+
+ String name = element.getAttribute(NAME);
+ BeanDefinition def = new GenericBeanDefinition();
+ def.setBeanClassName(CommandLocationImpl.class.getName());
+ def.getConstructorArgumentValues().addGenericArgumentValue(name);
+ command.addPropertyValue(LOCATION, def);
+
+ child = getChildElement(element, ACTION);
+ BeanDefinitionHolder action = parseCommandAction(child);
+ type.wire(command, action);
+
+ // Optional children elements
+
+ child = getChildElement(element, DOCUMENTER);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(DOCUMENTER, holder.getBeanDefinition());
+ }
+
+ child = getChildElement(element, COMPLETER);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(COMPLETER, holder.getBeanDefinition());
+ }
+
+ child = getChildElement(element, COMPLETERS);
+ if (child != null) {
+ BeanDefinitionBuilder completer = parseCommandCompleters(child);
+ command.addPropertyValue(COMPLETER, completer.getBeanDefinition());
+ }
+
+ child = getChildElement(element, MESSAGE_SOURCE);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(MESSAGES, holder.getBeanDefinition());
+ }
+
+ //String id = resolveId(element, command.getBeanDefinition());
+ //BeanDefinitionHolder holder = register(command.getBeanDefinition(), id);
+
+ return command;
+ }
+
+ //
+ // <gshell:completers>
+ //
+
+ private BeanDefinitionBuilder parseCommandCompleters(final Element element) {
+ assert element != null;
+
+ BeanDefinitionBuilder completer = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableCommandCompleter.class);
+
+ ManagedList completers = new ManagedList();
+
+ List<Element> children = getChildElements(element, new String[] {BEAN, REF, NULL});
+
+ for (Element child : children) {
+ if (DomUtils.nodeNameEquals(child, BEAN)) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ // noinspection unchecked
+ completers.add(holder.getBeanDefinition());
+ }
+ else if (DomUtils.nodeNameEquals(child, REF)) {
+ BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(child);
+ RuntimeBeanReference ref = (RuntimeBeanReference) parser.parsePropertySubElement(child, completer.getRawBeanDefinition());
+ // noinspection unchecked
+ completers.add(ref);
+ }
+ else if (DomUtils.nodeNameEquals(child, NULL)) {
+ // noinspection unchecked
+ completers.add(null);
+ }
+ }
+
+ completer.addConstructorArgValue(completers);
+
+ return completer;
+ }
+
+ //
+ // <gshell:action>
+ //
+
+ private BeanDefinitionHolder parseCommandAction(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command action; element; {}", element);
+
+ // Construct the action
+ BeanDefinition action = parseBeanDefinitionElement(element).getBeanDefinition();
+
+ // All actions are configured as prototypes
+ action.setScope(PROTOTYPE);
+
+ // Generate id and register the bean
+ String id = resolveId(element, action);
+ return register(action, id);
+ }
+
+ //
+ // <gshell:link>
+ //
+
+ private List<Link> parseLinks(final Element element) {
+ assert element != null;
+
+ log.trace("Parse links; element; {}", element);
+
+ List<Link> links = new ArrayList<Link>();
+
+ List<Element> children = getChildElements(element, LINK);
+
+ for (Element child : children) {
+ String name = child.getAttribute(NAME);
+ String target = child.getAttribute(TARGET);
+
+ links.add(new LinkImpl(name, target));
+ }
+
+ return links;
+ }
+
+ //
+ // <gshell:alias>
+ //
+
+ private List<Alias> parseAliases(final Element element) {
+ assert element != null;
+
+ log.trace("Parse aliases; element; {}", element);
+
+ List<Alias> aliases = new ArrayList<Alias>();
+
+ List<Element> children = getChildElements(element, ALIAS);
+
+ for (Element child : children) {
+ String name = child.getAttribute(NAME);
+ String alias = child.getAttribute(ALIAS);
+
+ aliases.add(new AliasImpl(name, alias));
+ }
+
+ return aliases;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
new file mode 100644
index 0000000..3e464d3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+public class NamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser(CommandParser.COMMAND_BUNDLE, new CommandParser());
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
new file mode 100644
index 0000000..34e2527
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+
+public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+
+ public Object authenticate(String username, String password) {
+ return (username != null && username.equals(password)) ? username : null;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java
new file mode 100644
index 0000000..fbf9041
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.SshServer;
+
+public class SshServerFactory {
+
+ private SshServer server;
+
+ private boolean start;
+
+ public SshServerFactory(SshServer server) {
+ this.server = server;
+ }
+
+ public boolean isStart() {
+ return start;
+ }
+
+ public void setStart(boolean start) {
+ this.start = start;
+ }
+
+ public void start() throws Exception {
+ if (start) {
+ try {
+ server.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ }
+
+ public void stop() throws Exception {
+ if (start) {
+ server.stop();
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java
new file mode 100644
index 0000000..41a2bb8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.URLFileName;
+import org.apache.commons.vfs.provider.url.UrlFileObject;
+
+public class MvnFileObject extends UrlFileObject {
+
+ public MvnFileObject(MvnFileSystem fs, FileName fileName) {
+ super(fs, fileName);
+ }
+
+ protected URL createURL(final FileName name) throws MalformedURLException, FileSystemException, URIException
+ {
+ String url;
+ if (name instanceof URLFileName)
+ {
+ URLFileName urlName = (URLFileName) getName();
+
+ // TODO: charset
+ url = urlName.getURIEncoded(null);
+ }
+ else
+ {
+ url = getName().getURI();
+ }
+ if (url.startsWith("mvn:///")) {
+ url = "mvn:" + url.substring("mvn:///".length());
+ }
+ return new URL(url);
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
new file mode 100644
index 0000000..7d88cd6
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileProvider;
+
+public class MvnFileProvider extends UrlFileProvider {
+
+ /**
+ * Locates a file object, by absolute URI.
+ */
+ public synchronized FileObject findFile(final FileObject baseFile,
+ final String uri,
+ final FileSystemOptions fileSystemOptions)
+ throws FileSystemException
+ {
+ try
+ {
+ final URL url = new URL(uri);
+
+ URL rootUrl = new URL(url, "/");
+ final String key = this.getClass().getName() + rootUrl.toString();
+ FileSystem fs = findFileSystem(key, fileSystemOptions);
+ if (fs == null)
+ {
+ String extForm = rootUrl.toExternalForm();
+ final FileName rootName = getContext().parseURI(extForm);
+ // final FileName rootName =
+ // new BasicFileName(rootUrl, FileName.ROOT_PATH);
+ fs = new MvnFileSystem(rootName, fileSystemOptions);
+ addFileSystem(key, fs);
+ }
+ return fs.resolveFile(url.getPath());
+ }
+ catch (final MalformedURLException e)
+ {
+ throw new FileSystemException("vfs.provider.url/badly-formed-uri.error", uri, e);
+ }
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java
new file mode 100644
index 0000000..7583eb5
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileSystem;
+
+public class MvnFileSystem extends UrlFileSystem {
+
+ protected MvnFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) {
+ super(fileName, fileSystemOptions);
+ }
+
+ protected FileObject createFile(FileName fileName) {
+ return new MvnFileObject(this, fileName);
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..2315764
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell=org.apache.servicemix.kernel.gshell.core.config.NamespaceHandler
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..67eca68
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd=org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
new file mode 100644
index 0000000..dbcafd3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="vfsCommandActionTemplate" abstract="true">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="about">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+ <constructor-arg ref="application"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="help">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+ <constructor-arg ref="commandResolver"/>
+ </gshell:action>
+ <gshell:completers>
+ <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+ <constructor-arg>
+ <list>
+ <ref bean="aliasNameCompleter"/>
+ <ref bean="commandNameCompleter"/>
+ </list>
+ </constructor-arg>
+ </bean>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="exit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+ </gshell:command>
+
+ <gshell:link name="quit" target="exit"/>
+
+ <gshell:command name="echo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+ </gshell:command>
+
+ <gshell:command name="clear" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+ </gshell:command>
+
+ <gshell:command name="source">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+ <constructor-arg ref="commandLineExecutor"/>
+ <constructor-arg ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="set">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+ </gshell:command>
+
+ <gshell:command name="unset">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+ <gshell:completers>
+ <ref bean="variableNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="alias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="unalias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="aliasNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="history">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+ </gshell:command>
+
+ <gshell:command name="info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+ <constructor-arg ref="branding" />
+ </gshell:action>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="ssh">
+ <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshAction"/>
+ </gshell:command>
+
+ <gshell:command name="sshd">
+ <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshServerAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="exec">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.ExecuteAction"/>
+ </gshell:command>
+
+ <gshell:command name="java">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.JavaAction"/>
+ </gshell:command>
+
+ <gshell:command name="sleep">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.SleepAction"/>
+ </gshell:command>
+
+ <gshell:command name="date">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.DateAction"/>
+ </gshell:command>
+
+ <gshell:command name="edit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.EditAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="find">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="sort">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.SortAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="hostname">
+ <gshell:action class="org.apache.geronimo.gshell.commands.network.HostnameAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:link name="print" target="echo"/>
+
+ <gshell:command name="printf">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.PrintfAction"/>
+ </gshell:command>
+
+ <gshell:command name="cat">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="grep">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.GrepAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="cd">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ChangeDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="pwd" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.CurrentDirectoryAction" parent="vfsCommandActionTemplate"/>
+ </gshell:command>
+
+ <gshell:command name="ls">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="dir" target="ls"/>
+
+ <gshell:command name="cp">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.CopyAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="copy" target="cp"/>
+
+ <gshell:command name="rm">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.RemoveAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="del" target="rm"/>
+
+ <gshell:command name="fileinfo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.FileInfoAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="touch">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.TouchAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
new file mode 100644
index 0000000..a047339
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="localShellWrapped" parent="shell" init-method="init" destroy-method="close"/>
+
+ <bean id="localShell" class="org.apache.servicemix.kernel.gshell.core.ShellWrapper">
+ <constructor-arg ref="localShellWrapped" />
+ </bean>
+
+ <osgi:reference id="mainService" interface="org.apache.servicemix.kernel.main.spi.MainService"/>
+
+ <bean id="localConsole" class="org.apache.servicemix.kernel.gshell.core.LocalConsole">
+ <property name="createLocalShell" value="${servicemix.startLocalConsole}"/>
+ <property name="shell" ref="localShell"/>
+ <property name="mainService" ref="mainService" />
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..b99626a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="osgiCommandRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiCommandRegistry">
+ <constructor-arg ref="commandRegistry" />
+ </bean>
+
+ <bean id="osgiAliasRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiAliasRegistry">
+ <constructor-arg ref="aliasRegistry" />
+ </bean>
+
+ <osgi:list id="osgiCommands" interface="org.apache.geronimo.gshell.command.Command" cardinality="0..N">
+ <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:list id="osgiLinkCommands" interface="org.apache.geronimo.gshell.command.Link" cardinality="0..N">
+ <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:list id="osgiAliases" interface="org.apache.geronimo.gshell.command.Alias" cardinality="0..N">
+ <osgi:listener ref="osgiAliasRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:service ref="localShell" interface="org.apache.geronimo.gshell.shell.Shell">
+ </osgi:service>
+
+ <osgi:service ref="commandLineExecutor" interface="org.apache.geronimo.gshell.commandline.CommandLineExecutor">
+ </osgi:service>
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.shell">
+ <prop key="sshPort">8101</prop>
+ <prop key="sshRealm">servicemix</prop>
+ <prop key="hostKey">${servicemix.base}/etc/host.key</prop>
+ </osgix:cm-properties>
+
+ <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
new file mode 100644
index 0000000..d68ee51
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:jaas="http://servicemix.apache.org/jaas"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/jaas
+ http://servicemix.apache.org/schema/servicemix-jaas.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean name="sshClient" class="org.apache.sshd.SshClient" factory-method="setUpDefaultClient" init-method="start" destroy-method="stop">
+ </bean>
+
+ <bean name="sshServer" class="org.apache.sshd.SshServer" factory-method="setUpDefaultServer" scope="prototype">
+ <property name="port" value="${sshPort}" />
+ <property name="shellFactory">
+ <bean class="org.apache.geronimo.gshell.commands.ssh.ShellFactoryImpl">
+ <property name="application" ref="application" />
+ <property name="completers">
+ <list>
+ <ref bean="commandsCompleter"/>
+ <ref bean="aliasNameCompleter"/>
+ </list>
+ </property>
+ <property name="executor" ref="commandLineExecutor" />
+ <property name="prompter">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ <property name="errorHandler">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+ </property>
+ <property name="history">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ <property name="keyPairProvider" ref="keyPairProvider" />
+ <property name="passwordAuthenticator" ref="passwordAuthenticator" />
+ </bean>
+
+ <bean name="keyPairProvider" class="org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider">
+ <property name="path" value="${hostKey}" />
+ </bean>
+ <bean name="passwordAuthenticator" class="org.apache.sshd.server.jaas.JaasPasswordAuthenticator">
+ <property name="domain" value="${sshRealm}" />
+ </bean>
+
+ <bean id="sshServerFactory" class="org.apache.servicemix.kernel.gshell.core.sshd.SshServerFactory" init-method="start" destroy-method="stop">
+ <constructor-arg ref="sshServer" />
+ <property name="start" value="${servicemix.startRemoteShell}" />
+ </bean>
+
+ <!--
+ <jaas:config id="SshServer" rank="-1">
+ <jaas:module className="org.apache.geronimo.gshell.remote.server.auth.BogusLoginModule" flags="required" />
+ </jaas:config>
+ -->
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
new file mode 100644
index 0000000..3992f54
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="fileSystemAccess" class="org.apache.geronimo.gshell.vfs.FileSystemAccessImpl">
+ <constructor-arg ref="fileSystemManager"/>
+ </bean>
+
+ <bean id="fileObjectNameCompleter" class="org.apache.geronimo.gshell.vfs.FileObjectNameCompleter">
+ <constructor-arg ref="fileSystemAccess"/>
+ </bean>
+
+ <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
+ <property name="customEditors">
+ <map>
+ <entry key="org.apache.commons.vfs.CacheStrategy">
+ <bean class="org.apache.geronimo.gshell.vfs.config.CacheStrategyEditor"/>
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <!--
+ TODO: Set tempDir File arg in constructor to set the temporary location, may need 2 replicators?
+ -->
+ <bean id="defaultFileReplicator" class="org.apache.commons.vfs.impl.DefaultFileReplicator"/>
+
+ <bean id="fileSystemManager" class="org.apache.geronimo.gshell.vfs.config.FileSystemManagerFactoryBean">
+ <property name="filesCache">
+ <bean class="org.apache.commons.vfs.cache.SoftRefFilesCache"/>
+ </property>
+
+ <property name="cacheStrategy" value="ON_RESOLVE"/>
+
+ <property name="fileReplicator">
+ <bean class="org.apache.commons.vfs.impl.PrivilegedFileReplicator">
+ <constructor-arg ref="defaultFileReplicator"/>
+ </bean>
+ </property>
+
+ <!--
+ TODO: Try and root the temporary store under ${gshell.home}/tmp or something
+ -->
+ <property name="temporaryFileStore">
+ <ref bean="defaultFileReplicator"/>
+ </property>
+
+ <property name="fileContentInfoFactory">
+ <bean class="org.apache.commons.vfs.impl.FileContentInfoFilenameFactory"/>
+ </property>
+
+ <property name="defaultProvider">
+ <bean class="org.apache.commons.vfs.provider.url.UrlFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="tmp"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.temp.TemporaryFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="ram"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.ram.RamFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="file"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.local.DefaultLocalFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="mvn"/>
+ <property name="provider">
+ <bean class="org.apache.servicemix.kernel.gshell.core.vfs.mvn.MvnFileProvider"/>
+ </property>
+ </bean>
+
+ <bean id="metaDataRegistry" class="org.apache.geronimo.gshell.vfs.provider.meta.data.MetaDataRegistryImpl">
+ <constructor-arg ref="eventManager" />
+ </bean>
+
+ <bean id="metaFileProviderConfigurer" class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="meta"/>
+ <property name="provider">
+ <bean class="org.apache.geronimo.gshell.vfs.provider.meta.MetaFileProvider">
+ <constructor-arg ref="metaDataRegistry"/>
+ </bean>
+ </property>
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
new file mode 100644
index 0000000..8bc494e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="io" class="org.apache.geronimo.gshell.io.IO">
+ <property name="verbosity" value="DEBUG"/>
+ </bean>
+
+ <bean id="branding" class="org.apache.servicemix.kernel.gshell.core.ServiceMixBranding">
+ <property name="prompt" value="@|bold %{gshell.username}|@%{application.id}:@|bold %{gshell.group}|> " />
+ </bean>
+
+ <bean id="application" class="org.apache.servicemix.kernel.gshell.core.ApplicationImpl">
+ <property name="id" value="${servicemix.name}"/>
+ <property name="io" ref="io"/>
+ <property name="model">
+ <bean class="org.apache.geronimo.gshell.application.model.ApplicationModel">
+ <property name="branding" ref="branding"/>
+ </bean>
+ </property>
+ <property name="variables">
+ <bean class="org.apache.geronimo.gshell.command.Variables"/>
+ </property>
+ </bean>
+
+ <bean id="eventManager" class="org.apache.geronimo.gshell.event.EventManagerImpl"/>
+
+ <bean id="applicationManager" class="org.apache.servicemix.kernel.gshell.core.ApplicationManagerImpl">
+ <constructor-arg ref="eventManager" />
+ <constructor-arg ref="application" />
+ </bean>
+
+ <bean id="commandLineParser" class="org.apache.geronimo.gshell.parser.CommandLineParser"/>
+
+ <bean id="aliasRegistry" class="org.apache.geronimo.gshell.wisdom.registry.AliasRegistryImpl">
+ <constructor-arg ref="eventManager"/>
+ </bean>
+
+ <bean id="aliasMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.AliasMetaMapper">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="metaDataRegistry"/>
+ <constructor-arg ref="aliasRegistry"/>
+ </bean>
+
+ <bean id="commandRegistry" class="org.apache.geronimo.gshell.wisdom.registry.CommandRegistryImpl">
+ <constructor-arg ref="eventManager"/>
+ </bean>
+
+ <bean id="commandMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.CommandMetaMapper">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="metaDataRegistry"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="groupDirResolver" class="org.apache.geronimo.gshell.wisdom.registry.GroupDirectoryResolver">
+ <constructor-arg ref="fileSystemAccess"/>
+ </bean>
+
+ <bean id="commandResolver" class="org.apache.geronimo.gshell.wisdom.registry.CommandResolverImpl">
+ <constructor-arg ref="fileSystemAccess"/>
+ <constructor-arg ref="groupDirResolver"/>
+ </bean>
+
+ <bean class="org.apache.servicemix.kernel.gshell.core.WorkAroundAliasCommand" scope="prototype">
+ <constructor-arg ref="commandLineExecutor"/>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.wisdom.command.GroupCommand" scope="prototype"/>
+
+ <bean id="commandLineBuilder" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineBuilderImpl">
+ <constructor-arg ref="commandLineParser"/>
+ </bean>
+
+ <bean id="commandLineExecutor" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineExecutorImpl">
+ <constructor-arg ref="commandResolver"/>
+ <constructor-arg ref="commandLineBuilder"/>
+ </bean>
+
+ <bean id="shell" class="org.apache.geronimo.gshell.wisdom.shell.ShellImpl" scope="prototype" init-method="init" destroy-method="close">
+ <constructor-arg ref="application"/>
+ <constructor-arg ref="commandLineExecutor"/>
+
+ <property name="completers">
+ <list>
+ <ref bean="commandsCompleter"/>
+ <ref bean="aliasNameCompleter"/>
+ </list>
+ </property>
+ <property name="prompter">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ <property name="errorHandler">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+ </property>
+ <property name="history">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="commandNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandNameCompleter"
+ lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="aliasNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.AliasNameCompleter"
+ lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="aliasRegistry"/>
+ </bean>
+
+ <bean id="commandsCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandsCompleter" lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="variableNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.VariableNameCompleter" lazy-init="true">
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
new file mode 100644
index 0000000..e4e787d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Sort lines of text.
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
new file mode 100644
index 0000000..8b91ee1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd">
+
+ <bean class="org.apache.servicemix.kernel.gshell.core.BeanContainerAwareProcessor" />
+
+ <bean id="statelessCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatelessCommand" abstract="true">
+ <property name="documenter">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+ </property>
+
+ <property name="messages">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+ </property>
+ </bean>
+
+ <bean id="statefulCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatefulCommand" abstract="true">
+ <property name="documenter">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+ </property>
+
+ <property name="messages">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+ </property>
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
new file mode 100644
index 0000000..755f0c8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display JVM informations about the current application.
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
new file mode 100644
index 0000000..36a1e1d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://servicemix.apache.org/schema/servicemix-gshell"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ targetNamespace="http://servicemix.apache.org/schema/servicemix-gshell"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+
+ <xsd:import namespace="http://www.springframework.org/schema/beans"/>
+
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the configuration elements for Apache ServiceMix Kernel commands support.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:element name="command-bundle">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command bundle.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="command"/>
+ <xsd:element ref="alias"/>
+ <xsd:element ref="link"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="command">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ <!--
+ NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+ validate the min/max of the containted elements. W/o the xsd:choice the validation
+ works, though have to define elements in order :-(
+ -->
+ <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+ <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+ <xsd:choice minOccurs="0" maxOccurs="1">
+ <xsd:element ref="completer"/>
+ <xsd:element ref="completers"/>
+ </xsd:choice>
+ <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="type" use="optional" default="stateful">
+ <xsd:annotation>
+ <xsd:documentation>
+ The command type.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="stateless"/>
+ <xsd:enumeration value="stateful"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="link">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a link command.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:group name="commandComponentElements">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the valid elements for command components. This is based on beans:beanElements,
+ stripping off the bits which are not valid in the command component context.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0"/>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="beans:meta"/>
+ <xsd:element ref="beans:constructor-arg"/>
+ <xsd:element ref="beans:property"/>
+ <xsd:element ref="beans:qualifier"/>
+ <xsd:element ref="beans:lookup-method"/>
+ <xsd:element ref="beans:replaced-method"/>
+ <!--
+ NOTE: This seems to cause schema validation problems... not really sure why
+ <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+ -->
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:group>
+
+ <xsd:attributeGroup name="commandComponentAttributes">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the valid attributes for command components. This is based on beans:beanAttributes,
+ stripping off the bits which are not valid in the command component context.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="class" type="xsd:string"/>
+ <xsd:attribute name="parent" type="xsd:string"/>
+ <xsd:attribute name="autowire" default="default">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="default"/>
+ <xsd:enumeration value="no"/>
+ <xsd:enumeration value="byName"/>
+ <xsd:enumeration value="byType"/>
+ <xsd:enumeration value="constructor"/>
+ <xsd:enumeration value="autodetect"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="dependency-check" default="default">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="default"/>
+ <xsd:enumeration value="none"/>
+ <xsd:enumeration value="simple"/>
+ <xsd:enumeration value="objects"/>
+ <xsd:enumeration value="all"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="depends-on" type="xsd:string"/>
+ <xsd:attribute name="autowire-candidate" default="default" type="beans:defaultable-boolean"/>
+ <xsd:attribute name="primary" type="xsd:boolean"/>
+ <xsd:attribute name="init-method" type="xsd:string"/>
+ <xsd:attribute name="destroy-method" type="xsd:string"/>
+ <xsd:attribute name="factory-method" type="xsd:string"/>
+ <xsd:attribute name="factory-bean" type="xsd:string"/>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ </xsd:attributeGroup>
+
+ <xsd:complexType name="commandComponent" abstract="true">
+ <xsd:annotation>
+ <xsd:documentation>
+ Support for command component elements, which are all basically just beans.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:group ref="commandComponentElements"/>
+ <xsd:attributeGroup ref="commandComponentAttributes"/>
+ </xsd:complexType>
+
+ <xsd:element name="action">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command action.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="documenter">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command documenter.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="completer">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command completer.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="completers">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a configurable command completer with a set of completers.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="beans:bean"/>
+ <xsd:element ref="beans:ref"/>
+ <xsd:element ref="beans:null"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="message-source">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command message source.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="alias">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command alias.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="alias" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
new file mode 100644
index 0000000..90e405e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+/**
+ * TODO: remove this file when gshell is upgraded
+ */
+public class SortTest extends TestCase {
+
+ public void testFieldIndexesDefaultSep() {
+ SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '\0', null);
+ List<Integer> indexes = comparator.getFieldIndexes(" ad re t ");
+ assertTrue(Arrays.asList(0, 2, 3, 6, 7, 9, 10, 10).equals(indexes));
+ }
+
+ public void testFieldIndexesWithSep() {
+ SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '[', null);
+ List<Integer> indexes = comparator.getFieldIndexes("[ 10] [Active ] [ ] [ 8] OPS4J Pax Logging - Service (1.3.0)");
+ assertTrue(Arrays.asList(1, 6, 8, 20, 22, 30, 32, 73 ).equals(indexes));
+
+ indexes = comparator.getFieldIndexes(" ad re t ");
+ assertTrue(Arrays.asList(0, 10).equals(indexes));
+ }
+
+ public void testSort() {
+ String s0 = "0321 abcd ddcba a";
+ String s1 = " 57t bcad ddacb b";
+ String s2 = " 128 cab ddbac c";
+ List<String> strings = Arrays.asList(s0, s1, s2);
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2")));
+ assertTrue(Arrays.asList(s0, s1, s2).equals(strings));
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2.2b")));
+ assertTrue(Arrays.asList(s2, s0, s1).equals(strings));
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, true, '\0', null));
+ assertTrue(Arrays.asList(s1, s2, s0).equals(strings));
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
new file mode 100644
index 0000000..0983b12
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
@@ -0,0 +1,154 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import junit.framework.TestCase;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class Test extends TestCase {
+
+ public void test() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "META-INF/spring/gshell-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml" });
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ assertNotNull(shell);
+ shell.execute("help");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testBanner() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "META-INF/spring/gshell-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+ shell.execute("about");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testLs() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+ shell.execute("vfs/ls meta:/commands/");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testCommandGroups() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+
+ shell.execute("vfs");
+ shell.execute("help");
+ shell.execute("..");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testFileAccessCommands() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ assertNotNull(shell);
+ shell.execute("optional/cat src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+ shell.execute("optional/find src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/resources/log4j.properties b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
@@ -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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
new file mode 100644
index 0000000..25e4de1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="vfsCommandActionTemplate" abstract="true">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="about">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+ <constructor-arg ref="application"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="help">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+ <constructor-arg ref="commandResolver"/>
+ </gshell:action>
+ <gshell:completers>
+ <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+ <constructor-arg>
+ <list>
+ <ref bean="aliasNameCompleter"/>
+ <ref bean="commandNameCompleter"/>
+ </list>
+ </constructor-arg>
+ </bean>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="exit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+ </gshell:command>
+
+ <gshell:link name="quit" target="exit"/>
+
+ <gshell:command name="echo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+ </gshell:command>
+
+ <gshell:command name="clear" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+ </gshell:command>
+
+ <gshell:command name="source">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+ <constructor-arg ref="commandLineExecutor"/>
+ <constructor-arg ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="set">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+ </gshell:command>
+
+ <gshell:command name="unset">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+ <gshell:completers>
+ <ref bean="variableNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="alias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="unalias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="aliasNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="history">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+ </gshell:command>
+
+ <gshell:command name="info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+ <constructor-arg ref="branding" />
+ </gshell:action>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="vfs/ls">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="optional/cat">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="optional/find">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
new file mode 100644
index 0000000..127543f
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/pom.xml b/karaf/gshell/gshell-features/pom.xml
new file mode 100644
index 0000000..f53d9a8
--- /dev/null
+++ b/karaf/gshell/gshell-features/pom.xml
@@ -0,0 +1,114 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Features</name>
+
+ <description>
+ Provides Features in GShell
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.features*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.servicemix.kernel.gshell.core,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.springframework.beans.factory.config,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java
new file mode 100644
index 0000000..45e8aa1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.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.servicemix.kernel.gshell.features;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A feature is a list of bundles associated identified by its name.
+ */
+public interface Feature {
+
+ String getId();
+
+ String getName();
+
+ String getVersion();
+
+ List<Feature> getDependencies();
+
+ List<String> getBundles();
+
+ Map<String, Map<String, String>> getConfigurations();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java
new file mode 100644
index 0000000..33e1f6e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.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.servicemix.kernel.gshell.features;
+
+/**
+ * Main interface for a Feature Registry which tracks available and installed features.
+ * Tracks features and repositories.
+ */
+public interface FeaturesRegistry {
+ void register(Feature feature);
+
+ void unregister(Feature feature);
+
+ void registerInstalled(Feature feature);
+
+ void unregisterInstalled(Feature feature);
+
+ void register(Repository repository);
+
+ void unregister(Repository repository);
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java
new file mode 100644
index 0000000..1226016
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * The service managing features repositories.
+ */
+public interface FeaturesService {
+
+ void addRepository(URI url) throws Exception;
+
+ void removeRepository(URI url);
+
+ Repository[] listRepositories();
+
+ void installFeature(String name) throws Exception;
+
+ void installFeature(String name, String version) throws Exception;
+
+ void uninstallFeature(String name) throws Exception;
+
+ void uninstallFeature(String name, String version) throws Exception;
+
+ String[] listFeatures() throws Exception;
+
+ String[] listInstalledFeatures();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java
new file mode 100644
index 0000000..2bfbc99
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * A repository of features.
+ */
+public interface Repository {
+
+ URI getURI();
+
+ URI[] getRepositories() throws Exception;
+
+ Feature[] getFeatures() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java
new file mode 100644
index 0000000..213e1e5
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class AddUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ for (String url : urls) {
+ admin.addRepository(new URI(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java
new file mode 100644
index 0000000..06e0636
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.osgi.framework.ServiceReference;
+
+public abstract class FeaturesCommandSupport extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ // Get repository admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(FeaturesService.class.getName());
+ if (ref == null) {
+ io.out.println("FeaturesService service is unavailable.");
+ return null;
+ }
+ try {
+ FeaturesService admin = (FeaturesService) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("FeaturesService service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(FeaturesService admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java
new file mode 100644
index 0000000..a170f3c
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class InstallFeatureCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, description = "The name of the feature")
+ String name;
+ @Argument(description = "The version of the feature", index = 1)
+ String version;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (version != null && version.length() > 0) {
+ admin.installFeature(name, version);
+ } else {
+ admin.installFeature(name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
new file mode 100644
index 0000000..fba0475
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class ListFeaturesCommand extends FeaturesCommandSupport {
+
+ @Option(name = "-i", aliases={"--installed"}, description="Display the list of installed features")
+ boolean installed;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ String[] features;
+ if (installed) {
+ features = admin.listInstalledFeatures();
+ } else {
+ // Print column headers.
+ io.out.println(" State Version Name");
+ features = admin.listFeatures();
+ }
+ if ((features != null) && (features.length > 0)) {
+ for (int i = 0; i < features.length; i++) {
+ io.out.println(features[i]);
+ }
+ } else {
+ if (installed) {
+ io.out.println("No features installed.");
+ } else {
+ io.out.println("No features available.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
new file mode 100644
index 0000000..4c84e09
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
@@ -0,0 +1,34 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class ListUrlCommand extends FeaturesCommandSupport {
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ io.out.println(repos[i].getURI());
+ }
+ } else {
+ io.out.println("No repository URLs are set.");
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java
new file mode 100644
index 0000000..cd594b1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class RefreshUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (urls == null || urls.isEmpty()) {
+ urls = new ArrayList<String>();
+ for (Repository repo : admin.listRepositories()) {
+ urls.add(repo.getURI().toString());
+ }
+ }
+ for (String strUri : urls) {
+ URI uri = new URI(strUri);
+ admin.removeRepository(uri);
+ admin.addRepository(uri);
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java
new file mode 100644
index 0000000..d7e6e01
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class RemoveUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ for (String url : urls) {
+ admin.removeRepository(new URI(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java
new file mode 100644
index 0000000..522c721
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class UninstallFeatureCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, description = "The name of the feature")
+ String name;
+ @Argument(description = "The version of the feature", index = 1)
+ String version;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (version != null && version.length() > 0) {
+ admin.uninstallFeature(name, version);
+ } else {
+ admin.uninstallFeature(name );
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java
new file mode 100644
index 0000000..991ba59
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for available features.
+ *
+ * Displays a list of available features from installed repositories.
+ *
+ */
+public class AvailableFeatureCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+ private StringsCompleter delegate;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+
+ Collection<ManagedFeature> features = featuresRegistry.getAvailableFeatures().values();
+ delegate = new StringsCompleter();
+
+ for (ManagedFeature feature : features) {
+ delegate.getStrings().add(feature.getName());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java
new file mode 100644
index 0000000..461ca32
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.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.servicemix.kernel.gshell.features.completers;
+
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for Feature Repository URLs.
+ *
+ * Displays a list of currently installed Feature repositories.
+ *
+ */
+
+public class FeatureRepositoryCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ StringsCompleter delegate = new StringsCompleter(featuresRegistry.getRepositories().keySet());
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java
new file mode 100644
index 0000000..6a121a6
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for installed features.
+ *
+ * Displays a list of currently installed features.
+ *
+ */
+public class InstalledFeatureCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+ private StringsCompleter delegate;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ Collection<ManagedFeature> features = featuresRegistry.getInstalledFeatures().values();
+ delegate = new StringsCompleter();
+
+ for (ManagedFeature feature : features) {
+ delegate.getStrings().add(feature.getName());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java
new file mode 100644
index 0000000..e4a036b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.filemonitor.DeploymentListener;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * A deployment listener able to hot deploy a feature descriptor
+ */
+public class FeatureDeploymentListener implements DeploymentListener, SynchronousBundleListener, BundleContextAware,
+ InitializingBean, DisposableBean {
+
+ public static final String FEATURE_PATH = "org.apache.servicemix.kernel.gshell.features";
+
+ private static final Log LOGGER = LogFactory.getLog(FeatureDeploymentListener.class);
+
+ private DocumentBuilderFactory dbf;
+ private FeaturesServiceImpl featuresService;
+ private BundleContext bundleContext;
+
+ public void setFeaturesService(FeaturesServiceImpl featuresService) {
+ this.featuresService = featuresService;
+ }
+
+ public FeaturesServiceImpl getFeaturesService() {
+ return featuresService;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ bundleContext.addBundleListener(this);
+ for (Bundle bundle : bundleContext.getBundles()) {
+ bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bundle));
+ }
+ }
+
+ public void destroy() throws Exception {
+ bundleContext.removeBundleListener(this);
+ }
+
+ public boolean canHandle(File artifact) {
+ try {
+ if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
+ Document doc = parse(artifact);
+ String name = doc.getDocumentElement().getLocalName();
+ String uri = doc.getDocumentElement().getNamespaceURI();
+ if ("features".equals(name) && (uri == null || "".equals(uri))) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Unable to parse deployed file " + artifact.getAbsolutePath(), e);
+ }
+ return false;
+ }
+
+ public File handle(File artifact, File tmpDir) {
+ // We can't really install the feature right now and just return nothing.
+ // We would not be aware of the fact that the bundle has been uninstalled
+ // and therefore require the feature to be uninstalled.
+ // So instead, create a fake bundle with the file inside, which will be listened by
+ // this deployer: installation / uninstallation of the feature will be done
+ // while the bundle is installed / uninstalled.
+ try {
+ File destFile = new File(tmpDir, artifact.getName() + ".jar");
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(destFile));
+
+ String name = artifact.getCanonicalPath();
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0) {
+ name = name.substring(idx + 1);
+ }
+ String[] str = extractNameVersionType(name);
+ // Create manifest
+ Manifest m = new Manifest();
+ m.getMainAttributes().putValue("Manifest-Version", "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
+ m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
+ // Put content
+ JarOutputStream out = new JarOutputStream(os);
+ ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
+ out.putNextEntry(e);
+ m.write(out);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/");
+ out.putNextEntry(e);
+ e = new ZipEntry("META-INF/" + FEATURE_PATH + "/");
+ out.putNextEntry(e);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/" + FEATURE_PATH + "/" + name);
+ out.putNextEntry(e);
+ InputStream fis = new BufferedInputStream(new FileInputStream(artifact));
+ copyInputStream(fis, out);
+ fis.close();
+ out.closeEntry();
+ out.close();
+ os.close();
+ return destFile;
+ } catch (Exception e) {
+ LOGGER.error("Unable to build spring application bundle", e);
+ return null;
+ }
+ }
+
+ public void bundleChanged(BundleEvent bundleEvent) {
+ try {
+ Bundle bundle = bundleEvent.getBundle();
+ if (bundleEvent.getType() == BundleEvent.INSTALLED) {
+ Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+ while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+ URL url = (URL) featuresUrlEnumeration.nextElement();
+ RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+ for (Feature f : repo.getFeatures()) {
+ featuresService.installFeature(f.getName(), f.getVersion());
+ }
+ featuresService.internalRemoveRepository(url.toURI());
+ }
+ } else if (bundleEvent.getType() == BundleEvent.UNINSTALLED) {
+ Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+ while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+ URL url = (URL) featuresUrlEnumeration.nextElement();
+ RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+ for (Feature f : repo.getFeatures()) {
+ featuresService.uninstallFeature(f.getName(), f.getVersion());
+ }
+ featuresService.internalRemoveRepository(url.toURI());
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Unable to install / uninstall feature", e);
+ }
+ }
+
+ protected Document parse(File artifact) throws Exception {
+ if (dbf == null) {
+ dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ }
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ return db.parse(artifact);
+ }
+
+ private static void copyInputStream(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[8192];
+ int len = in.read(buffer);
+ while (len >= 0) {
+ out.write(buffer, 0, len);
+ len = in.read(buffer);
+ }
+ }
+ private static final String DEFAULT_VERSION = "0.0.0";
+
+ private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
+ private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
+
+ public static String[] extractNameVersionType(String url) {
+ Matcher m = ARTIFACT_MATCHER.matcher(url);
+ if (!m.matches()) {
+ return new String[] { url, DEFAULT_VERSION };
+ }
+ else {
+ //System.err.println(m.groupCount());
+ //for (int i = 1; i <= m.groupCount(); i++) {
+ // System.err.println("Group " + i + ": " + m.group(i));
+ //}
+
+ StringBuffer v = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(2);
+ String d3 = m.group(3);
+ String d4 = m.group(4);
+ String d5 = m.group(5);
+ String d6 = m.group(6);
+ if (d2 != null) {
+ v.append(d2);
+ if (d3 != null) {
+ v.append('.');
+ v.append(d3);
+ if (d4 != null) {
+ v.append('.');
+ v.append(d4);
+ if (d5 != null) {
+ v.append(".");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.0.");
+ cleanupModifier(v, d5);
+ }
+ }
+ return new String[] { d1, v.toString(), d6 };
+ }
+ }
+
+ private static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = FUZZY_MODIFIDER.matcher(modifier);
+ if (m.matches()) {
+ modifier = m.group(1);
+ }
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
+ result.append(c);
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
new file mode 100644
index 0000000..07806d3
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * A feature
+ */
+@ManagedResource(description = "Managed Feature", currencyTimeLimit = 15)
+public class FeatureImpl implements Feature {
+
+ private String id;
+ private String name;
+ private String version;
+ private List<Feature> dependencies = new ArrayList<Feature>();
+ private List<String> bundles = new ArrayList<String>();
+ private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>();
+ public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+ public static String DEFAULT_VERSION = "0.0.0";
+
+ public FeatureImpl(String name) {
+ this(name, DEFAULT_VERSION);
+ }
+
+ public FeatureImpl(String name, String version) {
+ this.name = name;
+ this.version = version;
+ this.id = name + "-" + version;
+ }
+
+ @ManagedAttribute(description = "Feature Unique ID")
+ public String getId() {
+ return id;
+ }
+
+ @ManagedAttribute(description = "Feature Name")
+ public String getName() {
+ return name;
+ }
+
+ @ManagedAttribute(description = "Feature Version")
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ @ManagedAttribute(description = "List of Dependencies")
+ public List<Feature> getDependencies() {
+ return dependencies;
+ }
+
+ public List<String> getBundles() {
+ return bundles;
+ }
+
+ public Map<String, Map<String, String>> getConfigurations() {
+ return configs;
+ }
+
+ public void addDependency(Feature dependency) {
+ dependencies.add(dependency);
+ }
+
+ public void addBundle(String bundle) {
+ bundles.add(bundle);
+ }
+
+ public void addConfig(String name, Map<String,String> properties) {
+ configs.put(name, properties);
+ }
+
+ public String toString() {
+ String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+ return ret;
+ }
+
+ public static Feature valueOf(String str) {
+ if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+ String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+ String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
+ + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+ return new FeatureImpl(strName, strVersion);
+ } else {
+ return new FeatureImpl(str);
+ }
+
+
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FeatureImpl feature = (FeatureImpl) o;
+
+ if (!name.equals(feature.name)) return false;
+ if (!version.equals(feature.version)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + version.hashCode();
+ return result;
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
new file mode 100644
index 0000000..1c03515
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
@@ -0,0 +1,599 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * The Features service implementation.
+ * Adding a repository url will load the features contained in this repository and
+ * create dummy sub shells. When invoked, these commands will prompt the user for
+ * installing the needed bundles.
+ *
+ */
+public class FeaturesServiceImpl implements FeaturesService, BundleContextAware {
+
+ private static final String ALIAS_KEY = "_alias_factory_pid";
+
+ private static final Log LOGGER = LogFactory.getLog(FeaturesServiceImpl.class);
+
+ private BundleContext bundleContext;
+ private ConfigurationAdmin configAdmin;
+ private PreferencesService preferences;
+ private Set<URI> uris;
+ private Map<URI, RepositoryImpl> repositories = new HashMap<URI, RepositoryImpl>();
+ private Map<String, Map<String, Feature>> features;
+ private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
+ private String boot;
+ private boolean bootFeaturesInstalled;
+ private FeaturesRegistry featuresRegistry;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public ConfigurationAdmin getConfigAdmin() {
+ return configAdmin;
+ }
+
+ public void setConfigAdmin(ConfigurationAdmin configAdmin) {
+ this.configAdmin = configAdmin;
+ }
+
+ public PreferencesService getPreferences() {
+ return preferences;
+ }
+
+ public void setPreferences(PreferencesService preferences) {
+ this.preferences = preferences;
+ }
+
+ public void setFeaturesServiceRegistry(FeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public void setUrls(String uris) throws URISyntaxException {
+ String[] s = uris.split(",");
+ this.uris = new HashSet<URI>();
+ for (int i = 0; i < s.length; i++) {
+ this.uris.add(new URI(s[i]));
+ }
+ }
+
+ public void setBoot(String boot) {
+ this.boot = boot;
+ }
+
+ public void addRepository(URI uri) throws Exception {
+ if (!repositories.values().contains(uri)) {
+ internalAddRepository(uri);
+ saveState();
+ }
+ }
+
+ protected RepositoryImpl internalAddRepository(URI uri) throws Exception {
+ RepositoryImpl repo = new RepositoryImpl(uri);
+ repositories.put(uri, repo);
+ featuresRegistry.register(repo);
+ features = null;
+ return repo;
+ }
+
+ public void removeRepository(URI uri) {
+ if (repositories.containsKey(uri)) {
+ internalRemoveRepository(uri);
+ saveState();
+ }
+ }
+
+ public void internalRemoveRepository(URI uri) {
+ featuresRegistry.unregister(repositories.get(uri));
+ repositories.remove(uri);
+ features = null;
+ }
+
+ public Repository[] listRepositories() {
+ Collection<RepositoryImpl> repos = repositories.values();
+ return repos.toArray(new Repository[repos.size()]);
+ }
+
+ public void installAllFeatures(URI uri) throws Exception {
+ RepositoryImpl repo = internalAddRepository(uri);
+ for (Feature f : repo.getFeatures()) {
+ installFeature(f.getName(), f.getVersion());
+ }
+ internalRemoveRepository(uri);
+ }
+
+ public void uninstallAllFeatures(URI uri) throws Exception {
+ RepositoryImpl repo = internalAddRepository(uri);
+ for (Feature f : repo.getFeatures()) {
+ uninstallFeature(f.getName(), f.getVersion());
+ }
+ internalRemoveRepository(uri);
+ }
+
+ public void installFeature(String name) throws Exception {
+ installFeature(name, FeatureImpl.DEFAULT_VERSION);
+ }
+
+ public void installFeature(String name, String version) throws Exception {
+ Feature f = getFeature(name, version);
+ if (f == null) {
+ throw new Exception("No feature named '" + name
+ + "' with version '" + version + "' available");
+ }
+ for (Feature dependency : f.getDependencies()) {
+ installFeature(dependency.getName(), dependency.getVersion());
+ }
+ for (String config : f.getConfigurations().keySet()) {
+ Dictionary<String,String> props = new Hashtable<String, String>(f.getConfigurations().get(config));
+ String[] pid = parsePid(config);
+ if (pid[1] != null) {
+ props.put(ALIAS_KEY, pid[1]);
+ }
+ Configuration cfg = getConfiguration(configAdmin, pid[0], pid[1]);
+ if (cfg.getBundleLocation() != null) {
+ cfg.setBundleLocation(null);
+ }
+ cfg.update(props);
+ }
+ Set<Long> bundles = new HashSet<Long>();
+ for (String bundleLocation : f.getBundles()) {
+ Bundle b = installBundleIfNeeded(bundleLocation);
+ bundles.add(b.getBundleId());
+ }
+ for (long id : bundles) {
+ bundleContext.getBundle(id).start();
+ }
+
+ featuresRegistry.registerInstalled(f);
+ installed.put(f, bundles);
+ saveState();
+ }
+ protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+ LOGGER.debug("Checking " + bundleLocation);
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ } catch (RuntimeException e) {
+ LOGGER.error(e.getMessage());
+ throw e;
+ }
+ try {
+ is.mark(256 * 1024);
+ JarInputStream jar = new JarInputStream(is);
+ Manifest m = jar.getManifest();
+ String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName() != null && b.getSymbolicName().equals(sn)) {
+ vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+ Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ if (v.equals(bv)) {
+ LOGGER.debug(" found installed bundle: " + b);
+ return b;
+ }
+ }
+ }
+ try {
+ is.reset();
+ } catch (IOException e) {
+ is.close();
+ is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ }
+ LOGGER.debug("Installing bundle " + bundleLocation);
+ return getBundleContext().installBundle(bundleLocation, is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public void uninstallFeature(String name) throws Exception {
+ List<String> versions = new ArrayList<String>();
+ for (Feature f : installed.keySet()) {
+ if (name.equals(f.getName())) {
+ versions.add(f.getVersion());
+ }
+ }
+ if (versions.size() == 0) {
+ throw new Exception("Feature named '" + name + "' is not installed");
+ } else if (versions.size() > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Feature named '" + name + "' has multiple versions installed (");
+ for (int i = 0; i < versions.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(versions.get(i));
+ }
+ sb.append("). Please specify the version to uninstall.");
+ throw new Exception(sb.toString());
+ }
+ uninstallFeature(name, versions.get(0));
+ }
+
+ public void uninstallFeature(String name, String version) throws Exception {
+ Feature feature = getFeature(name, version);
+ if (feature == null || !installed.containsKey(feature)) {
+ throw new Exception("Feature named '" + name
+ + "' with version '" + version + "' is not installed");
+ }
+ // Grab all the bundles installed by this feature
+ // and remove all those who will still be in use.
+ // This gives this list of bundles to uninstall.
+ Set<Long> bundles = installed.remove(feature);
+ for (Set<Long> b : installed.values()) {
+ bundles.removeAll(b);
+ }
+ for (long bundleId : bundles) {
+ getBundleContext().getBundle(bundleId).uninstall();
+ }
+ featuresRegistry.unregisterInstalled(feature);
+ saveState();
+ }
+
+ public String[] listFeatures() throws Exception {
+ Collection<String> features = new ArrayList<String>();
+ for (Map<String, Feature> featureWithDifferentVersion : getFeatures()
+ .values()) {
+ for (Feature f : featureWithDifferentVersion.values()) {
+ String installStatus = installed.containsKey(f) ? "installed "
+ : "uninstalled";
+ String version = f.getVersion();
+ switch (version.length()) {
+ case 1:
+ version = " " + version;
+ case 2:
+ version = " " + version;
+ case 3:
+ version = " " + version;
+ case 4:
+ version = " " + version;
+ case 5:
+ version = " " + version;
+ case 6:
+ version = " " + version;
+ case 7:
+ version = " " + version;
+ }
+ features.add("[" + installStatus + "] " + " [" + version + "] "
+ + f.getName());
+ }
+ }
+ return features.toArray(new String[features.size()]);
+ }
+
+ public String[] listInstalledFeatures() {
+ List<String> result = new ArrayList<String>();
+ for (Feature feature : installed.keySet()) {
+ result.add(feature.getName());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ protected Feature getFeature(String name, String version) throws Exception {
+ Map<String, Feature> versions = getFeatures().get(name);
+ if (versions == null || versions.isEmpty()) {
+ return null;
+ } else {
+ Feature feature = versions.get(version);
+ if (feature == null && FeatureImpl.DEFAULT_VERSION.equals(version)) {
+ Version latest = new Version(cleanupVersion(version));
+ for (String available : versions.keySet()) {
+ Version availableVersion = new Version(cleanupVersion(available));
+ if (availableVersion.compareTo(latest) > 0) {
+ feature = versions.get(available);
+ latest = availableVersion;
+ }
+ }
+ }
+ return feature;
+ }
+ }
+
+ protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+ if (features == null) {
+ //the outer map's key is feature name, the inner map's key is feature version
+ Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
+ // Two phase load:
+ // * first load dependent repositories
+ for (;;) {
+ boolean newRepo = false;
+ for (Repository repo : listRepositories()) {
+ for (URI uri : repo.getRepositories()) {
+ if (!repositories.keySet().contains(uri)) {
+ internalAddRepository(uri);
+ newRepo = true;
+ }
+ }
+ }
+ if (!newRepo) {
+ break;
+ }
+ }
+ // * then load all features
+ for (Repository repo : repositories.values()) {
+ for (Feature f : repo.getFeatures()) {
+ if (map.get(f.getName()) == null) {
+ Map<String, Feature> versionMap = new HashMap<String, Feature>();
+ versionMap.put(f.getVersion(), f);
+ map.put(f.getName(), versionMap);
+ } else {
+ map.get(f.getName()).put(f.getVersion(), f);
+ }
+ }
+ }
+ features = map;
+ }
+ return features;
+ }
+
+ public void start() throws Exception {
+ if (!loadState()) {
+ if (uris != null) {
+ for (URI uri : uris) {
+ internalAddRepository(uri);
+ }
+ }
+ saveState();
+ }
+ if (boot != null && !bootFeaturesInstalled) {
+ new Thread() {
+ public void run() {
+ String[] list = boot.split(",");
+ for (String f : list) {
+ if (f.length() > 0) {
+ try {
+ installFeature(f);
+ } catch (Exception e) {
+ LOGGER.error("Error installing boot feature " + f, e);
+ }
+ }
+ }
+ bootFeaturesInstalled = true;
+ saveState();
+ }
+ }.start();
+ }
+ }
+
+ public void stop() throws Exception {
+ uris = new HashSet<URI>(repositories.keySet());
+ while (!repositories.isEmpty()) {
+ internalRemoveRepository(repositories.keySet().iterator().next());
+ }
+ }
+
+ protected String[] parsePid(String pid) {
+ int n = pid.indexOf('-');
+ if (n > 0) {
+ String factoryPid = pid.substring(n + 1);
+ pid = pid.substring(0, n);
+ return new String[]{pid, factoryPid};
+ } else {
+ return new String[]{pid, null};
+ }
+ }
+
+ protected Configuration getConfiguration(ConfigurationAdmin configurationAdmin,
+ String pid, String factoryPid) throws IOException, InvalidSyntaxException {
+ if (factoryPid != null) {
+ Configuration[] configs = configurationAdmin.listConfigurations("(|(" + ALIAS_KEY + "=" + pid + ")(.alias_factory_pid=" + factoryPid + "))");
+ if (configs == null || configs.length == 0) {
+ return configurationAdmin.createFactoryConfiguration(pid, null);
+ } else {
+ return configs[0];
+ }
+ } else {
+ return configurationAdmin.getConfiguration(pid, null);
+ }
+ }
+
+ protected void saveState() {
+ try {
+ Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+ saveSet(prefs.node("repositories"), repositories.keySet());
+ saveMap(prefs.node("features"), installed);
+ prefs.putBoolean("bootFeaturesInstalled", bootFeaturesInstalled);
+ prefs.flush();
+ } catch (Exception e) {
+ LOGGER.error("Error persisting FeaturesService state", e);
+ }
+ }
+
+ protected boolean loadState() {
+ try {
+ Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+ if (prefs.nodeExists("repositories")) {
+ Set<URI> repositories = loadSet(prefs.node("repositories"));
+ for (URI repo : repositories) {
+ internalAddRepository(repo);
+ }
+ installed = loadMap(prefs.node("features"));
+ for (Feature f : installed.keySet()) {
+ featuresRegistry.registerInstalled(f);
+ }
+ bootFeaturesInstalled = prefs.getBoolean("bootFeaturesInstalled", false);
+ return true;
+ }
+ } catch (Exception e) {
+ LOGGER.error("Error loading FeaturesService state", e);
+ }
+ return false;
+ }
+
+ protected void saveSet(Preferences node, Set<URI> set) throws BackingStoreException {
+ List<URI> l = new ArrayList<URI>(set);
+ node.clear();
+ node.putInt("count", l.size());
+ for (int i = 0; i < l.size(); i++) {
+ node.put("item." + i, l.get(i).toString());
+ }
+ }
+
+ protected Set<URI> loadSet(Preferences node) {
+ Set<URI> l = new HashSet<URI>();
+ int count = node.getInt("count", 0);
+ for (int i = 0; i < count; i++) {
+ l.add(URI.create(node.get("item." + i, null)));
+ }
+ return l;
+ }
+
+ protected void saveMap(Preferences node, Map<Feature, Set<Long>> map) throws BackingStoreException {
+ node.clear();
+ for (Map.Entry<Feature, Set<Long>> entry : map.entrySet()) {
+ Feature key = entry.getKey();
+ String val = createValue(entry.getValue());
+ node.put(key.toString(), val);
+ }
+ }
+
+ protected Map<Feature, Set<Long>> loadMap(Preferences node) throws BackingStoreException {
+ Map<Feature, Set<Long>> map = new HashMap<Feature, Set<Long>>();
+ for (String key : node.keys()) {
+ String val = node.get(key, null);
+ Set<Long> set = readValue(val);
+ map.put(FeatureImpl.valueOf(key), set);
+ }
+ return map;
+ }
+
+ protected String createValue(Set<Long> set) {
+ StringBuilder sb = new StringBuilder();
+ for (long i : set) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(i);
+ }
+ return sb.toString();
+ }
+
+ protected Set<Long> readValue(String val) {
+ Set<Long> set = new HashSet<Long>();
+ for (String str : val.split(",")) {
+ set.add(Long.parseLong(str));
+ }
+ return set;
+ }
+
+ /**
+ * Clean up version parameters. Other builders use more fuzzy definitions of
+ * the version syntax. This method cleans up such a version to match an OSGi
+ * version.
+ *
+ * @param version
+ * @return
+ */
+ static Pattern fuzzyVersion = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+ Pattern.DOTALL);
+ static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
+ Pattern.DOTALL);
+
+ static public String cleanupVersion(String version) {
+ Matcher m = fuzzyVersion.matcher(version);
+ if (m.matches()) {
+ StringBuffer result = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(3);
+ String d3 = m.group(5);
+ String qualifier = m.group(7);
+
+ if (d1 != null) {
+ result.append(d1);
+ if (d2 != null) {
+ result.append(".");
+ result.append(d2);
+ if (d3 != null) {
+ result.append(".");
+ result.append(d3);
+ if (qualifier != null) {
+ result.append(".");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.0.");
+ cleanupModifier(result, qualifier);
+ }
+ return result.toString();
+ }
+ }
+ return version;
+ }
+
+ static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = fuzzyModifier.matcher(modifier);
+ if (m.matches())
+ modifier = m.group(2);
+
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+ result.append(c);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
new file mode 100644
index 0000000..485c3d4
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
@@ -0,0 +1,168 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.xml.sax.SAXException;
+
+/**
+ * The repository implementation.
+ */
+@ManagedResource
+public class RepositoryImpl implements Repository {
+
+ private URI uri;
+ private List<Feature> features;
+ private List<URI> repositories;
+
+ public RepositoryImpl(URI uri) {
+ this.uri = uri;
+ }
+
+ @ManagedAttribute
+ public URI getURI() {
+ return uri;
+ }
+
+ @ManagedOperation
+ public URI[] getRepositories() throws Exception {
+ if (repositories == null) {
+ load();
+ }
+ return repositories.toArray(new URI[repositories.size()]);
+ }
+
+ @ManagedOperation(description = "List of Features provided by this repository")
+ public Feature[] getFeatures() throws Exception {
+ if (features == null) {
+ load();
+ }
+ return features.toArray(new Feature[features.size()]);
+ }
+
+ public void load() throws IOException {
+ try {
+ repositories = new ArrayList<URI>();
+ features = new ArrayList<Feature>();
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc = factory.newDocumentBuilder().parse(uri.toURL().openStream());
+ NodeList nodes = doc.getDocumentElement().getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (!(node instanceof Element)) {
+ continue;
+ }
+ if ("repository".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ repositories.add(new URI(e.getTextContent()));
+ } else if ("feature".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ String name = e.getAttribute("name");
+ String version = e.getAttribute("version");
+ FeatureImpl f;
+ if (version != null && version.length() > 0) {
+ f = new FeatureImpl(name, version);
+ } else {
+ f = new FeatureImpl(name);
+ }
+
+ NodeList featureNodes = e.getElementsByTagName("feature");
+ for (int j = 0; j < featureNodes.getLength(); j++) {
+ Element b = (Element) featureNodes.item(j);
+ String dependencyFeatureVersion = b.getAttribute("version");
+ if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+ f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+ } else {
+ f.addDependency(new FeatureImpl(b.getTextContent()));
+ }
+ }
+ NodeList configNodes = e.getElementsByTagName("config");
+ for (int j = 0; j < configNodes.getLength(); j++) {
+ Element c = (Element) configNodes.item(j);
+ String cfgName = c.getAttribute("name");
+ String data = c.getTextContent();
+ Properties properties = new Properties();
+ properties.load(new ByteArrayInputStream(data.getBytes()));
+ interpolation(properties);
+ Map<String, String> hashtable = new Hashtable<String, String>();
+ for (Object key : properties.keySet()) {
+ String n = key.toString();
+ hashtable.put(n, properties.getProperty(n));
+ }
+ f.addConfig(cfgName, hashtable);
+ }
+ NodeList bundleNodes = e.getElementsByTagName("bundle");
+ for (int j = 0; j < bundleNodes.getLength(); j++) {
+ Element b = (Element) bundleNodes.item(j);
+ f.addBundle(b.getTextContent());
+ }
+ features.add(f);
+ }
+ }
+ } catch (SAXException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (ParserConfigurationException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (URISyntaxException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ } catch (IllegalArgumentException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ }
+ }
+
+ protected void interpolation(Properties properties) {
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
+ String key = (String)e.nextElement();
+ String val = properties.getProperty(key);
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+ while (matcher.find()) {
+ String rep = System.getProperty(matcher.group(1));
+ if (rep != null) {
+ val = val.replace(matcher.group(0), rep);
+ matcher.reset(val);
+ }
+ }
+ properties.put(key, val);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
new file mode 100644
index 0000000..6bee5fc
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+/**
+ * Naming strategy for JMX MBeans.
+ */
+public class DefaultNamingStrategy implements NamingStrategy {
+
+ private String jmxDomainName;
+
+ public String getJmxDomainName() {
+ return jmxDomainName;
+ }
+
+ public void setJmxDomainName(String jmxDomainName) {
+ this.jmxDomainName = jmxDomainName;
+ }
+
+ public ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException {
+ return getObjectName(feature, false);
+ }
+
+ public ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException {
+ StringBuffer sb = new StringBuffer();
+ sb.append(jmxDomainName).append(":Service=Features,");
+
+ if (installed) {
+ sb.append("Type=Installed,");
+ } else {
+ sb.append("Type=Available,");
+ }
+
+ sb.append("Name=").append(sanitize(feature.getName())).append(",")
+ .append("FeatureVersion=").append(sanitize(feature.getVersion()));
+
+ return new ObjectName(sb.toString());
+ }
+
+ public ObjectName getObjectName(ManagedRepository repository) throws MalformedObjectNameException {
+ return new ObjectName(jmxDomainName + ":" +
+ "Service=Features," +
+ "Type=Repositories," +
+ "Name=" + sanitize(repository.getUri().toString())); // + "," +
+ }
+
+ public ObjectName getObjectName(FeaturesRegistry featuresRegistry) throws MalformedObjectNameException {
+ return new ObjectName(jmxDomainName + ":" +
+ "Service=Features," +
+ "Name=FeaturesService");
+ }
+
+ private String sanitize(String in) {
+ String result = null;
+ if (in != null) {
+ result = in.replace(':', '_');
+ result = result.replace('/', '_');
+ result = result.replace('\\', '_');
+ result = result.replace('?', '_');
+ result = result.replace('=', '_');
+ result = result.replace(',', '_');
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
new file mode 100644
index 0000000..1da589b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * Managed Repository MBean
+ */
+@ManagedResource(description = "Feature")
+public class ManagedFeature {
+ private Feature feature;
+ private FeaturesService featuresService;
+ private String id;
+
+ public ManagedFeature(Feature feature, FeaturesService featuresService) {
+ this.feature = feature;
+ id = feature.getName() + "-" + feature.getVersion();
+ this.featuresService = featuresService;
+ }
+
+ @ManagedAttribute
+ public String getId() {
+ return id;
+ }
+
+ @ManagedAttribute
+ public String getName() {
+ return feature.getName();
+ }
+
+ @ManagedAttribute
+ public String getVersion() {
+ return feature.getVersion();
+ }
+
+ @ManagedAttribute
+ public List<Feature> getDependencies() {
+ return feature.getDependencies();
+ }
+
+ @ManagedAttribute
+ public List<String> getBundles() {
+ return feature.getBundles();
+ }
+
+ @ManagedOperation
+ public void uninstallFeature() throws Exception {
+ featuresService.uninstallFeature(feature.getName(), feature.getVersion());
+ }
+
+ @ManagedOperation
+ public void installFeature() throws Exception {
+ featuresService.installFeature(feature.getName(), feature.getVersion());
+ }
+
+}
+
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
new file mode 100644
index 0000000..9fbdb46
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
@@ -0,0 +1,210 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.MBeanServer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * The FeaturesServiceRegistry maintains the managed Features and Repositories
+ * for JMX management.
+ */
+@ManagedResource(description = "Features Service Registry and Management")
+public class ManagedFeaturesRegistry implements InitializingBean, FeaturesRegistry {
+
+ private static final transient Log LOG = LogFactory.getLog(ManagedFeaturesRegistry.class);
+
+ private NamingStrategy namingStrategy;
+ private ManagementAgent managementAgent;
+ private Map<String, ManagedFeature> availableFeatures;
+ private Map<String, ManagedFeature> installedFeatures;
+ private Map<String, ManagedRepository> repositories;
+ private boolean mbeanServerInitialized;
+ private FeaturesService featuresService;
+
+ @ManagedOperation
+ public void installFeature(String name) throws Exception {
+ featuresService.installFeature(name);
+ }
+
+ @ManagedOperation
+ public void installFeature(String name, String version) throws Exception {
+ featuresService.installFeature(name, version);
+ }
+
+ @ManagedOperation
+ public void installRepository(String repositoryUri) throws Exception {
+ featuresService.addRepository(new URI(repositoryUri));
+ }
+
+ public ManagedFeaturesRegistry() {
+ availableFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+ installedFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+ repositories = new ConcurrentHashMap<String, ManagedRepository>();
+ }
+
+ public Map<String, ManagedFeature> getAvailableFeatures() {
+ return availableFeatures;
+ }
+
+ public Map<String, ManagedFeature> getInstalledFeatures() {
+ return installedFeatures;
+ }
+
+ public Map<String, ManagedRepository> getRepositories() {
+ return repositories;
+ }
+
+ public void setFeaturesService(FeaturesService featuresService) {
+ this.featuresService = featuresService;
+ }
+
+ public void setNamingStrategy(NamingStrategy namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+ public void setManagementAgent(ManagementAgent managementAgent) {
+ this.managementAgent = managementAgent;
+ }
+
+ public void register(Feature feature) {
+ try {
+ ManagedFeature mf = new ManagedFeature(feature, featuresService);
+ availableFeatures.put(feature.getId(), mf);
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mf, namingStrategy.getObjectName(mf));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed feature: " + e, e);
+ }
+ }
+
+ public void unregister(Feature feature) {
+ try {
+ ManagedFeature mf = availableFeatures.remove(feature.getId());
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mf));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed feature: " + e, e);
+ }
+ }
+
+ public void registerInstalled(Feature feature) {
+ try {
+ ManagedFeature mf = new ManagedFeature(feature, featuresService);
+ installedFeatures.put(feature.getId(), mf);
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mf, namingStrategy.getObjectName(mf, true));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed feature: " + e, e);
+ }
+ }
+
+ public void unregisterInstalled(Feature feature) {
+ try {
+ ManagedFeature mf = installedFeatures.remove(feature.getId());
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mf, true));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed feature: " + e, e);
+ }
+ }
+
+ public void register(Repository repository) {
+ try {
+ ManagedRepository mr = new ManagedRepository(repository, featuresService);
+ repositories.put(repository.getURI().toString(), mr);
+
+ for (Feature f : repository.getFeatures()) {
+ // TODO: Associate the feature with the Repo?
+ register(f);
+ }
+
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mr, namingStrategy.getObjectName(mr));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed repository: " + e, e);
+ }
+ }
+
+ public void unregister(Repository repository) {
+ try {
+ ManagedRepository mr = repositories.remove(repository.getURI().toString());
+
+ for (Feature f : repository.getFeatures()) {
+ // TODO: Associate the feature with the Repo?
+ unregister(f);
+ }
+
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mr));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed repository: " + e, e);
+ }
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (managementAgent == null) {
+ throw new IllegalArgumentException("managementAgent must not be null");
+ }
+ if (namingStrategy == null) {
+ throw new IllegalArgumentException("namingStrategy must not be null");
+ }
+ }
+
+ public void registerMBeanServer(MBeanServer mbeanServer, Map props ) throws Exception {
+ if (mbeanServer != null) {
+ mbeanServerInitialized = true;
+ }
+
+ managementAgent.register(this, namingStrategy.getObjectName(this));
+
+ for (ManagedRepository repository : repositories.values()) {
+ managementAgent.register(repository, namingStrategy.getObjectName(repository));
+ }
+
+ for (ManagedFeature feature : availableFeatures.values()) {
+ managementAgent.register(feature, namingStrategy.getObjectName(feature));
+ }
+
+ for (ManagedFeature feature : installedFeatures.values()) {
+ installedFeatures.put(feature.getId(), feature);
+ managementAgent.register(feature, namingStrategy.getObjectName(feature, true));
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java
new file mode 100644
index 0000000..2d3ae17
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+@ManagedResource(description = "Features Repository")
+public class ManagedRepository {
+ private Repository repository;
+ private FeaturesService featuresService;
+
+ public ManagedRepository(Repository repository, FeaturesService featuresService) {
+ this.repository = repository;
+ this.featuresService = featuresService;
+ }
+
+ @ManagedAttribute
+ public URI getUri() {
+ return repository.getURI();
+ }
+
+ @ManagedAttribute
+ public URI[] getRepositories() throws Exception {
+ return repository.getRepositories();
+ }
+
+ @ManagedAttribute
+ public Feature[] getFeatures() throws Exception {
+ return repository.getFeatures();
+ }
+
+ @ManagedOperation
+ public void removeRepository() throws Exception {
+ featuresService.removeRepository(repository.getURI());
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
new file mode 100644
index 0000000..611ec6b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
@@ -0,0 +1,130 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+import javax.management.modelmbean.ModelMBeanInfo;
+import javax.management.modelmbean.RequiredModelMBean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
+import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
+
+/**
+ * Management Agent that registers MBeans with JMX MBeanServer.
+ */
+public class ManagementAgent implements DisposableBean {
+
+ private static final transient Log LOG = LogFactory.getLog(ManagementAgent.class);
+
+ private MBeanServer mbeanServer;
+ private MetadataMBeanInfoAssembler assembler;
+ private Set<ObjectName> mbeans = new HashSet<ObjectName>();
+
+ public ManagementAgent() {
+ assembler = new MetadataMBeanInfoAssembler();
+ assembler.setAttributeSource(new AnnotationJmxAttributeSource());
+ }
+
+ public MBeanServer getMbeanServer() {
+ return mbeanServer;
+ }
+
+ public void setMbeanServer(MBeanServer mbeanServer) {
+ this.mbeanServer = mbeanServer;
+ }
+
+ public void destroy() throws Exception {
+ // Using the array to hold the busMBeans to avoid the
+ // CurrentModificationException
+ Object[] mBeans = mbeans.toArray();
+ int caught = 0;
+ for (Object name : mBeans) {
+ mbeans.remove((ObjectName)name);
+ try {
+ unregister((ObjectName)name);
+ } catch (JMException jmex) {
+ LOG.info("Exception unregistering MBean", jmex);
+ caught++;
+ }
+ }
+ if (caught > 0) {
+ LOG.warn("A number of " + caught
+ + " exceptions caught while unregistering MBeans during stop operation. "
+ + "See INFO log for details.");
+ }
+ }
+
+ public void register(Object obj, ObjectName name) throws JMException {
+ register(obj, name, false);
+ }
+
+ public void register(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+ try {
+ registerMBeanWithServer(obj, name, forceRegistration);
+ } catch (NotCompliantMBeanException e) {
+ // If this is not a "normal" MBean, then try to deploy it using JMX
+ // annotations
+ ModelMBeanInfo mbi = assembler.getMBeanInfo(obj, name.toString());
+ RequiredModelMBean mbean = (RequiredModelMBean) mbeanServer.instantiate(RequiredModelMBean.class.getName());
+ mbean.setModelMBeanInfo(mbi);
+ try {
+ mbean.setManagedResource(obj, "ObjectReference");
+ } catch (InvalidTargetObjectTypeException itotex) {
+ throw new JMException(itotex.getMessage());
+ }
+ registerMBeanWithServer(mbean, name, forceRegistration);
+ }
+ }
+
+ public synchronized void unregister(ObjectName name) throws JMException {
+ if (mbeans.contains(name)) {
+ //check if this bean already get removed in destory method
+ mbeanServer.unregisterMBean(name);
+ }
+ }
+
+ private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+ ObjectInstance instance = null;
+ try {
+ instance = mbeanServer.registerMBean(obj, name);
+ } catch (InstanceAlreadyExistsException e) {
+ if (forceRegistration) {
+ mbeanServer.unregisterMBean(name);
+ instance = mbeanServer.registerMBean(obj, name);
+ }
+ } catch (NotCompliantMBeanException e) {
+ throw e;
+ }
+
+ if (instance != null) {
+ mbeans.add(name);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java
new file mode 100644
index 0000000..7a03399
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+public interface NamingStrategy {
+
+ ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException;
+
+ ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException;
+
+ ObjectName getObjectName(ManagedRepository component) throws MalformedObjectNameException;
+
+ String getJmxDomainName();
+
+ ObjectName getObjectName(FeaturesRegistry features) throws MalformedObjectNameException;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
new file mode 100644
index 0000000..d45b6be
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="location" value="file:${servicemix.home}/etc/org.apache.servicemix.features.cfg"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="features/addUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.AddUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/listUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/removeUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RemoveUrlCommand" />
+ <gshell:completers>
+ <ref bean="removeUrlCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/refreshUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RefreshUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.InstallFeatureCommand" />
+ <gshell:completers>
+ <ref bean="installFeatureCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/uninstall">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.UninstallFeatureCommand" />
+ <gshell:completers>
+ <ref bean="uninstallFeatureCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListFeaturesCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="featuresService" class="org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl" init-method="start" destroy-method="stop">
+ <property name="urls" value="${featuresRepositories}" />
+ <property name="boot" value="${featuresBoot}" />
+ <property name="configAdmin" ref="configAdmin" />
+ <property name="preferences" ref="preferences" />
+ <property name="featuresServiceRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="featureDeploymentListener" class="org.apache.servicemix.kernel.gshell.features.internal.FeatureDeploymentListener">
+ <property name="featuresService" ref="featuresService" />
+ </bean>
+
+ <bean id="namingStrategy" class="org.apache.servicemix.kernel.gshell.features.management.DefaultNamingStrategy">
+ <property name="jmxDomainName" value="org.apache.servicemix" />
+ </bean>
+
+ <bean id="managementAgent" class="org.apache.servicemix.kernel.gshell.features.management.ManagementAgent">
+ <property name="mbeanServer" ref="mbeanServer" />
+ </bean>
+
+ <bean id="featureServiceRegistry" class="org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry">
+ <property name="managementAgent" ref="managementAgent" />
+ <property name="namingStrategy" ref="namingStrategy" />
+ <property name="featuresService" ref="featuresService" />
+ </bean>
+
+ <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+ <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+ <osgi:service ref="featuresService" interface="org.apache.servicemix.kernel.gshell.features.FeaturesService" />
+
+ <osgi:service ref="featureDeploymentListener" interface="org.apache.servicemix.kernel.filemonitor.DeploymentListener" />
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.features">
+ <prop key="featuresRepositories"></prop>
+ <prop key="featuresBoot"></prop>
+ </osgix:cm-properties>
+
+ <!-- <ctx:property-placeholder properties-ref="cmProps" /> -->
+
+ <osgi:reference id="mbeanServer"
+ interface="javax.management.MBeanServer"
+ cardinality="0..1" >
+ <osgi:listener ref="featureServiceRegistry" bind-method="registerMBeanServer" />
+ </osgi:reference>
+
+ <bean id="installFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.AvailableFeatureCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="uninstallFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.InstalledFeatureCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="removeUrlCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.FeatureRepositoryCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
new file mode 100644
index 0000000..cf92ffe
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
new file mode 100644
index 0000000..97fcbe0
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install a feature.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
new file mode 100644
index 0000000..4c807af
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing features.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
new file mode 100644
index 0000000..a544da9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
new file mode 100644
index 0000000..12687fd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of features.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
new file mode 100644
index 0000000..34c2126
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
new file mode 100644
index 0000000..ab3348e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall a feature.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java
new file mode 100644
index 0000000..97d6332
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.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.servicemix.kernel.gshell.features;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+
+public class FeatureTest extends TestCase {
+
+ public void testValueOf() {
+ Feature feature = FeatureImpl.valueOf("name" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "version");
+ assertEquals(feature.getName(), "name");
+ assertEquals(feature.getVersion(), "version");
+ feature = FeatureImpl.valueOf("name");
+ assertEquals(feature.getName(), "name");
+ assertEquals(feature.getVersion(), FeatureImpl.DEFAULT_VERSION);
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
new file mode 100644
index 0000000..5909722
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
@@ -0,0 +1,700 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.context.ApplicationContext;
+
+public class FeaturesServiceTest extends TestCase {
+
+ public void testInstallFeature() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ featuresRegistry.register(isA(Repository.class));
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ Repository[] repositories = svc.listRepositories();
+ assertNotNull(repositories);
+ assertEquals(1, repositories.length);
+ assertNotNull(repositories[0]);
+ Feature[] features = repositories[0].getFeatures();
+ assertNotNull(features);
+ assertEquals(1, features.length);
+ assertNotNull(features[0]);
+ assertEquals("f1", features[0].getName());
+ assertNotNull(features[0].getDependencies());
+ assertEquals(0, features[0].getDependencies().size());
+ assertNotNull(features[0].getBundles());
+ assertEquals(1, features[0].getBundles().size());
+ assertEquals(name, features[0].getBundles().get(0));
+
+ verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+ svc.installFeature("f1");
+
+ String[] installed = svc.listInstalledFeatures();
+ assertEquals(1, installed.length);
+ assertEquals("f1", installed[0]);
+ }
+
+ public void testUninstallFeature() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ featuresRegistry.register(isA(Repository.class));
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ // Installs f1 and 0.1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs f1 and 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(123456L);
+ expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // UnInstalls f1 and 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // UnInstalls f1 and 0.2
+ expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+ try {
+ svc.uninstallFeature("f1");
+ fail("Uninstall should have failed as feature is not installed");
+ } catch (Exception e) {
+ // ok
+ }
+
+ svc.installFeature("f1", "0.1");
+ svc.installFeature("f1", "0.2");
+
+ try {
+ svc.uninstallFeature("f1");
+ fail("Uninstall should have failed as feature is installed in multiple versions");
+ } catch (Exception e) {
+ // ok
+ }
+
+ svc.uninstallFeature("f1", "0.1");
+ svc.uninstallFeature("f1");
+ }
+
+ // Tests Add and Remove Repository
+ public void testAddAndRemoveRepository() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ featuresRegistry.register(isA(Repository.class));
+
+ // SaveState for addRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // SaveState for removeRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+
+ // Adds Repository
+ svc.addRepository(uri);
+
+ // Removes Repository
+ svc.removeRepository(uri);
+ }
+
+ // Tests installing all features in a repo and uninstalling
+ // all features in a repo
+ public void testInstallUninstallAllFeatures() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs first feature name = f1, version = 0.1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs second feature name = f1, version = 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(123456L);
+ expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs third feature name = f2, version = 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(1234567L);
+ expect(bundleContext.getBundle(1234567L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstallAllFeatures
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f1, version = 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls third feature name = f2, version = 0.2
+ expect(bundleContext.getBundle(1234567)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls second feature name = f1, version = 0.2
+ expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.installAllFeatures(uri);
+
+ // Uninstalls features with versions.
+ svc.uninstallAllFeatures(uri);
+ }
+
+
+ // Tests install of a Repository that includes a feature
+ // with a feature dependency
+ // The dependant feature is in the same repository
+ // Tests uninstall of features
+ public void testInstallFeatureWithDependantFeatures() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <feature version=\"0.1\">f2</feature>");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences repositoriesAvailableNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ // savestate from addRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs feature f1 with dependency on f2
+ // so will install f2 first
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Then installs f1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(1234L);
+ expect(bundleContext.getBundle(1234L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "1234");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f1, version = 0.1
+ expect(bundleContext.getBundle(1234)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f2, version = 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ svc.installFeature("f1", "0.1");
+
+ // Uninstall repository
+ svc.uninstallFeature("f1", "0.1");
+ svc.uninstallFeature("f2", "0.1");
+
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java
new file mode 100644
index 0000000..e8cfffd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.RepositoryImpl;
+
+
+public class RepositoryTest extends TestCase {
+
+ public void testLoad() throws Exception {
+ RepositoryImpl r = new RepositoryImpl(getClass().getResource("repo1.xml").toURI());
+ // Check repo
+ URI[] repos = r.getRepositories();
+ assertNotNull(repos);
+ assertEquals(1, repos.length);
+ assertEquals(URI.create("urn:r1"), repos[0]);
+ // Check features
+ Feature[] features = r.getFeatures();
+ assertNotNull(features);
+ assertEquals(2, features.length);
+ assertNotNull(features[0]);
+ assertEquals("f1", features[0].getName());
+ assertNotNull(features[0].getConfigurations());
+ assertEquals(1, features[0].getConfigurations().size());
+ assertNotNull(features[0].getConfigurations().get("c1"));
+ assertEquals(1, features[0].getConfigurations().get("c1").size());
+ assertEquals("v", features[0].getConfigurations().get("c1").get("k"));
+ assertNotNull(features[0].getDependencies());
+ assertEquals(0, features[0].getDependencies().size());
+ assertNotNull(features[0].getBundles());
+ assertEquals(2, features[0].getBundles().size());
+ assertEquals("b1", features[0].getBundles().get(0));
+ assertEquals("b2", features[0].getBundles().get(1));
+ assertNotNull(features[1]);
+ assertEquals("f2", features[1].getName());
+ assertNotNull(features[1].getConfigurations());
+ assertEquals(0, features[1].getConfigurations().size());
+ assertNotNull(features[1].getDependencies());
+ assertEquals(1, features[1].getDependencies().size());
+ assertEquals("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, features[1].getDependencies().get(0).toString());
+ assertNotNull(features[1].getBundles());
+ assertEquals(1, features[1].getBundles().size());
+ assertEquals("b3", features[1].getBundles().get(0));
+ }
+
+ public void testShowWrongUriInException() throws Exception {
+ String uri = "src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml";
+ RepositoryImpl r = new RepositoryImpl(new URI(uri));
+ try {
+ r.load();
+ } catch (Exception e) {
+ assertTrue(e.getMessage().contains(uri));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java
new file mode 100644
index 0000000..8ee87de
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.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.servicemix.kernel.gshell.features.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+
+/**
+ * Test cases for {@link FeaturesServiceImpl}
+ */
+public class FeaturesServiceImplTest extends TestCase {
+
+ public void testGetFeature() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ FeatureImpl feature = new FeatureImpl("transaction");
+ versions.put("1.0.0", feature);
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ assertSame(feature, impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ }
+
+ public void testGetFeatureNotAvailable() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ versions.put("1.0.0", new FeatureImpl("transaction"));
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNull(impl.getFeature("activemq", FeatureImpl.DEFAULT_VERSION));
+ }
+
+ public void testGetFeatureHighestAvailable() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ versions.put("1.0.0", new FeatureImpl("transaction", "1.0.0"));
+ versions.put("2.0.0", new FeatureImpl("transaction", "2.0.0"));
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ assertSame("2.0.0", impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION).getVersion());
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
new file mode 100644
index 0000000..ea21df9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
@@ -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.
+-->
+<features>
+ <repository>urn:r1</repository>
+ <feature name="f1">
+ <config name="c1">
+ k=v
+ </config>
+ <bundle>b1</bundle>
+ <bundle>b2</bundle>
+ </feature>
+ <feature name="f2">
+ <feature>f1</feature>
+ <bundle>b3</bundle>
+ </feature>
+</features>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/pom.xml b/karaf/gshell/gshell-log/pom.xml
new file mode 100644
index 0000000..04ca7bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/pom.xml
@@ -0,0 +1,95 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Log Commands</name>
+
+ <description>
+ Provides the OSGi Log commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.log*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ org.ops4j.pax.logging,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
new file mode 100644
index 0000000..917e01c
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+public class DisplayException extends OsgiCommandSupport {
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ protected Object doExecute() throws Exception {
+ PaxLoggingEvent throwableEvent = null;
+ Iterable<PaxLoggingEvent> le = events.getElements(Integer.MAX_VALUE);
+ for (PaxLoggingEvent event : le) {
+ if (event.getThrowableStrRep() != null) {
+ throwableEvent = event;
+ // Do not break, as we iterate from the oldest to the newest event
+ }
+ }
+ if (throwableEvent != null) {
+ for (String r : throwableEvent.getThrowableStrRep()) {
+ io.out.println(r);
+ }
+ io.out.println();
+ }
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
new file mode 100644
index 0000000..3beff07
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternConverter;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternParser;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Displays the last log entries
+ */
+public class DisplayLog extends OsgiCommandSupport {
+
+ @Option(name = "-n", description="Number of entries to display")
+ protected int entries;
+
+ @Option(name = "-p", description="Output formatting pattern")
+ protected String overridenPattern;
+
+ protected String pattern;
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ protected Object doExecute() throws Exception {
+ PatternConverter cnv = new PatternParser(overridenPattern != null ? overridenPattern : pattern).parse();
+
+ Iterable<PaxLoggingEvent> le = events.getElements(entries == 0 ? Integer.MAX_VALUE : entries);
+ StringBuffer sb = new StringBuffer();
+ for (PaxLoggingEvent event : le) {
+ sb.setLength(0);
+ for (PatternConverter pc = cnv; pc != null; pc = pc.next) {
+ pc.format(sb, event);
+ }
+ io.out.print(sb.toString());
+ if (event.getThrowableStrRep() != null) {
+ for (String r : event.getThrowableStrRep()) {
+ io.out.println(r);
+ }
+ }
+ }
+ io.out.println();
+
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java
new file mode 100644
index 0000000..cf93797
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Get the log level for a given logger
+ */
+public class GetLogLevel extends OsgiCommandSupport {
+
+ @Argument(required = false, description = "Logger name, ALL or ROOT (default)")
+ String logger;
+
+ static final String CONFIGURATION_PID = "org.ops4j.pax.logging";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String ALL_LOGGER = "ALL";
+ static final String ROOT_LOGGER = "ROOT";
+
+ protected Object doExecute() throws Exception {
+ ConfigurationAdmin cfgAdmin = getConfigAdmin();
+ Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+ Dictionary props = cfg.getProperties();
+
+ if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+ this.logger = null;
+ }
+ if (ALL_LOGGER.equalsIgnoreCase(logger)) {
+ String root = getLevel((String) props.get(ROOT_LOGGER_PREFIX));
+ Map<String, String> loggers = new TreeMap<String, String>();
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ String prop = (String) e.nextElement();
+ if (prop.startsWith(LOGGER_PREFIX)) {
+ String val = getLevel((String) props.get(prop));
+ loggers.put(prop.substring(LOGGER_PREFIX.length()), val);
+ }
+ }
+ io.out.println("ROOT: " + root);
+ for (String logger : loggers.keySet()) {
+ io.out.println(logger + ": " + loggers.get(logger));
+ }
+ } else {
+ String logger = this.logger;
+ String val;
+ for (;;) {
+ String prop;
+ if (logger == null) {
+ prop = ROOT_LOGGER_PREFIX;
+ } else {
+ prop = LOGGER_PREFIX + logger;
+ }
+ val = (String) props.get(prop);
+ val = getLevel(val);
+ if (val != null || logger == null) {
+ break;
+ }
+ int idx = logger.lastIndexOf('.');
+ if (idx < 0) {
+ logger = null;
+ } else {
+ logger = logger.substring(0, idx);
+ }
+ }
+ String st = "Level: " + val;
+ if (logger != this.logger) {
+ st += " (inherited from " + (logger != null ? logger : "ROOT") + ")";
+ }
+ io.out.println(st);
+ }
+ return Result.SUCCESS;
+ }
+
+ protected String getLevel(String prop) {
+ if (prop == null) {
+ return null;
+ } else {
+ String val = prop.trim();
+ int idx = val.indexOf(",");
+ if (idx == 0) {
+ val = null;
+ } else if (idx > 0) {
+ val = val.substring(0, idx);
+ }
+ return val;
+ }
+ }
+
+ protected ConfigurationAdmin getConfigAdmin() {
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ return getService(ConfigurationAdmin.class, ref);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
new file mode 100644
index 0000000..a214a17
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.log;
+
+import java.util.Arrays;
+
+/**
+ * A list that only keep the last N elements added
+ */
+public class LruList<E> {
+
+ private E[] elements;
+ private transient int start = 0;
+ private transient int end = 0;
+ private transient boolean full = false;
+ private final int maxElements;
+
+ public LruList(int size) {
+ if (size <= 0) {
+ throw new IllegalArgumentException("The size must be greater than 0");
+ }
+ elements = (E[]) new Object[size];
+ maxElements = elements.length;
+ }
+
+ public int size() {
+ synchronized (elements) {
+ int size = 0;
+ if (end < start) {
+ size = maxElements - start + end;
+ } else if (end == start) {
+ size = (full ? maxElements : 0);
+ } else {
+ size = end - start;
+ }
+ return size;
+ }
+ }
+
+ public void add(E element) {
+ synchronized (elements) {
+ if (null == element) {
+ throw new NullPointerException("Attempted to add null object to buffer");
+ }
+ if (size() == maxElements) {
+ Object e = elements[start];
+ if (null != e) {
+ elements[start++] = null;
+ if (start >= maxElements) {
+ start = 0;
+ }
+ full = false;
+ }
+ }
+ elements[end++] = element;
+ if (end >= maxElements) {
+ end = 0;
+ }
+ if (end == start) {
+ full = true;
+ }
+ }
+ }
+
+ public Iterable<E> getElements() {
+ synchronized (elements) {
+ return getElements(size());
+ }
+ }
+
+ public Iterable<E> getElements(int nb) {
+ synchronized (elements) {
+ int s = size();
+ nb = Math.min(Math.max(0, nb), s);
+ E[] e = (E[]) new Object[nb];
+ for (int i = 0; i < nb; i++) {
+ e[i] = elements[(i + s - nb + start) % maxElements];
+ }
+ return Arrays.asList(e);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java
new file mode 100644
index 0000000..7da0f7f
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Set the log level for a given logger
+ */
+public class SetLogLevel extends OsgiCommandSupport {
+
+ @Argument(index = 0, required = true, description = "Level (TRACE, DEBUG, INFO, WARN, ERROR or - to unset")
+ String level;
+
+ @Argument(index = 1, required = false, description = "Logger name or ROOT (default)")
+ String logger;
+
+ static final String CONFIGURATION_PID = "org.ops4j.pax.logging";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String ROOT_LOGGER = "ROOT";
+
+ static final String TRACE = "TRACE";
+ static final String DEBUG = "DEBUG";
+ static final String INFO = "INFO";
+ static final String WARN = "WARN";
+ static final String ERROR = "ERROR";
+ static final String INHERITED = "-";
+
+ protected Object doExecute() throws Exception {
+ if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+ this.logger = null;
+ }
+ if (!TRACE.equals(level) &&
+ !DEBUG.equals(level) &&
+ !INFO.equals(level) &&
+ !WARN.equals(level) &&
+ !ERROR.equals(level) &&
+ !INHERITED.equals(level)) {
+ io.err.println("level must be set to TRACE, DEBUG, INFO, WARN or ERROR (or - to unset it)");
+ return Result.FAILURE;
+ }
+ if (INHERITED.equals(level) && logger == null) {
+ io.err.println("Can not unset the ROOT logger");
+ return Result.FAILURE;
+ }
+
+ ConfigurationAdmin cfgAdmin = getConfigAdmin();
+ Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+ Dictionary props = cfg.getProperties();
+
+ String logger = this.logger;
+ String val;
+ String prop;
+ if (logger == null) {
+ prop = ROOT_LOGGER_PREFIX;
+ } else {
+ prop = LOGGER_PREFIX + logger;
+ }
+ val = (String) props.get(prop);
+ if (INHERITED.equals(level)) {
+ if (val != null) {
+ val = val.trim();
+ int idx = val.indexOf(",");
+ if (idx > 0) {
+ val = val.substring(idx);
+ } else {
+ val = null;
+ }
+ }
+ } else {
+ if (val == null) {
+ val = level;
+ } else {
+ val = val.trim();
+ int idx = val.indexOf(",");
+ if (idx == 0) {
+ val = level + val;
+ } else if (idx > 0) {
+ val = level + val.substring(idx);
+ }
+ }
+ }
+ if (val == null) {
+ props.remove(prop);
+ } else {
+ props.put(prop, val);
+ }
+ cfg.update(props);
+
+ return Result.SUCCESS;
+ }
+
+ protected ConfigurationAdmin getConfigAdmin() {
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ return getService(ConfigurationAdmin.class, ref);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java
new file mode 100644
index 0000000..a1607b4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.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.servicemix.kernel.gshell.log;
+
+import org.ops4j.pax.logging.spi.PaxAppender;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * A Pax Logging appender that keep a list of last events
+ */
+public class VmLogAppender implements PaxAppender {
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ public void doAppend(PaxLoggingEvent event) {
+ if (events != null) {
+ events.add(event);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
new file mode 100644
index 0000000..03b81ff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
@@ -0,0 +1,145 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+ Formats a {@link Date} in the format "HH:mm:ss,SSS" for example,
+ "15:49:37,459".
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class AbsoluteTimeDateFormat extends DateFormat {
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current
+ value is <b>ABSOLUTE</b>. */
+ public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.DateTimeDateFormat} in layouts. Current
+ value is <b>DATE</b>.
+ */
+ public final static String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current
+ value is <b>ISO8601</b>.
+ */
+ public final static String ISO8601_DATE_FORMAT = "ISO8601";
+
+ public
+ AbsoluteTimeDateFormat() {
+ setCalendar(Calendar.getInstance());
+ }
+
+ public
+ AbsoluteTimeDateFormat(TimeZone timeZone) {
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ private static long previousTime;
+ private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss."
+
+ /**
+ Appends to <code>sbuf</code> the time in the format
+ "HH:mm:ss,SSS" for example, "15:49:37,459"
+
+ @param date the date to format
+ @param sbuf the string buffer to write to
+ @param fieldPosition remains untouched
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != previousTime) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
+
+ previousTime = now - millis;
+ }
+ else {
+ sbuf.append(previousTimeWithoutMillis);
+ }
+
+
+
+ if(millis < 100)
+ sbuf.append('0');
+ if(millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java
new file mode 100644
index 0000000..0412ac8
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+ Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example,
+ "06 Nov 1994 15:49:37,459".
+
+ @author Ceki Gülcü
+ @since 0.7.5
+*/
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+
+ String[] shortMonths;
+
+ public
+ DateTimeDateFormat() {
+ super();
+ shortMonths = new DateFormatSymbols().getShortMonths();
+ }
+
+ public
+ DateTimeDateFormat(TimeZone timeZone) {
+ this();
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ /**
+ Appends to <code>sbuf</code> the date in the format "dd MMM yyyy
+ HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459".
+
+ @param sbuf the string buffer to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ calendar.setTime(date);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+ sbuf.append(' ');
+ sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]);
+ sbuf.append(' ');
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+ sbuf.append(' ');
+
+ return super.format(date, sbuf, fieldPosition);
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
new file mode 100644
index 0000000..b5d9dff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+
+/**
+ * Copied from log4j
+ */
+/**
+ FormattingInfo instances contain the information obtained when parsing
+ formatting modifiers in conversion modifiers.
+
+ @author <a href=mailto:jim_cakalic@na.biomerieux.com>Jim Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ void reset() {
+ min = -1;
+ max = 0x7FFFFFFF;
+ leftAlign = false;
+ }
+
+ void dump() {
+ //LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign);
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
new file mode 100644
index 0000000..f19b8bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
@@ -0,0 +1,155 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+// Contributors: Arndt Schoenewald <arndt@ibm23093i821.mc.schoenewald.de>
+/**
+ Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example
+ "1999-11-27 15:49:37,459".
+
+ <p>Refer to the <a
+ href=http://www.cl.cam.ac.uk/~mgk25/iso-time.html>summary of the
+ International Standard Date and Time Notation</a> for more
+ information on this format.
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+
+ public
+ ISO8601DateFormat() {
+ }
+
+ public
+ ISO8601DateFormat(TimeZone timeZone) {
+ super(timeZone);
+ }
+
+ static private long lastTime;
+ static private char[] lastTimeString = new char[20];
+
+ /**
+ Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS"
+ to <code>sbuf</code>. For example: "1999-11-27 15:49:37,459".
+
+ @param sbuf the <code>StringBuffer</code> to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != lastTime) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+
+ String month;
+ switch(calendar.get(Calendar.MONTH)) {
+ case Calendar.JANUARY: month = "-01-"; break;
+ case Calendar.FEBRUARY: month = "-02-"; break;
+ case Calendar.MARCH: month = "-03-"; break;
+ case Calendar.APRIL: month = "-04-"; break;
+ case Calendar.MAY: month = "-05-"; break;
+ case Calendar.JUNE: month = "-06-"; break;
+ case Calendar.JULY: month = "-07-"; break;
+ case Calendar.AUGUST: month = "-08-"; break;
+ case Calendar.SEPTEMBER: month = "-09-"; break;
+ case Calendar.OCTOBER: month = "-10-"; break;
+ case Calendar.NOVEMBER: month = "-11-"; break;
+ case Calendar.DECEMBER: month = "-12-"; break;
+ default: month = "-NA-"; break;
+ }
+ sbuf.append(month);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+
+ sbuf.append(' ');
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+ lastTime = now - millis;
+ }
+ else {
+ sbuf.append(lastTimeString);
+ }
+
+
+ if (millis < 100)
+ sbuf.append('0');
+ if (millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
new file mode 100644
index 0000000..1652d57
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+
+ <p>PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+ <p>Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(PaxLoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, PaxLoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<<i)) != 0) {
+ sbuf.append(SPACES[i]);
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
new file mode 100644
index 0000000..4a74fd1
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
@@ -0,0 +1,527 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.ops4j.pax.logging.spi.PaxLocationInfo;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Copied from log4j
+ */
+// Contributors: Nelson Minar <(nelson@monkey.org>
+// Igor E. Poteryaev <jah@mail.ru>
+// Reinhard Deschler <reinhard.deschler@web.de>
+/**
+ Most of the work of the {@link org.apache.log4j.PatternLayout} class
+ is delegated to the PatternParser class.
+
+ @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.2
+*/
+public class PatternParser {
+
+ private static final String LINE_SEP = System.getProperty("line.separator");
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int MINUS_STATE = 2;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public
+ PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private
+ void addToList(PatternConverter pc) {
+ if(head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected
+ String extractOption() {
+ if((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end+1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ The option is expected to be in decimal and positive. In case of
+ error, zero is returned. */
+ protected
+ int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if(opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if(r <= 0) {
+ //LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ }
+ catch (NumberFormatException e) {
+ //LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public
+ PatternConverter parse() {
+ char c;
+ i = 0;
+ while(i < patternLength) {
+ c = pattern.charAt(i++);
+ switch(state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if(i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if(c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch(pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(
+ currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ }
+ else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch(c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if(c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ }
+ else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min*10 + (c - '0');
+ else if(c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ }
+ else {
+ //LogLog.error("Error occured in position "+i+".\n Was expecting digit, instead got char \""+c+"\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max*10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected
+ void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch(c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CATEGORY converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CLASS_NAME converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if(dOpt != null)
+ dateFormatStr = dOpt;
+
+ if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ }
+ catch (IllegalArgumentException e) {
+ //LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
+ df = new ISO8601DateFormat();
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo,
+ FILE_LOCATION_CONVERTER);
+ //LogLog.debug("File name converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'l':
+ pc = new LocationPatternConverter(formattingInfo,
+ FULL_LOCATION_CONVERTER);
+ //LogLog.debug("Location converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;*/
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo,
+ LINE_LOCATION_CONVERTER);
+ //LogLog.debug("LINE NUMBER converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ //LogLog.debug("MESSAGE converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo,
+ METHOD_LOCATION_CONVERTER);
+ //LogLog.debug("METHOD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ //LogLog.debug("LEVEL converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo,
+ RELATIVE_TIME_CONVERTER);
+ //LogLog.debug("RELATIVE time converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ //LogLog.debug("THREAD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'u':
+ if(i < patternLength) {
+ char cNext = pattern.charAt(i);
+ if(cNext >= '0' && cNext <= '9') {
+ pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+ LogLog.debug("USER converter ["+cNext+"].");
+ formattingInfo.dump();
+ currentLiteral.setLength(0);
+ i++;
+ }
+ else
+ LogLog.error("Unexpected char" +cNext+" at position "+i);
+ }
+ break;*/
+ /*case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ //LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;*/
+ default:
+ //LogLog.error("Unexpected char [" +c+"] at position "+i+" in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected
+ void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ switch(type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.getTimeStamp() - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ // case NDC_CONVERTER:
+ //return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default: return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public
+ final
+ void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ date.setTime(event.getTimeStamp());
+ String converted = null;
+ try {
+ converted = df.format(date);
+ }
+ catch (Exception ex) {
+ //LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ PaxLocationInfo locationInfo = event.getLocationInformation();
+ switch(type) {
+ /*case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;*/
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default: return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract
+ String getFullyQualifiedName(PaxLoggingEvent event);
+
+ public
+ String convert(PaxLoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if(precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len -1 ;
+ for(int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end-1);
+ if(end == -1)
+ return n;
+ }
+ return n.substring(end+1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(PaxLoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(PaxLoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
new file mode 100644
index 0000000..2055fe4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="log/display">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayLog">
+ <property name="events" ref="events" />
+ <property name="pattern" value="${pattern}" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:link name="log/d" target="log/display" />
+ <gshell:command name="log/display-exception">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayException">
+ <property name="events" ref="events" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:link name="log/de" target="log/display-exception" />
+ <gshell:command name="log/get">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.GetLogLevel" />
+ </gshell:command>
+ <gshell:command name="log/set">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.SetLogLevel" />
+ </gshell:command>
+
+ <gshell:alias name="ld" alias="log/d" />
+ <gshell:alias name="lde" alias="log/de" />
+ </gshell:command-bundle>
+
+ <bean id="vmLogAppender" class="org.apache.servicemix.kernel.gshell.log.VmLogAppender">
+ <property name="events" ref="events" />
+ </bean>
+
+ <bean id="events" class="org.apache.servicemix.kernel.gshell.log.LruList">
+ <constructor-arg value="${size}" />
+ </bean>
+
+ <osgi:service ref="vmLogAppender" interface="org.ops4j.pax.logging.spi.PaxAppender">
+ <osgi:service-properties>
+ <entry>
+ <key><util:constant static-field="org.ops4j.pax.logging.PaxLoggingService.APPENDER_NAME_PROPERTY"/></key>
+ <value>VmLogAppender</value>
+ </entry>
+ </osgi:service-properties>
+ </osgi:service>
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.log">
+ <prop key="size">500</prop>
+ <prop key="pattern">%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n</prop>
+ </osgix:cm-properties>
+
+ <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
new file mode 100644
index 0000000..ff84091
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the last exception from the log.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
new file mode 100644
index 0000000..d360b1a
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display log entries.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
new file mode 100644
index 0000000..94ee1d3
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Show log level.
+
+command.manual=\
+ TODO: date manual
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
new file mode 100644
index 0000000..511734b
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set log level.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/pom.xml b/karaf/gshell/gshell-obr/pom.xml
new file mode 100644
index 0000000..58cc9e8
--- /dev/null
+++ b/karaf/gshell/gshell-obr/pom.xml
@@ -0,0 +1,85 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell OBR Commands</name>
+
+ <description>
+ Provides the OBR GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.obr*;version=${project.version}</Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java
new file mode 100644
index 0000000..d416674
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class AddUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String url : urls) {
+ admin.addRepository(new URL(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java
new file mode 100644
index 0000000..7bbd1dc
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.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.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class DeployCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "List of bundles")
+ protected List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ doDeploy(admin, bundles, false);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java
new file mode 100644
index 0000000..344e592
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.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.servicemix.kernel.gshell.obr;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class FileUtil
+{
+ public static void downloadSource(
+ PrintWriter out, PrintWriter err,
+ URL srcURL, String dirStr, boolean extract)
+ {
+ // Get the file name from the URL.
+ String fileName = (srcURL.getFile().lastIndexOf('/') > 0)
+ ? srcURL.getFile().substring(srcURL.getFile().lastIndexOf('/') + 1)
+ : srcURL.getFile();
+
+ try
+ {
+ out.println("Connecting...");
+
+ File dir = new File(dirStr);
+ if (!dir.exists())
+ {
+ err.println("Destination directory does not exist.");
+ }
+ File file = new File(dir, fileName);
+
+ OutputStream os = new FileOutputStream(file);
+ URLConnection conn = srcURL.openConnection();
+ int total = conn.getContentLength();
+ InputStream is = conn.getInputStream();
+
+ if (total > 0)
+ {
+ out.println("Downloading " + fileName
+ + " ( " + total + " bytes ).");
+ }
+ else
+ {
+ out.println("Downloading " + fileName + ".");
+ }
+ byte[] buffer = new byte[4096];
+ int count = 0;
+ for (int len = is.read(buffer); len > 0; len = is.read(buffer))
+ {
+ count += len;
+ os.write(buffer, 0, len);
+ }
+
+ os.close();
+ is.close();
+
+ if (extract)
+ {
+ is = new FileInputStream(file);
+ JarInputStream jis = new JarInputStream(is);
+ out.println("Extracting...");
+ unjar(jis, dir);
+ jis.close();
+ file.delete();
+ }
+ }
+ catch (Exception ex)
+ {
+ err.println(ex);
+ }
+ }
+
+ public static void unjar(JarInputStream jis, File dir)
+ throws IOException
+ {
+ // Reusable buffer.
+ byte[] buffer = new byte[4096];
+
+ // Loop through JAR entries.
+ for (JarEntry je = jis.getNextJarEntry();
+ je != null;
+ je = jis.getNextJarEntry())
+ {
+ if (je.getName().startsWith("/"))
+ {
+ throw new IOException("JAR resource cannot contain absolute paths.");
+ }
+
+ File target = new File(dir, je.getName());
+
+ // Check to see if the JAR entry is a directory.
+ if (je.isDirectory())
+ {
+ if (!target.exists())
+ {
+ if (!target.mkdirs())
+ {
+ throw new IOException("Unable to create target directory: "
+ + target);
+ }
+ }
+ // Just continue since directories do not have content to copy.
+ continue;
+ }
+
+ int lastIndex = je.getName().lastIndexOf('/');
+ String name = (lastIndex >= 0) ?
+ je.getName().substring(lastIndex + 1) : je.getName();
+ String destination = (lastIndex >= 0) ?
+ je.getName().substring(0, lastIndex) : "";
+
+ // JAR files use '/', so convert it to platform separator.
+ destination = destination.replace('/', File.separatorChar);
+ copy(jis, dir, name, destination, buffer);
+ }
+ }
+
+ public static void copy(
+ InputStream is, File dir, String destName, String destDir, byte[] buffer)
+ throws IOException
+ {
+ if (destDir == null)
+ {
+ destDir = "";
+ }
+
+ // Make sure the target directory exists and
+ // that is actually a directory.
+ File targetDir = new File(dir, destDir);
+ if (!targetDir.exists())
+ {
+ if (!targetDir.mkdirs())
+ {
+ throw new IOException("Unable to create target directory: "
+ + targetDir);
+ }
+ }
+ else if (!targetDir.isDirectory())
+ {
+ throw new IOException("Target is not a directory: "
+ + targetDir);
+ }
+
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(new File(targetDir, destName)));
+ int count = 0;
+ while ((count = is.read(buffer)) > 0)
+ {
+ bos.write(buffer, 0, count);
+ }
+ bos.close();
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java
new file mode 100644
index 0000000..50ede86
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.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.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Capability;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resource;
+
+public class InfoCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true)
+ List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource[] resources = searchRepository(admin, target[0], target[1]);
+ if (resources == null)
+ {
+ io.err.println("Unknown bundle and/or version: "
+ + target[0]);
+ }
+ else
+ {
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ if (resIdx > 0)
+ {
+ io.out.println("");
+ }
+ printResource(io.out, resources[resIdx]);
+ }
+ }
+ }
+ }
+
+ private void printResource(PrintWriter out, Resource resource)
+ {
+ printUnderline(out, resource.getPresentationName().length());
+ out.println(resource.getPresentationName());
+ printUnderline(out, resource.getPresentationName().length());
+
+ Map map = resource.getProperties();
+ for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (entry.getValue().getClass().isArray())
+ {
+ out.println(entry.getKey() + ":");
+ for (int j = 0; j < Array.getLength(entry.getValue()); j++)
+ {
+ out.println(" " + Array.get(entry.getValue(), j));
+ }
+ }
+ else
+ {
+ out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+
+ Requirement[] reqs = resource.getRequirements();
+ if ((reqs != null) && (reqs.length > 0))
+ {
+ out.println("Requires:");
+ for (int i = 0; i < reqs.length; i++)
+ {
+ out.println(" " + reqs[i].getFilter());
+ }
+ }
+
+ Capability[] caps = resource.getCapabilities();
+ if ((caps != null) && (caps.length > 0))
+ {
+ out.println("Capabilities:");
+ for (int i = 0; i < caps.length; i++)
+ {
+ out.println(" " + caps[i].getProperties());
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java
new file mode 100644
index 0000000..37ff08f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.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.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class ListCommand extends ObrCommandSupport {
+
+ @Argument(required = false, multiValued = true)
+ List<String> args;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ String substr = null;
+
+ if (args != null) {
+ for (String arg : args)
+ {
+ // Add a space in between tokens.
+ if (substr == null)
+ {
+ substr = "";
+ }
+ else
+ {
+ substr += " ";
+ }
+
+ substr += arg;
+ }
+ }
+
+ StringBuffer sb = new StringBuffer();
+ if ((substr == null) || (substr.length() == 0))
+ {
+ sb.append("(|(presentationname=*)(symbolicname=*))");
+ }
+ else
+ {
+ sb.append("(|(presentationname=*");
+ sb.append(substr);
+ sb.append("*)(symbolicname=*");
+ sb.append(substr);
+ sb.append("*))");
+ }
+ Resource[] resources = admin.discoverResources(sb.toString());
+ for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+ {
+ String name = resources[resIdx].getPresentationName();
+ Version version = resources[resIdx].getVersion();
+ if (version != null)
+ {
+ io.out.println(name + " (" + version + ")");
+ }
+ else
+ {
+ io.out.println(name);
+ }
+ }
+
+ if (resources == null)
+ {
+ io.out.println("No matching bundles.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java
new file mode 100644
index 0000000..ae8eaa2
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class ListUrlCommand extends ObrCommandSupport {
+
+ protected void doExecute(RepositoryAdmin admin) {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ io.out.println(repos[i].getURL());
+ }
+ } else {
+ io.out.println("No repository URLs are set.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
new file mode 100644
index 0000000..dd6af1a
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+public abstract class ObrCommandSupport extends OsgiCommandSupport {
+
+ protected static final char VERSION_DELIM = ',';
+
+ protected Object doExecute() throws Exception {
+ // Get repository admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(RepositoryAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("RepositoryAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ RepositoryAdmin admin = (RepositoryAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("RepositoryAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(RepositoryAdmin admin) throws Exception;
+
+ protected Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion)
+ {
+ // Try to see if the targetId is a bundle ID.
+ try
+ {
+ Bundle bundle = getBundleContext().getBundle(Long.parseLong(targetId));
+ targetId = bundle.getSymbolicName();
+ }
+ catch (NumberFormatException ex)
+ {
+ // It was not a number, so ignore.
+ }
+
+ // The targetId may be a bundle name or a bundle symbolic name,
+ // so create the appropriate LDAP query.
+ StringBuffer sb = new StringBuffer("(|(presentationname=");
+ sb.append(targetId);
+ sb.append(")(symbolicname=");
+ sb.append(targetId);
+ sb.append("))");
+ if (targetVersion != null)
+ {
+ sb.insert(0, "(&");
+ sb.append("(version=");
+ sb.append(targetVersion);
+ sb.append("))");
+ }
+ return admin.discoverResources(sb.toString());
+ }
+
+ public Resource selectNewestVersion(Resource[] resources)
+ {
+ int idx = -1;
+ Version v = null;
+ for (int i = 0; (resources != null) && (i < resources.length); i++)
+ {
+ if (i == 0)
+ {
+ idx = 0;
+ v = resources[i].getVersion();
+ }
+ else
+ {
+ Version vtmp = resources[i].getVersion();
+ if (vtmp.compareTo(v) > 0)
+ {
+ idx = i;
+ v = vtmp;
+ }
+ }
+ }
+ return (idx < 0) ? null : resources[idx];
+ }
+
+ protected String[] getTarget(String bundle) {
+ String[] target;
+ int idx = bundle.indexOf(VERSION_DELIM);
+ if (idx > 0) {
+ target = new String[] { bundle.substring(0, idx), bundle.substring(idx) };
+ }
+ else
+ {
+ target = new String[] { bundle, null };
+ }
+ return target;
+ }
+
+ protected void printUnderline(PrintWriter out, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ out.print('-');
+ }
+ out.println("");
+ }
+
+ protected void doDeploy(RepositoryAdmin admin, List<String> bundles, boolean start) throws Exception {
+ Resolver resolver = admin.resolver();
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+ if (resource != null)
+ {
+ resolver.add(resource);
+ }
+ else
+ {
+ io.err.println("Unknown bundle - " + target[0]);
+ }
+ }
+ if ((resolver.getAddedResources() != null) &&
+ (resolver.getAddedResources().length > 0))
+ {
+ if (resolver.resolve())
+ {
+ io.out.println("Target resource(s):");
+ printUnderline(io.out, 19);
+ Resource[] resources = resolver.getAddedResources();
+ for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ resources = resolver.getRequiredResources();
+ if ((resources != null) && (resources.length > 0))
+ {
+ io.out.println("\nRequired resource(s):");
+ printUnderline(io.out, 21);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ }
+ resources = resolver.getOptionalResources();
+ if ((resources != null) && (resources.length > 0))
+ {
+ io.out.println("\nOptional resource(s):");
+ printUnderline(io.out, 21);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ }
+
+ try
+ {
+ io.out.print("\nDeploying...");
+ resolver.deploy(start);
+ io.out.println("done.");
+ }
+ catch (IllegalStateException ex)
+ {
+ io.err.println(ex);
+ }
+ }
+ else
+ {
+ Requirement[] reqs = resolver.getUnsatisfiedRequirements();
+ if ((reqs != null) && (reqs.length > 0))
+ {
+ io.out.println("Unsatisfied requirement(s):");
+ printUnderline(io.out, 27);
+ for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+ {
+ io.out.println(" " + reqs[reqIdx].getFilter());
+ Resource[] resources = resolver.getResources(reqs[reqIdx]);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName());
+ }
+ }
+ }
+ else
+ {
+ io.out.println("Could not resolve targets.");
+ }
+ }
+ }
+
+ }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java
new file mode 100644
index 0000000..056d094
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RefreshUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+ List<String> urls;
+
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ if (urls != null || urls.isEmpty()) {
+ for (String url : urls) {
+ admin.removeRepository(new URL(url));
+ admin.addRepository(new URL(url));
+ }
+ } else {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ admin.removeRepository(repos[i].getURL());
+ admin.addRepository(repos[i].getURL());
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java
new file mode 100644
index 0000000..d122706
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RemoveUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String url : urls) {
+ admin.removeRepository(new URL(url));
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java
new file mode 100644
index 0000000..f15db46
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class SourceCommand extends ObrCommandSupport {
+
+ @Option(name = "-x", description = "Extract")
+ boolean extract;
+
+ @Argument(required = true, index = 0, description = "Local directory")
+ String localDir;
+
+ @Argument(required = true, index = 1, multiValued = true, description = "List of bundles")
+ List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+ if (resource == null)
+ {
+ io.err.println("Unknown bundle and/or version: " + target[0]);
+ }
+ else
+ {
+ URL srcURL = (URL) resource.getProperties().get(Resource.SOURCE_URL);
+ if (srcURL != null)
+ {
+ FileUtil.downloadSource(io.out, io.err, srcURL, localDir, extract);
+ }
+ else
+ {
+ io.err.println("Missing source URL: " + target[0]);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
new file mode 100644
index 0000000..1a34e26
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class StartCommand extends DeployCommand {
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ doDeploy(admin, bundles, true);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
new file mode 100644
index 0000000..250051f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="obr/addUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.AddUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/deploy">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.DeployCommand" />
+ </gshell:command>
+ <gshell:command name="obr/info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.InfoCommand" />
+ </gshell:command>
+ <gshell:command name="obr/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListCommand" />
+ </gshell:command>
+ <gshell:command name="obr/listUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/removeUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RemoveUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/refreshUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RefreshUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/source">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.SourceCommand" />
+ </gshell:command>
+ <gshell:command name="obr/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.StartCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
new file mode 100644
index 0000000..acf1131
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
new file mode 100644
index 0000000..d518148
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy a list of bundles using OBR
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
new file mode 100644
index 0000000..23a2f59
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print informations about OBR bundles
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
new file mode 100644
index 0000000..d73f3b7
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List OBR bundles
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
new file mode 100644
index 0000000..40b9fc6
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
new file mode 100644
index 0000000..0a4553f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of bundles.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
new file mode 100644
index 0000000..259dfa1
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
new file mode 100644
index 0000000..76b6888
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Download the sources for an OBR bundle
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
new file mode 100644
index 0000000..809ab4d
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy and start a list of bundles using OBR
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/pom.xml b/karaf/gshell/gshell-osgi/pom.xml
new file mode 100644
index 0000000..d232750
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/pom.xml
@@ -0,0 +1,87 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell OSGi Commands</name>
+
+ <description>
+ Provides the OSGi GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.osgi*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
new file mode 100644
index 0000000..5a3fd3f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public abstract class BundleCommand extends OsgiCommandSupport {
+
+ @Argument(required = true, index = 0)
+ long id;
+
+ @Option(name = "--force")
+ boolean force;
+
+ protected Object doExecute() throws Exception {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.out.println("Bundle " + id + " not found");
+ return Result.FAILURE;
+ }
+ if (!force) {
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ if (ref != null) {
+ StartLevel sl = getService(StartLevel.class, ref);
+ if (sl != null) {
+ int level = sl.getBundleStartLevel(bundle);
+ if (level < 50) {
+ for (;;) {
+ StringBuffer sb = new StringBuffer();
+ io.err.print("You are about to access a system bundle. Do you want to continue (yes/no): ");
+ io.err.flush();
+ for (;;) {
+ int c = io.in.read();
+ if (c < 0) {
+ return Result.FAILURE;
+ }
+ io.err.print((char) c);
+ if (c == '\r' || c == '\n') {
+ break;
+ }
+ sb.append((char) c);
+ }
+ String str = sb.toString();
+ if ("yes".equals(str)) {
+ break;
+ }
+ if ("no".equals(str)) {
+ return Result.FAILURE;
+ }
+ }
+ }
+ }
+ }
+ }
+ doExecute(bundle);
+ return Result.SUCCESS;
+ }
+
+ protected abstract void doExecute(Bundle bundle) throws Exception;
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
new file mode 100644
index 0000000..cf64476
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
@@ -0,0 +1,74 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public class BundleLevel extends BundleCommand {
+
+ @Argument(required = false, index = 1)
+ Integer level;
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ if (ref == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return;
+ }
+ StartLevel sl = getService(StartLevel.class, ref);
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return;
+ }
+
+ if (level == null) {
+ io.out.println("Level " + sl.getBundleStartLevel(bundle));
+ }
+ else if ((level < 50) && sl.getBundleStartLevel(bundle) > 50){
+ for (;;) {
+ StringBuffer sb = new StringBuffer();
+ io.err.println("You are about to designate bundle as a system bundle. Do you want to continue (yes/no): ");
+ io.err.flush();
+ for (;;) {
+ int c = io.in.read();
+ if (c < 0) {
+ return;
+ }
+ io.err.println((char) c);
+ if (c == '\r' || c == '\n') {
+ break;
+ }
+ sb.append((char) c);
+ }
+ String str = sb.toString();
+ if ("yes".equals(str)) {
+ sl.setBundleStartLevel(bundle, level);
+ break;
+ } else if ("no".equals(str)) {
+ break;
+ }
+ }
+ } else {
+ sl.setBundleStartLevel(bundle, level);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
new file mode 100644
index 0000000..7b937ee
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.osgi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public abstract class BundlesCommand extends OsgiCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Bundle IDs")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ List<Bundle> bundles = new ArrayList<Bundle>();
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.err.println("Bundle ID" + id + " is invalid");
+ } else {
+ bundles.add(bundle);
+ }
+ }
+ }
+ doExecute(bundles);
+ return Result.SUCCESS;
+ }
+
+ protected abstract void doExecute(List<Bundle> bundles) throws Exception;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
new file mode 100644
index 0000000..0acf414
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public class Headers extends OsgiCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Bundles ids")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printHeaders(bundle);
+ }
+ else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ Bundle[] bundles = getBundleContext().getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ printHeaders(bundles[i]);
+ }
+ }
+ return Result.SUCCESS;
+ }
+
+ protected void printHeaders(Bundle bundle) throws Exception {
+ String title = Util.getBundleName(bundle);
+ io.out.println("\n" + title);
+ io.out.println(Util.getUnderlineString(title));
+ Dictionary dict = bundle.getHeaders();
+ Enumeration keys = dict.keys();
+ while (keys.hasMoreElements())
+ {
+ Object k = (String) keys.nextElement();
+ Object v = dict.get(k);
+ io.out.println(k + " = " + Util.getValueString(v));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java
new file mode 100644
index 0000000..ad8d526
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.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.servicemix.kernel.gshell.osgi;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+public class InstallBundle extends OsgiCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Bundle URLs")
+ List<String> urls;
+
+ @Option(name = "-s", aliases={"--start"}, description="Start the bundles after installation")
+ boolean start;
+
+ protected Object doExecute() throws Exception {
+ List<Bundle> bundles = new ArrayList<Bundle>();
+ StringBuffer sb = new StringBuffer();
+ for (String url : urls) {
+ Bundle bundle = install(url, io.out, io.err);
+ if (bundle != null) {
+ bundles.add(bundle);
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(bundle.getBundleId());
+ }
+ }
+ if (start) {
+ for (Bundle bundle : bundles) {
+ bundle.start();
+ }
+ }
+ if (sb.toString().indexOf(',') > 0) {
+ io.out.println("Bundle IDs: " + sb.toString());
+ } else if (sb.length() > 0) {
+ io.out.println("Bundle ID: " + sb.toString());
+ }
+ return Result.SUCCESS;
+ }
+
+ protected Bundle install(String location, PrintWriter out, PrintWriter err) {
+ try {
+ return getBundleContext().installBundle(location, null);
+ } catch (IllegalStateException ex) {
+ err.println(ex.toString());
+ } catch (BundleException ex) {
+ if (ex.getNestedException() != null) {
+ err.println(ex.getNestedException().toString());
+ } else {
+ err.println(ex.toString());
+ }
+ } catch (Exception ex) {
+ err.println(ex.toString());
+ }
+ return null;
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
new file mode 100644
index 0000000..00604f3
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
@@ -0,0 +1,203 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+
+public class ListBundles extends OsgiCommandSupport {
+
+ @Option(name = "-l", description = "Show locations")
+ boolean showLoc;
+
+ @Option(name = "-s", description = "Show symbolic name")
+ boolean showSymbolic;
+
+ @Option(name = "-u", description = "Show update")
+ boolean showUpdate;
+
+ private SpringApplicationListener springApplicationListener;
+
+ public SpringApplicationListener getSpringApplicationListener() {
+ return springApplicationListener;
+ }
+
+ public void setSpringApplicationListener(SpringApplicationListener springApplicationListener) {
+ this.springApplicationListener = springApplicationListener;
+ }
+
+ protected Object doExecute() throws Exception {
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ StartLevel sl = null;
+ if (ref != null) {
+ sl = (StartLevel) getBundleContext().getService(ref);
+ }
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ }
+
+ ServiceReference pkgref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ PackageAdmin admin = null;
+ if (pkgref != null) {
+ admin = (PackageAdmin) getBundleContext().getService(pkgref);
+ if (admin == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ }
+ }
+
+ Bundle[] bundles = getBundleContext().getBundles();
+ if (bundles != null) {
+ // Display active start level.
+ if (sl != null) {
+ io.out.println("START LEVEL " + sl.getStartLevel());
+ }
+
+ // Print column headers.
+ String msg = " Name";
+ if (showLoc) {
+ msg = " Location";
+ }
+ else if (showSymbolic) {
+ msg = " Symbolic name";
+ }
+ else if (showUpdate) {
+ msg = " Update location";
+ }
+ String level = (sl == null) ? "" : " Level ";
+ io.out.println(" ID State Spring " + level + msg);
+ for (int i = 0; i < bundles.length; i++) {
+ // Get the bundle name or location.
+ String name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_NAME);
+ // If there is no name, then default to symbolic name.
+ name = (name == null) ? bundles[i].getSymbolicName() : name;
+ // If there is no symbolic name, resort to location.
+ name = (name == null) ? bundles[i].getLocation() : name;
+
+ // Overwrite the default value is the user specifically
+ // requested to display one or the other.
+ if (showLoc) {
+ name = bundles[i].getLocation();
+ }
+ else if (showSymbolic) {
+ name = bundles[i].getSymbolicName();
+ name = (name == null) ? "<no symbolic name>" : name;
+ }
+ else if (showUpdate) {
+ name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_UPDATELOCATION);
+ name = (name == null) ? bundles[i].getLocation() : name;
+ }
+ // Show bundle version if not showing location.
+ String version = (String) bundles[i].getHeaders().get(Constants.BUNDLE_VERSION);
+ name = (!showLoc && !showUpdate && (version != null)) ? name + " (" + version + ")" : name;
+ long l = bundles[i].getBundleId();
+ String id = String.valueOf(l);
+ if (sl == null) {
+ level = "1";
+ }
+ else {
+ level = String.valueOf(sl.getBundleStartLevel(bundles[i]));
+ }
+ while (level.length() < 5) {
+ level = " " + level;
+ }
+ while (id.length() < 4) {
+ id = " " + id;
+ }
+ io.out.println("[" + id + "] ["
+ + getStateString(bundles[i])
+ + "] [" + getSpringStateString(bundles[i])
+ + "] [" + level + "] " + name);
+
+ if (admin != null) {
+ Bundle[] fragments = admin.getFragments(bundles[i]);
+ Bundle[] hosts = admin.getHosts(bundles[i]);
+
+ if (fragments != null) {
+ io.out.print(" Fragments: ");
+ int ii = 0;
+ for (Bundle fragment : fragments) {
+ ii++;
+ io.out.print(fragment.getBundleId());
+ if ((fragments.length > 1) && ii < (fragments.length)) {
+ io.out.print(",");
+ }
+ }
+ io.out.println();
+ }
+
+ if (hosts != null) {
+ io.out.print(" Hosts: ");
+ int ii = 0;
+ for (Bundle host : hosts) {
+ ii++;
+ io.out.print(host.getBundleId());
+ if ((hosts.length > 1) && ii < (hosts.length)) {
+ io.out.print(",");
+ }
+ }
+ io.out.println();
+ }
+
+ }
+ }
+ }
+ else {
+ io.out.println("There are no installed bundles.");
+ }
+
+ getBundleContext().ungetService(ref);
+ getBundleContext().ungetService(pkgref);
+
+ return Result.SUCCESS;
+ }
+
+ public String getStateString(Bundle bundle)
+ {
+ int state = bundle.getState();
+ if (state == Bundle.ACTIVE) {
+ return "Active ";
+ } else if (state == Bundle.INSTALLED) {
+ return "Installed ";
+ } else if (state == Bundle.RESOLVED) {
+ return "Resolved ";
+ } else if (state == Bundle.STARTING) {
+ return "Starting ";
+ } else if (state == Bundle.STOPPING) {
+ return "Stopping ";
+ } else {
+ return "Unknown ";
+ }
+ }
+
+ public String getSpringStateString(Bundle bundle) {
+ SpringApplicationListener.SpringState state = springApplicationListener.getSpringState(bundle);
+ if (state == SpringApplicationListener.SpringState.Waiting) {
+ return "Waiting";
+ } else if (state == SpringApplicationListener.SpringState.Started) {
+ return "Started";
+ } else if (state == SpringApplicationListener.SpringState.Failed) {
+ return "Failed ";
+ } else {
+ return " ";
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
new file mode 100644
index 0000000..3753e1a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
@@ -0,0 +1,174 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class ListServices extends OsgiCommandSupport {
+
+ @Option(name = "-a", description = "Show all")
+ boolean showAll;
+
+ @Option(name = "-u", description = "Show in use")
+ boolean inUse;
+
+ @Argument(required = false, multiValued = true, description = "Bundles ids")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ boolean headerPrinted = false;
+ boolean needSeparator = false;
+ ServiceReference[] refs = null;
+
+ // Get registered or in-use services.
+ if (inUse) {
+ refs = bundle.getServicesInUse();
+ } else {
+ refs = bundle.getRegisteredServices();
+ }
+
+ // Print properties for each service.
+ for (int refIdx = 0;
+ (refs != null) && (refIdx < refs.length);
+ refIdx++) {
+ String[] objectClass = (String[])
+ refs[refIdx].getProperty("objectClass");
+
+ // Determine if we need to print the service, depending
+ // on whether it is a command service or not.
+ boolean print = true;
+ for (int ocIdx = 0;
+ !showAll && (ocIdx < objectClass.length);
+ ocIdx++) {
+ if (objectClass[ocIdx].equals(Command.class.getName())) {
+ print = false;
+ }
+ }
+
+ // Print header if we have not already done so.
+ if (!headerPrinted) {
+ headerPrinted = true;
+ String title = Util.getBundleName(bundle);
+ title = (inUse)
+ ? title + " uses:"
+ : title + " provides:";
+ io.out.println("");
+ io.out.println(title);
+ io.out.println(Util.getUnderlineString(title));
+ }
+
+ if (showAll || print) {
+ // Print service separator if necessary.
+ if (needSeparator) {
+ io.out.println("----");
+ }
+
+ // Print service properties.
+ String[] keys = refs[refIdx].getPropertyKeys();
+ for (int keyIdx = 0;
+ (keys != null) && (keyIdx < keys.length);
+ keyIdx++) {
+ Object v = refs[refIdx].getProperty(keys[keyIdx]);
+ io.out.println(
+ keys[keyIdx] + " = " + Util.getValueString(v));
+ }
+
+ needSeparator = true;
+ }
+ }
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else
+ {
+ Bundle[] bundles = getBundleContext().getBundles();
+ if (bundles != null)
+ {
+ // TODO: Sort list.
+ for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+ {
+ boolean headerPrinted = false;
+ ServiceReference[] refs = null;
+
+ // Get registered or in-use services.
+ if (inUse)
+ {
+ refs = bundles[bundleIdx].getServicesInUse();
+ }
+ else
+ {
+ refs = bundles[bundleIdx].getRegisteredServices();
+ }
+
+ for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
+ {
+ String[] objectClass = (String[])
+ refs[refIdx].getProperty("objectClass");
+
+ // Determine if we need to print the service, depending
+ // on whether it is a command service or not.
+ boolean print = true;
+ for (int ocIdx = 0;
+ !showAll && (ocIdx < objectClass.length);
+ ocIdx++)
+ {
+ if (objectClass[ocIdx].equals(Command.class.getName()))
+ {
+ print = false;
+ }
+ }
+
+ // Print the service if necessary.
+ if (showAll || print)
+ {
+ if (!headerPrinted)
+ {
+ headerPrinted = true;
+ String title = Util.getBundleName(bundles[bundleIdx]);
+ title = (inUse)
+ ? title + " uses:"
+ : title + " provides:";
+ io.out.println("\n" + title);
+ io.out.println(Util.getUnderlineString(title));
+ }
+ io.out.println(Util.getValueString(objectClass));
+ }
+ }
+ }
+ }
+ else
+ {
+ io.out.println("There are no registered services.");
+ }
+ }
+ return Result.SUCCESS;
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java
new file mode 100644
index 0000000..e554b3c
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class RefreshBundle extends OsgiCommandSupport {
+
+ @Argument(required = false)
+ Long id;
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return Result.FAILURE;
+ }
+ try {
+ PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+ if (pa == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return Result.FAILURE;
+ }
+ if (id == null) {
+ pa.refreshPackages(null);
+ }
+ else {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.out.println("Bundle " + id + " not found");
+ return Result.FAILURE;
+ }
+ pa.refreshPackages(new Bundle[] { bundle });
+ }
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java
new file mode 100644
index 0000000..607ef91
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ResolveBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return;
+ }
+ try {
+ PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+ if (pa == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return;
+ }
+ pa.resolveBundles(new Bundle[] { bundle });
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
new file mode 100644
index 0000000..6793ecb
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class RestartBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.stop();
+ bundle.start();
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java
new file mode 100644
index 0000000..76e8081
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.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.servicemix.kernel.gshell.osgi;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+/**
+ * Command to shut down ServiceMix Kernel
+ */
+public class Shutdown extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ new Thread() {
+ public void run() {
+ try {
+ Bundle bundle = getBundleContext().getBundle(0);
+ bundle.stop();
+ } catch (Exception e) {
+ log.error("Error when shutting down ServiceMix Kernel", e);
+ }
+ }
+ }.start();
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
new file mode 100644
index 0000000..240bd0a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
@@ -0,0 +1,102 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextEvent;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextListener;
+import org.springframework.osgi.context.event.OsgiBundleContextFailedEvent;
+import org.springframework.osgi.context.event.OsgiBundleContextRefreshedEvent;
+import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
+
+public class SpringApplicationListener implements OsgiBundleApplicationContextListener,
+ BundleListener, BundleContextAware,
+ InitializingBean, DisposableBean {
+
+ public static enum SpringState {
+ Unknown,
+ Waiting,
+ Started,
+ Failed,
+ }
+
+ private static final Log LOG = LogFactory.getLog(SpringApplicationListener.class);
+
+ private final Map<Long, SpringState> states;
+ private BundleContext bundleContext;
+
+ public SpringApplicationListener() {
+ this.states = new ConcurrentHashMap<Long, SpringState>();
+ }
+
+ public SpringState getSpringState(Bundle bundle) {
+ SpringState state = states.get(bundle.getBundleId());
+ if (state == null || bundle.getState() != Bundle.ACTIVE) {
+ state = SpringState.Unknown;
+ }
+ return state;
+ }
+
+ public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) {
+ SpringState state = null;
+ if (event instanceof BootstrappingDependencyEvent) {
+ OsgiServiceDependencyEvent de = ((BootstrappingDependencyEvent) event).getDependencyEvent();
+ if (de instanceof OsgiServiceDependencyWaitStartingEvent) {
+ state = SpringState.Waiting;
+ }
+ } else if (event instanceof OsgiBundleContextFailedEvent) {
+ state = SpringState.Failed;
+ } else if (event instanceof OsgiBundleContextRefreshedEvent) {
+ state = SpringState.Started;
+ }
+ if (state != null) {
+ LOG.debug("Spring app state changed to " + state + " for bundle " + event.getBundle().getBundleId());
+ states.put(event.getBundle().getBundleId(), state);
+ }
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.UNINSTALLED) {
+ states.remove(event.getBundle().getBundleId());
+ }
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ bundleContext.addBundleListener(this);
+ }
+
+ public void destroy() throws Exception {
+ bundleContext.removeBundleListener(this);
+ }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
new file mode 100644
index 0000000..6639802
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StartBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.start();
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java
new file mode 100644
index 0000000..a04a688
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+
+public class StartLevel extends OsgiCommandSupport {
+
+ @Argument(required = false, index = 0)
+ Integer level;
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName());
+ if (ref == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return null;
+ }
+ try {
+ org.osgi.service.startlevel.StartLevel sl = (org.osgi.service.startlevel.StartLevel) getBundleContext().getService(ref);
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return null;
+ }
+
+ if (level == null) {
+ io.out.println("Level " + sl.getStartLevel());
+ }
+ else {
+ sl.setStartLevel(level);
+ }
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return Result.SUCCESS;
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
new file mode 100644
index 0000000..867a0a0
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StopBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
new file mode 100644
index 0000000..f92cc9b
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class UninstallBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.uninstall();
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java
new file mode 100644
index 0000000..1e02e6f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.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.servicemix.kernel.gshell.osgi;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+
+public class UpdateBundle extends BundleCommand {
+
+ @Argument(required = false, description = "Bundle location", index=1)
+ String location;
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ if (location != null) {
+ InputStream is = new URL(location).openStream();
+ bundle.update(is);
+ } else {
+ bundle.update();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
new file mode 100644
index 0000000..c8481d6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+public class Util
+{
+ public static String getBundleName(Bundle bundle)
+ {
+ if (bundle != null)
+ {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+ private static StringBuffer m_sb = new StringBuffer();
+
+ public static String getUnderlineString(String s)
+ {
+ synchronized (m_sb)
+ {
+ m_sb.delete(0, m_sb.length());
+ for (int i = 0; i < s.length(); i++)
+ {
+ m_sb.append('-');
+ }
+ return m_sb.toString();
+ }
+ }
+
+ public static String getValueString(Object obj)
+ {
+ synchronized (m_sb)
+ {
+ if (obj instanceof String)
+ {
+ return (String) obj;
+ }
+ else if (obj instanceof String[])
+ {
+ String[] array = (String[]) obj;
+ m_sb.delete(0, m_sb.length());
+ for (int i = 0; i < array.length; i++)
+ {
+ if (i != 0)
+ {
+ m_sb.append(", ");
+ }
+ m_sb.append(array[i].toString());
+ }
+ return m_sb.toString();
+ }
+ else if (obj instanceof Boolean)
+ {
+ return ((Boolean) obj).toString();
+ }
+ else if (obj instanceof Long)
+ {
+ return ((Long) obj).toString();
+ }
+ else if (obj instanceof Integer)
+ {
+ return ((Integer) obj).toString();
+ }
+ else if (obj instanceof Short)
+ {
+ return ((Short) obj).toString();
+ }
+ else if (obj instanceof Double)
+ {
+ return ((Double) obj).toString();
+ }
+ else if (obj instanceof Float)
+ {
+ return ((Float) obj).toString();
+ }
+ else if (obj == null)
+ {
+ return "null";
+ }
+ else
+ {
+ return obj.toString();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..7f06ebf
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="osgi/bundle-level">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.BundleLevel" />
+ </gshell:command>
+ <gshell:command name="osgi/headers">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Headers" />
+ </gshell:command>
+ <gshell:command name="osgi/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.InstallBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListBundles">
+ <property name="springApplicationListener" ref="springApplicationListener" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:command name="osgi/ls">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListServices" />
+ </gshell:command>
+ <gshell:command name="osgi/refresh">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RefreshBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/update">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UpdateBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/resolve">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ResolveBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/restart">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RestartBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/shutdown">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Shutdown" />
+ </gshell:command>
+ <gshell:command name="osgi/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/start-level">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartLevel" />
+ </gshell:command>
+ <gshell:command name="osgi/stop">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StopBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/uninstall">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UninstallBundle" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="springApplicationListener" class="org.apache.servicemix.kernel.gshell.osgi.SpringApplicationListener" />
+
+ <osgi:service ref="springApplicationListener" interface="org.springframework.osgi.context.event.OsgiBundleApplicationContextListener" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
new file mode 100644
index 0000000..9a9bee9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level of a given bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
new file mode 100644
index 0000000..7d53e5f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display OSGi headers
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
new file mode 100644
index 0000000..85858db
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
new file mode 100644
index 0000000..204792a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List bundles
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
new file mode 100644
index 0000000..1f8ccf9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List services
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
new file mode 100644
index 0000000..db60c15
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Refresh bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
new file mode 100644
index 0000000..9f5de69
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Resolve bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
new file mode 100644
index 0000000..762aa40
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Restart bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
new file mode 100644
index 0000000..940f5f6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Shutdown OSGi
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
new file mode 100644
index 0000000..7a6e7ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
new file mode 100644
index 0000000..1b9761f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
new file mode 100644
index 0000000..40fc360
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
new file mode 100644
index 0000000..36351ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
new file mode 100644
index 0000000..9b07581
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Update bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/pom.xml b/karaf/gshell/gshell-packages/pom.xml
new file mode 100644
index 0000000..e127f98
--- /dev/null
+++ b/karaf/gshell/gshell-packages/pom.xml
@@ -0,0 +1,88 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell PackageAdmin Commands</name>
+
+ <description>
+ Provides the PackageAdmin GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.packages*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java
new file mode 100644
index 0000000..7bb15f1
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ExportsCommand extends PackageCommandSupport {
+
+ @Option(name = "-i", aliases = { "--imports"}, description = "List bundles importing the packages")
+ boolean imports;
+
+ @Argument(required = false, multiValued = true, description = "bundle ids")
+ List<Long> ids;
+
+ protected void doExecute(PackageAdmin admin) throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printExports(io.out, bundle, admin.getExportedPackages(bundle));
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ printExports(io.out, null, admin.getExportedPackages((Bundle) null));
+ }
+ }
+
+ protected void printExports(PrintWriter out, Bundle target, ExportedPackage[] exports) {
+ if ((exports != null) && (exports.length > 0)) {
+ for (int i = 0; i < exports.length; i++) {
+ Bundle bundle = exports[i].getExportingBundle();
+ out.print(getBundleName(bundle));
+ out.println(": " + exports[i]);
+ if (imports) {
+ Bundle[] bs = exports[i].getImportingBundles();
+ if (bs != null) {
+ for (Bundle b : bs) {
+ out.println("\t" + getBundleName(b));
+ }
+ }
+ }
+ }
+ } else {
+ out.println(getBundleName(target) + ": No active exported packages.");
+ }
+ }
+
+ public static String getBundleName(Bundle bundle) {
+ if (bundle != null) {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
new file mode 100644
index 0000000..7d109cb
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ImportsCommand extends PackageCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "bundle ids")
+ List<Long> ids;
+
+ protected void doExecute(PackageAdmin admin) throws Exception {
+ Map<Long, List<ExportedPackage>> packages = new HashMap<Long, List<ExportedPackage>>();
+ ExportedPackage[] exported = admin.getExportedPackages((Bundle) null);
+ for (ExportedPackage pkg : exported) {
+ Bundle[] bundles = pkg.getImportingBundles();
+ if (bundles != null) {
+ for (Bundle b : bundles) {
+ List<ExportedPackage> p = packages.get(b.getBundleId());
+ if (p == null) {
+ p = new ArrayList<ExportedPackage>();
+ packages.put(b.getBundleId(), p);
+ }
+ p.add(pkg);
+ }
+ }
+ }
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printImports(io.out, bundle, packages.get(bundle.getBundleId()));
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ List<ExportedPackage> pkgs = new ArrayList<ExportedPackage>();
+ for (List<ExportedPackage> l : packages.values()) {
+ pkgs.addAll(l);
+ }
+ printImports(io.out, null, pkgs);
+ }
+ }
+
+ protected void printImports(PrintWriter out, Bundle target, List<ExportedPackage> imports) {
+ if ((imports != null) && (imports.size() > 0)) {
+ for (ExportedPackage p : imports) {
+ Bundle bundle = p.getExportingBundle();
+ out.print(getBundleName(bundle));
+ out.println(": " + p);
+ }
+ } else {
+ out.println(getBundleName(target) + ": No active imported packages.");
+ }
+ }
+
+ public static String getBundleName(Bundle bundle) {
+ if (bundle != null) {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java
new file mode 100644
index 0000000..ef99b94
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.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.servicemix.kernel.gshell.packages;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Abstract class from which all commands related to the PackageAdmin
+ * service should derive.
+ * This command retrieves a reference to the PackageAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class PackageCommandSupport extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ PackageAdmin admin = (PackageAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(PackageAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
new file mode 100644
index 0000000..9e3060d
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="packages/exports">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ExportsCommand" />
+ </gshell:command>
+ <gshell:command name="packages/imports">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ImportsCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
new file mode 100644
index 0000000..9487118
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print exported packages.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
new file mode 100644
index 0000000..917d860
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print imported packages.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/pom.xml b/karaf/gshell/gshell-wrapper/pom.xml
new file mode 100644
index 0000000..341e837
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/pom.xml
@@ -0,0 +1,115 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.wrapper</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Service Wrapper</name>
+
+ <description>
+ Provides the Service Wrapper GShell integration
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+ org.apache.geronimo.gshell*,
+ </gshell.osgi.import>
+ <gshell.osgi.export>
+ </gshell.osgi.export>
+ <gshell.osgi.private>
+ org.apache.servicemix.kernel.gshell.wrapper.*,
+ </gshell.osgi.private>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.wrapper.*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java
new file mode 100644
index 0000000..f58c3cc
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.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.servicemix.kernel.gshell.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.io.PumpStreamHandler;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+/**
+ * Installs this ServiceMix instance as a service in your operating systems.
+ *
+ * @version $Rev: 603634 $ $Date: 2007-12-12 16:07:16 +0100 (Wed, 12 Dec 2007) $
+ */
+public class InstallCommand extends OsgiCommandSupport
+{
+
+ @Option(name="-n", aliases={"--name"}, description="The service name that will be used when installing the service.")
+ private String name="servicemix";
+ @Option(name="-d", aliases={"--display"}, description="The display name of the service.")
+ private String displayName;
+ @Option(name="-D", aliases={"--description"}, description="The description of the service.")
+ private String description="";
+ @Option(name="-s", aliases={"--start-type"}, description="Mode in which the service is installed. AUTO_START or DEMAND_START")
+ private String startType="AUTO_START";
+
+ protected Object doExecute() throws Exception {
+
+ try {
+ String name = getName();
+ File base = new File(System.getProperty("servicemix.base"));
+ File bin = new File(base, "bin");
+ File etc = new File(base, "etc");
+ File lib = new File(base, "lib");
+
+ HashMap<String, String> props = new HashMap<String, String>();
+ props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+ props.put("${servicemix.base}", base.getPath());
+ props.put("${name}", name);
+ props.put("${displayName}", getDisplayName());
+ props.put("${description}", getDescription());
+ props.put("${startType}", getStartType());
+
+ String os = System.getProperty("os.name", "Unknown");
+ File serviceFile=null;
+ if( os.startsWith("Win") ) {
+ mkdir(bin);
+ copyResourceTo(new File(bin, name+"-wrapper.exe"), "windows/servicemix-wrapper.exe", false);
+ serviceFile = new File(bin,name+"-service.bat");
+ copyFilteredResourceTo(serviceFile, "windows/servicemix-service.bat", props);
+ mkdir(lib);
+ copyResourceTo(new File(bin, "wrapper.dll"), "windows/wrapper.dll", false);
+ } else if( os.startsWith("Mac OS X") ) {
+ mkdir(bin);
+
+ File file = new File(bin, name+"-wrapper");
+ copyResourceTo(file, "macosx/servicemix-wrapper", false);
+ chmod(file, "a+x");
+
+ serviceFile = new File(bin,name+"-service");
+ copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+ chmod(serviceFile, "a+x");
+
+ mkdir(lib);
+ copyResourceTo(new File(lib, "libwrapper.jnilib"), "macosx/libwrapper.jnilib", false);
+
+ // TODO: figure out how to hook in the service that it starts up
+ // when the machine boots up.
+ } else if( os.startsWith("Linux") ) {
+ mkdir(bin);
+
+ File file = new File(bin, name+"-wrapper");
+ copyResourceTo(file, "linux/servicemix-wrapper", false);
+ chmod(file, "a+x");
+
+ serviceFile = new File(bin,name+"-service");
+ copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+ chmod(serviceFile, "a+x");
+
+ mkdir(lib);
+ copyResourceTo(new File(lib, "libwrapper.so"), "linux/libwrapper.so", false);
+
+ // TODO: figure out how to hook in the service that it starts up
+ // when the machine boots up.
+ } else {
+ io.out.println("Your operating system '"+os+"' is not currently supported.");
+ return 1;
+ }
+
+ // Install the wrapper jar to the lib directory..
+ mkdir(lib);
+ copyResourceTo(new File(lib, "servicemix-wrapper.jar"), "all/servicemix-wrapper.jar", false);
+ mkdir(etc);
+ File wrapperConf = new File(etc,name+"-wrapper.conf");
+ copyFilteredResourceTo(wrapperConf, "all/servicemix-wrapper.conf", props);
+
+ io.out.println("");
+ io.out.println("Setup complete. You may want to tweak the JVM properties in the wrapper configuration file: "+wrapperConf.getPath());
+ io.out.println("before installing and starting the service.");
+ io.out.println("");
+ if( os.startsWith("Win") ) {
+ io.out.println("");
+ io.out.println("To install the service, run: ");
+ io.out.println(" C:> "+serviceFile.getPath()+" install");
+ io.out.println("");
+ io.out.println("Once installed, to start the service run: ");
+ io.out.println(" C:> net start \""+name+"\"");
+ io.out.println("");
+ io.out.println("Once running, to stop the service run: ");
+ io.out.println(" C:> net stop \""+name+"\"");
+ io.out.println("");
+ io.out.println("Once stopped, to remove the installed the service run: ");
+ io.out.println(" C:> "+serviceFile.getPath()+" remove");
+ io.out.println("");
+ } else if( os.startsWith("Mac OS X") ) {
+ io.out.println("");
+ io.out.println("At this time it is not known how to get this service to start when the machine is rebooted.");
+ io.out.println("If you know how to install the following service script so that it gets started");
+ io.out.println("when OS X starts, please email dev@servicemix.apache.org and let us know how so");
+ io.out.println("we can update this message.");
+ io.out.println(" ");
+ io.out.println(" To start the service:");
+ io.out.println(" $ "+serviceFile.getPath()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ "+serviceFile.getPath()+" stop");
+ io.out.println("");
+ } else if( os.startsWith("Linux") ) {
+ io.out.println("The way the service is installed depends upon your flavor of Linux.");
+
+ // TODO: figure out if we can detect the Linux flavor
+
+ io.out.println("");
+ io.out.println("@|cyan On Redhat/Fedora/CentOS Systems:|");
+ io.out.println(" To install the service:");
+ io.out.println(" $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" --add");
+ io.out.println("");
+ io.out.println(" To start the service when the machine is rebooted:");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" on");
+ io.out.println("");
+ io.out.println(" To disable starting the service when the machine is rebooted:");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" off");
+ io.out.println("");
+ io.out.println(" To start the service:");
+ io.out.println(" $ service "+serviceFile.getName()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ service "+serviceFile.getName()+" stop");
+ io.out.println("");
+ io.out.println(" To uninstall the service :");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" --del");
+ io.out.println(" $ rm /etc/init.d/"+serviceFile.getName());
+
+ io.out.println("");
+ io.out.println("@|cyan On Ubuntu/Debian Systems:|");
+ io.out.println(" To install the service:");
+ io.out.println(" $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+ io.out.println("");
+ io.out.println(" To start the service when the machine is rebooted:");
+ io.out.println(" $ update-rc.d "+serviceFile.getName()+" defaults");
+ io.out.println("");
+ io.out.println(" To disable starting the service when the machine is rebooted:");
+ io.out.println(" $ update-rc.d -f "+serviceFile.getName()+" remove");
+ io.out.println("");
+ io.out.println(" To start the service:");
+ io.out.println(" $ /etc/init.d/"+serviceFile.getName()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ /etc/init.d/"+serviceFile.getName()+" stop");
+ io.out.println("");
+ io.out.println(" To uninstall the service :");
+ io.out.println(" $ rm /etc/init.d/"+serviceFile.getName());
+
+ }
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+
+ return 0;
+ }
+
+ private int chmod(File serviceFile, String mode) throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("chmod", mode, serviceFile.getCanonicalPath());
+ Process p = builder.start();
+
+ PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+ handler.attach(p);
+ handler.start();
+ int status = p.waitFor();
+ handler.stop();
+ return status;
+ }
+
+ private void copyResourceTo(File outFile, String resource, boolean text) throws Exception {
+ if( !outFile.exists() ) {
+ io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+ InputStream is = InstallCommand.class.getResourceAsStream(resource);
+ try {
+ if( text ) {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ io.out.println("writing: "+line);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } else {
+ // Binary so just write it out the way it came in.
+ FileOutputStream out = new FileOutputStream(outFile);
+ try {
+ int c=0;
+ while((c=is.read())>=0) {
+ out.write(c);
+ }
+ } finally {
+ safeClose(out);
+ }
+ }
+ } finally {
+ safeClose(is);
+ }
+ } else {
+ io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+ }
+ }
+
+ private void copyFilteredResourceTo(File outFile, String resource, HashMap<String, String> props) throws Exception {
+ if( !outFile.exists() ) {
+ io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+ InputStream is = InstallCommand.class.getResourceAsStream(resource);
+ try {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ } else {
+ io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+ }
+ }
+
+ private void safeClose(InputStream is) throws IOException {
+ if( is==null)
+ return;
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private void safeClose(OutputStream is) throws IOException {
+ if( is==null)
+ return;
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private String filter(String line, HashMap<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private void mkdir(File file) {
+ if( !file.exists() ) {
+ io.out.println("Creating missing directory: @|green "+file.getPath()+"|");
+ file.mkdirs();
+ }
+ }
+
+ public String getName() {
+ if( name == null ) {
+ File base = new File(System.getProperty("servicemix.base"));
+ name = base.getName();
+ }
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDisplayName() {
+ if( displayName == null ) {
+ displayName = getName();
+ }
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getStartType() {
+ return startType;
+ }
+
+ public void setStartType(String startType) {
+ this.startType = startType;
+ }
+}
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
new file mode 100644
index 0000000..3eb5a60
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="wrapper/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.wrapper.InstallCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
new file mode 100644
index 0000000..e5a5cb8
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install ServiceMix Kernel as a system service in the OS.
+
+command.manual=\
+ TODO: install manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
new file mode 100644
index 0000000..021872d
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
@@ -0,0 +1,124 @@
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+#********************************************************************
+# Wrapper Properties
+#********************************************************************
+set.default.SERVICEMIX_HOME=${servicemix.home}
+set.default.SERVICEMIX_BASE=${servicemix.base}
+
+# Java Application
+wrapper.working.dir=%SERVICEMIX_BASE%
+wrapper.java.command=java
+wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
+wrapper.java.classpath.1=%SERVICEMIX_BASE%/lib/servicemix-wrapper.jar
+wrapper.java.classpath.2=%SERVICEMIX_HOME%/lib/servicemix.jar
+wrapper.java.classpath.3=%SERVICEMIX_HOME%/lib/servicemix-jaas-boot.jar
+wrapper.java.library.path.1=%SERVICEMIX_BASE%/lib/
+
+# Application Parameters. Add parameters as needed starting from 1
+wrapper.app.parameter.1=org.apache.servicemix.kernel.main.Main
+
+# JVM Parameters
+# note that n is the parameter number starting from 1.
+wrapper.java.additional.1=-Dservicemix.home=%SERVICEMIX_HOME%
+wrapper.java.additional.2=-Dservicemix.base=%SERVICEMIX_BASE%
+wrapper.java.additional.3=-Dcom.sun.management.jmxremote
+wrapper.java.additional.4=-Dservicemix.startLocalConsole=false
+wrapper.java.additional.5=-Dservicemix.startRemoteShell=true
+
+# Uncomment to enable jmx
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.port=1616
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.authenticate=false
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.ssl=false
+
+# Uncomment to enable YourKit profiling
+#wrapper.java.additional.n=-Xrunyjpagent
+
+# Uncomment to enable remote debugging
+#wrapper.java.additional.n=-Xdebug -Xnoagent -Djava.compiler=NONE
+#wrapper.java.additional.n=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+
+# Initial Java Heap Size (in MB)
+#wrapper.java.initmemory=3
+
+# Maximum Java Heap Size (in MB)
+wrapper.java.maxmemory=512
+
+
+#********************************************************************
+# Wrapper Logging Properties
+#********************************************************************
+# Format of output for the console. (See docs for formats)
+wrapper.console.format=PM
+
+# Log Level for console output. (See docs for log levels)
+wrapper.console.loglevel=INFO
+
+# Log file to use for wrapper output logging.
+wrapper.logfile=%SERVICEMIX_BASE%/data/log/wrapper.log
+
+# Format of output for the log file. (See docs for formats)
+wrapper.logfile.format=LPTM
+
+# Log Level for log file output. (See docs for log levels)
+wrapper.logfile.loglevel=INFO
+
+# Maximum size that the log file will be allowed to grow to before
+# the log is rolled. Size is specified in bytes. The default value
+# of 0, disables log rolling. May abbreviate with the 'k' (kb) or
+# 'm' (mb) suffix. For example: 10m = 10 megabytes.
+wrapper.logfile.maxsize=10m
+
+# Maximum number of rolled log files which will be allowed before old
+# files are deleted. The default value of 0 implies no limit.
+wrapper.logfile.maxfiles=5
+
+# Log Level for sys/event log output. (See docs for log levels)
+wrapper.syslog.loglevel=NONE
+
+#********************************************************************
+# Wrapper Windows Properties
+#********************************************************************
+# Title to use when running as a console
+wrapper.console.title=${name}
+
+#********************************************************************
+# Wrapper Windows NT/2000/XP Service Properties
+#********************************************************************
+# WARNING - Do not modify any of these properties when an application
+# using this configuration file has been installed as a service.
+# Please uninstall the service before modifying this section. The
+# service can then be reinstalled.
+
+# Name of the service
+wrapper.ntservice.name=${name}
+
+# Display name of the service
+wrapper.ntservice.displayname=${displayName}
+
+# Description of the service
+wrapper.ntservice.description=${description}
+
+# Service dependencies. Add dependencies as needed starting from 1
+wrapper.ntservice.dependency.1=
+
+# Mode in which the service is installed. AUTO_START or DEMAND_START
+wrapper.ntservice.starttype=${startType}
+
+# Allow the service to interact with the desktop.
+wrapper.ntservice.interactive=false
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
new file mode 100755
index 0000000..c766405
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
new file mode 100644
index 0000000..df25ec6
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
new file mode 100644
index 0000000..c58d4f7
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
new file mode 100644
index 0000000..ae5a14e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
new file mode 100644
index 0000000..8c281b9
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
new file mode 100755
index 0000000..d80c13b
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
@@ -0,0 +1,543 @@
+#! /bin/sh
+
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+# Application
+APP_NAME="${name}"
+APP_LONG_NAME="${displayName}"
+
+# Wrapper
+WRAPPER_CMD="${servicemix.base}/bin/${APP_NAME}-wrapper"
+WRAPPER_CONF="${servicemix.base}/etc/${APP_NAME}-wrapper.conf"
+
+# Priority at which to run the wrapper. See "man nice" for valid priorities.
+# nice is only used if a priority is specified.
+PRIORITY=
+
+# Location of the pid file.
+PIDDIR="${servicemix.base}/data"
+
+# If uncommented, causes the Wrapper to be shutdown using an anchor file.
+# When launched with the 'start' command, it will also ignore all INT and
+# TERM signals.
+#IGNORE_SIGNALS=true
+
+# If specified, the Wrapper will be run as the specified user.
+# IMPORTANT - Make sure that the user has the required privileges to write
+# the PID file and wrapper.log files. Failure to be able to write the log
+# file will cause the Wrapper to exit without any way to write out an error
+# message.
+# NOTE - This will set the user which is used to run the Wrapper as well as
+# the JVM and is not useful in situations where a privileged resource or
+# port needs to be allocated prior to the user being changed.
+#RUN_AS_USER=
+
+# The following two lines are used by the chkconfig command. Change as is
+# appropriate for your application. They should remain commented.
+# chkconfig: 2345 20 80
+# description: ${displayName}
+
+# Do not modify anything beyond this point
+#-----------------------------------------------------------------------------
+
+# Get the fully qualified path to the script
+case $0 in
+ /*)
+ SCRIPT="$0"
+ ;;
+ *)
+ PWD=`pwd`
+ SCRIPT="$PWD/$0"
+ ;;
+esac
+
+# Resolve the true real path without any sym links.
+CHANGED=true
+while [ "X$CHANGED" != "X" ]
+do
+ # Change spaces to ":" so the tokens can be parsed.
+ SCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'`
+ # Get the real path to this script, resolving any symbolic links
+ TOKENS=`echo $SCRIPT | sed -e 's;/; ;g'`
+ REALPATH=
+ for C in $TOKENS; do
+ REALPATH="$REALPATH/$C"
+ while [ -h "$REALPATH" ] ; do
+ LS="`ls -ld "$REALPATH"`"
+ LINK="`expr "$LS" : '.*-> \(.*\)$'`"
+ if expr "$LINK" : '/.*' > /dev/null; then
+ REALPATH="$LINK"
+ else
+ REALPATH="`dirname "$REALPATH"`""/$LINK"
+ fi
+ done
+ done
+ # Change ":" chars back to spaces.
+ REALPATH=`echo $REALPATH | sed -e 's;:; ;g'`
+
+ if [ "$REALPATH" = "$SCRIPT" ]
+ then
+ CHANGED=""
+ else
+ SCRIPT="$REALPATH"
+ fi
+done
+
+# Change the current directory to the location of the script
+cd "`dirname "$REALPATH"`"
+REALDIR=`pwd`
+
+# If the PIDDIR is relative, set its value relative to the full REALPATH to avoid problems if
+# the working directory is later changed.
+FIRST_CHAR=`echo $PIDDIR | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ PIDDIR=$REALDIR/$PIDDIR
+fi
+# Same test for WRAPPER_CMD
+FIRST_CHAR=`echo $WRAPPER_CMD | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ WRAPPER_CMD=$REALDIR/$WRAPPER_CMD
+fi
+# Same test for WRAPPER_CONF
+FIRST_CHAR=`echo $WRAPPER_CONF | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ WRAPPER_CONF=$REALDIR/$WRAPPER_CONF
+fi
+
+# Process ID
+ANCHORFILE="$PIDDIR/$APP_NAME.anchor"
+PIDFILE="$PIDDIR/$APP_NAME.pid"
+LOCKDIR="/var/lock/subsys"
+LOCKFILE="$LOCKDIR/$APP_NAME"
+pid=""
+
+# Resolve the location of the 'ps' command
+PSEXE="/usr/bin/ps"
+if [ ! -x $PSEXE ]
+then
+ PSEXE="/bin/ps"
+ if [ ! -x $PSEXE ]
+ then
+ echo "Unable to locate 'ps'."
+ echo "Please report this message along with the location of the command on your system."
+ exit 1
+ fi
+fi
+
+# Resolve the os
+DIST_OS=`uname -s | tr [:upper:] [:lower:] | tr -d [:blank:]`
+case "$DIST_OS" in
+ 'sunos')
+ DIST_OS="solaris"
+ ;;
+ 'hp-ux' | 'hp-ux64')
+ DIST_OS="hpux"
+ ;;
+ 'darwin')
+ DIST_OS="macosx"
+ ;;
+ 'unix_sv')
+ DIST_OS="unixware"
+ ;;
+esac
+
+# Resolve the architecture
+DIST_ARCH=`uname -p | tr [:upper:] [:lower:] | tr -d [:blank:]`
+if [ "$DIST_ARCH" = "unknown" ]
+then
+ DIST_ARCH=`uname -m | tr [:upper:] [:lower:] | tr -d [:blank:]`
+fi
+case "$DIST_ARCH" in
+ 'amd64' | 'ia32' | 'ia64' | 'i386' | 'i486' | 'i586' | 'i686' | 'x86_64')
+ DIST_ARCH="x86"
+ ;;
+ 'ip27')
+ DIST_ARCH="mips"
+ ;;
+ 'power' | 'powerpc' | 'power_pc' | 'ppc64')
+ DIST_ARCH="ppc"
+ ;;
+ 'pa_risc' | 'pa-risc')
+ DIST_ARCH="parisc"
+ ;;
+ 'sun4u' | 'sparcv9')
+ DIST_ARCH="sparc"
+ ;;
+ '9000/800')
+ DIST_ARCH="parisc"
+ ;;
+esac
+
+# Decide on the wrapper binary to use.
+# If a 32-bit wrapper binary exists then it will work on 32 or 64 bit
+# platforms, if the 64-bit binary exists then the distribution most
+# likely wants to use long names. Otherwise, look for the default.
+# For macosx, we also want to look for universal binaries.
+WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+if [ -x $WRAPPER_TEST_CMD ]
+then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+else
+ if [ "$DIST_OS" = "macosx" ]
+ then
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-32"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ if [ ! -x $WRAPPER_CMD ]
+ then
+ echo "Unable to locate any of the following binaries:"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+ echo " $WRAPPER_CMD-$DIST_OS-universal-32"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ echo " $WRAPPER_CMD-$DIST_OS-universal-64"
+ echo " $WRAPPER_CMD"
+ exit 1
+ fi
+ fi
+ fi
+ fi
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ if [ ! -x $WRAPPER_CMD ]
+ then
+ echo "Unable to locate any of the following binaries:"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ echo " $WRAPPER_CMD"
+ exit 1
+ fi
+ fi
+ fi
+fi
+
+# Build the nice clause
+if [ "X$PRIORITY" = "X" ]
+then
+ CMDNICE=""
+else
+ CMDNICE="nice -$PRIORITY"
+fi
+
+# Build the anchor file clause.
+if [ "X$IGNORE_SIGNALS" = "X" ]
+then
+ ANCHORPROP=
+ IGNOREPROP=
+else
+ ANCHORPROP=wrapper.anchorfile=$ANCHORFILE
+ IGNOREPROP=wrapper.ignore_signals=TRUE
+fi
+
+# Build the lock file clause. Only create a lock file if the lock directory exists on this platform.
+if [ -d $LOCKDIR ]
+then
+ LOCKPROP=wrapper.lockfile=$LOCKFILE
+else
+ LOCKPROP=
+fi
+
+checkUser() {
+ # Check the configured user. If necessary rerun this script as the desired user.
+ if [ "X$RUN_AS_USER" != "X" ]
+ then
+ # Resolve the location of the 'id' command
+ IDEXE="/usr/xpg4/bin/id"
+ if [ ! -x $IDEXE ]
+ then
+ IDEXE="/usr/bin/id"
+ if [ ! -x $IDEXE ]
+ then
+ echo "Unable to locate 'id'."
+ echo "Please report this message along with the location of the command on your system."
+ exit 1
+ fi
+ fi
+
+ if [ "`$IDEXE -u -n`" = "$RUN_AS_USER" ]
+ then
+ # Already running as the configured user. Avoid password prompts by not calling su.
+ RUN_AS_USER=""
+ fi
+ fi
+ if [ "X$RUN_AS_USER" != "X" ]
+ then
+ # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be
+ # able to create the lock file. The Wrapper will be able to update this file once it
+ # is created but will not be able to delete it on shutdown. If $2 is defined then
+ # the lock file should be created for the current command
+ if [ "X$LOCKPROP" != "X" ]
+ then
+ if [ "X$2" != "X" ]
+ then
+ # Resolve the primary group
+ RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1`
+ if [ "X$RUN_AS_GROUP" = "X" ]
+ then
+ RUN_AS_GROUP=RUN_AS_USER
+ fi
+ touch $LOCKFILE
+ chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE
+ fi
+ fi
+
+ # Still want to change users, recurse. This means that the user will only be
+ # prompted for a password once.
+ su -m $RUN_AS_USER -c "$REALPATH $1"
+
+ # Now that we are the original user again, we may need to clean up the lock file.
+ if [ "X$LOCKPROP" != "X" ]
+ then
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ # Wrapper is not running so make sure the lock file is deleted.
+ if [ -f $LOCKFILE ]
+ then
+ rm $LOCKFILE
+ fi
+ fi
+ fi
+
+ exit 0
+ fi
+}
+
+getpid() {
+ if [ -f $PIDFILE ]
+ then
+ if [ -r $PIDFILE ]
+ then
+ pid=`cat $PIDFILE`
+ if [ "X$pid" != "X" ]
+ then
+ # It is possible that 'a' process with the pid exists but that it is not the
+ # correct process. This can happen in a number of cases, but the most
+ # common is during system startup after an unclean shutdown.
+ # The ps statement below looks for the specific wrapper command running as
+ # the pid. If it is not found then the pid file is considered to be stale.
+ pidtest=`$PSEXE -p $pid -o command | grep $WRAPPER_CMD | tail -1`
+ if [ "X$pidtest" = "X" ]
+ then
+ # This is a stale pid file.
+ rm -f $PIDFILE
+ echo "Removed stale pid file: $PIDFILE"
+ pid=""
+ fi
+ fi
+ else
+ echo "Cannot read $PIDFILE."
+ exit 1
+ fi
+ fi
+}
+
+testpid() {
+ pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1`
+ if [ "X$pid" = "X" ]
+ then
+ # Process is gone so remove the pid file.
+ rm -f $PIDFILE
+ pid=""
+ fi
+}
+
+console() {
+ echo "Running $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE $ANCHORPROP $LOCKPROP"
+ exec $COMMAND_LINE
+ else
+ echo "$APP_LONG_NAME is already running."
+ exit 1
+ fi
+}
+
+start() {
+ echo "Starting $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ if [ ! -d ../../data ]; then
+ mkdir ../../data
+ fi
+ if [ ! -d ../../data/log ]; then
+ mkdir ../../data/log
+ fi
+ COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $LOCKPROP"
+ exec $COMMAND_LINE
+ else
+ echo "$APP_LONG_NAME is already running."
+ exit 1
+ fi
+}
+
+stopit() {
+ echo "Stopping $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME was not running."
+ else
+ if [ "X$IGNORE_SIGNALS" = "X" ]
+ then
+ # Running so try to stop it.
+ kill $pid
+ if [ $? -ne 0 ]
+ then
+ # An explanation for the failure should have been given
+ echo "Unable to stop $APP_LONG_NAME."
+ exit 1
+ fi
+ else
+ rm -f $ANCHORFILE
+ if [ -f $ANCHORFILE ]
+ then
+ # An explanation for the failure should have been given
+ echo "Unable to stop $APP_LONG_NAME."
+ exit 1
+ fi
+ fi
+
+ # We can not predict how long it will take for the wrapper to
+ # actually stop as it depends on settings in wrapper.conf.
+ # Loop until it does.
+ savepid=$pid
+ CNT=0
+ TOTCNT=0
+ while [ "X$pid" != "X" ]
+ do
+ # Show a waiting message every 5 seconds.
+ if [ "$CNT" -lt "5" ]
+ then
+ CNT=`expr $CNT + 1`
+ else
+ echo "Waiting for $APP_LONG_NAME to exit..."
+ CNT=0
+ fi
+ TOTCNT=`expr $TOTCNT + 1`
+
+ sleep 1
+
+ testpid
+ done
+
+ pid=$savepid
+ testpid
+ if [ "X$pid" != "X" ]
+ then
+ echo "Failed to stop $APP_LONG_NAME."
+ exit 1
+ else
+ echo "Stopped $APP_LONG_NAME."
+ fi
+ fi
+}
+
+status() {
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME is not running."
+ exit 1
+ else
+ echo "$APP_LONG_NAME is running ($pid)."
+ exit 0
+ fi
+}
+
+dump() {
+ echo "Dumping $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME was not running."
+
+ else
+ kill -3 $pid
+
+ if [ $? -ne 0 ]
+ then
+ echo "Failed to dump $APP_LONG_NAME."
+ exit 1
+ else
+ echo "Dumped $APP_LONG_NAME."
+ fi
+ fi
+}
+
+case "$1" in
+
+ 'console')
+ checkUser $1 touchlock
+ console
+ ;;
+
+ 'start')
+ checkUser $1 touchlock
+ start
+ ;;
+
+ 'stop')
+ checkUser $1
+ stopit
+ ;;
+
+ 'restart')
+ checkUser $1 touchlock
+ stopit
+ start
+ ;;
+
+ 'status')
+ checkUser $1
+ status
+ ;;
+
+ 'dump')
+ checkUser $1
+ dump
+ ;;
+
+ *)
+ echo "Usage: $0 { console | start | stop | restart | status | dump }"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
new file mode 100644
index 0000000..df0160e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
@@ -0,0 +1,50 @@
+@echo off
+
+REM ------------------------------------------------------------------------
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements. See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License. You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM ------------------------------------------------------------------------
+
+setlocal
+
+set APP_NAME=${name}
+set APP_LONG_NAME=${displayName}
+set APP_BASE=${servicemix.base}
+
+if ""%1"" == ""run"" goto doRun
+if ""%1"" == ""install"" goto doInstall
+if ""%1"" == ""remove"" goto doRemove
+
+echo Usage: servicemix-service ( commands ... )
+echo commands:
+echo run Start %APP_NAME% in the current console
+echo install Install %APP_NAME% as a Windows service
+echo remove Remove the %APP_NAME% Windows service
+goto end
+
+:doRun
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -c "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doInstall
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -i "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doRemove
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -r "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:end
+if not "%PAUSE%" == "" pause
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
new file mode 100644
index 0000000..a46a2ac
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
new file mode 100644
index 0000000..37c4f33
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
Binary files differ
diff --git a/karaf/gshell/itests/pom.xml b/karaf/gshell/itests/pom.xml
new file mode 100644
index 0000000..c684ceb
--- /dev/null
+++ b/karaf/gshell/itests/pom.xml
@@ -0,0 +1,153 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.itests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell ITests</name>
+
+ <description>
+ Provides the OSGi GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pom.basedir}/target/test-classes/</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/test/filtered-resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- generate dependencies versions -->
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>ci-build-profile</id>
+ <activation>
+ <property>
+ <name>maven.repo.local</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- when the local repo location has been specified, we need to pass on this information to PAX mvn url -->
+ <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <activation>
+ <os>
+ <family>Windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/karaf/gshell/itests/src/test/filtered-resources/features.xml b/karaf/gshell/itests/src/test/filtered-resources/features.xml
new file mode 100644
index 0000000..8316b3f
--- /dev/null
+++ b/karaf/gshell/itests/src/test/filtered-resources/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<features>
+ <feature name="wrapper" version="${version}">
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.wrapper/${version}</bundle>
+ </feature>
+ <feature name="obr" version="${version}">
+ <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/${felix.bundlerepository.version}</bundle>
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.obr/${version}</bundle>
+ </feature>
+</features>
diff --git a/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java
new file mode 100644
index 0000000..6f90dc2
--- /dev/null
+++ b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.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.servicemix.kernel.gshell.itests;
+
+import org.apache.geronimo.gshell.commandline.CommandLineExecutionFailed;
+import org.apache.geronimo.gshell.registry.NoSuchCommandException;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.osgi.framework.Bundle;
+
+public class CoreTest extends AbstractIntegrationTest {
+
+ static {
+ System.setProperty("servicemix.startLocalConsole", "false");
+ System.setProperty("servicemix.startRemoteShell", "false");
+ }
+
+ @Override
+ protected String getManifestLocation() {
+ return "classpath:org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF";
+ }
+
+ @Override
+ protected String[] getTestBundlesNames() {
+ return new String[] {
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jline"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-httpclient"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-jexl"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-vfs"),
+ getBundle("org.apache.mina", "mina-core"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.oro"),
+ getBundle("org.apache.servicemix.kernel.jaas", "org.apache.servicemix.kernel.jaas.config"),
+ getBundle("org.apache.sshd", "sshd-core"),
+ getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.core"),
+ getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.osgi")
+ };
+ }
+
+ public void testHelp() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("help");
+ }
+
+ public void testInstallCommand() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+
+ try {
+ shell.execute("log/display");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+
+ Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+
+ shell.execute("log/display");
+
+ b.uninstall();
+
+ try {
+ shell.execute("log/display");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ }
+
+ public void testCommandGroup() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("osgi");
+ shell.execute("help");
+ shell.execute("..");
+ }
+
+ public void testInstallFeature() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+
+ try {
+ shell.execute("obr");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ try {
+ shell.execute("wrapper");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ String url = getClass().getClassLoader().getResource("features.xml").toString();
+ addFeatureRepo(url);
+ installFeature("obr");
+ installFeature("wrapper");
+ shell.execute("obr");
+ shell.execute("wrapper");
+ }
+
+ /**
+ * TODO: This test seems to fail, there must be a timing issue somewhere
+ *
+ public void testCommandGroupAfterInstall() throws Exception {
+ Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("log");
+ shell.execute("help");
+ shell.execute("..");
+ }
+ */
+
+
+}
diff --git a/karaf/gshell/itests/src/test/resources/log4j.properties b/karaf/gshell/itests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/log4j.properties
@@ -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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
new file mode 100644
index 0000000..df8a1a7
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+License-00: .
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements. See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License. You may obtain a copy of the License at
+License-07: .
+License-08: http://www.apache.org/licenses/LICENSE-2.0
+License-09: .
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15: .
+Bundle-Name: org.apache.servicemix.testing.itests
+Bundle-SymbolicName: org.apache.servicemix.testing.itests
+Bundle-Vendor: Spring Framework
+Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
+Import-Package: junit.framework,
+ org.osgi.framework;specification-version="1.3.0",
+ org.apache.commons.logging,
+ org.springframework.core.io,
+ org.springframework.osgi.test,
+ org.apache.servicemix.kernel.testing.support,
+ org.apache.geronimo.gshell.commandline,
+ org.apache.geronimo.gshell.registry,
+ org.apache.geronimo.gshell.shell
diff --git a/karaf/gshell/pom.xml b/karaf/gshell/pom.xml
new file mode 100644
index 0000000..3c2d24f
--- /dev/null
+++ b/karaf/gshell/pom.xml
@@ -0,0 +1,49 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell</name>
+
+ <modules>
+ <module>gshell-core</module>
+ <module>gshell-osgi</module>
+ <module>gshell-admin</module>
+ <module>gshell-features</module>
+ <module>gshell-obr</module>
+ <module>gshell-wrapper</module>
+ <module>gshell-log</module>
+ <module>gshell-config</module>
+ <module>gshell-packages</module>
+ <module>itests</module>
+ </modules>
+
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
new file mode 100644
index 0000000..7779a61
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+#
+startLocalConsole=false
diff --git a/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
new file mode 100644
index 0000000..23075bf
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+#
+log4j.rootLogger=DEBUG, out
+
+log4j.logger.org.springframework=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=target/gshell.log
+log4j.appender.out.append=true
diff --git a/karaf/jaas/jaas-boot/pom.xml b/karaf/jaas/jaas-boot/pom.xml
new file mode 100644
index 0000000..4cf046e
--- /dev/null
+++ b/karaf/jaas/jaas-boot/pom.xml
@@ -0,0 +1,65 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>jaas</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: JAAS Boot</name>
+
+ <description>
+ Provides the JAAS Boot classes
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>*</Import-Package>
+ <Private-Package>!*</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/jaas/jaas-boot/src/main/java/org/apache/servicemix/kernel/jaas/boot/ProxyLoginModule.java b/karaf/jaas/jaas-boot/src/main/java/org/apache/servicemix/kernel/jaas/boot/ProxyLoginModule.java
new file mode 100644
index 0000000..3b3aed0
--- /dev/null
+++ b/karaf/jaas/jaas-boot/src/main/java/org/apache/servicemix/kernel/jaas/boot/ProxyLoginModule.java
@@ -0,0 +1,107 @@
+/*
+ * 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.servicemix.kernel.jaas.boot;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * An OSGi proxy login module that should be used instead of a plain reference to
+ * a given login module. Two properties must be set, the name of the login module
+ * class and the bundle to be used to load it.
+ * This class must be available from all modules, so it has to be either in a fragment
+ * bundle attached to the system bundle or be made available through the boot delegation
+ * class path.
+ */
+public class ProxyLoginModule implements LoginModule {
+
+ public static final String PROPERTY_MODULE = "org.apache.servicemix.kernel.jaas.module";
+ public static final String PROPERTY_BUNDLE = "org.apache.servicemix.kernel.jaas.bundle";
+
+ private static BundleContext bundleContext = null;
+
+ private LoginModule target = null;
+
+ public static void init(BundleContext context) {
+ bundleContext = context;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
+ */
+ public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
+ if (bundleContext == null) {
+ throw new IllegalStateException("ProxyLoginModule not initialized. Init must be called prior any invocation.");
+ }
+ Map<String,?> newOptions = new HashMap<String,Object>(options);
+ String module = (String) newOptions.remove(PROPERTY_MODULE);
+ if (module == null) {
+ throw new IllegalStateException("Option " + PROPERTY_MODULE + " must be set to the name of the factory service");
+ }
+ String bundleId = (String) newOptions.remove(PROPERTY_BUNDLE);
+ if (bundleId == null) {
+ throw new IllegalStateException("Option " + PROPERTY_BUNDLE + " must be set to the name of the factory service");
+ }
+ Bundle bundle = bundleContext.getBundle(Long.parseLong(bundleId));
+ if (bundle == null) {
+ throw new IllegalStateException("No bundle found for id " + bundleId);
+ }
+ try {
+ target = (LoginModule) bundle.loadClass(module).newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException("Can not load or create login module " + module + " for bundle " + bundleId, e);
+ }
+ target.initialize(subject, callbackHandler, sharedState, options);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.security.auth.spi.LoginModule#login()
+ */
+ public boolean login() throws LoginException {
+ return target.login();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.security.auth.spi.LoginModule#commit()
+ */
+ public boolean commit() throws LoginException {
+ return target.commit();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.security.auth.spi.LoginModule#abort()
+ */
+ public boolean abort() throws LoginException {
+ return target.abort();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.security.auth.spi.LoginModule#logout()
+ */
+ public boolean logout() throws LoginException {
+ return target.logout();
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/pom.xml b/karaf/jaas/jaas-config/pom.xml
new file mode 100644
index 0000000..bc47433
--- /dev/null
+++ b/karaf/jaas/jaas-config/pom.xml
@@ -0,0 +1,95 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>jaas</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: JAAS Config</name>
+
+ <description>
+ Provides the JAAS Config
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ ${artifactId};version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>*</Import-Package>
+ <Private-Package>${artifactId}.impl</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/JaasRealm.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/JaasRealm.java
new file mode 100644
index 0000000..b5237d1
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/JaasRealm.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.jaas.config;
+
+import javax.security.auth.login.AppConfigurationEntry;
+
+public interface JaasRealm {
+
+ public String getName();
+
+ public int getRank();
+
+ public AppConfigurationEntry[] getEntries();
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeyIsLocked.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeyIsLocked.java
new file mode 100644
index 0000000..5ff4736
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeyIsLocked.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.servicemix.kernel.jaas.config;
+
+import java.security.GeneralSecurityException;
+
+public class KeyIsLocked extends GeneralSecurityException {
+
+ public KeyIsLocked() {
+ super();
+ }
+
+ public KeyIsLocked(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public KeyIsLocked(String msg) {
+ super(msg);
+ }
+
+ public KeyIsLocked(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreInstance.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreInstance.java
new file mode 100644
index 0000000..022d95b
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreInstance.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.servicemix.kernel.jaas.config;
+
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
+
+/**
+ * Based on http://svn.apache.org/repos/asf/geronimo/trunk/modules/management/
+ * src/java/org/apache/geronimo/management/geronimo/KeystoreInstance.java
+ *
+ * @version $Rev: $ $Date: $
+ */
+public interface KeystoreInstance {
+
+ String getName();
+
+ int getRank();
+
+ String[] listPrivateKeys();
+
+ String[] listTrustCertificates();
+
+ Certificate getCertificate(String alias);
+
+ String getCertificateAlias(Certificate cert);
+
+ Certificate[] getCertificateChain(String alias);
+
+ PrivateKey getPrivateKey(String alias);
+
+ boolean isKeystoreLocked();
+
+ boolean isKeyLocked(String keyAlias);
+
+ KeyManager[] getKeyManager(String algorithm, String keyAlias) throws NoSuchAlgorithmException,
+ UnrecoverableKeyException, KeyStoreException, KeystoreIsLocked;
+
+ TrustManager[] getTrustManager(String algorithm) throws KeyStoreException, NoSuchAlgorithmException, KeystoreIsLocked;
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreIsLocked.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreIsLocked.java
new file mode 100644
index 0000000..34c9a2b
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreIsLocked.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.servicemix.kernel.jaas.config;
+
+import java.security.GeneralSecurityException;
+
+public class KeystoreIsLocked extends GeneralSecurityException {
+
+ public KeystoreIsLocked() {
+ super();
+ }
+
+ public KeystoreIsLocked(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public KeystoreIsLocked(String msg) {
+ super(msg);
+ }
+
+ public KeystoreIsLocked(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreManager.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreManager.java
new file mode 100644
index 0000000..74fa466
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/KeystoreManager.java
@@ -0,0 +1,110 @@
+/*
+ * 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.servicemix.kernel.jaas.config;
+
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Based on http://svn.apache.org/repos/asf/geronimo/trunk/modules/management/
+ * src/java/org/apache/geronimo/management/geronimo/KeystoreManager.java
+ *
+ */
+public interface KeystoreManager {
+
+ KeystoreInstance getKeystore(String name);
+
+ /**
+ * Gets a SSLContext using one Keystore to access the private key
+ * and another to provide the list of trusted certificate authorities.
+ * @param provider
+ * @param protocol The SSL protocol to use
+ * @param algorithm The SSL algorithm to use
+ * @param keyStore The key keystore name as provided by listKeystores. The
+ * KeystoreInstance for this keystore must be unlocked.
+ * @param keyAlias The name of the private key in the keystore. The
+ * KeystoreInstance for this keystore must have unlocked
+ * this key.
+ * @param trustStore The trust keystore name as provided by listKeystores.
+ * The KeystoreInstance for this keystore must have
+ * unlocked this key.
+ *
+ * @throws KeystoreIsLocked Occurs when the requested key keystore cannot
+ * be used because it has not been unlocked.
+ * @throws KeyIsLocked Occurs when the requested private key in the key
+ * keystore cannot be used because it has not been
+ * unlocked.
+ */
+ SSLContext createSSLContext(String provider, String protocol,
+ String algorithm, String keyStore,
+ String keyAlias, String trustStore) throws GeneralSecurityException;
+
+ /**
+ * Gets a ServerSocketFactory using one Keystore to access the private key
+ * and another to provide the list of trusted certificate authorities.
+ * @param provider
+ * @param protocol The SSL protocol to use
+ * @param algorithm The SSL algorithm to use
+ * @param keyStore The key keystore name as provided by listKeystores. The
+ * KeystoreInstance for this keystore must be unlocked.
+ * @param keyAlias The name of the private key in the keystore. The
+ * KeystoreInstance for this keystore must have unlocked
+ * this key.
+ * @param trustStore The trust keystore name as provided by listKeystores.
+ * The KeystoreInstance for this keystore must have
+ * unlocked this key.
+ *
+ * @throws KeystoreIsLocked Occurs when the requested key keystore cannot
+ * be used because it has not been unlocked.
+ * @throws KeyIsLocked Occurs when the requested private key in the key
+ * keystore cannot be used because it has not been
+ * unlocked.
+ */
+ SSLServerSocketFactory createSSLServerFactory(String provider, String protocol,
+ String algorithm, String keyStore,
+ String keyAlias, String trustStore) throws GeneralSecurityException;
+
+ /**
+ * Gets a SocketFactory using one Keystore to access the private key
+ * and another to provide the list of trusted certificate authorities.
+ * @param provider The SSL provider to use, or null for the default
+ * @param protocol The SSL protocol to use
+ * @param algorithm The SSL algorithm to use
+ * @param keyStore The key keystore name as provided by listKeystores. The
+ * KeystoreInstance for this keystore must be unlocked.
+ * @param keyAlias The name of the private key in the keystore. The
+ * KeystoreInstance for this keystore must have unlocked
+ * this key.
+ * @param trustStore The trust keystore name as provided by listKeystores.
+ * The KeystoreInstance for this keystore must have
+ * unlocked this key.
+ *
+ * @throws KeystoreIsLocked Occurs when the requested key keystore cannot
+ * be used because it has not been unlocked.
+ * @throws KeyIsLocked Occurs when the requested private key in the key
+ * keystore cannot be used because it has not been
+ * unlocked.
+ * @throws GeneralSecurityException
+ */
+ SSLSocketFactory createSSLFactory(String provider, String protocol,
+ String algorithm, String keyStore,
+ String keyAlias, String trustStore) throws GeneralSecurityException;
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Config.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Config.java
new file mode 100644
index 0000000..7ad0b04
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Config.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.jaas.config.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.login.AppConfigurationEntry;
+
+import org.apache.servicemix.kernel.jaas.boot.ProxyLoginModule;
+import org.apache.servicemix.kernel.jaas.config.JaasRealm;
+import org.osgi.framework.BundleContext;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * An implementation of JaasRealm which is created
+ * by the spring namespace handler.
+ */
+public class Config implements JaasRealm, BundleContextAware {
+
+ private String name;
+ private int rank;
+ private Module[] modules;
+ private BundleContext bundleContext;
+ private transient AppConfigurationEntry[] entries;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getRank() {
+ return rank;
+ }
+
+ public void setRank(int rank) {
+ this.rank = rank;
+ }
+
+ public Module[] getModules() {
+ return modules;
+ }
+
+ public void setModules(Module[] modules) {
+ this.modules = modules;
+ this.entries = null;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public AppConfigurationEntry[] getEntries() {
+ if (this.entries == null && this.modules != null) {
+ Module[] modules = this.modules;
+ AppConfigurationEntry[] entries = new AppConfigurationEntry[modules.length];
+ for (int i = 0; i < modules.length; i++) {
+ Map<String,String> options = new HashMap<String,String>();
+ if (modules[i].getOptions() != null) {
+ options.putAll(modules[i].getOptions());
+ }
+ options.put(ProxyLoginModule.PROPERTY_MODULE, modules[i].getClassName());
+ options.put(ProxyLoginModule.PROPERTY_BUNDLE, Long.toString(bundleContext.getBundle().getBundleId()));
+ entries[i] = new AppConfigurationEntry(ProxyLoginModule.class.getName(),
+ getControlFlag(modules[i].getFlags()),
+ options);
+ }
+ this.entries = entries;
+ }
+ return this.entries;
+ }
+
+ private AppConfigurationEntry.LoginModuleControlFlag getControlFlag(String flags) {
+ if ("required".equalsIgnoreCase(flags)) {
+ return AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
+ }
+ if ("optional".equalsIgnoreCase(flags)) {
+ return AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
+ }
+ if ("requisite".equalsIgnoreCase(flags)) {
+ return AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
+ }
+ if ("sufficient".equalsIgnoreCase(flags)) {
+ return AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
+ }
+ return null;
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ConfigParser.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ConfigParser.java
new file mode 100644
index 0000000..5f8e16a
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ConfigParser.java
@@ -0,0 +1,102 @@
+/*
+ * 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.servicemix.kernel.jaas.config.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.w3c.dom.Element;
+
+import org.apache.servicemix.kernel.jaas.boot.ProxyLoginModule;
+import org.apache.servicemix.kernel.jaas.config.JaasRealm;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean;
+import org.springframework.util.xml.DomUtils;
+
+public class ConfigParser extends AbstractSingleBeanDefinitionParser {
+
+ protected Class getBeanClass(Element element) {
+ return Config.class;
+ }
+
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ String name = element.getAttribute("name");
+ if (name == null || name.length() == 0) {
+ name = element.getAttribute("id");
+ }
+ builder.addPropertyValue("name", name);
+ String rank = element.getAttribute("rank");
+ if (rank != null && rank.length() > 0) {
+ builder.addPropertyValue("rank", Integer.parseInt(rank));
+ }
+ List childElements = DomUtils.getChildElementsByTagName(element, "module");
+ if (childElements != null && childElements.size() > 0) {
+ ManagedList children = new ManagedList(childElements.size());
+ for (int i = 0; i < childElements.size(); ++i) {
+ Element childElement = (Element) childElements.get(i);
+ BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(Module.class);
+ bd.addPropertyValue("className", childElement.getAttribute("className"));
+ if (childElement.getAttribute("flags") != null) {
+ bd.addPropertyValue("flags", childElement.getAttribute("flags"));
+ }
+ String options = DomUtils.getTextValue(childElement);
+ if (options != null && options.length() > 0) {
+ Properties props = new Properties();
+ try {
+ props.load(new ByteArrayInputStream(options.getBytes()));
+ } catch (IOException e) {
+ throw new IllegalStateException("Can not load options for JAAS module "
+ + childElement.getAttribute("className") + " in config " + name);
+ }
+ bd.addPropertyValue("options", props);
+ }
+ children.add(bd.getBeanDefinition());
+ }
+ builder.addPropertyValue("modules", children);
+ }
+ // Publish to OSGi
+ String publish = element.getAttribute("publish");
+ if (Boolean.valueOf(publish)) {
+ // Publish Config
+ BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(OsgiServiceFactoryBean.class);
+ bd.addPropertyValue("target", builder.getBeanDefinition());
+ bd.addPropertyValue("interfaces", new Class[] { JaasRealm.class });
+ Map<String,String> props = new HashMap<String,String>();
+ props.put(ProxyLoginModule.PROPERTY_MODULE, name);
+ bd.addPropertyValue("serviceProperties", props);
+ BeanDefinition def = bd.getBeanDefinition();
+ String id = parserContext.getReaderContext().generateBeanName(def);
+ BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
+ registerBeanDefinition(holder, parserContext.getRegistry());
+ if (shouldFireEvents()) {
+ BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
+ postProcessComponentDefinition(componentDefinition);
+ parserContext.registerComponent(componentDefinition);
+ }
+ }
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Module.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Module.java
new file mode 100644
index 0000000..488f19a
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/Module.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.servicemix.kernel.jaas.config.impl;
+
+import java.util.Map;
+
+/**
+ * POJO for a login module.
+ * It contains the class name, flags and a map of options.
+ */
+public class Module {
+
+ private String className;
+ private String flags;
+ private Map<String,String> options;
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String getFlags() {
+ return flags;
+ }
+
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+ public Map<String, String> getOptions() {
+ return options;
+ }
+
+ public void setOptions(Map<String, String> options) {
+ this.options = options;
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/NamespaceHandler.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/NamespaceHandler.java
new file mode 100644
index 0000000..d1b8192
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/NamespaceHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.jaas.config.impl;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+public class NamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser("config", new ConfigParser());
+ registerBeanDefinitionParser("keystore", new ResourceKeystoreInstanceParser());
+ }
+
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiConfiguration.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiConfiguration.java
new file mode 100644
index 0000000..a2e6119
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiConfiguration.java
@@ -0,0 +1,71 @@
+/*
+ * 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.servicemix.kernel.jaas.config.impl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import org.apache.servicemix.kernel.jaas.config.JaasRealm;
+
+public class OsgiConfiguration extends Configuration {
+
+ private List<JaasRealm> realms;
+
+ public void init() {
+ realms = new CopyOnWriteArrayList<JaasRealm>();
+ Configuration.setConfiguration(this);
+ }
+
+ public void close() {
+ realms.clear();
+ realms = null;
+ Configuration.setConfiguration(null);
+ }
+
+ public void register(JaasRealm realm, Map<String,?> properties) {
+ realms.add(realm);
+ }
+
+ public void unregister(JaasRealm realm, Map<String,?> properties) {
+ if (realms != null) {
+ realms.remove(realm);
+ }
+ }
+
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ JaasRealm realm = null;
+ for (JaasRealm r : realms) {
+ if (r.getName().equals(name)) {
+ if (realm == null || r.getRank() > realm.getRank()) {
+ realm = r;
+ }
+ }
+ }
+ if (realm != null) {
+ return realm.getEntries();
+ }
+ return null;
+ }
+
+ public void refresh() {
+ // Nothing to do, as we auto-update the configuration
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiKeystoreManager.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiKeystoreManager.java
new file mode 100644
index 0000000..41135c2
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/OsgiKeystoreManager.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.jaas.config.impl;
+
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.servicemix.kernel.jaas.config.KeystoreInstance;
+import org.apache.servicemix.kernel.jaas.config.KeystoreIsLocked;
+import org.apache.servicemix.kernel.jaas.config.KeystoreManager;
+
+/**
+ * Implementation of KeystoreManager
+ */
+public class OsgiKeystoreManager implements KeystoreManager {
+
+ private List<KeystoreInstance> keystores = new CopyOnWriteArrayList<KeystoreInstance>();
+
+ public void register(KeystoreInstance keystore, Map<String,?> properties) {
+ keystores.add(keystore);
+ }
+
+ public void unregister(KeystoreInstance keystore, Map<String,?> properties) {
+ keystores.remove(keystore);
+ }
+
+ public KeystoreInstance getKeystore(String name) {
+ KeystoreInstance keystore = null;
+ for (KeystoreInstance ks : keystores) {
+ if (ks.getName().equals(name)) {
+ if (keystore == null || keystore.getRank() < ks.getRank()) {
+ keystore = ks;
+ }
+ }
+ }
+ return keystore;
+ }
+
+ public SSLContext createSSLContext(String provider, String protocol, String algorithm, String keyStore, String keyAlias, String trustStore) throws GeneralSecurityException {
+ KeystoreInstance keyInstance = getKeystore(keyStore);
+ if (keyInstance != null && keyInstance.isKeystoreLocked()) {
+ throw new KeystoreIsLocked("Keystore '" + keyStore + "' is locked");
+ }
+ if (keyInstance != null && keyInstance.isKeyLocked(keyAlias)) {
+ throw new KeystoreIsLocked("Key '" + keyAlias + "' in keystore '" + keyStore + "' is locked");
+ }
+ KeystoreInstance trustInstance = trustStore == null ? null : getKeystore(trustStore);
+ if (trustInstance != null && trustInstance.isKeystoreLocked()) {
+ throw new KeystoreIsLocked("Keystore '" + trustStore + "' is locked");
+ }
+ SSLContext context;
+ if (provider == null) {
+ context = SSLContext.getInstance(protocol);
+ } else {
+ context = SSLContext.getInstance(protocol, provider);
+ }
+ context.init(keyInstance == null ? null : keyInstance.getKeyManager(algorithm, keyAlias),
+ trustInstance == null ? null : trustInstance.getTrustManager(algorithm), new SecureRandom());
+ return context;
+ }
+
+ public SSLServerSocketFactory createSSLServerFactory(String provider, String protocol, String algorithm, String keyStore, String keyAlias, String trustStore) throws GeneralSecurityException {
+ SSLContext context = createSSLContext(provider, protocol, algorithm, keyStore, keyAlias, trustStore);
+ return context.getServerSocketFactory();
+ }
+
+ public SSLSocketFactory createSSLFactory(String provider, String protocol, String algorithm, String keyStore, String keyAlias, String trustStore) throws GeneralSecurityException {
+ SSLContext context = createSSLContext(provider, protocol, algorithm, keyStore, keyAlias, trustStore);
+ return context.getSocketFactory();
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ProxyLoginModuleInitializer.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ProxyLoginModuleInitializer.java
new file mode 100644
index 0000000..f2b8c32
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ProxyLoginModuleInitializer.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.servicemix.kernel.jaas.config.impl;
+
+import org.apache.servicemix.kernel.jaas.boot.ProxyLoginModule;
+import org.osgi.framework.BundleContext;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class ProxyLoginModuleInitializer implements BundleContextAware {
+
+ private BundleContext bundleContext;
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void init() {
+ BundleContext context = bundleContext.getBundle(0).getBundleContext();
+ ProxyLoginModule.init(context);
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstance.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstance.java
new file mode 100644
index 0000000..7974ed6
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstance.java
@@ -0,0 +1,289 @@
+/*
+ * 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.servicemix.kernel.jaas.config.impl;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.jaas.config.KeystoreInstance;
+import org.apache.servicemix.kernel.jaas.config.KeystoreIsLocked;
+import org.springframework.core.io.Resource;
+
+/**
+ *
+ */
+public class ResourceKeystoreInstance implements KeystoreInstance {
+
+ private static final Log LOG = LogFactory.getLog(ResourceKeystoreInstance.class);
+ private static final String JKS = "JKS";
+
+ private String name;
+ private int rank;
+ private Resource path;
+ private String keystorePassword;
+ private Map keyPasswords = new HashMap();
+ private File keystoreFile; // Only valid after startup and if the resource points to a file
+
+ // The following variables are the state of the keystore, which should be chucked if the file on disk changes
+ private List privateKeys = new ArrayList();
+ private List trustCerts = new ArrayList();
+ private KeyStore keystore;
+ private long keystoreReadDate = Long.MIN_VALUE;
+
+ /**
+ * @return the keystoreName
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param keystoreName the keystoreName to set
+ */
+ public void setName(String keystoreName) {
+ this.name = keystoreName;
+ }
+
+ /**
+ * @return the rank
+ */
+ public int getRank() {
+ return rank;
+ }
+
+ /**
+ * @param rank the rank to set
+ */
+ public void setRank(int rank) {
+ this.rank = rank;
+ }
+
+ /**
+ * @return the keystorePath
+ */
+ public Resource getPath() {
+ return path;
+ }
+
+ /**
+ * @param keystorePath the keystorePath to set
+ */
+ public void setPath(Resource keystorePath) throws IOException {
+ this.path = keystorePath;
+ if (keystorePath.getURL().getProtocol().equals("file")) {
+ this.keystoreFile = keystorePath.getFile();
+ }
+ }
+
+ /**
+ * @param keystorePassword the keystorePassword to set
+ */
+ public void setKeystorePassword(String keystorePassword) {
+ this.keystorePassword = keystorePassword;
+ }
+
+ /**
+ * @param keyPasswords the keyPasswords to set
+ */
+ public void setKeyPasswords(String keyPasswords) {
+ if (keyPasswords != null) {
+ String[] keys = keyPasswords.split("\\]\\!\\[");
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ int pos = key.indexOf('=');
+ this.keyPasswords.put(key.substring(0, pos), key.substring(pos + 1).toCharArray());
+ }
+ }
+ }
+
+ public Certificate getCertificate(String alias) {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ try {
+ return keystore.getCertificate(alias);
+ } catch (KeyStoreException e) {
+ LOG.error("Unable to read certificate from keystore", e);
+ }
+ return null;
+ }
+
+ public String getCertificateAlias(Certificate cert) {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ try {
+ return keystore.getCertificateAlias(cert);
+ } catch (KeyStoreException e) {
+ LOG.error("Unable to read retrieve alias for given certificate from keystore", e);
+ }
+ return null;
+ }
+
+ public Certificate[] getCertificateChain(String alias) {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ try {
+ return keystore.getCertificateChain(alias);
+ } catch (KeyStoreException e) {
+ LOG.error("Unable to read certificate chain from keystore", e);
+ }
+ return null;
+ }
+
+ public KeyManager[] getKeyManager(String algorithm, String keyAlias) throws KeystoreIsLocked,
+ NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
+ if (isKeystoreLocked()) {
+ throw new KeystoreIsLocked("Keystore '" + name + "' is locked.");
+ }
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(algorithm);
+ keyFactory.init(keystore, (char[]) keyPasswords.get(keyAlias));
+ return keyFactory.getKeyManagers();
+ }
+
+ public PrivateKey getPrivateKey(String alias) {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ try {
+ if (isKeyLocked(alias)) {
+ return null;
+ }
+ Key key = keystore.getKey(alias, (char[]) keyPasswords.get(alias));
+ if (key instanceof PrivateKey) {
+ return (PrivateKey) key;
+ }
+ } catch (KeyStoreException e) {
+ LOG.error("Unable to read private key from keystore", e);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Unable to read private key from keystore", e);
+ } catch (UnrecoverableKeyException e) {
+ LOG.error("Unable to read private key from keystore", e);
+ }
+ return null;
+ }
+
+ public TrustManager[] getTrustManager(String algorithm) throws KeyStoreException,
+ NoSuchAlgorithmException, KeystoreIsLocked {
+ if (isKeystoreLocked()) {
+ throw new KeystoreIsLocked("Keystore '" + name + "' is locked.");
+ }
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(algorithm);
+ trustFactory.init(keystore);
+ return trustFactory.getTrustManagers();
+ }
+
+ public boolean isKeyLocked(String keyAlias) {
+ return keyPasswords.get(keyAlias) == null;
+ }
+
+ public boolean isKeystoreLocked() {
+ return keystorePassword == null;
+ }
+
+ public String[] listPrivateKeys() {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ return (String[]) privateKeys.toArray(new String[privateKeys.size()]);
+ }
+
+ public String[] listTrustCertificates() {
+ if (!loadKeystoreData()) {
+ return null;
+ }
+ return (String[]) trustCerts.toArray(new String[trustCerts.size()]);
+ }
+
+ // ==================== Internals =====================
+
+ private boolean loadKeystoreData() {
+ // Check to reload the data if needed
+ if (keystoreFile != null && keystoreReadDate >= keystoreFile.lastModified()) {
+ return true;
+ }
+ // If not a file, just not reload the data if it has already been loaded
+ if (keystoreFile == null && keystore != null) {
+ return true;
+ }
+ // Check if the file is invalid
+ if (keystoreFile != null && (!keystoreFile.exists() || !keystoreFile.canRead())) {
+ throw new IllegalArgumentException("Invalid keystore file (" + path + " = " + keystoreFile.getAbsolutePath() + ")");
+ }
+ // Load the keystore data
+ try {
+ keystoreReadDate = System.currentTimeMillis();
+ privateKeys.clear();
+ trustCerts.clear();
+ if (keystore == null) {
+ keystore = KeyStore.getInstance(JKS);
+ }
+ InputStream in = new BufferedInputStream(path.getInputStream());
+ keystore.load(in, keystorePassword == null ? new char[0] : keystorePassword.toCharArray());
+ in.close();
+ Enumeration aliases = keystore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = (String) aliases.nextElement();
+ if (keystore.isKeyEntry(alias)) {
+ privateKeys.add(alias);
+ } else if (keystore.isCertificateEntry(alias)) {
+ trustCerts.add(alias);
+ }
+ }
+ return true;
+ } catch (KeyStoreException e) {
+ LOG.error("Unable to open keystore with provided password", e);
+ } catch (IOException e) {
+ LOG.error("Unable to open keystore with provided password", e);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Unable to open keystore with provided password", e);
+ } catch (CertificateException e) {
+ LOG.error("Unable to open keystore with provided password", e);
+ }
+ return false;
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstanceParser.java b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstanceParser.java
new file mode 100644
index 0000000..01f3531
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/java/org/apache/servicemix/kernel/jaas/config/impl/ResourceKeystoreInstanceParser.java
@@ -0,0 +1,89 @@
+/*
+ * 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.servicemix.kernel.jaas.config.impl;
+
+import org.w3c.dom.Element;
+
+import org.apache.servicemix.kernel.jaas.config.KeystoreInstance;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean;
+
+/**
+ * Spring parser for a keystore instance
+ */
+public class ResourceKeystoreInstanceParser extends AbstractSingleBeanDefinitionParser {
+
+ public static final String PUBLISH_ATTRIBUTE = "publish";
+
+ protected Class getBeanClass(Element element) {
+ return ResourceKeystoreInstance.class;
+ }
+
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ // Parse name
+ String name = element.getAttribute("name");
+ if (name == null || name.length() == 0) {
+ name = element.getAttribute("id");
+ }
+ if (name != null && name.length() > 0) {
+ builder.addPropertyValue("name", name);
+ }
+ // Parse rank
+ String rank = element.getAttribute("rank");
+ if (rank != null && rank.length() > 0) {
+ builder.addPropertyValue("rank", rank);
+ }
+ // Parse path
+ String path = element.getAttribute("path");
+ if (path != null && path.length() > 0) {
+ builder.addPropertyValue("path", path);
+ }
+ // Parse keystorePassword
+ String keystorePassword = element.getAttribute("keystorePassword");
+ if (keystorePassword != null && keystorePassword.length() > 0) {
+ builder.addPropertyValue("keystorePassword", keystorePassword);
+ }
+ // Parse keyPasswords
+ String keyPasswords = element.getAttribute("keyPasswords");
+ if (keyPasswords != null && keyPasswords.length() > 0) {
+ builder.addPropertyValue("keyPasswords", keyPasswords);
+ }
+ // Parse publish
+ String publish = element.getAttribute("publish");
+ if (Boolean.valueOf(publish)) {
+ // Publish Config
+ BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(OsgiServiceFactoryBean.class);
+ bd.addPropertyValue("target", builder.getBeanDefinition());
+ bd.addPropertyValue("interfaces", new Class[] { KeystoreInstance.class });
+ BeanDefinition def = bd.getBeanDefinition();
+ String id = parserContext.getReaderContext().generateBeanName(def);
+ BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
+ registerBeanDefinition(holder, parserContext.getRegistry());
+ if (shouldFireEvents()) {
+ BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
+ postProcessComponentDefinition(componentDefinition);
+ parserContext.registerComponent(componentDefinition);
+ }
+ }
+ }
+
+}
diff --git a/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.handlers b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..fe59e34
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,20 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+http\://servicemix.apache.org/jaas = org.apache.servicemix.kernel.jaas.config.impl.NamespaceHandler
+
\ No newline at end of file
diff --git a/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.schemas b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..3001be1
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,19 @@
+################################################################################
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+http\://servicemix.apache.org/schema/servicemix-jaas.xsd = org/apache/servicemix/kernel/jaas/config/servicemix-jaas.xsd
diff --git a/karaf/jaas/jaas-config/src/main/resources/META-INF/spring/servicemix-jaas.xml b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring/servicemix-jaas.xml
new file mode 100644
index 0000000..eca5fbd
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/resources/META-INF/spring/servicemix-jaas.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+ <bean id="config"
+ class="org.apache.servicemix.kernel.jaas.config.impl.OsgiConfiguration"
+ init-method="init"
+ destroy-method="close"/>
+
+ <osgi:list id="realms"
+ interface="org.apache.servicemix.kernel.jaas.config.JaasRealm"
+ cardinality="0..N">
+ <osgi:listener ref="config" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <bean id="proxyLoginModuleInitializer"
+ class="org.apache.servicemix.kernel.jaas.config.impl.ProxyLoginModuleInitializer"
+ init-method="init"/>
+
+ <!-- Register the Straight-Through flow -->
+ <bean id="keystoreManager" class="org.apache.servicemix.kernel.jaas.config.impl.OsgiKeystoreManager" />
+ <osgi:service ref="keystoreManager">
+ <osgi:interfaces>
+ <value>org.apache.servicemix.kernel.jaas.config.KeystoreManager</value>
+ </osgi:interfaces>
+ </osgi:service>
+
+ <osgi:list id="keystores"
+ interface="org.apache.servicemix.kernel.jaas.config.KeystoreInstance"
+ cardinality="0..N">
+ <osgi:listener ref="keystoreManager" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+</beans>
diff --git a/karaf/jaas/jaas-config/src/main/resources/org/apache/servicemix/kernel/jaas/config/servicemix-jaas.xsd b/karaf/jaas/jaas-config/src/main/resources/org/apache/servicemix/kernel/jaas/config/servicemix-jaas.xsd
new file mode 100644
index 0000000..320e04d
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/main/resources/org/apache/servicemix/kernel/jaas/config/servicemix-jaas.xsd
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xs:schema elementFormDefault='qualified'
+ targetNamespace='http://servicemix.apache.org/jaas'
+ xmlns:xs='http://www.w3.org/2001/XMLSchema'
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:tns='http://servicemix.apache.org/jaas'>
+
+ <xs:import namespace="http://www.springframework.org/schema/beans"/>
+
+ <xs:element name="config">
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="beans:identifiedType">
+ <xs:sequence>
+ <xs:element name="module" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType mixed="true">
+ <xs:attribute name="className" use="required" type="xs:string" />
+ <xs:attribute name="flags" default="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="required"/>
+ <xs:enumeration value="requisite"/>
+ <xs:enumeration value="sufficient"/>
+ <xs:enumeration value="optional"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" use="optional" type="xs:string" />
+ <xs:attribute name="rank" use="optional" default="0" type="xs:int" />
+ <xs:attribute name="publish" use="optional" default="true" type="xs:boolean" />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="keystore">
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="beans:identifiedType">
+ <xs:attribute name="name" use="optional" type="xs:string" />
+ <xs:attribute name="rank" use="optional" default="0" type="xs:int" />
+ <xs:attribute name="publish" use="optional" default="true" type="xs:boolean" />
+ <xs:attribute name="path" use="required" type="xs:string" />
+ <xs:attribute name="keystorePassword" use="optional" type="xs:string" />
+ <xs:attribute name="keyPasswords" use="optional" type="xs:string" />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
diff --git a/karaf/jaas/jaas-config/src/test/java/org/apache/servicemix/kernel/jaas/config/NamespaceHandlerTest.java b/karaf/jaas/jaas-config/src/test/java/org/apache/servicemix/kernel/jaas/config/NamespaceHandlerTest.java
new file mode 100644
index 0000000..fb282e9
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/test/java/org/apache/servicemix/kernel/jaas/config/NamespaceHandlerTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.servicemix.kernel.jaas.config;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.security.auth.login.AppConfigurationEntry;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.jaas.boot.ProxyLoginModule;
+import org.apache.servicemix.kernel.jaas.config.impl.Config;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.osgi.context.support.BundleContextAwareProcessor;
+
+public class NamespaceHandlerTest extends TestCase {
+
+ public void testConfig() throws Exception {
+ final Dictionary headers = new Hashtable();
+ headers.put(Constants.BUNDLE_VERSION, "1.0.0.SNAPSHOT");
+
+ final BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ final Bundle bundle = EasyMock.createMock(Bundle.class);
+ final ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+
+ expect(bundleContext.getBundle()).andReturn(bundle).anyTimes();
+ expect(bundle.getSymbolicName()).andReturn("symbolic-name").anyTimes();
+ expect(bundle.getBundleId()).andReturn(Long.valueOf(32)).anyTimes();
+ expect(bundle.getHeaders()).andReturn(headers).anyTimes();
+ expect(bundleContext.registerService(aryEq(new String[] { JaasRealm.class.getName() }),
+ anyObject(), EasyMock.<Dictionary>anyObject())).andReturn(reg);
+ expect(bundleContext.registerService(aryEq(new String[] { KeystoreInstance.class.getName() }),
+ anyObject(), EasyMock.<Dictionary>anyObject())).andReturn(reg);
+
+ replay(bundleContext, bundle);
+
+ AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] { "classpath:config.xml" }, false) {
+ protected DefaultListableBeanFactory createBeanFactory() {
+ DefaultListableBeanFactory f = super.createBeanFactory();
+ f.addBeanPostProcessor(new BundleContextAwareProcessor(bundleContext));
+ return f;
+ }
+ };
+ ctx.refresh();
+
+ verify(bundleContext, bundle);
+
+ // Test realm
+ Object obj = ctx.getBean("realm");
+ assertNotNull(obj);
+ assertTrue(obj instanceof Config);
+ Config cfg = (Config) obj;
+ assertNotNull(cfg.getBundleContext());
+ assertEquals("realm", cfg.getName());
+ assertNotNull(cfg.getModules());
+ assertEquals(1, cfg.getModules().length);
+ assertNotNull(cfg.getModules()[0]);
+ assertEquals("org.apache.servicemix.kernel.jaas.config.SimpleLoginModule", cfg.getModules()[0].getClassName());
+ assertEquals("required", cfg.getModules()[0].getFlags());
+ assertNotNull(cfg.getModules()[0].getOptions());
+ assertEquals(1, cfg.getModules()[0].getOptions().size());
+ assertEquals("value", cfg.getModules()[0].getOptions().get("key"));
+ AppConfigurationEntry[] entries = cfg.getEntries();
+ assertNotNull(entries);
+ assertEquals(1, entries.length);
+ assertNotNull(entries[0]);
+ assertEquals(ProxyLoginModule.class.getName(), entries[0].getLoginModuleName());
+ assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, entries[0].getControlFlag());
+ Map<String,?> options = entries[0].getOptions();
+ assertNotNull(options);
+ assertEquals(3, options.size());
+ assertEquals("value", options.get("key"));
+ assertEquals("org.apache.servicemix.kernel.jaas.config.SimpleLoginModule", options.get(ProxyLoginModule.PROPERTY_MODULE));
+ assertEquals("32", options.get(ProxyLoginModule.PROPERTY_BUNDLE));
+
+ // Test keystore
+ obj = ctx.getBean("keystore");
+ assertNotNull(obj);
+ assertTrue(obj instanceof KeystoreInstance);
+ KeystoreInstance ks = (KeystoreInstance) obj;
+ assertEquals("ks", ks.getName());
+ assertEquals(1, ks.getRank());
+ assertNotNull(ks.getPrivateKey("myalias"));
+ }
+}
diff --git a/karaf/jaas/jaas-config/src/test/resources/config.xml b/karaf/jaas/jaas-config/src/test/resources/config.xml
new file mode 100644
index 0000000..868b7c7
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/test/resources/config.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:jaas="http://servicemix.apache.org/jaas"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/jaas
+ http://servicemix.apache.org/schema/servicemix-jaas.xsd">
+
+ <jaas:config id="realm">
+ <jaas:module className="org.apache.servicemix.kernel.jaas.config.SimpleLoginModule" flags="required">
+ key=value
+ </jaas:module>
+ </jaas:config>
+
+ <jaas:keystore id="keystore"
+ name="ks"
+ rank="1"
+ path="classpath:privatestore.jks"
+ keystorePassword="keyStorePassword"
+ keyPasswords="myalias=myAliasPassword" />
+
+</beans>
diff --git a/karaf/jaas/jaas-config/src/test/resources/privatestore.jks b/karaf/jaas/jaas-config/src/test/resources/privatestore.jks
new file mode 100644
index 0000000..26fc0bc
--- /dev/null
+++ b/karaf/jaas/jaas-config/src/test/resources/privatestore.jks
Binary files differ
diff --git a/karaf/jaas/jaas-modules/pom.xml b/karaf/jaas/jaas-modules/pom.xml
new file mode 100644
index 0000000..b3d0300
--- /dev/null
+++ b/karaf/jaas/jaas-modules/pom.xml
@@ -0,0 +1,92 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>jaas</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.modules</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: JAAS Modules</name>
+
+ <description>
+ Provides the JAAS Config
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.jaas.modules*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.servicemix.kernel.jaas.config,
+ org.springframework.beans.factory.config,
+ *
+ </Import-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/RolePrincipal.java b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/RolePrincipal.java
new file mode 100644
index 0000000..03da146
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/RolePrincipal.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.servicemix.kernel.jaas.modules;
+
+import java.security.Principal;
+
+public class RolePrincipal implements Principal {
+
+ private final String name;
+
+ public RolePrincipal(String name) {
+ assert name != null;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RolePrincipal)) return false;
+
+ RolePrincipal that = (RolePrincipal) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name != null ? name.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "RolePrincipal[" + name + "]";
+ }
+}
diff --git a/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/UserPrincipal.java b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/UserPrincipal.java
new file mode 100644
index 0000000..4978076
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/UserPrincipal.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.servicemix.kernel.jaas.modules;
+
+import java.security.Principal;
+
+public class UserPrincipal implements Principal {
+
+ private final String name;
+
+ public UserPrincipal(String name) {
+ assert name != null;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UserPrincipal)) return false;
+
+ UserPrincipal that = (UserPrincipal) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name != null ? name.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "UserPrincipal[" + name + "]";
+ }
+}
diff --git a/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/ConfigAdminHolder.java b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/ConfigAdminHolder.java
new file mode 100644
index 0000000..ec93095
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/ConfigAdminHolder.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.servicemix.kernel.jaas.modules.osgi;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class ConfigAdminHolder {
+
+ private static ConfigurationAdmin configAdmin;
+
+ public static ConfigurationAdmin getService() {
+ return configAdmin;
+ }
+
+ public void setService(ConfigurationAdmin configAdmin) {
+ ConfigAdminHolder.configAdmin = configAdmin;
+ }
+
+}
diff --git a/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/OsgiConfigLoginModule.java b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/OsgiConfigLoginModule.java
new file mode 100644
index 0000000..2b48eb4
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/osgi/OsgiConfigLoginModule.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.servicemix.kernel.jaas.modules.osgi;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.servicemix.kernel.jaas.modules.RolePrincipal;
+import org.apache.servicemix.kernel.jaas.modules.UserPrincipal;
+import org.osgi.service.cm.Configuration;
+
+public class OsgiConfigLoginModule implements LoginModule {
+
+ public static final String PID = "pid";
+ public static final String USER_PREFIX = "user.";
+
+ private Subject subject;
+ private CallbackHandler callbackHandler;
+ private Map<String, ?> options;
+
+ private Set<Principal> principals;
+
+ public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
+ this.subject = subject;
+ this.callbackHandler = callbackHandler;
+ this.options = options;
+ }
+
+ public boolean login() throws LoginException {
+ try {
+ String pid = (String) options.get(PID);
+ Configuration config = ConfigAdminHolder.getService().getConfiguration(pid);
+ Dictionary properties = config.getProperties();
+
+ Callback[] callbacks = new Callback[2];
+
+ callbacks[0] = new NameCallback("Username: ");
+ callbacks[1] = new PasswordCallback("Password: ", false);
+ try {
+ callbackHandler.handle(callbacks);
+ } catch (IOException ioe) {
+ throw new LoginException(ioe.getMessage());
+ } catch (UnsupportedCallbackException uce) {
+ throw new LoginException(uce.getMessage() + " not available to obtain information from user");
+ }
+ String user = ((NameCallback) callbacks[0]).getName();
+ char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
+ if (tmpPassword == null) {
+ tmpPassword = new char[0];
+ }
+
+ String userInfos = (String) properties.get(USER_PREFIX + user);
+ if (userInfos == null) {
+ throw new FailedLoginException("User does not exist");
+ }
+ String[] infos = userInfos.split(",");
+ if (!new String(tmpPassword).equals(infos[0])) {
+ throw new FailedLoginException("Password does not match");
+ }
+
+ principals = new HashSet<Principal>();
+ principals.add(new UserPrincipal(user));
+ for (int i = 1; i < infos.length; i++) {
+ principals.add(new RolePrincipal(infos[i]));
+ }
+
+ return true;
+ } catch (LoginException e) {
+ throw e;
+ } catch (Exception e) {
+ throw (LoginException) new LoginException("Unable to authenticate user").initCause(e);
+ } finally {
+ callbackHandler = null;
+ options = null;
+ }
+ }
+
+ public boolean commit() throws LoginException {
+ subject.getPrincipals().addAll(principals);
+ return true;
+ }
+
+ public boolean abort() throws LoginException {
+ subject = null;
+ principals = null;
+ return true;
+ }
+
+ public boolean logout() throws LoginException {
+ try {
+ subject.getPrincipals().removeAll(principals);
+ principals.clear();
+ return true;
+ } finally {
+ subject = null;
+ principals = null;
+ }
+ }
+
+}
diff --git a/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/properties/PropertiesLoginModule.java b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/properties/PropertiesLoginModule.java
new file mode 100644
index 0000000..5971745
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/java/org/apache/servicemix/kernel/jaas/modules/properties/PropertiesLoginModule.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.servicemix.kernel.jaas.modules.properties;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.jaas.modules.RolePrincipal;
+import org.apache.servicemix.kernel.jaas.modules.UserPrincipal;
+
+/**
+ * JAAS Login module for user / password, based on two properties files.
+ *
+ */
+public class PropertiesLoginModule implements LoginModule {
+
+ private static final String USER_FILE = "users";
+ private static final Log LOG = LogFactory.getLog(PropertiesLoginModule.class);
+
+ private Subject subject;
+ private CallbackHandler callbackHandler;
+ private boolean debug;
+ private String usersFile;
+ private String user;
+ private Set principals = new HashSet();
+
+ public void initialize(Subject sub, CallbackHandler handler, Map sharedState, Map options) {
+ this.subject = sub;
+ this.callbackHandler = handler;
+
+ debug = "true".equalsIgnoreCase((String) options.get("debug"));
+ usersFile = (String) options.get(USER_FILE) + "";
+
+ if (debug) {
+ LOG.debug("Initialized debug=" + debug + " usersFile=" + usersFile);
+ }
+ }
+
+ public boolean login() throws LoginException {
+ Properties users = new Properties();
+ File f = new File(usersFile);
+ try {
+ users.load(new java.io.FileInputStream(f));
+ } catch (IOException ioe) {
+ throw new LoginException("Unable to load user properties file " + f);
+ }
+
+ Callback[] callbacks = new Callback[2];
+
+ callbacks[0] = new NameCallback("Username: ");
+ callbacks[1] = new PasswordCallback("Password: ", false);
+ try {
+ callbackHandler.handle(callbacks);
+ } catch (IOException ioe) {
+ throw new LoginException(ioe.getMessage());
+ } catch (UnsupportedCallbackException uce) {
+ throw new LoginException(uce.getMessage() + " not available to obtain information from user");
+ }
+ user = ((NameCallback) callbacks[0]).getName();
+ char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
+ if (tmpPassword == null) {
+ tmpPassword = new char[0];
+ }
+
+ String userInfos = (String) users.get(user);
+ if (userInfos == null) {
+ throw new FailedLoginException("User does not exist");
+ }
+ String[] infos = userInfos.split(",");
+ if (!new String(tmpPassword).equals(infos[0])) {
+ throw new FailedLoginException("Password does not match");
+ }
+
+ principals = new HashSet<Principal>();
+ principals.add(new UserPrincipal(user));
+ for (int i = 1; i < infos.length; i++) {
+ principals.add(new RolePrincipal(infos[i]));
+ }
+
+ users.clear();
+
+ if (debug) {
+ LOG.debug("login " + user);
+ }
+ return true;
+ }
+
+ public boolean commit() throws LoginException {
+ subject.getPrincipals().addAll(principals);
+ clear();
+ if (debug) {
+ LOG.debug("commit");
+ }
+ return true;
+ }
+
+ public boolean abort() throws LoginException {
+ clear();
+ if (debug) {
+ LOG.debug("abort");
+ }
+ return true;
+ }
+
+ public boolean logout() throws LoginException {
+ subject.getPrincipals().removeAll(principals);
+ principals.clear();
+ if (debug) {
+ LOG.debug("logout");
+ }
+ return true;
+ }
+
+ private void clear() {
+ user = null;
+ }
+}
diff --git a/karaf/jaas/jaas-modules/src/main/resources/META-INF/spring/servicemix-jaas-module.xml b/karaf/jaas/jaas-modules/src/main/resources/META-INF/spring/servicemix-jaas-module.xml
new file mode 100644
index 0000000..e8acabe
--- /dev/null
+++ b/karaf/jaas/jaas-modules/src/main/resources/META-INF/spring/servicemix-jaas-module.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:jaas="http://servicemix.apache.org/jaas"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/jaas
+ http://servicemix.apache.org/schema/servicemix-jaas.xsd">
+
+ <!--
+ - The following beans define an alternate realm which uses the OSGi Configuration Admin service
+ - to obtain passwords. However, this realm is not really secure as there is no access restriction
+ - on this service, which means any bundle could access all the passwords.
+ -->
+ <!--
+ <bean id="configAdminHolder" class="org.apache.servicemix.kernel.jaas.modules.osgi.ConfigAdminHolder">
+ <property name="service" ref="configAdmin" />
+ </bean>
+
+ <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+ <jaas:config id="servicemix">
+ <jaas:module className="org.apache.servicemix.kernel.jaas.modules.osgi.OsgiConfigLoginModule" flags="required">
+ pid = org.apache.servicemix.users
+ </jaas:module>
+ </jaas:config>
+ -->
+
+ <!-- Bean to allow the ${servicemix.home} property to be correctly resolved -->
+ <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+ <jaas:config id="servicemix">
+ <jaas:module className="org.apache.servicemix.kernel.jaas.modules.properties.PropertiesLoginModule" flags="required">
+ users = ${servicemix.base}/etc/users.properties
+ </jaas:module>
+ </jaas:config>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/jaas/pom.xml b/karaf/jaas/pom.xml
new file mode 100644
index 0000000..5be1cd7
--- /dev/null
+++ b/karaf/jaas/pom.xml
@@ -0,0 +1,42 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>jaas</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: JAAS</name>
+
+ <modules>
+ <module>jaas-boot</module>
+ <module>jaas-config</module>
+ <module>jaas-modules</module>
+ </modules>
+
+</project>
\ No newline at end of file
diff --git a/karaf/main/pom.xml b/karaf/main/pom.xml
new file mode 100644
index 0000000..4166785
--- /dev/null
+++ b/karaf/main/pom.xml
@@ -0,0 +1,202 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Main</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <_donotcopy>(CVS|.svn|config.properties)</_donotcopy>
+ <Main-Class>org.apache.servicemix.kernel.main.Main</Main-Class>
+ <Bundle-Name>Apache ServiceMix</Bundle-Name>
+ <Bundle-Description>OSGi R4 framework.</Bundle-Description>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.main.spi.*;version=${pom.version}</Export-Package>
+ <Private-Package>
+ org.apache.servicemix.kernel.main;-split-package:=merge-first,
+ org.apache.felix.moduleloader.*;-split-package:=merge-first,
+ org.apache.felix.framework.*;-split-package:=merge-first,
+ org.osgi.framework.launch*,
+ org.osgi.framework.hooks*,
+ org.osgi.framework;-split-package:=merge-first,
+ org.osgi.service.packageadmin;-split-package:=merge-first,
+ org.osgi.service.startlevel;-split-package:=merge-first,
+ org.osgi.service.url;-split-package:=merge-first,
+ org.osgi.util.tracker;-split-package:=merge-first,
+ META-INF;-split-package:=merge-first
+ </Private-Package>
+ <Import-Package>!*</Import-Package>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>org.apache.felix:org.apache.felix.framework</include>
+ <include>org.apache.felix:org.osgi.core</include>
+ <include>${project.groupId}:${project.artifactId}</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+ <artifact>org.apache.felix:org.apache.felix.framework</artifact>
+ <excludes>
+ <exclude>org/apache/felix/**</exclude>
+ <exclude>org/osgi/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.felix:org.osgi.core</artifact>
+ <excludes>
+ <exclude>org/osgi/**</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <createSourcesJar>${createSourcesJar}</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>true</createDependencyReducedPom>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>deploy</id>
+ <properties>
+ <createSourcesJar>true</createSourcesJar>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-sources</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <classifier>sources</classifier>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <classifier>sources</classifier>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.build.directory}/sources</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <minmemory>128m</minmemory>
+ <maxmemory>512m</maxmemory>
+ <sourcepath>${project.build.directory}/sources</sourcepath>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/DefaultJDBCLock.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/DefaultJDBCLock.java
new file mode 100644
index 0000000..bf4bfba
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/DefaultJDBCLock.java
@@ -0,0 +1,194 @@
+/*
+ * 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.servicemix.kernel.main;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * Represents an exclusive lock on a database,
+ * used to avoid multiple SMX instances attempting
+ * to become master.
+ *
+ * @version $Revision: $
+ */
+public class DefaultJDBCLock implements Lock {
+
+ private static final String PROPERTY_LOCK_URL = "servicemix.lock.jdbc.url";
+ private static final String PROPERTY_LOCK_JDBC_DRIVER = "servicemix.lock.jdbc.driver";
+ private static final String PROPERTY_LOCK_JDBC_USER = "servicemix.lock.jdbc.user";
+ private static final String PROPERTY_LOCK_JDBC_PASSWORD = "servicemix.lock.jdbc.password";
+ private static final String PROPERTY_LOCK_JDBC_TABLE = "servicemix.lock.jdbc.table";
+ private static final String PROPERTY_LOCK_JDBC_CLUSTERNAME = "servicemix.lock.jdbc.clustername";
+ private static final String PROPERTY_LOCK_JDBC_TIMEOUT = "servicemix.lock.jdbc.timeout";
+
+ private final Statements statements;
+ private Connection lockConnection;
+ private String url;
+ private String driver;
+ private String user;
+ private String password;
+ private String table;
+ private String clusterName;
+ private int timeout;
+
+ public DefaultJDBCLock(Properties props) {
+ this.url = props.getProperty(PROPERTY_LOCK_URL);
+ this.driver = props.getProperty(PROPERTY_LOCK_JDBC_DRIVER);
+ this.user = props.getProperty(PROPERTY_LOCK_JDBC_USER);
+ this.password = props.getProperty(PROPERTY_LOCK_JDBC_PASSWORD);
+ this.table = props.getProperty(PROPERTY_LOCK_JDBC_TABLE);
+ this.clusterName = props.getProperty(PROPERTY_LOCK_JDBC_CLUSTERNAME);
+ String time = props.getProperty(PROPERTY_LOCK_JDBC_TIMEOUT);
+ this.lockConnection = null;
+ if (table == null) { table = "SERVICEMIX_LOCK"; }
+ if ( clusterName == null) { clusterName = "smx4"; }
+ this.statements = new Statements(table, clusterName);
+ if (time != null) {
+ this.timeout = Integer.parseInt(time) * 1000;
+ } else {
+ this.timeout = 10000; // 10 seconds
+ }
+ if (user == null) { user = ""; }
+ if (password == null) { password = ""; }
+ try {
+ obtainLock();
+ } catch (Exception e) {
+ System.err.println("Error occured while attempting to obtain connection: " + e.getMessage());
+ }
+ }
+
+ /**
+ * obtainLock - obtain the lock connection.
+ *
+ * @throws Exception
+ */
+ private void obtainLock() throws Exception {
+ PreparedStatement statement = null;
+ while (true) {
+ try {
+ lockConnection = getConnection(driver, url, user, password);
+ lockConnection.setAutoCommit(false);
+ statements.init(lockConnection);
+ String sql = statements.testLockTableStatus();
+ statement = lockConnection.prepareStatement(sql);
+ statement.execute();
+ break;
+ } catch (Exception e) {
+ System.err.println("Could not obtain lock: " + e.getMessage());
+ Thread.sleep(this.timeout);
+ } finally {
+ if (null != statement) {
+ try {
+ statement.close();
+ } catch (SQLException e1) {
+ System.err.println("Caught while closing statement: " + e1.getMessage());
+ }
+ statement = null;
+ }
+ }
+ Thread.sleep(this.timeout);
+ }
+ System.out.println("Connected to data source: " + url);
+ }
+
+ /**
+ * lock - a KeepAlive function to maintain lock.
+ *
+ * @return true if connection lock retained, false otherwise.
+ */
+ public boolean lock() {
+ PreparedStatement statement = null;
+ boolean result = false;
+ try {
+ if (lockConnection.isClosed()) { obtainLock(); }
+ long time = System.currentTimeMillis();
+ statement = lockConnection.prepareStatement(statements.getLockUpdateStatement(time));
+ int rows = statement.executeUpdate();
+ if (rows == 1) {
+ result=true;
+ }
+ } catch (Exception e) {
+ System.err.println("Failed to acquire database lock: " + e.getMessage());
+ }finally {
+ if (statement != null) {
+ try {
+ statement.close();
+ } catch (SQLException e) {
+ System.err.println("Failed to close statement" + e);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * release - terminate the lock connection safely.
+ */
+ public void release() throws Exception {
+ if (lockConnection != null && !lockConnection.isClosed()) {
+ lockConnection.rollback();
+ lockConnection.close();
+ }
+ }
+
+ /**
+ * isAlive - test if lock still exists.
+ */
+ public boolean isAlive() throws Exception {
+ if (lockConnection == null) { return false; }
+ PreparedStatement statement = null;
+ try {
+ lockConnection.setAutoCommit(false);
+ statements.init(lockConnection);
+ String sql = statements.testLockTableStatus();
+ statement = lockConnection.prepareStatement(sql);
+ statement.execute();
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * getConnection - Obtain connection to database via jdbc driver.
+ *
+ * @throws Exception
+ * @param driver, the JDBC driver class.
+ * @param url, url to data source.
+ * @param username, user to access data source.
+ * @param password, password for specified user.
+ * @return connection, null returned if conenction fails.
+ */
+ private Connection getConnection(String driver, String url,
+ String username, String password) throws Exception {
+ Connection conn = null;
+ try {
+ Class.forName(driver);
+ conn = DriverManager.getConnection(url + ";create=true", username, password);
+ } catch (Exception e) {
+ throw e;
+ }
+ return conn;
+ }
+
+}
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Lock.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Lock.java
new file mode 100644
index 0000000..f4e8ab8
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Lock.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.main;
+
+public interface Lock {
+
+ boolean lock() throws Exception;
+
+ void release() throws Exception;
+
+ boolean isAlive() throws Exception;
+
+}
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Main.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Main.java
new file mode 100644
index 0000000..d09a033
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Main.java
@@ -0,0 +1,1150 @@
+/*
+ * 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.servicemix.kernel.main;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.security.Provider;
+import java.security.Security;
+import java.util.ArrayList;
+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 java.util.StringTokenizer;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.cache.BundleCache;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * <p>
+ * This class is the default way to instantiate and execute the framework. It is not
+ * intended to be the only way to instantiate and execute the framework; rather, it is
+ * one example of how to do so. When embedding the framework in a host application,
+ * this class can serve as a simple guide of how to do so. It may even be
+ * worthwhile to reuse some of its property handling capabilities. This class
+ * is completely static and is only intended to start a single instance of
+ * the framework.
+ * </p>
+ */
+public class Main implements MainService, BundleActivator {
+ /**
+ * The default name used for the system properties file.
+ */
+ public static final String SYSTEM_PROPERTIES_FILE_NAME = "system.properties";
+ /**
+ * The default name used for the configuration properties file.
+ */
+ public static final String CONFIG_PROPERTIES_FILE_NAME = "config.properties";
+ /**
+ * The default name used for the startup properties file.
+ */
+ public static final String STARTUP_PROPERTIES_FILE_NAME = "startup.properties";
+ /**
+ * The property name prefix for the launcher's auto-install property.
+ */
+ public static final String PROPERTY_AUTO_INSTALL = "felix.auto.install";
+ /**
+ * The property for auto-discovering the bundles
+ */
+ public static final String PROPERTY_AUTO_START = "felix.auto.start";
+ /**
+ * The system property for specifying the ServiceMix home directory. The home directory
+ * hold the binary install of ServiceMix.
+ */
+ public static final String PROP_SERVICEMIX_HOME = "servicemix.home";
+ /**
+ * The environment variable for specifying the ServiceMix home directory. The home directory
+ * hold the binary install of ServiceMix.
+ */
+ public static final String ENV_SERVICEMIX_HOME = "SERVICEMIX_HOME";
+ /**
+ * The system property for specifying the ServiceMix base directory. The base directory
+ * holds the configuration and data for a ServiceMix instance.
+ */
+ public static final String PROP_SERVICEMIX_BASE = "servicemix.base";
+ /**
+ * The environment variable for specifying the ServiceMix base directory. The base directory
+ * holds the configuration and data for a ServiceMix instance.
+ */
+ public static final String ENV_SERVICEMIX_BASE = "SERVICEMIX_BASE";
+
+ /**
+ * Config property which identifies directories which contain bundles to be loaded by SMX
+ */
+ public static final String BUNDLE_LOCATIONS = "bundle.locations";
+
+ /**
+ * Config property that indicates we want to convert bundles locations to maven style urls
+ */
+ public static final String PROPERTY_CONVERT_TO_MAVEN_URL = "servicemix.maven.convert";
+
+ /**
+ * If a lock should be used before starting the runtime
+ */
+ public static final String PROPERTY_USE_LOCK = "servicemix.lock";
+
+ /**
+ * The lock implementation
+ */
+ public static final String PROPERTY_LOCK_CLASS = "servicemix.lock.class";
+
+ public static final String PROPERTY_LOCK_DELAY = "servicemix.lock.delay";
+
+ public static final String PROPERTY_LOCK_LEVEL = "servicemix.lock.level";
+
+ public static final String PROPERTY_LOCK_CLASS_DEFAULT = SimpleFileLock.class.getName();
+
+
+ private File servicemixHome;
+ private File servicemixBase;
+ private static Properties m_configProps = null;
+ private static Felix m_felix = null;
+ private final String[] args;
+ private int exitCode;
+ private Lock lock;
+ private CountDownLatch shutdown = new CountDownLatch(1);
+ private int defaultStartLevel = 100;
+ private int lockStartLevel = 0;
+ private int lockDelay = 1000;
+ private boolean exiting = false;
+
+ public Main(String[] args) {
+ this.args = args;
+ }
+
+ public void launch() throws Exception {
+ servicemixHome = getServiceMixHome();
+ servicemixBase = getServiceMixBase(servicemixHome);
+
+ //System.out.println("ServiceMix Home: "+main.servicemixHome.getPath());
+ //System.out.println("ServiceMix Base: "+main.servicemixBase.getPath());
+
+ System.setProperty(PROP_SERVICEMIX_HOME, servicemixHome.getPath());
+ System.setProperty(PROP_SERVICEMIX_BASE, servicemixBase.getPath());
+
+ // Load system properties.
+ loadSystemProperties();
+
+ // Read configuration properties.
+ m_configProps = loadConfigProperties();
+
+ // Copy framework properties from the system properties.
+ Main.copySystemProperties(m_configProps);
+
+ processSecurityProperties(m_configProps);
+
+ m_configProps.setProperty(BundleCache.CACHE_ROOTDIR_PROP, servicemixBase.getPath() + "/data");
+ m_configProps.setProperty(Constants.FRAMEWORK_STORAGE, "cache");
+
+ // Register the Main class so that other bundles can inspect the command line args.
+ BundleActivator activator = new BundleActivator() {
+ private ServiceRegistration registration;
+
+ public void start(BundleContext context) {
+ registration = context.registerService(MainService.class.getName(), Main.this, null);
+ }
+
+ public void stop(BundleContext context) {
+ registration.unregister();
+ shutdown.countDown();
+ }
+ };
+ List<BundleActivator> activations = new ArrayList<BundleActivator>();
+ activations.add(this);
+ activations.add(activator);
+
+ m_configProps.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, activations);
+
+ try {
+ defaultStartLevel = Integer.parseInt(m_configProps.getProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL));
+ lockStartLevel = Integer.parseInt(m_configProps.getProperty(PROPERTY_LOCK_LEVEL, Integer.toString(lockStartLevel)));
+ lockDelay = Integer.parseInt(m_configProps.getProperty(PROPERTY_LOCK_DELAY, Integer.toString(lockDelay)));
+ m_configProps.setProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, Integer.toString(lockStartLevel));
+ // Start up the OSGI framework
+ m_felix = new Felix(new StringMap(m_configProps, false));
+ m_felix.start();
+ // Start lock monitor
+ new Thread() {
+ public void run() {
+ lock(m_configProps);
+ }
+ }.start();
+ }
+ catch (Exception ex) {
+ setExitCode(-1);
+ throw new Exception("Could not create framework", ex);
+ }
+ }
+
+ public void destroy(boolean await) throws Exception {
+ try {
+ if (await) {
+ shutdown.await();
+ }
+ exiting = true;
+ if (m_felix.getState() == Bundle.ACTIVE) {
+ m_felix.stop();
+ }
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Used to instigate auto-install and auto-start configuration
+ * property processing via a custom framework activator during
+ * framework startup.
+ *
+ * @param context The system bundle context.
+ */
+ public void start(BundleContext context) {
+ Main.processAutoProperties(context);
+ }
+
+ /**
+ * Currently does nothing as part of framework shutdown.
+ *
+ * @param context The system bundle context.
+ */
+ public void stop(BundleContext context) {
+ // Do nothing.
+ }
+
+ /**
+ * <p>
+ * This method performs the main task of constructing an framework instance
+ * and starting its execution. The following functions are performed
+ * when invoked:
+ * </p>
+ * <ol>
+ * <li><i><b>Read the system properties file.<b></i> This is a file
+ * containing properties to be pushed into <tt>System.setProperty()</tt>
+ * before starting the framework. This mechanism is mainly shorthand
+ * for people starting the framework from the command line to avoid having
+ * to specify a bunch of <tt>-D</tt> system property definitions.
+ * The only properties defined in this file that will impact the framework's
+ * behavior are the those concerning setting HTTP proxies, such as
+ * <tt>http.proxyHost</tt>, <tt>http.proxyPort</tt>, and
+ * <tt>http.proxyAuth</tt>.
+ * </li>
+ * <li><i><b>Perform system property variable substitution on system
+ * properties.</b></i> Any system properties in the system property
+ * file whose value adheres to <tt>${<system-prop-name>}</tt>
+ * syntax will have their value substituted with the appropriate
+ * system property value.
+ * </li>
+ * <li><i><b>Read the framework's configuration property file.</b></i> This is
+ * a file containing properties used to configure the framework
+ * instance and to pass configuration information into
+ * bundles installed into the framework instance. The configuration
+ * property file is called <tt>config.properties</tt> by default
+ * and is located in the <tt>conf/</tt> directory of the Felix
+ * installation directory, which is the parent directory of the
+ * directory containing the <tt>felix.jar</tt> file. It is possible
+ * to use a different location for the property file by specifying
+ * the desired URL using the <tt>felix.config.properties</tt>
+ * system property; this should be set using the <tt>-D</tt> syntax
+ * when executing the JVM. Refer to the
+ * <a href="Felix.html#Felix(java.util.Map, java.util.List)">
+ * <tt>Felix</tt></a> constructor documentation for more
+ * information on the framework configuration options.
+ * </li>
+ * <li><i><b>Perform system property variable substitution on configuration
+ * properties.</b></i> Any configuration properties whose value adheres to
+ * <tt>${<system-prop-name>}</tt> syntax will have their value
+ * substituted with the appropriate system property value.
+ * </li>
+ * <li><i><b>Ensure the default bundle cache has sufficient information to
+ * initialize.</b></i> The default implementation of the bundle cache
+ * requires either a profile name or a profile directory in order to
+ * start. The configuration properties are checked for at least one
+ * of the <tt>felix.cache.profile</tt> or <tt>felix.cache.profiledir</tt>
+ * properties. If neither is found, the user is asked to supply a profile
+ * name that is added to the configuration property set. See the
+ * <a href="cache/DefaultBundleCache.html"><tt>DefaultBundleCache</tt></a>
+ * documentation for more details its configuration options.
+ * </li>
+ * <li><i><b>Creates and starts a framework instance.</b></i> A
+ * case insensitive
+ * <a href="util/StringMap.html"><tt>StringMap</tt></a>
+ * is created for the configuration property file and is passed
+ * into the framework.
+ * </li>
+ * </ol>
+ * <p>
+ * It should be noted that simply starting an instance of the framework is not enough
+ * to create an interactive session with it. It is necessary to install
+ * and start bundles that provide an interactive impl; this is generally
+ * done by specifying an "auto-start" property in the framework configuration
+ * property file. If no interactive impl bundles are installed or if
+ * the configuration property file cannot be found, the framework will appear to
+ * be hung or deadlocked. This is not the case, it is executing correctly,
+ * there is just no way to interact with it. Refer to the
+ * <a href="Felix.html#Felix(java.util.Map, java.util.List)">
+ * <tt>Felix</tt></a> constructor documentation for more information on
+ * framework configuration options.
+ * </p>
+ * @param args An array of arguments, all of which are ignored.
+ * @throws Exception If an error occurs.
+ **/
+ public static void main(String[] args) throws Exception {
+ final Main main = new Main(args);
+ try {
+ main.launch();
+ main.destroy(true);
+ }
+ catch (Exception ex) {
+ System.err.println("Error occured shutting down framework: " + ex);
+ ex.printStackTrace();
+ } finally {
+ System.exit(main.getExitCode());
+ }
+ }
+
+ private static File getServiceMixHome() throws IOException {
+ File rc = null;
+
+ // Use the system property if specified.
+ String path = System.getProperty(PROP_SERVICEMIX_HOME);
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + PROP_SERVICEMIX_HOME + " system property");
+ }
+
+ if (rc == null) {
+ path = System.getenv(ENV_SERVICEMIX_HOME);
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + ENV_SERVICEMIX_HOME + " environment variable");
+ }
+ }
+
+ // Try to figure it out using the jar file this class was loaded from.
+ if (rc == null) {
+ // guess the home from the location of the jar
+ URL url = Main.class.getClassLoader().getResource(Main.class.getName().replace(".", "/") + ".class");
+ if (url != null) {
+ try {
+ JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
+ url = jarConnection.getJarFileURL();
+ rc = new File(new URI(url.toString())).getCanonicalFile().getParentFile().getParentFile();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ if (rc == null) {
+ // Dig into the classpath to guess the location of the jar
+ String classpath = System.getProperty("java.class.path");
+ int index = classpath.toLowerCase().indexOf("servicemix.jar");
+ int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
+ if (index >= start) {
+ String jarLocation = classpath.substring(start, index);
+ rc = new File(jarLocation).getCanonicalFile().getParentFile();
+ }
+ }
+ if (rc == null) {
+ throw new IOException("The ServiceMix install directory could not be determined. Please set the " + PROP_SERVICEMIX_HOME + " system property or the " + ENV_SERVICEMIX_HOME + " environment variable.");
+ }
+
+ return rc;
+ }
+
+ private static File validateDirectoryExists(String path, String errPrefix) {
+ File rc;
+ try {
+ rc = new File(path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : " + e.getMessage());
+ }
+ if (!rc.exists()) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : does not exist");
+ }
+ if (!rc.isDirectory()) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : is not a directory");
+ }
+ return rc;
+ }
+
+ private static File getServiceMixBase(File defaultValue) {
+ File rc = null;
+
+ String path = System.getProperty(PROP_SERVICEMIX_BASE);
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + PROP_SERVICEMIX_BASE + " system property");
+ }
+
+ if (rc == null) {
+ path = System.getenv(ENV_SERVICEMIX_BASE);
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + ENV_SERVICEMIX_BASE + " environment variable");
+ }
+ }
+
+ if (rc == null) {
+ rc = defaultValue;
+ }
+ return rc;
+ }
+
+ private static void processSecurityProperties(Properties m_configProps) {
+ String prop = m_configProps.getProperty("org.apache.servicemix.security.providers");
+ if (prop != null) {
+ String[] providers = prop.split(",");
+ for (String provider : providers) {
+ try {
+ Security.addProvider((Provider) Class.forName(provider).newInstance());
+ } catch (Throwable t) {
+ System.err.println("Unable to register security provider: " + t);
+ }
+ }
+ }
+ }
+
+ /**
+ * <p/>
+ * Processes the auto-install and auto-start properties from the
+ * specified configuration properties.
+ */
+ private static void processAutoProperties(BundleContext context) {
+ // Check if we want to convert URLs to maven style
+ boolean convertToMavenUrls = Boolean.parseBoolean(m_configProps.getProperty(PROPERTY_CONVERT_TO_MAVEN_URL, "true"));
+
+ // Retrieve the Start Level service, since it will be needed
+ // to set the start level of the installed bundles.
+ StartLevel sl = (StartLevel) context.getService(
+ context.getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName()));
+
+ // The auto-install property specifies a space-delimited list of
+ // bundle URLs to be automatically installed into each new profile;
+ // the start level to which the bundles are assigned is specified by
+ // appending a ".n" to the auto-install property name, where "n" is
+ // the desired start level for the list of bundles.
+ for (Iterator i = m_configProps.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+
+ // Ignore all keys that are not the auto-install property.
+ if (!key.startsWith(PROPERTY_AUTO_INSTALL)) {
+ continue;
+ }
+
+ // If the auto-install property does not have a start level,
+ // then assume it is the default bundle start level, otherwise
+ // parse the specified start level.
+ int startLevel = sl.getInitialBundleStartLevel();
+ if (!key.equals(PROPERTY_AUTO_INSTALL)) {
+ try {
+ startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
+ }
+ catch (NumberFormatException ex) {
+ System.err.println("Invalid property: " + key);
+ }
+ }
+
+ StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ", true);
+ if (st.countTokens() > 0) {
+ String location = null;
+ do {
+ location = nextLocation(st);
+ if (location != null) {
+ try {
+ String[] parts = convertToMavenUrlsIfNeeded(location, convertToMavenUrls);
+ Bundle b = context.installBundle(parts[0], new URL(parts[1]).openStream());
+ sl.setBundleStartLevel(b, startLevel);
+ }
+ catch (Exception ex) {
+ System.err.println("Auto-properties install: " + ex);
+ }
+ }
+ }
+ while (location != null);
+ }
+ }
+
+ // The auto-start property specifies a space-delimited list of
+ // bundle URLs to be automatically installed and started into each
+ // new profile; the start level to which the bundles are assigned
+ // is specified by appending a ".n" to the auto-start property name,
+ // where "n" is the desired start level for the list of bundles.
+ // The following code starts bundles in two passes, first it installs
+ // them, then it starts them.
+ for (Iterator i = m_configProps.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+
+ // Ignore all keys that are not the auto-start property.
+ if (!key.startsWith(PROPERTY_AUTO_START)) {
+ continue;
+ }
+
+ // If the auto-start property does not have a start level,
+ // then assume it is the default bundle start level, otherwise
+ // parse the specified start level.
+ int startLevel = sl.getInitialBundleStartLevel();
+ if (!key.equals(PROPERTY_AUTO_START)) {
+ try {
+ startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
+ }
+ catch (NumberFormatException ex) {
+ System.err.println("Invalid property: " + key);
+ }
+ }
+
+ StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ", true);
+ if (st.countTokens() > 0) {
+ String location = null;
+ do {
+ location = nextLocation(st);
+ if (location != null) {
+ try {
+ String[] parts = convertToMavenUrlsIfNeeded(location, convertToMavenUrls);
+ Bundle b = context.installBundle(parts[0], new URL(parts[1]).openStream());
+ sl.setBundleStartLevel(b, startLevel);
+ }
+ catch (Exception ex) {
+ System.err.println("Auto-properties install:" + ex);
+ }
+ }
+ }
+ while (location != null);
+ }
+ }
+
+ // Now loop through and start the installed bundles.
+ for (Iterator i = m_configProps.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ if (key.startsWith(PROPERTY_AUTO_START)) {
+ StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ", true);
+ if (st.countTokens() > 0) {
+ String location = null;
+ do {
+ location = nextLocation(st);
+ if (location != null) {
+ // Installing twice just returns the same bundle.
+ try {
+ String[] parts = convertToMavenUrlsIfNeeded(location, convertToMavenUrls);
+ Bundle b = context.installBundle(parts[0], new URL(parts[1]).openStream());
+ if (b != null) {
+ b.start();
+ }
+ }
+ catch (Exception ex) {
+ System.err.println("Auto-properties start: " + ex);
+ }
+ }
+ }
+ while (location != null);
+ }
+ }
+ }
+ }
+
+ private static String[] convertToMavenUrlsIfNeeded(String location, boolean convertToMavenUrls) {
+ String[] parts = location.split("\\|");
+ if (convertToMavenUrls) {
+ String[] p = parts[1].split("/");
+ if (p.length >= 4 && p[p.length-1].startsWith(p[p.length-3] + "-" + p[p.length-2])) {
+ String groupId = null;
+ String artifactId = p[p.length-3];
+ String version = p[p.length-2];
+ String classifier;
+ String type;
+ String artifactIdVersion = artifactId + "-" + version;
+ StringBuffer sb = new StringBuffer();
+ if (p[p.length-1].charAt(artifactIdVersion.length()) == '-') {
+ classifier = p[p.length-1].substring(artifactIdVersion.length() + 1, p[p.length-1].lastIndexOf('.'));
+ } else {
+ classifier = null;
+ }
+ type = p[p.length-1].substring(p[p.length-1].lastIndexOf('.') + 1);
+ sb.append("mvn:");
+ for (int j = 0; j < p.length - 3; j++) {
+ if (j > 0) {
+ sb.append('.');
+ }
+ sb.append(p[j]);
+ }
+ sb.append('/').append(artifactId).append('/').append(version);
+ if (!"jar".equals(type) || classifier != null) {
+ sb.append('/');
+ if (!"jar".equals(type)) {
+ sb.append(type);
+ }
+ if (classifier != null) {
+ sb.append('/').append(classifier);
+ }
+ }
+ parts[1] = parts[0];
+ parts[0] = sb.toString();
+ } else {
+ parts[1] = parts[0];
+ }
+ } else {
+ parts[1] = parts[0];
+ }
+ return parts;
+ }
+
+ private static String nextLocation(StringTokenizer st) {
+ String retVal = null;
+
+ if (st.countTokens() > 0) {
+ String tokenList = "\" ";
+ StringBuffer tokBuf = new StringBuffer(10);
+ String tok = null;
+ boolean inQuote = false;
+ boolean tokStarted = false;
+ boolean exit = false;
+ while ((st.hasMoreTokens()) && (!exit)) {
+ tok = st.nextToken(tokenList);
+ if (tok.equals("\"")) {
+ inQuote = !inQuote;
+ if (inQuote) {
+ tokenList = "\"";
+ } else {
+ tokenList = "\" ";
+ }
+
+ } else if (tok.equals(" ")) {
+ if (tokStarted) {
+ retVal = tokBuf.toString();
+ tokStarted = false;
+ tokBuf = new StringBuffer(10);
+ exit = true;
+ }
+ } else {
+ tokStarted = true;
+ tokBuf.append(tok.trim());
+ }
+ }
+
+ // Handle case where end of token stream and
+ // still got data
+ if ((!exit) && (tokStarted)) {
+ retVal = tokBuf.toString();
+ }
+ }
+
+ return retVal;
+ }
+
+ /**
+ * <p>
+ * Loads the properties in the system property file associated with the
+ * framework installation into <tt>System.setProperty()</tt>. These properties
+ * are not directly used by the framework in anyway. By default, the system
+ * property file is located in the <tt>conf/</tt> directory of the Felix
+ * installation directory and is called "<tt>system.properties</tt>". The
+ * installation directory of Felix is assumed to be the parent directory of
+ * the <tt>felix.jar</tt> file as found on the system class path property.
+ * The precise file from which to load system properties can be set by
+ * initializing the "<tt>felix.system.properties</tt>" system property to an
+ * arbitrary URL.
+ * </p>
+ */
+ private void loadSystemProperties() {
+ // The system properties file is either specified by a system
+ // property or it is in the same directory as the Felix JAR file.
+ // Try to load it from one of these places.
+
+ // See if the property URL was specified as a property.
+ URL propURL = null;
+ try {
+ File file = new File(new File(servicemixBase, "etc"), SYSTEM_PROPERTIES_FILE_NAME);
+ propURL = file.toURL();
+ }
+ catch (MalformedURLException ex) {
+ System.err.print("Main: " + ex);
+ return;
+ }
+
+ // Read the properties file.
+ Properties props = new Properties();
+ InputStream is = null;
+ try {
+ is = propURL.openConnection().getInputStream();
+ props.load(is);
+ is.close();
+ }
+ catch (FileNotFoundException ex) {
+ // Ignore file not found.
+ }
+ catch (Exception ex) {
+ System.err.println(
+ "Main: Error loading system properties from " + propURL);
+ System.err.println("Main: " + ex);
+ try {
+ if (is != null) is.close();
+ }
+ catch (IOException ex2) {
+ // Nothing we can do.
+ }
+ return;
+ }
+
+ // Perform variable substitution on specified properties.
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ System.setProperty(name,
+ substVars(props.getProperty(name), name, null, null));
+ }
+ }
+
+ /**
+ * <p>
+ * Loads the configuration properties in the configuration property file
+ * associated with the framework installation; these properties
+ * are accessible to the framework and to bundles and are intended
+ * for configuration purposes. By default, the configuration property
+ * file is located in the <tt>conf/</tt> directory of the Felix
+ * installation directory and is called "<tt>config.properties</tt>".
+ * The installation directory of Felix is assumed to be the parent
+ * directory of the <tt>felix.jar</tt> file as found on the system class
+ * path property. The precise file from which to load configuration
+ * properties can be set by initializing the "<tt>felix.config.properties</tt>"
+ * system property to an arbitrary URL.
+ * </p>
+ *
+ * @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an error.
+ * @throws Exception
+ */
+ private Properties loadConfigProperties() throws Exception {
+ // The config properties file is either specified by a system
+ // property or it is in the conf/ directory of the Felix
+ // installation directory. Try to load it from one of these
+ // places.
+
+ ArrayList<File> bundleDirs = new ArrayList<File>();
+
+ // See if the property URL was specified as a property.
+ URL configPropURL = null;
+ URL startupPropURL = null;
+
+ try {
+ File file = new File(new File(servicemixBase, "etc"), CONFIG_PROPERTIES_FILE_NAME);
+ configPropURL = file.toURL();
+
+ file = new File(new File(servicemixBase, "etc"), STARTUP_PROPERTIES_FILE_NAME);
+ startupPropURL = file.toURL();
+
+ if (servicemixBase.equals(servicemixHome)) {
+ bundleDirs.add(new File(servicemixHome, "system"));
+ } else {
+ bundleDirs.add(new File(servicemixBase, "system"));
+ bundleDirs.add(new File(servicemixHome, "system"));
+ }
+
+ }
+ catch (MalformedURLException ex) {
+ System.err.print("Main: " + ex);
+ return null;
+ }
+
+
+ Properties configProps = loadPropertiesFile(configPropURL);
+ Properties startupProps = loadPropertiesFile(startupPropURL);
+
+ String locations = configProps.getProperty(BUNDLE_LOCATIONS);
+
+ if (locations != null) {
+ StringTokenizer st = new StringTokenizer(locations, "\" ", true);
+ if (st.countTokens() > 0) {
+ String location = null;
+ do {
+ location = nextLocation(st);
+ if (location != null) {
+ File f = new File(location);
+ if (f.exists() && f.isDirectory()) {
+ bundleDirs.add(f);
+ } else {
+ System.err.println("Bundle location " + location
+ + " does not exist or is not a directory.");
+ }
+ }
+ }
+
+ while (location != null);
+ }
+ }
+
+ // Perform variable substitution for system properties.
+ for (Enumeration e = configProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ configProps.setProperty(name,
+ substVars(configProps.getProperty(name), name, null, configProps));
+ }
+
+ // Mutate properties
+ Main.processConfigurationProperties(configProps, startupProps, bundleDirs);
+
+ return configProps;
+ }
+
+ private static Properties loadPropertiesFile(URL configPropURL) throws Exception {
+ // Read the properties file.
+ Properties configProps = new Properties();
+ InputStream is = null;
+ try {
+ is = configPropURL.openConnection().getInputStream();
+ configProps.load(is);
+ is.close();
+ }
+ catch (FileNotFoundException ex) {
+ if (configPropURL.getFile().lastIndexOf(STARTUP_PROPERTIES_FILE_NAME) != -1) {
+ throw ex;
+ }
+ }
+ catch (Exception ex) {
+ System.err.println(
+ "Error loading config properties from " + configPropURL);
+ System.err.println("Main: " + ex);
+ try {
+ if (is != null) is.close();
+ }
+ catch (IOException ex2) {
+ // Nothing we can do.
+ }
+ return null;
+ }
+ return configProps;
+ }
+
+ private static void copySystemProperties(Properties configProps) {
+ for (Enumeration e = System.getProperties().propertyNames();
+ e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ if (key.startsWith("felix.") ||
+ key.startsWith("servicemix.") ||
+ key.equals("org.osgi.framework.system.packages") ||
+ key.equals("org.osgi.framework.bootdelegation")) {
+ configProps.setProperty(key, System.getProperty(key));
+ }
+ }
+ }
+
+ /**
+ * Process properties to customize default felix behavior
+ *
+ * @param startupProps
+ */
+ private static void processConfigurationProperties(Properties props, Properties startupProps, ArrayList<File> bundleDirs) {
+ if (bundleDirs == null) {
+ return;
+ }
+ if ("all".equals(props.getProperty(PROPERTY_AUTO_START, "").trim())) {
+ props.remove(PROPERTY_AUTO_START);
+ ArrayList<File> jars = new ArrayList<File>();
+
+ // We should start all the bundles in the system dir.
+ for (File bundleDir : bundleDirs) {
+ findJars(bundleDir, jars);
+ }
+
+ StringBuffer sb = new StringBuffer();
+
+ for (File jar : jars) {
+ try {
+ sb.append("\"").append(jar.toURL().toString()).append("\" ");
+ } catch (MalformedURLException e) {
+ System.err.print("Ignoring " + jar.toString() + " (" + e + ")");
+ }
+ }
+
+ props.setProperty(PROPERTY_AUTO_START, sb.toString());
+
+ } else if (STARTUP_PROPERTIES_FILE_NAME.equals(props.getProperty(PROPERTY_AUTO_START, "").trim())) {
+ props.remove(PROPERTY_AUTO_START);
+ // We should start the bundles in the startup.properties file.
+ HashMap<Integer, StringBuffer> levels = new HashMap<Integer, StringBuffer>();
+ for (Iterator iterator = startupProps.keySet().iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ File file = findFile(bundleDirs, name);
+
+ if (file != null) {
+ Integer level;
+ try {
+ level = new Integer(startupProps.getProperty(name).trim());
+ } catch (NumberFormatException e1) {
+ System.err.print("Ignoring " + file.toString() + " (run level must be an integer)");
+ continue;
+ }
+ StringBuffer sb = levels.get(level);
+ if (sb == null) {
+ sb = new StringBuffer(256);
+ levels.put(level, sb);
+ }
+ try {
+ sb.append("\"").append(file.toURL().toString()).append("|").append(name).append("\" ");
+ } catch (MalformedURLException e) {
+ System.err.print("Ignoring " + file.toString() + " (" + e + ")");
+ }
+ } else {
+ System.err.println("Bundle listed in " + STARTUP_PROPERTIES_FILE_NAME + " configuration not found: " + name);
+ }
+ }
+
+ for (Map.Entry<Integer, StringBuffer> entry : levels.entrySet()) {
+ props.setProperty(PROPERTY_AUTO_START + "." + entry.getKey(), entry.getValue().toString());
+ }
+ }
+
+ }
+
+ private static File findFile(ArrayList<File> bundleDirs, String name) {
+ for (File bundleDir : bundleDirs) {
+ File file = findFile(bundleDir, name);
+ if (file != null) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+ private static File findFile(File dir, String name) {
+ File theFile = new File(dir, name);
+
+ if (theFile.exists() && !theFile.isDirectory()) {
+ return theFile;
+ }
+
+ for (File file : dir.listFiles()) {
+ if (file.isDirectory()) {
+ return findFile(file, name);
+ }
+ }
+
+ return null;
+ }
+
+ private static void findJars(File dir, ArrayList<File> jars) {
+ for (File file : dir.listFiles()) {
+ if (file.isDirectory()) {
+ findJars(file, jars);
+ } else {
+ if (file.toString().endsWith(".jar")) {
+ jars.add(file);
+ }
+ }
+ }
+ }
+
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+ * refers to either a configuration property or a system property,
+ * then the corresponding property value is substituted for the variable
+ * placeholder. Multiple variable placeholders may exist in the
+ * specified value as well as nested variable placeholders, which
+ * are substituted from inner most to outer most. Configuration
+ * properties override system properties.
+ * </p>
+ *
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to
+ * detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * property placeholder syntax or a recursive variable reference.
+ */
+ public static String substVars(String val, String currentKey,
+ Map<String, String> cycleMap, Properties configProps)
+ throws IllegalArgumentException {
+ // If there is currently no cycle map, then create
+ // one for detecting cycles for this invocation.
+ if (cycleMap == null) {
+ cycleMap = new HashMap<String, String>();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0) {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim)) {
+ break;
+ } else if (idx < stopDelim) {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) && (stopDelim < 0)) {
+ return val;
+ }
+ // At this point, we found a stop delimiter without a start,
+ // so throw an exception.
+ else if (((startDelim < 0) || (startDelim > stopDelim))
+ && (stopDelim >= 0)) {
+ throw new IllegalArgumentException(
+ "stop delimiter with no start delimiter: "
+ + val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable =
+ val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null) {
+ throw new IllegalArgumentException(
+ "recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (configProps != null)
+ ? configProps.getProperty(variable, null)
+ : null;
+ if (substValue == null) {
+ // Ignore unknown property values.
+ substValue = System.getProperty(variable, "");
+ }
+
+ // Remove the found variable from the cycle map, since
+ // it may appear more than once in the value and we don't
+ // want such situations to appear as a recursive reference.
+ cycleMap.remove(variable);
+
+ // Append the leading characters, the substituted value of
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim)
+ + substValue
+ + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps);
+
+ // Return the value.
+ return val;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.servicemix.main.MainService#getArgs()
+ */
+ public String[] getArgs() {
+ return args;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public void setExitCode(int exitCode) {
+ this.exitCode = exitCode;
+ }
+
+ public File getServicemixHome() {
+ return servicemixHome;
+ }
+
+ public File getServicemixBase() {
+ return servicemixBase;
+ }
+
+ public void lock(Properties props) {
+ try {
+ if (Boolean.parseBoolean(props.getProperty(PROPERTY_USE_LOCK, "true"))) {
+ String clz = props.getProperty(PROPERTY_LOCK_CLASS, PROPERTY_LOCK_CLASS_DEFAULT);
+ lock = (Lock) Class.forName(clz).getConstructor(Properties.class).newInstance(props);
+ boolean lockLogged = false;
+ for (;;) {
+ if (lock.lock()) {
+ if (lockLogged) {
+ System.out.println("Lock acquired.");
+ }
+ setStartLevel(defaultStartLevel);
+ for (;;) {
+ if (!lock.isAlive()) {
+ break;
+ }
+ Thread.sleep(lockDelay);
+ }
+ if (m_felix.getState() == Bundle.ACTIVE && !exiting) {
+ System.out.println("Lost the lock, stopping this instance ...");
+ setStartLevel(lockStartLevel);
+ }
+ break;
+ } else if (!lockLogged) {
+ System.out.println("Waiting for the lock ...");
+ lockLogged = true;
+ }
+ Thread.sleep(lockDelay);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void unlock() throws Exception {
+ if (lock != null) {
+ lock.release();
+ }
+ }
+
+ protected void setStartLevel(int level) throws Exception {
+ BundleContext ctx = m_felix.getBundleContext();
+ ServiceReference[] refs = ctx.getServiceReferences(StartLevel.class.getName(), null);
+ StartLevel sl = (StartLevel) ctx.getService(refs[0]);
+ sl.setStartLevel(level);
+ }
+
+}
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/SimpleFileLock.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/SimpleFileLock.java
new file mode 100644
index 0000000..c9791fd
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/SimpleFileLock.java
@@ -0,0 +1,108 @@
+/*
+ * 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.servicemix.kernel.main;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileLock;
+import java.util.Properties;
+
+public class SimpleFileLock implements Lock {
+
+ private static final String PROPERTY_LOCK_DIR = "servicemix.lock.dir";
+ private static final String PROP_SERVICEMIX_BASE = "servicemix.base";
+ private RandomAccessFile lockFile;
+ private FileLock lock;
+
+ public SimpleFileLock(Properties props) {
+ try {
+ String lock = props.getProperty(PROPERTY_LOCK_DIR);
+
+ if (lock != null) {
+ File servicemixLock = getServiceMixLock(new File(lock), props);
+ props.setProperty(PROPERTY_LOCK_DIR, servicemixLock.getPath());
+ } else {
+ props.setProperty(PROPERTY_LOCK_DIR, System.getProperty(PROP_SERVICEMIX_BASE));
+ }
+
+ File base = new File(props.getProperty(PROPERTY_LOCK_DIR));
+ lockFile = new RandomAccessFile(new File(base, "lock"), "rw");
+ } catch (IOException e) {
+ throw new RuntimeException("Could not create file lock", e);
+ }
+ }
+
+ public boolean lock() throws Exception {
+ if (lock == null) {
+ lock = lockFile.getChannel().tryLock();
+ }
+ return lock != null;
+ }
+
+ public void release() throws Exception {
+ if (lock != null && lock.isValid()) {
+ lock.release();
+ lock.channel().close();
+ }
+ lock = null;
+ }
+
+ public boolean isAlive() throws Exception {
+ return lock != null;
+ }
+
+ private static File getServiceMixLock(File lock,Properties props) {
+ File rc = null;
+
+ String path = lock.getPath();
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + PROPERTY_LOCK_DIR + " system property");
+ }
+
+ if (rc == null) {
+ path = props.getProperty(PROP_SERVICEMIX_BASE);
+ if (path != null) {
+ rc = validateDirectoryExists(path, "Invalid " + PROP_SERVICEMIX_BASE + " property");
+ }
+ }
+
+ if (rc == null) {
+ rc = lock;
+ }
+ return rc;
+ }
+
+ private static File validateDirectoryExists(String path, String errPrefix) {
+ File rc;
+ try {
+ rc = new File(path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : " + e.getMessage());
+ }
+ if (!rc.exists()) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : does not exist");
+ }
+ if (!rc.isDirectory()) {
+ throw new IllegalArgumentException(errPrefix + " '" + path + "' : is not a directory");
+ }
+ return rc;
+ }
+
+}
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Statements.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Statements.java
new file mode 100644
index 0000000..0681792
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/Statements.java
@@ -0,0 +1,110 @@
+/*
+ * 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.servicemix.kernel.main;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Statements {
+
+ private String lockTableName = "SERVICEMIX_LOCK";
+ private String clusterName = "smx4";
+ private String lockCreateStatement;
+ private String lockPopulateStatement;
+ private String lockUpdateStatement;
+
+ public Statements(String tableName, String clusterName) {
+ this.lockTableName = tableName;
+ this.clusterName = clusterName;
+ this.lockCreateStatement="create table " + lockTableName + " (TIME bigint, CLUSTER varchar(20))";
+ this.lockPopulateStatement="insert into " + lockTableName + " (TIME, CLUSTER) values (1, '" + clusterName + "')";
+ }
+
+ public String testLockTableStatus() {
+ String test = "SELECT * FROM " + lockTableName + " FOR UPDATE";
+ return test;
+ }
+
+ public String getLockUpdateStatement(long timeStamp) {
+ lockUpdateStatement = "UPDATE " + lockTableName +
+ " SET TIME=" + timeStamp +
+ " WHERE CLUSTER = '" + clusterName + "'";
+ return lockUpdateStatement;
+ }
+
+ /**
+ * init - initialize db
+ */
+ public void init (Connection lockConnection) {
+ Statement s = null;
+ try {
+ // Check to see if the table already exists. If it does, then don't
+ // log warnings during startup.
+ // Need to run the scripts anyways since they may contain ALTER
+ // statements that upgrade a previous version
+ // of the table
+ boolean alreadyExists = false;
+ ResultSet rs = null;
+ try {
+ rs = lockConnection.getMetaData().getTables(null, null, lockTableName, new String[] {"TABLE"});
+ alreadyExists = rs.next();
+ } catch (Throwable ignore) {
+ System.err.println(ignore);
+ } finally {
+ close(rs);
+ }
+ if (alreadyExists) {
+ return;
+ }
+ s = lockConnection.createStatement();
+ String[] createStatments = {lockCreateStatement, lockPopulateStatement};
+ for (int i = 0; i < createStatments.length; i++) {
+ // This will fail usually since the tables will be
+ // created already.
+ try {
+ s.execute(createStatments[i]);
+ } catch (SQLException e) {
+ System.err.println("Could not create JDBC tables; they could already exist."
+ + " Failure was: " + createStatments[i] + " Message: " + e.getMessage()
+ + " SQLState: " + e.getSQLState() + " Vendor code: " + e.getErrorCode());
+ }
+ }
+ lockConnection.commit();
+ } catch (Exception ignore) {
+ System.err.println(ignore);
+ } finally {
+ try {
+ s.close();
+ } catch (Throwable e) {
+ // ignore
+ }
+ }
+ }
+
+ private static void close(ResultSet rs) {
+ try {
+ rs.close();
+ } catch (Throwable e) {
+ // ignore
+ }
+ }
+
+}
diff --git a/karaf/main/src/main/java/org/apache/servicemix/kernel/main/spi/MainService.java b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/spi/MainService.java
new file mode 100644
index 0000000..ab6a24b
--- /dev/null
+++ b/karaf/main/src/main/java/org/apache/servicemix/kernel/main/spi/MainService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.main.spi;
+
+public interface MainService {
+
+ public abstract String[] getArgs();
+ public int getExitCode();
+ public void setExitCode(int exitCode);
+
+}
\ No newline at end of file
diff --git a/karaf/main/src/main/resources/config.properties b/karaf/main/src/main/resources/config.properties
new file mode 100644
index 0000000..100d484
--- /dev/null
+++ b/karaf/main/src/main/resources/config.properties
@@ -0,0 +1,528 @@
+# 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.
+
+#
+# Framework config properties.
+#
+org.osgi.framework.system.packages=org.osgi.framework; version=1.4.0, \
+ org.osgi.service.packageadmin; version=1.2.0, \
+ org.osgi.service.startlevel; version=1.1.0, \
+ org.osgi.service.url; version=1.0.0, \
+ org.osgi.util.tracker; version=1.3.2 \
+ ${jre-${java.specification.version}}
+
+#org.osgi.framework.bootdelegation=sun.*,com.sun.*
+#felix.cache.profile=foo
+felix.auto.start.1= \
+ file:bundle/org.apache.felix.shell-1.0.0.jar \
+ file:bundle/org.apache.felix.shell.tui-1.0.0.jar \
+ file:bundle/org.apache.felix.bundlerepository-1.0.0.jar
+felix.log.level=4
+felix.startlevel.framework=1
+felix.startlevel.bundle=1
+#framework.service.urlhandlers=false
+
+#
+# Bundle config properties.
+#
+org.osgi.service.http.port=8080
+osgi.shell.telnet=on
+#obr.repository.url=http://bundles.osgi.org/obr/browse?_xml=1&cmd=repository
+
+#list of directories containing bundles to be loaded by SMX
+#bundle.locations=
+
+#
+# Java platform package export properties.
+#
+jre-1.3=, \
+ javax.accessibility; \
+ javax.accessibility.resources; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.basic.resources; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.metal.resources; \
+ javax.swing.plaf.multi; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ javax.transaction; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ version="1.3.0"
+
+jre-1.4=, \
+ javax.accessibility; \
+ javax.imageio; \
+ javax.imageio.event; \
+ javax.imageio.metadata; \
+ javax.imageio.plugins.jpeg; \
+ javax.imageio.spi; \
+ javax.imageio.stream; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.net; \
+ javax.net.ssl; \
+ javax.print; \
+ javax.print.attribute; \
+ javax.print.attribute.standard; \
+ javax.print.event; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.security.auth; \
+ javax.security.auth.callback; \
+ javax.security.auth.kerberos; \
+ javax.security.auth.login; \
+ javax.security.auth.spi; \
+ javax.security.auth.x500; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.sql; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.multi; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ javax.transaction; \
+ javax.transaction.xa; \
+ javax.xml.parsers; \
+ javax.xml.transform; \
+ javax.xml.transform.dom; \
+ javax.xml.transform.sax; \
+ javax.xml.transform.stream; \
+ org.ietf.jgss; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextExtPackage; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.Dynamic; \
+ org.omg.DynamicAny; \
+ org.omg.DynamicAny.DynAnyFactoryPackage; \
+ org.omg.DynamicAny.DynAnyPackage; \
+ org.omg.IOP; \
+ org.omg.IOP.CodecFactoryPackage; \
+ org.omg.IOP.CodecPackage; \
+ org.omg.Messaging; \
+ org.omg.PortableInterceptor; \
+ org.omg.PortableInterceptor.ORBInitInfoPackage; \
+ org.omg.PortableServer; \
+ org.omg.PortableServer.CurrentPackage; \
+ org.omg.PortableServer.POAManagerPackage; \
+ org.omg.PortableServer.POAPackage; \
+ org.omg.PortableServer.portable; \
+ org.omg.PortableServer.ServantLocatorPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ org.w3c.dom; \
+ org.w3c.dom.css; \
+ org.w3c.dom.events; \
+ org.w3c.dom.html; \
+ org.w3c.dom.stylesheets; \
+ org.w3c.dom.traversal; \
+ org.w3c.dom.views; \
+ org.xml.sax; \
+ org.xml.sax.ext; \
+ org.xml.sax.helpers; \
+ version="1.4.0"
+
+jre-1.5=, \
+ javax.accessibility; \
+ javax.activity; \
+ javax.imageio; \
+ javax.imageio.event; \
+ javax.imageio.metadata; \
+ javax.imageio.plugins.bmp; \
+ javax.imageio.plugins.jpeg; \
+ javax.imageio.spi; \
+ javax.imageio.stream; \
+ javax.management; \
+ javax.management.loading; \
+ javax.management.modelmbean; \
+ javax.management.monitor; \
+ javax.management.openmbean; \
+ javax.management.relation; \
+ javax.management.remote; \
+ javax.management.remote.rmi; \
+ javax.management.timer; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.net; \
+ javax.net.ssl; \
+ javax.print; \
+ javax.print.attribute; \
+ javax.print.attribute.standard; \
+ javax.print.event; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.rmi.ssl; \
+ javax.security.auth; \
+ javax.security.auth.callback; \
+ javax.security.auth.kerberos; \
+ javax.security.auth.login; \
+ javax.security.auth.spi; \
+ javax.security.auth.x500; \
+ javax.security.sasl; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.sql; \
+ javax.sql.rowset; \
+ javax.sql.rowset.serial; \
+ javax.sql.rowset.spi; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.multi; \
+ javax.swing.plaf.synth; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ javax.transaction; \
+ javax.transaction.xa; \
+ javax.xml; \
+ javax.xml.datatype; \
+ javax.xml.namespace; \
+ javax.xml.parsers; \
+ javax.xml.transform; \
+ javax.xml.transform.dom; \
+ javax.xml.transform.sax; \
+ javax.xml.transform.stream; \
+ javax.xml.validation; \
+ javax.xml.xpath; \
+ org.ietf.jgss; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextExtPackage; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.Dynamic; \
+ org.omg.DynamicAny; \
+ org.omg.DynamicAny.DynAnyFactoryPackage; \
+ org.omg.DynamicAny.DynAnyPackage; \
+ org.omg.IOP; \
+ org.omg.IOP.CodecFactoryPackage; \
+ org.omg.IOP.CodecPackage; \
+ org.omg.Messaging; \
+ org.omg.PortableInterceptor; \
+ org.omg.PortableInterceptor.ORBInitInfoPackage; \
+ org.omg.PortableServer; \
+ org.omg.PortableServer.CurrentPackage; \
+ org.omg.PortableServer.POAManagerPackage; \
+ org.omg.PortableServer.POAPackage; \
+ org.omg.PortableServer.portable; \
+ org.omg.PortableServer.ServantLocatorPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ org.omg.stub.javax.management.remote.rmi; \
+ org.w3c.dom; \
+ org.w3c.dom.bootstrap; \
+ org.w3c.dom.css; \
+ org.w3c.dom.events; \
+ org.w3c.dom.html; \
+ org.w3c.dom.ls; \
+ org.w3c.dom.ranges; \
+ org.w3c.dom.stylesheets; \
+ org.w3c.dom.traversal; \
+ org.w3c.dom.views; \
+ org.xml.sax; \
+ org.xml.sax.ext; \
+ org.xml.sax.helpers; \
+ version="1.5.0"
+
+jre-1.6=, \
+ java.applet; \
+ java.awt; \
+ java.awt.color; \
+ java.awt.datatransfer; \
+ java.awt.dnd; \
+ java.awt.dnd.peer; \
+ java.awt.event; \
+ java.awt.font; \
+ java.awt.geom; \
+ java.awt.im; \
+ java.awt.image; \
+ java.awt.image.renderable; \
+ java.awt.im.spi; \
+ java.awt.peer; \
+ java.awt.print; \
+ java.beans; \
+ java.beans.beancontext; \
+ java.io; \
+ java.lang; \
+ java.lang.annotation; \
+ java.lang.instrument; \
+ java.lang.management; \
+ java.lang.ref; \
+ java.lang.reflect; \
+ java.math; \
+ java.net; \
+ java.nio; \
+ java.nio.channels; \
+ java.nio.channels.spi; \
+ java.nio.charset; \
+ java.nio.charset.spi; \
+ java.rmi; \
+ java.rmi.activation; \
+ java.rmi.dgc; \
+ java.rmi.registry; \
+ java.rmi.server; \
+ java.security; \
+ java.security.acl; \
+ java.security.cert; \
+ java.security.interfaces; \
+ java.security.spec; \
+ java.sql; \
+ java.text; \
+ java.text.spi; \
+ java.util; \
+ java.util.concurrent; \
+ java.util.concurrent.atomic; \
+ java.util.concurrent.locks; \
+ java.util.jar; \
+ java.util.logging; \
+ java.util.prefs; \
+ java.util.regex; \
+ java.util.spi; \
+ java.util.zip; \
+ javax.accessibility; \
+ javax.activation; \
+ javax.activity; \
+ javax.annotation; \
+ javax.annotation.processing; \
+ javax.imageio; \
+ javax.imageio.event; \
+ javax.imageio.metadata; \
+ javax.imageio.plugins.bmp; \
+ javax.imageio.plugins.jpeg; \
+ javax.imageio.spi; \
+ javax.imageio.stream; \
+ javax.jws; \
+ javax.jws.soap; \
+ javax.lang.model; \
+ javax.lang.model.element; \
+ javax.lang.model.type; \
+ javax.lang.model.util; \
+ javax.management; \
+ javax.management.loading; \
+ javax.management.modelmbean; \
+ javax.management.monitor; \
+ javax.management.openmbean; \
+ javax.management.relation; \
+ javax.management.remote; \
+ javax.management.remote.rmi; \
+ javax.management.timer; \
+ javax.naming; \
+ javax.naming.directory; \
+ javax.naming.event; \
+ javax.naming.ldap; \
+ javax.naming.spi; \
+ javax.net; \
+ javax.net.ssl; \
+ javax.print; \
+ javax.print.attribute; \
+ javax.print.attribute.standard; \
+ javax.print.event; \
+ javax.rmi; \
+ javax.rmi.CORBA; \
+ javax.rmi.ssl; \
+ javax.script; \
+ javax.security.auth; \
+ javax.security.auth.callback; \
+ javax.security.auth.kerberos; \
+ javax.security.auth.login; \
+ javax.security.auth.spi; \
+ javax.security.auth.x500; \
+ javax.security.sasl; \
+ javax.smartcardio; \
+ javax.sound.midi; \
+ javax.sound.midi.spi; \
+ javax.sound.sampled; \
+ javax.sound.sampled.spi; \
+ javax.sql; \
+ javax.sql.rowset; \
+ javax.sql.rowset.serial; \
+ javax.sql.rowset.spi; \
+ javax.swing; \
+ javax.swing.border; \
+ javax.swing.colorchooser; \
+ javax.swing.event; \
+ javax.swing.filechooser; \
+ javax.swing.plaf; \
+ javax.swing.plaf.basic; \
+ javax.swing.plaf.metal; \
+ javax.swing.plaf.multi; \
+ javax.swing.plaf.synth; \
+ javax.swing.table; \
+ javax.swing.text; \
+ javax.swing.text.html; \
+ javax.swing.text.html.parser; \
+ javax.swing.text.rtf; \
+ javax.swing.tree; \
+ javax.swing.undo; \
+ javax.tools; \
+ javax.transaction; \
+ javax.transaction.xa; \
+ javax.xml; \
+ javax.xml.bind; \
+ javax.xml.bind.annotation; \
+ javax.xml.bind.annotation.adapters; \
+ javax.xml.bind.attachment; \
+ javax.xml.bind.helpers; \
+ javax.xml.bind.util; \
+ javax.xml.crypto; \
+ javax.xml.crypto.dom; \
+ javax.xml.crypto.dsig; \
+ javax.xml.crypto.dsig.dom; \
+ javax.xml.crypto.dsig.keyinfo; \
+ javax.xml.crypto.dsig.spec; \
+ javax.xml.datatype; \
+ javax.xml.namespace; \
+ javax.xml.parsers; \
+ javax.xml.soap; \
+ javax.xml.stream; \
+ javax.xml.stream.events; \
+ javax.xml.stream.util; \
+ javax.xml.transform; \
+ javax.xml.transform.dom; \
+ javax.xml.transform.sax; \
+ javax.xml.transform.stax; \
+ javax.xml.transform.stream; \
+ javax.xml.validation; \
+ javax.xml.ws; \
+ javax.xml.ws.handler; \
+ javax.xml.ws.handler.soap; \
+ javax.xml.ws.http; \
+ javax.xml.ws.soap; \
+ javax.xml.ws.spi; \
+ javax.xml.xpath; \
+ org.ietf.jgss; \
+ org.jcp.xml.dsig.internal; \
+ org.jcp.xml.dsig.internal.dom; \
+ org.omg.CORBA; \
+ org.omg.CORBA_2_3; \
+ org.omg.CORBA_2_3.portable; \
+ org.omg.CORBA.DynAnyPackage; \
+ org.omg.CORBA.ORBPackage; \
+ org.omg.CORBA.portable; \
+ org.omg.CORBA.TypeCodePackage; \
+ org.omg.CosNaming; \
+ org.omg.CosNaming.NamingContextExtPackage; \
+ org.omg.CosNaming.NamingContextPackage; \
+ org.omg.Dynamic; \
+ org.omg.DynamicAny; \
+ org.omg.DynamicAny.DynAnyFactoryPackage; \
+ org.omg.DynamicAny.DynAnyPackage; \
+ org.omg.IOP; \
+ org.omg.IOP.CodecFactoryPackage; \
+ org.omg.IOP.CodecPackage; \
+ org.omg.Messaging; \
+ org.omg.PortableInterceptor; \
+ org.omg.PortableInterceptor.ORBInitInfoPackage; \
+ org.omg.PortableServer; \
+ org.omg.PortableServer.CurrentPackage; \
+ org.omg.PortableServer.POAManagerPackage; \
+ org.omg.PortableServer.POAPackage; \
+ org.omg.PortableServer.portable; \
+ org.omg.PortableServer.ServantLocatorPackage; \
+ org.omg.SendingContext; \
+ org.omg.stub.java.rmi; \
+ org.omg.stub.javax.management.remote.rmi; \
+ org.w3c.dom; \
+ org.w3c.dom.bootstrap; \
+ org.w3c.dom.css; \
+ org.w3c.dom.events; \
+ org.w3c.dom.html; \
+ org.w3c.dom.ls; \
+ org.w3c.dom.ranges; \
+ org.w3c.dom.stylesheets; \
+ org.w3c.dom.traversal; \
+ org.w3c.dom.views; \
+ org.w3c.dom.xpath; \
+ org.xml.sax; \
+ org.xml.sax.ext; \
+ org.xml.sax.helpers; \
+ version=\"1.6.0\"
diff --git a/karaf/management/pom.xml b/karaf/management/pom.xml
new file mode 100644
index 0000000..ed0112b
--- /dev/null
+++ b/karaf/management/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.management</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Management</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Import-Package>
+ javax.management,
+ org.springframework.jmx.support,
+ *
+ </Import-Package>
+ <Export-Package>org.apache.servicemix.management*</Export-Package>
+ <Spring-Context>*;publish-context:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+
+</project>
diff --git a/karaf/management/src/main/java/org/apache/servicemix/management/JaasAuthenticator.java b/karaf/management/src/main/java/org/apache/servicemix/management/JaasAuthenticator.java
new file mode 100644
index 0000000..1353d3f
--- /dev/null
+++ b/karaf/management/src/main/java/org/apache/servicemix/management/JaasAuthenticator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.management;
+
+import java.io.IOException;
+
+import javax.management.remote.JMXAuthenticator;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+public class JaasAuthenticator implements JMXAuthenticator {
+
+ private String realm;
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public Subject authenticate(Object credentials) throws SecurityException {
+ if (!(credentials instanceof String[])) {
+ throw new IllegalArgumentException("Expected String[2], got "
+ + (credentials != null ? credentials.getClass().getName() : null));
+ }
+ final String[] params = (String[]) credentials;
+ if (params.length != 2) {
+ throw new IllegalArgumentException("Expected String[2] but length was " + params.length);
+ }
+ try {
+ LoginContext loginContext = new LoginContext(realm, new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (int i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] instanceof NameCallback) {
+ ((NameCallback) callbacks[i]).setName(params[0]);
+ } else if (callbacks[i] instanceof PasswordCallback) {
+ ((PasswordCallback) callbacks[i]).setPassword((params[1].toCharArray()));
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+ }
+ });
+ loginContext.login();
+ return loginContext.getSubject();
+ } catch (LoginException e) {
+ throw new SecurityException("Authentication failed", e);
+ }
+ }
+}
diff --git a/karaf/management/src/main/java/org/apache/servicemix/management/RmiRegistryFactoryBean.java b/karaf/management/src/main/java/org/apache/servicemix/management/RmiRegistryFactoryBean.java
new file mode 100644
index 0000000..1a5a995
--- /dev/null
+++ b/karaf/management/src/main/java/org/apache/servicemix/management/RmiRegistryFactoryBean.java
@@ -0,0 +1,118 @@
+/*
+ * 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.servicemix.management;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ *
+ * @author gnodet
+ */
+public class RmiRegistryFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
+
+ private int port = Registry.REGISTRY_PORT;
+ private Registry registry;
+ private boolean locate;
+ private boolean create = true;
+ private boolean locallyCreated;
+
+ /**
+ * @return the create
+ */
+ public boolean isCreate() {
+ return create;
+ }
+
+ /**
+ * @param create the create to set
+ */
+ public void setCreate(boolean create) {
+ this.create = create;
+ }
+
+ /**
+ * @return the locate
+ */
+ public boolean isLocate() {
+ return locate;
+ }
+
+ /**
+ * @param locate the locate to set
+ */
+ public void setLocate(boolean locate) {
+ this.locate = locate;
+ }
+
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * @param port the port to set
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public Object getObject() throws Exception {
+ return registry;
+ }
+
+ public Class getObjectType() {
+ return Registry.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public void afterPropertiesSet() throws RemoteException {
+ if (registry == null && locate) {
+ try {
+ Registry reg = LocateRegistry.getRegistry(getPort());
+ reg.list();
+ registry = reg;
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ if (registry == null && create) {
+ registry = LocateRegistry.createRegistry(getPort());
+ locallyCreated = true;
+ }
+ }
+
+ public void destroy() throws RemoteException {
+ if (registry != null && locallyCreated) {
+ Registry reg = registry;
+ registry = null;
+ UnicastRemoteObject.unexportObject(reg, true);
+ }
+ }
+
+}
diff --git a/karaf/management/src/main/resources/META-INF/spring/servicemix-management.xml b/karaf/management/src/main/resources/META-INF/spring/servicemix-management.xml
new file mode 100644
index 0000000..6a9bcc3
--- /dev/null
+++ b/karaf/management/src/main/resources/META-INF/spring/servicemix-management.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd">
+
+ <!-- MBeanServer bean -->
+ <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
+ <property name="locateExistingServerIfPossible" value="true"/>
+ </bean>
+
+ <!-- Export the MBeanServer as an OSGi service -->
+ <osgi:service ref="mbeanServer">
+ <osgi:interfaces>
+ <value>javax.management.MBeanServer</value>
+ </osgi:interfaces>
+ </osgi:service>
+
+ <!-- Create a RMI registry -->
+ <bean id="rmiRegistry" class="org.apache.servicemix.management.RmiRegistryFactoryBean">
+ <property name="create" value="true" />
+ <property name="locate" value="true" />
+ <property name="port" value="${rmiRegistryPort}" />
+ </bean>
+
+ <!-- Create a JMX connector ServiceFactory -->
+ <bean id="jmxConnectorService" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
+ <property name="server" ref="mbeanServer" />
+ <property name="serviceUrl" value="${serviceUrl}" />
+ <property name="daemon" value="${daemon}" />
+ <property name="threaded" value="${threaded}" />
+ <property name="objectName" value="${objectName}" />
+ <property name="environment" ref="jmxConnectorEnvironment" />
+ </bean>
+
+ <!-- Environment map for connectors -->
+ <util:map id="jmxConnectorEnvironment">
+ <entry key="jmx.remote.authenticator" value-ref="jaasAuthenticator" />
+ </util:map>
+
+ <!-- JAAS authenticator -->
+ <bean id="jaasAuthenticator" class="org.apache.servicemix.management.JaasAuthenticator">
+ <property name="realm" value="${jmxRealm}" />
+ </bean>
+
+ <!-- Property place holder -->
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.management">
+ <prop key="rmiRegistryPort">1099</prop>
+ <prop key="jmxRealm">servicemix</prop>
+ <prop key="serviceUrl">service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi</prop>
+ <prop key="daemon">true</prop>
+ <prop key="threaded">true</prop>
+ <prop key="objectName">connector:name=rmi</prop>
+ </osgix:cm-properties>
+
+ <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
+
diff --git a/karaf/management/src/test/configs/factories/management.properties b/karaf/management/src/test/configs/factories/management.properties
new file mode 100644
index 0000000..10c22db
--- /dev/null
+++ b/karaf/management/src/test/configs/factories/management.properties
@@ -0,0 +1,26 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+service.pid=org.apache.servicemix.management.JmxConnectorServiceFactory
+instances=1
+keys=objectName,serviceUrl,threaded,daemon,
+objectName.1=connector:name=rmi
+serviceUrl.1=service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
+threaded.1=true
+daemon.1=true
diff --git a/karaf/management/src/test/configs/services/org.ops4j.pax.logging.properties b/karaf/management/src/test/configs/services/org.ops4j.pax.logging.properties
new file mode 100644
index 0000000..23075bf
--- /dev/null
+++ b/karaf/management/src/test/configs/services/org.ops4j.pax.logging.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+#
+log4j.rootLogger=DEBUG, out
+
+log4j.logger.org.springframework=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=target/gshell.log
+log4j.appender.out.append=true
diff --git a/karaf/pom.xml b/karaf/pom.xml
new file mode 100644
index 0000000..31f8796
--- /dev/null
+++ b/karaf/pom.xml
@@ -0,0 +1,1098 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix</groupId>
+ <artifactId>servicemix-pom</artifactId>
+ <version>2</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel</name>
+ <inceptionYear>2007</inceptionYear>
+
+ <modules>
+ <module>main</module>
+ <module>filemonitor</module>
+ <module>gshell</module>
+ <module>spring</module>
+ <module>testing</module>
+ <module>jaas</module>
+ <module>client</module>
+ <module>management</module>
+ <module>assembly</module>
+ <module>demos</module>
+ </modules>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/servicemix/smx4/kernel/trunk</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/servicemix/smx4/kernel/trunk</developerConnection>
+ <url>http://svn.apache.org/viewvc/servicemix/smx4/kernel/trunk/</url>
+ </scm>
+
+ <issueManagement>
+ <system>jira</system>
+ <url>http://issues.apache.org/activemq/browse/SMX4KNL</url>
+ </issueManagement>
+
+ <prerequisites>
+ <maven>2.0.7</maven>
+ </prerequisites>
+
+ <properties>
+ <config.location>etc</config.location>
+ <ant.version>1.7.0_1</ant.version>
+ <aopalliance.version>1.0_1</aopalliance.version>
+ <asm.version>2.2.3_1</asm.version>
+ <cglib.version>2.1_3_2-SNAPSHOT</cglib.version>
+ <commons.codec.version>1.2_1</commons.codec.version>
+ <commons.httpclient.version>3.1_1</commons.httpclient.version>
+ <commons.io.version>1.3.2_1</commons.io.version>
+ <commons.jexl.version>1.1_1</commons.jexl.version>
+ <commons.logging.version>1.1.1</commons.logging.version>
+ <commons.vfs.version>1.0_1</commons.vfs.version>
+ <depends.maven.plugin.version>1.0</depends.maven.plugin.version>
+ <easymock.version>2.4</easymock.version>
+ <felix.configadmin.version>1.0.4</felix.configadmin.version>
+ <felix.plugin.version>1.4.3</felix.plugin.version>
+ <felix.framework.version>1.5.0-r752991</felix.framework.version>
+ <felix.osgi.version>1.2.0</felix.osgi.version>
+ <felix.compendium.version>1.2.0</felix.compendium.version>
+ <felix.bundlerepository.version>1.2.1</felix.bundlerepository.version>
+ <felix.prefs.version>1.0.2</felix.prefs.version>
+ <geronimo.annotation.version>1.1.1</geronimo.annotation.version>
+ <geronimo.servlet.version>1.1.2</geronimo.servlet.version>
+ <gshell.version>1.0-alpha-2</gshell.version>
+ <jaxp.ri.version>1.4.2_1</jaxp.ri.version>
+ <junit.version>3.8.2_1</junit.version>
+ <jline.version>0.9.94_1</jline.version>
+ <log4j.version>1.2.14</log4j.version>
+ <mina.version>2.0.0-M5</mina.version>
+ <oro.version>2.0.8_1</oro.version>
+ <pax.logging.version>1.3.0</pax.logging.version>
+ <pax.url.version>0.3.3</pax.url.version>
+ <servicemix.legal.version>1.0</servicemix.legal.version>
+ <servicemix.specs.version>1.4-SNAPSHOT</servicemix.specs.version>
+ <spring.osgi.version>1.2.0-rc1</spring.osgi.version>
+ <spring.version>2.5.6</spring.version>
+ <sshd.version>1.0-SNAPSHOT</sshd.version>
+ <woodstox.version>3.2.7_1</woodstox.version>
+ <osgi.jmx.version>1.0-r6125-patched</osgi.jmx.version>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <repositories>
+ <!-- Default repository -->
+ <repository>
+ <id>central</id>
+ <name>Default maven repo</name>
+ <url>http://repo1.maven.org/maven2</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <!-- Apache snapshots -->
+ <repository>
+ <id>apache-snapshots</id>
+ <name>Apache Snapshots Repository</name>
+ <url>http://repository.apache.org/content/groups/snapshots-group</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ <!-- ServiceMix repo -->
+ <repository>
+ <id>servicemix</id>
+ <name>Apache ServiceMix Repository</name>
+ <url>http://svn.apache.org/repos/asf/servicemix/m2-repo</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <!-- OPS4J repo -->
+ <repository>
+ <id>ops4j</id>
+ <name>OPS4j Repository</name>
+ <url>http://repository.ops4j.org/maven2</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <!-- Spring milestones repository -->
+ <!-- TODO: remove this repo when spring-dm 1.2.0 is out -->
+ <repository>
+ <id>spring-milestone</id>
+ <name>Spring Portfolio Milestone Repository</name>
+ <url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+ <pluginRepositories>
+ <!-- ServiceMix repo -->
+ <pluginRepository>
+ <id>servicemix</id>
+ <name>Apache ServiceMix Repository</name>
+ <url>http://svn.apache.org/repos/asf/servicemix/m2-repo</url>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.client</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.spring</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.management</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.demos</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.modules</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.itests</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.demos</groupId>
+ <artifactId>smx4web</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>apache-servicemix-kernel</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>apache-servicemix-kernel</artifactId>
+ <type>zip</type>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>${felix.framework.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>${felix.compendium.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.foundation</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>javax.servlet</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>${felix.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ <version>${felix.configadmin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.prefs</artifactId>
+ <version>${felix.prefs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ <version>${felix.bundlerepository.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.service.obr</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.shell</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>net.sf.kxml</groupId>
+ <artifactId>kxml2</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-io</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-test</artifactId>
+ <version>${spring.osgi.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>asm.osgi</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.junit</groupId>
+ <artifactId>com.springsource.junit</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.objectweb.asm</groupId>
+ <artifactId>com.springsource.org.objectweb.asm</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-annotation</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ <version>${mina.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.oro</artifactId>
+ <version>${oro.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ <version>${aopalliance.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+ <version>${asm.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <version>${junit.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+ <version>${cglib.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-jexl</artifactId>
+ <version>${commons.jexl.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-vfs</artifactId>
+ <version>${commons.vfs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-codec</artifactId>
+ <version>${commons.codec.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-httpclient</artifactId>
+ <version>${commons.httpclient.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>${log4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jline</artifactId>
+ <version>${jline.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>${commons.logging.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.artifact</groupId>
+ <artifactId>maven-artifact</artifactId>
+ <version>3.0-alpha-1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-io</artifactId>
+ <version>${commons.io.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ <version>${mina.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>net.gleamynode</groupId>
+ <artifactId>netty2</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jzlib</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>logkit</groupId>
+ <artifactId>logkit</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>avalon-framework</groupId>
+ <artifactId>avalon-framework</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-terminal</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-spring</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-interpolation</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-builtin</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>oro</groupId>
+ <artifactId>oro</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-vfs</groupId>
+ <artifactId>commons-vfs</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-file</artifactId>
+ <version>${gshell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-network</artifactId>
+ <version>${gshell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-shell</artifactId>
+ <version>${gshell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-ssh</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-security</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-text</artifactId>
+ <version>${gshell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.wisdom</groupId>
+ <artifactId>gshell-wisdom-core</artifactId>
+ <version>${gshell.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-ivy</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-xstore</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-jexl</groupId>
+ <artifactId>commons-jexl</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-component-api</artifactId>
+ <version>1.0-alpha-32</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ <version>1.5.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ <version>1.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-nop</artifactId>
+ <version>1.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <version>1.3</version>
+ <exclusions>
+ <exclusion>
+ <!-- xom is an optional dependency of xstream. Its also
+ LGPL, so its really not ASF compatible. -->
+ <groupId>xom</groupId>
+ <artifactId>xom</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-api</artifactId>
+ <version>${pax.logging.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ <version>${pax.logging.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-mvn</artifactId>
+ <version>${pax.url.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-wrap</artifactId>
+ <version>${pax.url.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ <version>${geronimo.servlet.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ <version>${geronimo.annotation.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+ <version>${jaxp.ri.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.woodstox</artifactId>
+ <version>${woodstox.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>${easymock.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${sshd.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>jmx</artifactId>
+ <version>${osgi.jmx.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi_R4_core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi_R4_compendium</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl104-over-slf4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.oracle.osgi</groupId>
+ <artifactId>jmx-impl</artifactId>
+ <version>${osgi.jmx.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi_R4_core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi_R4_compendium</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl104-over-slf4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-eclipse-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <outputDirectory>${basedir}/eclipse-classes</outputDirectory>
+ <downloadSources>true</downloadSources>
+ <downloadJavadocs>true</downloadJavadocs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.0-beta-7</version>
+ <configuration>
+ <useReleaseProfile>false</useReleaseProfile>
+ <preparationGoals>clean verify install</preparationGoals>
+ <goals>deploy</goals>
+ <arguments>-Prelease,deploy</arguments>
+ <autoVersionSubmodules>true</autoVersionSubmodules>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${felix.plugin.version}</version>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0.2</version>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ <maxmem>256M</maxmem>
+ <fork>${compiler.fork}</fork>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-idea-plugin</artifactId>
+ <version>2.1</version>
+ <configuration>
+ <downloadSources>true</downloadSources>
+ <downloadJavadocs>true</downloadJavadocs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.3.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>1.2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.3</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <version>${depends.maven.plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.0.4</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-beta-3</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.0</version>
+ <executions>
+ <execution>
+ <id>copy-legal</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.servicemix.legal</groupId>
+ <artifactId>legal</artifactId>
+ <version>${servicemix.legal.version}</version>
+ <type>xml</type>
+ <outputDirectory>target/legal/</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ <stripVersion>true</stripVersion>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.legal</groupId>
+ <artifactId>legal</artifactId>
+ <version>${servicemix.legal.version}</version>
+ <type>xml</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <resourceBundles>
+ <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
+ </resourceBundles>
+ <supplementalModels>
+ <supplementalModel>target/legal/legal.xml</supplementalModel>
+ </supplementalModels>
+ <properties>
+ <addLicense>true</addLicense>
+ <addArtifact>true</addArtifact>
+ <projectName>Apache ServiceMix</projectName>
+ </properties>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.3</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>2.0</version>
+ </plugin>
+ </plugins>
+ </reporting>
+
+ <profiles>
+ <profile>
+ <id>fastinstall</id>
+ <properties>
+ <maven.test.skip>true</maven.test.skip>
+ </properties>
+ </profile>
+ <profile>
+ <id>rat</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <version>1.0-alpha-3</version>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <reportFile>${project.build.directory}/${project.build.finalName}.rat</reportFile>
+ <excludes>
+ <exclude>**/target/**/*</exclude>
+ <!-- IDEA files -->
+ <exclude>**/*.iml</exclude>
+ <exclude>**/*.ipr</exclude>
+ <exclude>**/*.iws</exclude>
+ <!-- Eclipse files -->
+ <exclude>**/.*</exclude>
+ <exclude>**/eclipse-classes/**/*</exclude>
+ <exclude>**/dependency-reduced-pom.xml</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <!-- We want to deploy the artifact to a staging location for perusal -->
+ <plugin>
+ <inherited>true</inherited>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <altDeploymentRepository>${deploy.altRepository}</altDeploymentRepository>
+ <updateReleaseInfo>true</updateReleaseInfo>
+ </configuration>
+ </plugin>
+ <!-- We want to sign the artifact, the POM, and all attached artifacts -->
+ <plugin>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.0-alpha-4</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>setup.eclipse</id>
+ <modules>
+ <module>assembly</module>
+ </modules>
+ <properties>
+ <eclipse.workspace.dir>${basedir}/../workspace</eclipse.workspace.dir>
+ </properties>
+ <build>
+ <defaultGoal>eclipse:eclipse</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-eclipse-plugin</artifactId>
+ <inherited>false</inherited>
+ <executions>
+ <execution>
+ <id>setup.eclipse.workspace</id>
+ <phase>process-test-sources</phase>
+ <goals>
+ <goal>add-maven-repo</goal>
+ </goals>
+ <configuration>
+ <workspace>${eclipse.workspace.dir}</workspace>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>deploy</id>
+ <properties>
+ <createSourcesJar>true</createSourcesJar>
+ </properties>
+ <build>
+ <defaultGoal>deploy</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.0.4</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <source>1.5</source>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/karaf/spring/pom.xml b/karaf/spring/pom.xml
new file mode 100644
index 0000000..6eb7b2b
--- /dev/null
+++ b/karaf/spring/pom.xml
@@ -0,0 +1,74 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.spring</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Spring Deployer</name>
+
+ <description>This deployer transforms a plain spring xml file to a deployable bundle</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Private-Package>org.apache.servicemix.kernel.spring</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringDeploymentListener.java b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringDeploymentListener.java
new file mode 100644
index 0000000..ce0c1e0
--- /dev/null
+++ b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringDeploymentListener.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.servicemix.kernel.spring;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.filemonitor.DeploymentListener;
+
+/**
+ * A deployment listener that listens for spring xml applications
+ * and creates bundles for these.
+ */
+public class SpringDeploymentListener implements DeploymentListener {
+
+
+ private static final Log LOGGER = LogFactory.getLog(SpringDeploymentListener.class);
+
+ private DocumentBuilderFactory dbf;
+
+ public boolean canHandle(File artifact) {
+ try {
+ if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
+ Document doc = parse(artifact);
+ String name = doc.getDocumentElement().getLocalName();
+ String uri = doc.getDocumentElement().getNamespaceURI();
+ if ("beans".equals(name) && "http://www.springframework.org/schema/beans".equals(uri)) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Unable to parse deployed file " + artifact.getAbsolutePath(), e);
+ }
+ return false;
+ }
+
+ public File handle(File artifact, File tmpDir) {
+ try {
+ File destFile = new File(tmpDir, artifact.getName() + ".jar");
+ FileOutputStream os = new FileOutputStream(destFile);
+ SpringTransformer.transform(artifact.toURL(), os);
+ os.close();
+ return destFile;
+ } catch (Exception e) {
+ LOGGER.error("Unable to build spring application bundle", e);
+ return null;
+ }
+ }
+
+ protected Document parse(File artifact) throws Exception {
+ if (dbf == null) {
+ dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ }
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ return db.parse(artifact);
+ }
+
+}
diff --git a/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringTransformer.java b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringTransformer.java
new file mode 100644
index 0000000..982fbc9
--- /dev/null
+++ b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringTransformer.java
@@ -0,0 +1,249 @@
+/**
+ *
+ * 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.servicemix.kernel.spring;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.osgi.framework.Constants;
+
+public class SpringTransformer {
+
+ static Transformer transformer;
+ static DocumentBuilderFactory dbf;
+ static TransformerFactory tf;
+
+
+ public static void transform(URL url, OutputStream os) throws Exception {
+ // Build dom document
+ Document doc = parse(url);
+ // Heuristicly retrieve name and version
+ String name = url.getPath();
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0) {
+ name = name.substring(idx + 1);
+ }
+ String[] str = extractNameVersionType(name);
+ // Create manifest
+ Manifest m = new Manifest();
+ m.getMainAttributes().putValue("Manifest-Version", "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
+ m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
+ m.getMainAttributes().putValue("Spring-Context", "*;publish-context:=false;create-asynchronously:=true");
+ String importPkgs = getImportPackages(analyze(new DOMSource(doc)));
+ if (importPkgs != null && importPkgs.length() > 0) {
+ m.getMainAttributes().putValue(Constants.IMPORT_PACKAGE, importPkgs);
+ }
+ m.getMainAttributes().putValue(Constants.DYNAMICIMPORT_PACKAGE, "*");
+ // Extract manifest entries from the DOM
+ NodeList l = doc.getElementsByTagName("manifest");
+ if (l != null) {
+ for (int i = 0; i < l.getLength(); i++) {
+ Element e = (Element) l.item(i);
+ String text = e.getTextContent();
+ Properties props = new Properties();
+ props.load(new ByteArrayInputStream(text.trim().getBytes()));
+ Enumeration en = props.propertyNames();
+ while (en.hasMoreElements()) {
+ String k = (String) en.nextElement();
+ String v = props.getProperty(k);
+ m.getMainAttributes().putValue(k, v);
+ }
+ e.getParentNode().removeChild(e);
+ }
+ }
+
+ JarOutputStream out = new JarOutputStream(os);
+ ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
+ out.putNextEntry(e);
+ m.write(out);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/");
+ out.putNextEntry(e);
+ e = new ZipEntry("META-INF/spring/");
+ out.putNextEntry(e);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/spring/" + name);
+ out.putNextEntry(e);
+ // Copy the new DOM
+ if (tf == null) {
+ tf = TransformerFactory.newInstance();
+ }
+ tf.newTransformer().transform(new DOMSource(doc), new StreamResult(out));
+ out.closeEntry();
+ out.close();
+ }
+
+ private static final String DEFAULT_VERSION = "0.0.0";
+
+ private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
+ private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
+
+ public static String[] extractNameVersionType(String url) {
+ Matcher m = ARTIFACT_MATCHER.matcher(url);
+ if (!m.matches()) {
+ return new String[] { url, DEFAULT_VERSION };
+ }
+ else {
+ //System.err.println(m.groupCount());
+ //for (int i = 1; i <= m.groupCount(); i++) {
+ // System.err.println("Group " + i + ": " + m.group(i));
+ //}
+
+ StringBuffer v = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(2);
+ String d3 = m.group(3);
+ String d4 = m.group(4);
+ String d5 = m.group(5);
+ String d6 = m.group(6);
+ if (d2 != null) {
+ v.append(d2);
+ if (d3 != null) {
+ v.append('.');
+ v.append(d3);
+ if (d4 != null) {
+ v.append('.');
+ v.append(d4);
+ if (d5 != null) {
+ v.append(".");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.0.");
+ cleanupModifier(v, d5);
+ }
+ }
+ return new String[] { d1, v.toString(), d6 };
+ }
+ }
+
+ private static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = FUZZY_MODIFIDER.matcher(modifier);
+ if (m.matches()) {
+ modifier = m.group(1);
+ }
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
+ result.append(c);
+ }
+ }
+ }
+
+ public static Set<String> analyze(Source source) throws Exception {
+ if (transformer == null) {
+ if (tf == null) {
+ tf = TransformerFactory.newInstance();
+ }
+ Source s = new StreamSource(SpringTransformer.class.getResourceAsStream("extract.xsl"));
+ transformer = tf.newTransformer(s);
+ }
+
+ Set<String> refers = new TreeSet<String>();
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ Result r = new StreamResult(bout);
+ transformer.transform(source, r);
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+ bout.close();
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(bin));
+
+ String line = br.readLine();
+ while (line != null) {
+ line = line.trim();
+ if (line.length() > 0) {
+ String parts[] = line.split("\\s*,\\s*");
+ for (int i = 0; i < parts.length; i++) {
+ int n = parts[i].lastIndexOf('.');
+ if (n > 0) {
+ refers.add(parts[i].substring(0, n));
+ }
+ }
+ }
+ line = br.readLine();
+ }
+ br.close();
+ return refers;
+ }
+
+ protected static String getImportPackages(Set<String> packages) {
+ StringBuilder sb = new StringBuilder();
+ for (String pkg : packages) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(pkg);
+ }
+ return sb.toString();
+ }
+
+ protected static Document parse(URL url) throws Exception {
+ if (dbf == null) {
+ dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ }
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ return db.parse(url.toString());
+ }
+
+ protected static void copyInputStream(InputStream in, OutputStream out) throws Exception {
+ byte[] buffer = new byte[4096];
+ int len = in.read(buffer);
+ while (len >= 0) {
+ out.write(buffer, 0, len);
+ len = in.read(buffer);
+ }
+ }
+}
diff --git a/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringURLHandler.java b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringURLHandler.java
new file mode 100644
index 0000000..e9fedaf
--- /dev/null
+++ b/karaf/spring/src/main/java/org/apache/servicemix/kernel/spring/SpringURLHandler.java
@@ -0,0 +1,97 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.spring;
+
+import java.io.File;
+import java.io.FileInputStream;
+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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+
+/**
+ * A URL handler that will transform a JBI artifact to an OSGi bundle
+ * on the fly. Needs to be registered in the OSGi registry.
+ */
+public class SpringURLHandler extends AbstractURLStreamHandlerService {
+
+ private static Log logger = LogFactory.getLog(SpringURLHandler.class);
+
+ private static String SYNTAX = "spring: spring-xml-uri";
+
+ private URL springXmlURL;
+
+ /**
+ * Open the connection for the given URL.
+ *
+ * @param url the url from which to open a connection.
+ * @return a connection on the specified URL.
+ * @throws IOException if an error occurs or if the URL is malformed.
+ */
+ @Override
+ public URLConnection openConnection(URL url) throws IOException {
+ if (url.getPath() == null || url.getPath().trim().length() == 0) {
+ throw new MalformedURLException ("Path can not be null or empty. Syntax: " + SYNTAX );
+ }
+ springXmlURL = new URL(url.getPath());
+
+ logger.debug("Spring xml URL is: [" + springXmlURL + "]");
+ return new Connection(url);
+ }
+
+ public URL getSpringXmlURL() {
+ return springXmlURL;
+ }
+
+ public class Connection extends URLConnection {
+
+ public Connection(URL url) {
+ super(url);
+ }
+
+ @Override
+ public void connect() throws IOException {
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ try {
+ final File f = File.createTempFile("smx", "xml");
+ FileOutputStream os = new FileOutputStream(f);
+ SpringTransformer.transform(springXmlURL, os);
+ os.close();
+ return new FileInputStream(f) {
+ public void close() throws IOException {
+ super.close();
+ f.delete();
+ }
+ };
+ } catch (Exception e) {
+ logger.error("Error opening spring xml url", e);
+ throw (IOException) new IOException("Error opening spring xml url").initCause(e);
+ }
+ }
+ }
+
+}
diff --git a/karaf/spring/src/main/resources/META-INF/spring/spring-deployer.xml b/karaf/spring/src/main/resources/META-INF/spring/spring-deployer.xml
new file mode 100644
index 0000000..779c2af
--- /dev/null
+++ b/karaf/spring/src/main/resources/META-INF/spring/spring-deployer.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+ <bean id="springDeploymentListener" class="org.apache.servicemix.kernel.spring.SpringDeploymentListener">
+
+ </bean>
+
+ <osgi:service ref="springDeploymentListener">
+ <osgi:interfaces>
+ <value>org.apache.servicemix.kernel.filemonitor.DeploymentListener</value>
+ </osgi:interfaces>
+ </osgi:service>
+
+ <bean id="springHandler" class="org.apache.servicemix.kernel.spring.SpringURLHandler" />
+
+ <osgi:service ref="springHandler" interface="org.osgi.service.url.URLStreamHandlerService">
+ <osgi:service-properties>
+ <entry key="url.handler.protocol" value="spring"/>
+ </osgi:service-properties>
+ </osgi:service>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/spring/src/main/resources/org/apache/servicemix/kernel/spring/extract.xsl b/karaf/spring/src/main/resources/org/apache/servicemix/kernel/spring/extract.xsl
new file mode 100644
index 0000000..4b06b69
--- /dev/null
+++ b/karaf/spring/src/main/resources/org/apache/servicemix/kernel/spring/extract.xsl
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:jee="http://www.springframework.org/schema/jee"
+ xmlns:jms="http://www.springframework.org/schema/jms"
+ xmlns:lang="http://www.springframework.org/schema/lang"
+ xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ xmlns:tx="http://www.springframework.org/schema/tx"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:webflow-config="http://www.springframework.org/schema/webflow-config">
+
+ <xsl:output method="text" />
+
+ <xsl:template match="/">
+
+ <!-- Match all attributes that holds a class or a comma delimited
+ list of classes and print them -->
+
+ <xsl:for-each select="
+ //beans:bean/@class
+ | //beans:*/@value-type
+ | //aop:*/@implement-interface
+ | //aop:*/@default-impl
+ | //context:load-time-weaver/@weaver-class
+ | //jee:jndi-lookup/@expected-type
+ | //jee:jndi-lookup/@proxy-interface
+ | //jee:remote-slsb/@ejbType
+ | //jee:*/@business-interface
+ | //lang:*/@script-interfaces
+ | //osgi:*/@interface
+ | //util:list/@list-class
+ | //util:set/@set-class
+ | //util:map/@map-class
+ | //webflow-config:*/@class
+ ">
+ <xsl:value-of select="." />
+ <xsl:text>
+ </xsl:text>
+ </xsl:for-each>
+
+ <!-- This seems some magic to get extra imports? -->
+
+ <xsl:for-each select="//beans:bean[@class='org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean'
+ or @class='org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean']">
+ <xsl:for-each select="beans:property[@name='interfaces']">
+ <xsl:value-of select="@value" />
+ <xsl:text>
+ </xsl:text>
+ </xsl:for-each>
+ </xsl:for-each>
+
+ </xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/karaf/spring/src/test/java/org/apache/servicemix/kernel/spring/SpringDeploymentListenerTest.java b/karaf/spring/src/test/java/org/apache/servicemix/kernel/spring/SpringDeploymentListenerTest.java
new file mode 100644
index 0000000..38a37bb
--- /dev/null
+++ b/karaf/spring/src/test/java/org/apache/servicemix/kernel/spring/SpringDeploymentListenerTest.java
@@ -0,0 +1,84 @@
+/**
+ *
+ * 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.servicemix.kernel.spring;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+
+import javax.xml.transform.dom.DOMSource;
+
+import junit.framework.TestCase;
+
+public class SpringDeploymentListenerTest extends TestCase {
+
+ public void testPackagesExtraction() throws Exception {
+ SpringDeploymentListener l = new SpringDeploymentListener();
+ File f = new File(getClass().getClassLoader().getResource("META-INF/spring/spring-deployer.xml").toURI());
+ Set<String> pkgs = SpringTransformer.analyze(new DOMSource(SpringTransformer.parse(f.toURL())));
+ assertNotNull(pkgs);
+ assertEquals(2, pkgs.size());
+ Iterator<String> it = pkgs.iterator();
+ assertEquals("org.apache.servicemix.kernel.spring", it.next());
+ assertEquals("org.osgi.service.url", it.next());
+ }
+
+ public void testCustomManifest() throws Exception {
+ File f = File.createTempFile("smx", ".jar");
+ try {
+ OutputStream os = new FileOutputStream(f);
+ SpringTransformer.transform(getClass().getClassLoader().getResource("test.xml"), os);
+ os.close();
+ InputStream is = new FileInputStream(f);
+ JarInputStream jar = new JarInputStream(is);
+ jar.getManifest().write(System.err);
+ is.close();
+ } finally {
+ f.delete();
+ }
+ }
+
+ public void testVersions() {
+ assertVersion("org.apache.servicemix.bundles.ant-1.7.0-1.0-m3-SNAPSHOT.jar",
+ "org.apache.servicemix.bundles.ant-1.7.0", "1.0.0.m3-SNAPSHOT", "jar");
+ assertVersion("org.apache.activemq.core-1.0-SNAPSHOT.xml",
+ "org.apache.activemq.core", "1.0.0.SNAPSHOT", "xml");
+ assertVersion("org.apache.activemq.core-1.0.0-SNAPSHOT.xml",
+ "org.apache.activemq.core", "1.0.0.SNAPSHOT", "xml");
+ assertVersion("org.apache.activemq.core-1.0.0.xml",
+ "org.apache.activemq.core", "1.0.0", "xml");
+ assertVersion("geronimo-servlet_2.5_spec-1.1.2.jar",
+ "geronimo-servlet_2.5_spec", "1.1.2", "jar");
+ assertVersion("spring-aop-2.5.1.jar",
+ "spring-aop", "2.5.1", "jar");
+ }
+
+ private void assertVersion(String s, String... expectedParts) {
+ String[] parts = SpringTransformer.extractNameVersionType(s);
+ assertEquals(expectedParts.length, parts.length);
+ for (int i = 0; i < expectedParts.length; i++) {
+ assertEquals(expectedParts[i], parts[i]);
+ }
+ }
+
+}
diff --git a/karaf/spring/src/test/resources/test.xml b/karaf/spring/src/test/resources/test.xml
new file mode 100644
index 0000000..7976cbb
--- /dev/null
+++ b/karaf/spring/src/test/resources/test.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+ <manifest>
+ RequireBundle=org.osgi
+ Header=value
+ </manifest>
+
+ <bean id="springDeploymentListener" class="org.apache.servicemix.kernel.spring.SpringDeploymentListener">
+
+ </bean>
+
+ <osgi:service ref="springDeploymentListener">
+ <osgi:interfaces>
+ <value>org.apache.servicemix.kernel.filemonitor.DeploymentListener</value>
+ </osgi:interfaces>
+ </osgi:service>
+
+ <bean id="springHandler" class="org.apache.servicemix.kernel.spring.SpringURLHandler" />
+
+ <osgi:service ref="springHandler" interface="org.osgi.service.url.URLStreamHandlerService">
+ <osgi:service-properties>
+ <entry key="url.handler.protocol" value="spring"/>
+ </osgi:service-properties>
+ </osgi:service>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/testing/itests/pom.xml b/karaf/testing/itests/pom.xml
new file mode 100644
index 0000000..8442902
--- /dev/null
+++ b/karaf/testing/itests/pom.xml
@@ -0,0 +1,77 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>testing</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.itests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Testing - ITests</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.woodstox</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- generate dependencies versions -->
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java b/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java
new file mode 100644
index 0000000..dec38ae
--- /dev/null
+++ b/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.servicemix.kernel.testing.itests;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.osgi.framework.Bundle;
+
+public class SimpleTest extends AbstractIntegrationTest {
+
+ static {
+ System.setProperty("jaxp.debug", "true");
+ System.setProperty("org.apache.servicemix.specs.debug", "true");
+ }
+
+ /**
+ * The manifest to use for the "virtual bundle" created
+ * out of the test classes and resources in this project
+ *
+ * This is actually the boilerplate manifest with one additional
+ * import-package added. We should provide a simpler customization
+ * point for such use cases that doesn't require duplication
+ * of the entire manifest...
+ */
+ protected String getManifestLocation() {
+ return "classpath:org/apache/servicemix/MANIFEST.MF";
+ }
+
+ /**
+ * The location of the packaged OSGi bundles to be installed
+ * for this test. Values are Spring resource paths. The bundles
+ * we want to use are part of the same multi-project maven
+ * build as this project is. Hence we use the localMavenArtifact
+ * helper method to find the bundles produced by the package
+ * phase of the maven build (these tests will run after the
+ * packaging phase, in the integration-test phase).
+ *
+ * JUnit, commons-logging, spring-core and the spring OSGi
+ * test bundle are automatically included so do not need
+ * to be specified here.
+ */
+ protected String[] getTestBundlesNames() {
+ return new String[0];
+ }
+
+ /**
+ * Do not include the jaxp-ri bundle by default, as we want to test it
+ * @return
+ */
+ @Override
+ protected String[] getTestFrameworkBundlesNames() {
+ return new String[] {
+ getBundle("org.apache.geronimo.specs", "geronimo-servlet_2.5_spec"),
+ getBundle("org.apache.servicemix.specs", "org.apache.servicemix.specs.jaxp-api-1.4"),
+ getBundle("org.apache.felix", "org.osgi.compendium"),
+ getBundle("org.apache.felix", "org.apache.felix.configadmin"),
+ getBundle("org.ops4j.pax.logging", "pax-logging-api"),
+ getBundle("org.ops4j.pax.logging", "pax-logging-service"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.aopalliance"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.asm"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.junit"),
+ getBundle("org.springframework", "spring-beans"),
+ getBundle("org.springframework", "spring-core"),
+ getBundle("org.springframework", "spring-context"),
+ getBundle("org.springframework", "spring-aop"),
+ getBundle("org.springframework", "spring-test"),
+ getBundle("org.springframework.osgi", "spring-osgi-core"),
+ getBundle("org.springframework.osgi", "spring-osgi-io"),
+ getBundle("org.springframework.osgi", "spring-osgi-extender"),
+ getBundle("org.springframework.osgi", "spring-osgi-test"),
+ getBundle("org.springframework.osgi", "spring-osgi-annotation"),
+ getBundle("org.apache.servicemix.kernel.testing", "org.apache.servicemix.kernel.testing.support"),
+ };
+ }
+
+ public void testDocumentBuilderFactory() throws Exception {
+ try {
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ fail("Implementation should not have been found");
+ } catch (Throwable t) {
+ }
+ Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+ try {
+ assertNotNull(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
+ } finally {
+ b.uninstall();
+ }
+ }
+
+ public void testTransformerFactory() throws Exception {
+ try {
+ TransformerFactory.newInstance().newTransformer();
+ fail("Implementation should not have been found");
+ } catch (Throwable t) {
+ }
+ Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+ try {
+ assertNotNull(TransformerFactory.newInstance().newTransformer());
+ } finally {
+ b.uninstall();
+ }
+ }
+
+ public void testSchemaFactory() throws Exception {
+ try {
+ SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema();
+ fail("Implementation should not have been found");
+ } catch (Throwable t) {
+ }
+ Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+ try {
+ assertNotNull(SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema());
+ } finally {
+ b.uninstall();
+ }
+ }
+
+ public void testStax() throws Exception {
+ try {
+ XMLInputFactory.newInstance();
+ fail("Implementation should not have been found");
+ } catch (Throwable t) {
+ }
+ Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+ try {
+ assertNotNull(XMLInputFactory.newInstance());
+ } finally {
+ b.uninstall();
+ }
+ }
+
+}
diff --git a/karaf/testing/itests/src/test/resources/log4j.properties b/karaf/testing/itests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7944dfb
--- /dev/null
+++ b/karaf/testing/itests/src/test/resources/log4j.properties
@@ -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.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=DEBUG, stdout
+
+log4j.logger.org.springframework=INFO
+log4j.logger.org.apache.activemq=INFO
+log4j.logger.org.apache.activemq.spring=WARN
+
+
+#log4j.logger.org.apache.servicemix=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.out.file=target/servicemix-test.log
+log4j.appender.out.append=true
diff --git a/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF b/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF
new file mode 100644
index 0000000..f26d98a
--- /dev/null
+++ b/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF
@@ -0,0 +1,32 @@
+Manifest-Version: 1.0
+License-00: .
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements. See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License. You may obtain a copy of the License at
+License-07: .
+License-08: http://www.apache.org/licenses/LICENSE-2.0
+License-09: .
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15: .
+Bundle-Name: org.apache.servicemix.testing.itests
+Bundle-SymbolicName: org.apache.servicemix.testing.itests
+Bundle-Vendor: Spring Framework
+Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
+Import-Package: junit.framework,
+ org.osgi.framework;specification-version="1.3.0",
+ org.apache.commons.logging,
+ org.springframework.core.io,
+ org.springframework.osgi.test,
+ org.apache.servicemix.kernel.testing.support,
+ org.springframework.osgi.util,
+ javax.xml.stream,
+ javax.xml.transform,
+ javax.xml.validation,
+ javax.xml.parsers
diff --git a/karaf/testing/pom.xml b/karaf/testing/pom.xml
new file mode 100644
index 0000000..9a62891
--- /dev/null
+++ b/karaf/testing/pom.xml
@@ -0,0 +1,41 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>testing</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Testing</name>
+
+ <modules>
+ <module>support</module>
+ <module>itests</module>
+ </modules>
+
+</project>
diff --git a/karaf/testing/support/pom.xml b/karaf/testing/support/pom.xml
new file mode 100644
index 0000000..7eb620a
--- /dev/null
+++ b/karaf/testing/support/pom.xml
@@ -0,0 +1,149 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>testing</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: Testing - Support</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-test</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-annotation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.woodstox</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-mvn</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/org/apache/servicemix/kernel/testing/support/</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../${config.location}</directory>
+ <includes>
+ <include>config.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Import-Package>
+ org.apache.felix*;resolution:=optional,
+ org.apache.servicemix.kernel.main;resolution:=optional,
+ org.apache.servicemix.kernel.main.spi;resolution:=optional;version="1.0.0",
+ *
+ </Import-Package>
+ <Export-Package>${pom.artifactId}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java
new file mode 100644
index 0000000..14cc73a
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.osgi.test.AbstractConfigurableBundleCreatorTests;
+import org.springframework.osgi.test.provisioning.ArtifactLocator;
+import org.springframework.osgi.util.OsgiFilterUtils;
+import org.springframework.osgi.util.OsgiListenerUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+
+public class AbstractIntegrationTest extends AbstractConfigurableBundleCreatorTests {
+
+ static {
+ try {
+ File f = new File("target/smx4");
+ f.mkdirs();
+ System.setProperty("servicemix.name", "root");
+ System.setProperty("servicemix.home", f.getAbsolutePath());
+ System.setProperty("servicemix.base", f.getAbsolutePath());
+ System.setProperty("org.apache.servicemix.filemonitor.configDir", new File(f, "etc").getAbsolutePath());
+ System.setProperty("org.apache.servicemix.filemonitor.monitorDir", new File(f, "deploy").getAbsolutePath());
+ System.setProperty("org.apache.servicemix.filemonitor.generatedJarDir", new File(f, "data/generate-bundles").getAbsolutePath());
+ System.setProperty("bundles.configuration.location", new File("src/test/conf").getAbsolutePath());
+ System.setProperty("org.osgi.vendor.framework", "org.apache.servicemix.kernel.testing.support");
+ PropertyConfigurator.configure("target/test-classes/log4j.properties");
+ } catch (Throwable t) {}
+ }
+
+ private Properties dependencies;
+ private FeatureInstaller featureInstaller;
+
+ @Override
+ protected String getPlatformName() {
+ String systemProperty = System.getProperty(OSGI_FRAMEWORK_SELECTOR);
+ if (logger.isTraceEnabled())
+ logger.trace("system property [" + OSGI_FRAMEWORK_SELECTOR + "] has value=" + systemProperty);
+
+ return (systemProperty == null ? SmxKernelPlatform.class.getName() : systemProperty);
+ }
+
+ protected String getBundle(String groupId, String artifactId) {
+ return groupId + "," + artifactId + "," + getBundleVersion(groupId, artifactId);
+ }
+
+ protected String getBundleVersion(String groupId, String artifactId) {
+ if (dependencies == null) {
+ try {
+ File f = new File(System.getProperty("basedir"), "target/classes/META-INF/maven/dependencies.properties");
+ Properties prop = new Properties();
+ prop.load(new FileInputStream(f));
+ dependencies = prop;
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to load dependencies informations", e);
+ }
+ }
+ String version = dependencies.getProperty(groupId + "/" + artifactId + "/version");
+ if (version == null) {
+ throw new IllegalStateException("Unable to find dependency information for: " + groupId + "/" + artifactId + "/version");
+ }
+ return version;
+ }
+
+ protected String[] getTestFrameworkBundlesNames() {
+ return new String[] {
+ getBundle("org.apache.geronimo.specs", "geronimo-servlet_2.5_spec"),
+ getBundle("org.apache.servicemix.specs", "org.apache.servicemix.specs.jaxp-api-1.4"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri"),
+ getBundle("org.apache.felix", "org.osgi.compendium"),
+ getBundle("org.apache.felix", "org.apache.felix.configadmin"),
+ getBundle("org.ops4j.pax.logging", "pax-logging-api"),
+ getBundle("org.ops4j.pax.logging", "pax-logging-service"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.aopalliance"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.asm"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.junit"),
+ getBundle("org.springframework", "spring-beans"),
+ getBundle("org.springframework", "spring-core"),
+ getBundle("org.springframework", "spring-context"),
+ getBundle("org.springframework", "spring-aop"),
+ getBundle("org.springframework", "spring-test"),
+ getBundle("org.springframework.osgi", "spring-osgi-core"),
+ getBundle("org.springframework.osgi", "spring-osgi-io"),
+ getBundle("org.springframework.osgi", "spring-osgi-extender"),
+ getBundle("org.springframework.osgi", "spring-osgi-test"),
+ getBundle("org.springframework.osgi", "spring-osgi-annotation"),
+ getBundle("org.ops4j.pax.url", "pax-url-mvn"),
+ getBundle("org.apache.servicemix.kernel.testing", "org.apache.servicemix.kernel.testing.support"),
+ };
+ }
+
+ protected Bundle installBundle(String groupId, String artifactId, String classifier, String type) throws Exception {
+ String version = getBundleVersion(groupId, artifactId);
+ File loc = localMavenBundle(groupId, artifactId, version, classifier, type);
+ Bundle bundle = bundleContext.installBundle(loc.toURI().toString());
+ bundle.start();
+ return bundle;
+ }
+
+ protected void addFeatureRepo(String url) throws Exception {
+ if (featureInstaller == null) {
+ featureInstaller = new FeatureInstaller();
+ featureInstaller.setBundleContext(bundleContext);
+ }
+ featureInstaller.addRepository(new URI(url));
+ }
+
+ protected void installFeature(String name) throws Exception {
+ installFeature(name, FeatureImpl.DEFAULT_VERSION);
+ }
+
+ protected void installFeature(String name, String version) throws Exception {
+ if (featureInstaller == null) {
+ featureInstaller = new FeatureInstaller();
+ featureInstaller.setBundleContext(bundleContext);
+
+ }
+ featureInstaller.installFeature(name, version);
+ }
+
+ protected Resource locateBundle(String bundleId) {
+ Assert.hasText(bundleId, "bundleId should not be empty");
+
+ // parse the String
+ String[] artifactId = StringUtils.commaDelimitedListToStringArray(bundleId);
+
+ Assert.isTrue(artifactId.length >= 3, "the CSV string " + bundleId + " contains too few values");
+ // TODO: add a smarter mechanism which can handle 1 or 2 values CSVs
+ for (int i = 0; i < artifactId.length; i++) {
+ artifactId[i] = StringUtils.trimWhitespace(artifactId[i]);
+ }
+
+ File f;
+ if (artifactId.length == 3) {
+ f = localMavenBundle(artifactId[0], artifactId[1], artifactId[2], null, ArtifactLocator.DEFAULT_ARTIFACT_TYPE);
+ } else {
+ f = localMavenBundle(artifactId[0], artifactId[1], artifactId[2], null, artifactId[3]);
+ }
+ return new FileSystemResource(f);
+ }
+
+
+ protected File localMavenBundle(String groupId, String artifact, String version, String classifier, String type) {
+ String defaultHome = new File(new File(System.getProperty("user.home")), ".m2/repository").getAbsolutePath();
+ File repositoryHome = new File(System.getProperty("localRepository", defaultHome));
+
+ StringBuffer location = new StringBuffer(groupId.replace('.', '/'));
+ location.append('/');
+ location.append(artifact);
+ location.append('/');
+ location.append(getSnapshot(version));
+ location.append('/');
+ location.append(artifact);
+ location.append('-');
+ location.append(version);
+ if (classifier != null) {
+ location.append('-');
+ location.append(classifier);
+ }
+ location.append(".");
+ location.append(type);
+
+ return new File(repositoryHome, location.toString());
+ }
+
+ protected static String getSnapshot(String version) {
+ if (isTimestamped(version)) {
+ return version.substring(0, version.lastIndexOf('-', version.lastIndexOf('-') - 1)) + "-SNAPSHOT";
+ }
+ return version;
+ }
+
+ protected static boolean isTimestamped(String version) {
+ return version.matches(".+-\\d\\d\\d\\d\\d\\d\\d\\d\\.\\d\\d\\d\\d\\d\\d-\\d+");
+ }
+
+ protected static boolean isSnapshot(String version) {
+ return version.matches(".+-SNAPSHOT");
+ }
+
+ public <T> T getOsgiService(Class<T> type) {
+ return getOsgiService(type, DEFAULT_WAIT_TIME);
+ }
+
+ public <T> T getOsgiService(Class<T> type, long timeout) {
+ // translate from seconds to miliseconds
+ long time = timeout * 1000;
+
+ // use the counter to make sure the threads block
+ final Counter counter = new Counter("waitForOsgiService on bnd=" + type.getName());
+
+ counter.increment();
+
+ final List<T> services = new ArrayList<T>();
+
+ ServiceListener listener = new ServiceListener() {
+ public void serviceChanged(ServiceEvent event) {
+ if (event.getType() == ServiceEvent.REGISTERED) {
+ services.add((T) bundleContext.getService(event.getServiceReference()));
+ counter.decrement();
+ }
+ }
+ };
+
+ String filter = OsgiFilterUtils.unifyFilter(type.getName(), null);
+ OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
+
+ if (logger.isDebugEnabled())
+ logger.debug("start waiting for OSGi service=" + type.getName());
+
+ try {
+ if (counter.waitForZero(time)) {
+ logger.warn("waiting for OSGi service=" + type.getName() + " timed out");
+ throw new RuntimeException("Gave up waiting for OSGi service '" + type.getName() + "' to be created");
+ }
+ else if (logger.isDebugEnabled()) {
+ logger.debug("found OSGi service=" + type.getName());
+ }
+ return services.get(0);
+ }
+ finally {
+ // inform waiting thread
+ bundleContext.removeServiceListener(listener);
+ }
+ }
+
+ protected void checkBundleStarted(String name) {
+ assertNotNull(bundleContext);
+ for (int i = 0; i < bundleContext.getBundles().length; i++) {
+ Bundle b = bundleContext.getBundles()[i];
+ if (b.getSymbolicName().equals(name)) {
+ assertEquals("Bundle '" + name + "' is not active", Bundle.ACTIVE, b.getState());
+ return;
+ }
+ }
+ fail("Bundle '" + name + "' not found");
+ }
+
+
+}
\ No newline at end of file
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java
new file mode 100644
index 0000000..95a3a44
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.testing.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Simple counting class which can be incremented or decremented in a
+ * synchronized manner. This class can be used as a synchronization mechanism
+ * between threads mainly though {@link #waitForZero(long)} method.
+ *
+ * The main usage of the class is to allow a master thread, to know when other
+ * threads (slaves) have passed a certain point in execution.
+ *
+ * <p/> As opposed to a Barrier or a Semaphore, this class should be used only
+ * with 1 waiting thread (a master) and any number of slave threads.
+ *
+ * <pre style="code">
+ * Thread 1:
+ * synchronized(counter) {
+ * counter.increment();
+ * thread2.start();
+ * counter.increment();
+ * thread3.start();
+ *
+ * // wait 1 second for other threads to complete
+ * counter.waitForZero(1000);
+ * }
+ *
+ * Thread 2:
+ * // do some work
+ * counter.decrement();
+ *
+ * Thread 3:
+ * // do some work
+ * counter.decrement();
+ *
+ * </pre>
+ *
+ * <p/> Mainly for usage inside the framework. All methods are thread-safe
+ * however for the master/slave pattern, synchronized blocks are recommended as
+ * multiple operations have to be executed at once.
+ *
+ * @author Costin Leau
+ *
+ */
+public class Counter {
+
+ private int counter = 0;
+
+ private static final Log log = LogFactory.getLog(Counter.class);
+
+ private final String name;
+
+ /**
+ * Create counter with a given name.
+ * @param name counter name
+ */
+ public Counter(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Increment the counter value.
+ */
+ public synchronized void increment() {
+ counter++;
+ if (log.isTraceEnabled())
+ log.trace("counter [" + name + "] incremented to " + counter);
+ }
+
+ /**
+ * Decrement the counter value.
+ */
+ public synchronized void decrement() {
+ counter--;
+ if (log.isTraceEnabled())
+ log.trace("counter [" + name + "] decremented to " + counter);
+ notifyAll();
+ }
+
+ public synchronized boolean decrementAndWait(long timeToWait) {
+ decrement();
+ if (counter > 0)
+ return waitForZero(timeToWait);
+ return true;
+ }
+
+ /**
+ * Check if the counter value is zero.
+ * @return true if value is equal or below zero, false otherwise.
+ */
+ public synchronized boolean isZero() {
+ return is(0);
+ }
+
+ public synchronized boolean is(int value) {
+ return counter == value;
+ }
+
+ /**
+ * Return the counter value.
+ *
+ * @return the counter value.
+ */
+ public synchronized int getValue() {
+ return counter;
+ }
+
+ public synchronized String toString() {
+ return "" + counter;
+ }
+
+ /**
+ * Specialized method which waits for 0. Identical to waitFor(0, waitTime).
+ *
+ * @see #waitFor(int, long)
+ * @param waitTime
+ * @return true if the waiting timed out, false otherwise
+ */
+ public synchronized boolean waitForZero(long waitTime) {
+ return waitFor(0, waitTime);
+ }
+
+ /**
+ * Wait maximum the givem amount of time, for the counter to reach the given
+ * value.. This mechanism relies on {@link Object#wait(long)} and
+ * {@link Object#notify()} mechanism to work appropriately. Please see the
+ * class javadoc for more info.
+ *
+ * <p/> This method will stop waiting and return true if the thread
+ * is interrupted.
+ *
+ * @param value the value to wait for
+ * @param waitTime the time (in miliseconds) to wait for zero value
+ * @return true if the waiting timed out, false otherwise
+ */
+ public synchronized boolean waitFor(int value, long waitTime) {
+ boolean timedout = false;
+ long remainingTime = waitTime;
+ long startTime = System.currentTimeMillis();
+
+ while (counter > value && !timedout) {
+ // start waiting
+ try {
+ this.wait(remainingTime);
+ // compute the remaining time
+ remainingTime = waitTime - (System.currentTimeMillis() - startTime);
+ timedout = remainingTime <= 0;
+ }
+ catch (InterruptedException ex) {
+ timedout = true;
+ }
+ }
+
+ return timedout;
+ }
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.java
new file mode 100644
index 0000000..56006cf
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.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.servicemix.kernel.testing.support;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A feature is a list of bundles associated identified by its name.
+ */
+public interface Feature {
+
+ String getId();
+
+ String getName();
+
+ String getVersion();
+
+ List<Feature> getDependencies();
+
+ List<String> getBundles();
+
+ Map<String, Map<String, String>> getConfigurations();
+
+ void addDependency(Feature dependency);
+
+ void addBundle(String bundle);
+
+ void addConfig(String name, Map<String,String> properties);
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java
new file mode 100644
index 0000000..9c8c9a2
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class FeatureImpl implements Feature {
+
+ private String id;
+ private String name;
+ private String version;
+ private List<Feature> dependencies = new ArrayList<Feature>();
+ private List<String> bundles = new ArrayList<String>();
+ private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>();
+ public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+ public static String DEFAULT_VERSION = "0.0.0";
+
+ public FeatureImpl(String name) {
+ this(name, DEFAULT_VERSION);
+ }
+
+ public FeatureImpl(String name, String version) {
+ this.name = name;
+ this.version = version;
+ this.id = name + "-" + version;
+ }
+
+
+ public String getId() {
+ return id;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+
+ public List<Feature> getDependencies() {
+ return dependencies;
+ }
+
+ public List<String> getBundles() {
+ return bundles;
+ }
+
+ public Map<String, Map<String, String>> getConfigurations() {
+ return configs;
+ }
+
+ public void addDependency(Feature dependency) {
+ dependencies.add(dependency);
+ }
+
+ public void addBundle(String bundle) {
+ bundles.add(bundle);
+ }
+
+ public void addConfig(String name, Map<String,String> properties) {
+ configs.put(name, properties);
+ }
+
+ public String toString() {
+ String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+ return ret;
+ }
+
+ public static Feature valueOf(String str) {
+ if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+ String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+ String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
+ + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+ return new FeatureImpl(strName, strVersion);
+ } else {
+ return new FeatureImpl(str);
+ }
+
+
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FeatureImpl feature = (FeatureImpl) o;
+
+ if (!name.equals(feature.name)) return false;
+ if (!version.equals(feature.version)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + version.hashCode();
+ return result;
+ }
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java
new file mode 100644
index 0000000..d200e40
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java
@@ -0,0 +1,246 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+
+public class FeatureInstaller {
+
+ private Map<URI, FeatureRepositoryImpl> repositories = new HashMap<URI, FeatureRepositoryImpl>();
+ private Map<String, Map<String, Feature>> features;
+ private BundleContext bundleContext;
+
+ public void addRepository(URI uri) throws Exception {
+ if (!repositories.values().contains(uri)) {
+ internalAddRepository(uri);
+ }
+ }
+
+ protected FeatureRepositoryImpl internalAddRepository(URI uri) throws Exception {
+ FeatureRepositoryImpl repo = new FeatureRepositoryImpl(uri);
+ repositories.put(uri, repo);
+ features = null;
+ return repo;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+
+ public void installFeature(String name, String version) throws Exception {
+ Feature f = getFeature(name, version);
+ if (f == null) {
+ throw new Exception("No feature named '" + name
+ + "' with version '" + version + "' available");
+ }
+ for (Feature dependency : f.getDependencies()) {
+ installFeature(dependency.getName(), dependency.getVersion());
+ }
+
+ Set<Long> bundles = new HashSet<Long>();
+ for (String bundleLocation : f.getBundles()) {
+ Bundle b = installBundleIfNeeded(bundleLocation);
+ bundles.add(b.getBundleId());
+ }
+ for (long id : bundles) {
+ bundleContext.getBundle(id).start();
+ }
+
+
+ }
+
+ protected Feature getFeature(String name, String version) throws Exception {
+ Map<String, Feature> versions = getFeatures().get(name);
+ if (versions == null || versions.isEmpty()) {
+ return null;
+ } else {
+ Feature feature = versions.get(version);
+ if (feature == null && FeatureImpl.DEFAULT_VERSION.equals(version)) {
+ Version latest = new Version(cleanupVersion(version));
+ for (String available : versions.keySet()) {
+ Version availableVersion = new Version(cleanupVersion(available));
+ if (availableVersion.compareTo(latest) > 0) {
+ feature = versions.get(available);
+ latest = availableVersion;
+ }
+ }
+ }
+ return feature;
+ }
+ }
+
+ protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+ if (features == null) {
+ //the outer map's key is feature name, the inner map's key is feature version
+ Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
+ // Two phase load:
+ // * first load dependent repositories
+ for (;;) {
+ boolean newRepo = false;
+ for (FeatureRepositoryImpl repo : listRepositories()) {
+ for (URI uri : repo.getRepositories()) {
+ if (!repositories.keySet().contains(uri)) {
+ internalAddRepository(uri);
+ newRepo = true;
+ }
+ }
+ }
+ if (!newRepo) {
+ break;
+ }
+ }
+ // * then load all features
+ for (FeatureRepositoryImpl repo : repositories.values()) {
+ for (Feature f : repo.getFeatures()) {
+ if (map.get(f.getName()) == null) {
+ Map<String, Feature> versionMap = new HashMap<String, Feature>();
+ versionMap.put(f.getVersion(), f);
+ map.put(f.getName(), versionMap);
+ } else {
+ map.get(f.getName()).put(f.getVersion(), f);
+ }
+ }
+ }
+ features = map;
+ }
+ return features;
+ }
+
+ public FeatureRepositoryImpl[] listRepositories() {
+ Collection<FeatureRepositoryImpl> repos = repositories.values();
+ return repos.toArray(new FeatureRepositoryImpl[repos.size()]);
+ }
+
+ protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+ InputStream is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ try {
+ is.mark(256 * 1024);
+ JarInputStream jar = new JarInputStream(is);
+ Manifest m = jar.getManifest();
+ String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName() != null && b.getSymbolicName().equals(sn)) {
+ vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+ Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ if (v.equals(bv)) {
+ return b;
+ }
+ }
+ }
+ try {
+ is.reset();
+ } catch (IOException e) {
+ is.close();
+ is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ }
+ return getBundleContext().installBundle(bundleLocation, is);
+ } finally {
+ is.close();
+ }
+ }
+
+ private BundleContext getBundleContext() {
+ return this.bundleContext;
+ }
+
+ /**
+ * Clean up version parameters. Other builders use more fuzzy definitions of
+ * the version syntax. This method cleans up such a version to match an OSGi
+ * version.
+ *
+ * @param version
+ * @return
+ */
+ static Pattern fuzzyVersion = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+ Pattern.DOTALL);
+ static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
+ Pattern.DOTALL);
+ static public String cleanupVersion(String version) {
+ Matcher m = fuzzyVersion.matcher(version);
+ if (m.matches()) {
+ StringBuffer result = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(3);
+ String d3 = m.group(5);
+ String qualifier = m.group(7);
+
+ if (d1 != null) {
+ result.append(d1);
+ if (d2 != null) {
+ result.append(".");
+ result.append(d2);
+ if (d3 != null) {
+ result.append(".");
+ result.append(d3);
+ if (qualifier != null) {
+ result.append(".");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.0.");
+ cleanupModifier(result, qualifier);
+ }
+ return result.toString();
+ }
+ }
+ return version;
+ }
+
+ static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = fuzzyModifier.matcher(modifier);
+ if (m.matches())
+ modifier = m.group(2);
+
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+ result.append(c);
+ }
+ }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java
new file mode 100644
index 0000000..fb296bb
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java
@@ -0,0 +1,139 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * The repository implementation.
+ */
+
+public class FeatureRepositoryImpl {
+
+ private URI uri;
+ private List<Feature> features;
+ private List<URI> repositories;
+
+ public FeatureRepositoryImpl(URI uri) {
+ this.uri = uri;
+ }
+
+
+ public URI getURI() {
+ return uri;
+ }
+
+ public URI[] getRepositories() throws Exception {
+ if (repositories == null) {
+ load();
+ }
+ return repositories.toArray(new URI[repositories.size()]);
+ }
+
+ public Feature[] getFeatures() throws Exception {
+ if (features == null) {
+ load();
+ }
+ return features.toArray(new Feature[features.size()]);
+ }
+
+ public void load() throws IOException {
+ try {
+ repositories = new ArrayList<URI>();
+ features = new ArrayList<Feature>();
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc = factory.newDocumentBuilder().parse(uri.toURL().openStream());
+ NodeList nodes = doc.getDocumentElement().getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (!(node instanceof Element)) {
+ continue;
+ }
+ if ("repository".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ repositories.add(new URI(e.getTextContent()));
+ } else if ("feature".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ String name = e.getAttribute("name");
+ String version = e.getAttribute("version");
+ Feature f;
+ if (version != null && version.length() > 0) {
+ f = new FeatureImpl(name, version);
+ } else {
+ f = new FeatureImpl(name);
+ }
+
+ NodeList featureNodes = e.getElementsByTagName("feature");
+ for (int j = 0; j < featureNodes.getLength(); j++) {
+ Element b = (Element) featureNodes.item(j);
+ String dependencyFeatureVersion = b.getAttribute("version");
+ if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+ f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+ } else {
+ f.addDependency(new FeatureImpl(b.getTextContent()));
+ }
+ }
+ NodeList configNodes = e.getElementsByTagName("config");
+ for (int j = 0; j < configNodes.getLength(); j++) {
+ Element c = (Element) configNodes.item(j);
+ String cfgName = c.getAttribute("name");
+ String data = c.getTextContent();
+ Properties properties = new Properties();
+ properties.load(new ByteArrayInputStream(data.getBytes()));
+ Map<String, String> hashtable = new Hashtable<String, String>();
+ for (Object key : properties.keySet()) {
+ String n = key.toString();
+ hashtable.put(n, properties.getProperty(n));
+ }
+ f.addConfig(cfgName, hashtable);
+ }
+ NodeList bundleNodes = e.getElementsByTagName("bundle");
+ for (int j = 0; j < bundleNodes.getLength(); j++) {
+ Element b = (Element) bundleNodes.item(j);
+ f.addBundle(b.getTextContent());
+ }
+ features.add(f);
+ }
+ }
+ } catch (SAXException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (ParserConfigurationException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (URISyntaxException e) {
+ throw (IOException) new IOException().initCause(e);
+ }
+ }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.java
new file mode 100644
index 0000000..e3427a9
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.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.servicemix.kernel.testing.support;
+
+import org.apache.felix.framework.FilterImpl;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ */
+public class FrameworkUtil {
+
+ public static Filter createFilter(String f) throws InvalidSyntaxException {
+ return new FilterImpl(f);
+ }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java
new file mode 100644
index 0000000..eed2d5e
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java
@@ -0,0 +1,428 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.util.CompoundEnumeration;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.servicemix.kernel.main.Main;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.springframework.osgi.test.platform.FelixPlatform;
+import org.springframework.osgi.test.platform.OsgiPlatform;
+import org.springframework.util.ClassUtils;
+
+public class SmxKernelPlatform implements OsgiPlatform {
+
+ private static final Log log = LogFactory.getLog(FelixPlatform.class);
+
+ private static final String FELIX_CONF_FILE = "config.properties";
+
+ private static final String FELIX_CONFIG_PROPERTY = "config.properties";
+
+ public final static String FRAMEWORK_STORAGE = "org.osgi.framework.storage";
+
+ private BundleContext context;
+
+ private Object platform;
+
+ private File felixStorageDir;
+
+ private Properties configurationProperties = new Properties();
+
+ protected Properties getPlatformProperties() {
+ // load Felix configuration
+ Properties props = new Properties();
+ props.putAll(getFelixConfiguration());
+ props.putAll(getLocalConfiguration());
+ return props;
+ }
+
+ public Properties getConfigurationProperties() {
+ // local properties
+ configurationProperties.putAll(getPlatformProperties());
+ // system properties
+ configurationProperties.putAll(System.getProperties());
+ return configurationProperties;
+ }
+
+ public BundleContext getBundleContext() {
+ return context;
+ }
+
+ private Set<String> getJars(Class... classes) {
+ Set<String> jars = new HashSet<String>();
+ for (Class cl : classes) {
+ String name = cl.getName().replace('.', '/') + ".class";
+ URL url = (cl.getClassLoader() != null ? cl.getClassLoader() : getClass().getClassLoader()).getResource(name);
+ String path = url.toString();
+ if (path.startsWith("jar:")) {
+ path = path.substring(0, path.indexOf('!'));
+ } else {
+ path = path.substring(0, path.indexOf(name));
+ }
+ jars.add(path);
+ }
+ return jars;
+ }
+
+ public void start() throws Exception {
+ Set<String> jars = getJars(Felix.class);
+ ClassLoader classLoader = new GuardClassLoader(toURLs(jars.toArray(new String[jars.size()])), null);
+
+ BundleActivator activator = new BundleActivator() {
+ private ServiceRegistration registration;
+
+ public void start(BundleContext context) {
+ registration = context.registerService(MainService.class.getName(), new MainService() {
+ public String[] getArgs() {
+ return new String[0];
+ }
+ public int getExitCode() {
+ return 0;
+ }
+ public void setExitCode(int exitCode) {
+ }
+ }, null);
+ }
+
+ public void stop(BundleContext context) {
+ registration.unregister();
+ }
+ };
+ List<BundleActivator> activations = new ArrayList<BundleActivator>();
+ activations.add(activator);
+
+ Properties props = getConfigurationProperties();
+ props.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, activations);
+
+ Thread.currentThread().setContextClassLoader(classLoader);
+ Class cl = classLoader.loadClass(Felix.class.getName());
+ Constructor cns = cl.getConstructor(Map.class);
+ platform = cns.newInstance(props);
+ platform.getClass().getMethod("start").invoke(platform);
+
+ Bundle systemBundle = (Bundle) platform;
+
+ // call getBundleContext
+ final Method getContext = systemBundle.getClass().getMethod("getBundleContext", null);
+
+ AccessController.doPrivileged(new PrivilegedAction() {
+
+ public Object run() {
+ getContext.setAccessible(true);
+ return null;
+ }
+ });
+ context = (BundleContext) getContext.invoke(systemBundle, null);
+ }
+
+ public void stop() throws Exception {
+ try {
+ platform.getClass().getMethod("stop").invoke(platform);
+ }
+ finally {
+ // remove cache folder
+ delete(felixStorageDir);
+ }
+ }
+
+ public String toString() {
+ return getClass().getName();
+ }
+
+ File createTempDir(String suffix) {
+ if (suffix == null)
+ suffix = "osgi";
+ File tempFileName;
+
+ try {
+ tempFileName = File.createTempFile("org.sfw.osgi", suffix);
+ }
+ catch (IOException ex) {
+ if (log.isWarnEnabled()) {
+ log.warn("Could not create temporary directory, returning a temp folder inside the current folder", ex);
+ }
+ return new File("./tmp-test");
+ }
+
+ tempFileName.delete(); // we want it to be a directory...
+ File tempFolder = new File(tempFileName.getAbsolutePath());
+ tempFolder.mkdir();
+ return tempFolder;
+ }
+
+ /**
+ * Configuration settings for the OSGi test run.
+ *
+ * @return
+ */
+ private Properties getLocalConfiguration() {
+ Properties props = new Properties();
+ felixStorageDir = createTempDir("felix");
+ props.setProperty(FRAMEWORK_STORAGE, this.felixStorageDir.getAbsolutePath());
+ if (log.isTraceEnabled())
+ log.trace("felix storage dir is " + felixStorageDir.getAbsolutePath());
+
+ return props;
+ }
+
+ /**
+ * Loads Felix config.properties.
+ *
+ * <strong>Note</strong> the current implementation uses Felix's Main class
+ * to resolve placeholders as opposed to loading the properties manually
+ * (through JDK's Properties class or Spring's PropertiesFactoryBean).
+ *
+ * @return
+ */
+ // TODO: this method should be removed once Felix 1.0.2 is released
+ private Properties getFelixConfiguration() {
+ String location = "/".concat(ClassUtils.classPackageAsResourcePath(getClass())).concat("/").concat(FELIX_CONF_FILE);
+ URL url = getClass().getResource(location);
+ if (url == null)
+ throw new RuntimeException("cannot find felix configuration properties file:" + location);
+
+ // used with Main
+ System.getProperties().setProperty(FELIX_CONFIG_PROPERTY, url.toExternalForm());
+
+ // load config.properties (use Felix's Main for resolving placeholders)
+ Properties props = new Properties();
+ InputStream is = null;
+ try {
+ is = url.openConnection().getInputStream();
+ props.load(is);
+ is.close();
+ }
+ catch (FileNotFoundException ex) {
+ // Ignore file not found.
+ }
+ catch (Exception ex) {
+ System.err.println("Main: Error loading system properties from " + url);
+ System.err.println("Main: " + ex);
+ try {
+ if (is != null) is.close();
+ }
+ catch (IOException ex2) {
+ // Nothing we can do.
+ }
+ return null;
+ }
+ // Perform variable substitution for system properties.
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ props.setProperty(name, Main.substVars(props.getProperty(name), name, null, props));
+ }
+ return props;
+ }
+
+ /**
+ * Delete the given file (can be a simple file or a folder).
+ *
+ * @param file the file to be deleted
+ * @return if the deletion succeded or not
+ */
+ public static boolean delete(File file) {
+
+ // bail out quickly
+ if (file == null)
+ return false;
+
+ // recursively delete children file
+ boolean success = true;
+
+ if (file.isDirectory()) {
+ String[] children = file.list();
+ for (int i = 0; i < children.length; i++) {
+ success &= delete(new File(file, children[i]));
+ }
+ }
+
+ // The directory is now empty so delete it
+ return (success &= file.delete());
+ }
+
+ private static URL[] toURLs(String[] jars) throws MalformedURLException {
+ URL[] urls = new URL[jars.length];
+ for (int i = 0; i < urls.length; i++) {
+ String s = jars[i];
+ if (s.startsWith("jar:")) {
+ s = s.substring("jar:".length());
+ }
+ urls[i] = new URL(s);
+ }
+ return urls;
+ }
+
+ public class GuardClassLoader extends URLClassLoader {
+ private Set<String> bootDelegationPackages = new HashSet<String>();
+ private Set<String> packages = new HashSet<String>();
+ private List<ClassLoader> parents = new ArrayList<ClassLoader>();
+
+ public GuardClassLoader(URL[] urls, List<String> additionalPackages) throws MalformedURLException {
+ super(urls, SmxKernelPlatform.class.getClassLoader());
+ Properties props = getConfigurationProperties();
+ String prop = props.getProperty("org.osgi.framework.system.packages");
+ String[] ps = prop.split(",");
+ for (String p : ps) {
+ String[] spack = p.split(";");
+ for (String sp : spack) {
+ sp = sp.trim();
+ if (!sp.startsWith("version")) {
+ packages.add(sp);
+ }
+ }
+ }
+ if (additionalPackages != null) {
+ packages.addAll(additionalPackages);
+ }
+ prop = props.getProperty("org.osgi.framework.bootdelegation");
+ ps = prop.split(",");
+ for (String p : ps) {
+ p = p.trim();
+ if (p.endsWith("*")) {
+ p = p.substring(0, p.length() - 1);
+ }
+ bootDelegationPackages.add(p);
+ }
+ ClassLoader cl = getParent();
+ while (cl != null) {
+ parents.add(0, cl);
+ cl = cl.getParent();
+ }
+ //System.err.println("Boot packages: " + packages);
+ }
+
+ protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ //System.err.println("Loading class: " + name);
+ Class c = findLoadedClass(name);
+ if (c == null) {
+ String pkg = name.substring(0, name.lastIndexOf('.'));
+ boolean match = name.startsWith("java.") || packages.contains(pkg);
+ if (!match) {
+ for (String p : bootDelegationPackages) {
+ if (pkg.startsWith(p)) {
+ match = true;
+ break;
+ }
+ }
+ }
+ if (match) {
+ for (ClassLoader cl : parents) {
+ try {
+ c = cl.loadClass(name);
+ //System.err.println("Class loaded from: " + cl.getResource(name.replace('.', '/') + ".class"));
+ break;
+ } catch (ClassNotFoundException e) {
+ }
+ }
+ if (c == null) {
+ throw new ClassNotFoundException(name);
+ }
+ //c = getParent().loadClass(name);
+ } else {
+ c = findClass(name);
+ }
+ }
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+
+ public URL getResource(String name) {
+ //System.err.println("GetResource: " + name);
+ URL url = getParent().getResource(name);
+ if (url != null && url.toString().startsWith("file:")) {
+ return url;
+ }
+ url = findResource(name);
+ System.err.println("Resource " + name + " found at " + url);
+ return url;
+ /*
+ URL u = getParent().getResource(name);
+ if (u != null) {
+ String path = u.toString();
+ int idx = path.indexOf('!');
+ if (idx > 0) {
+ path = path.substring(0, idx);
+ if (!jars.contains(path)) {
+ return null;
+ }
+ } else {
+ idx = 0;
+ }
+ }
+ return u;
+ */
+ }
+
+ public Enumeration<URL> getResources(final String name) throws IOException {
+ //System.err.println("GetResources: " + name);
+ Enumeration[] tmp = new Enumeration[2];
+ final Enumeration<URL> e = getParent().getResources(name);
+ tmp[0] = new Enumeration<URL>() {
+ URL next = null;
+ public boolean hasMoreElements() {
+ while (next == null && e.hasMoreElements()) {
+ next = e.nextElement();
+ String path = next.toString();
+ if (!path.startsWith("file:")) {
+ next = null;
+ }
+ }
+ return next != null;
+ }
+ public URL nextElement() {
+ return next;
+ }
+ };
+ tmp[1] = findResources(name);
+ return new CompoundEnumeration(tmp) {
+ public Object nextElement() {
+ Object next = super.nextElement();
+ System.err.println("Resources " + name + " found at " + next);
+ return next;
+ }
+ };
+ }
+ }
+}
diff --git a/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.java b/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.java
new file mode 100644
index 0000000..9acba05
--- /dev/null
+++ b/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.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.servicemix.kernel.testing.support;
+
+import junit.framework.TestCase;
+
+
+public class AbstractIntegrationTestTest extends TestCase {
+
+ public void testSnapshotVersion() {
+ assertTrue(AbstractIntegrationTest.isTimestamped("0.9.0-20070713.230317-1"));
+ assertTrue(AbstractIntegrationTest.isSnapshot("0.9.0-SNAPSHOT"));
+ assertFalse(AbstractIntegrationTest.isSnapshot("0.9.0"));
+ assertEquals("0.9.0-SNAPSHOT", AbstractIntegrationTest.getSnapshot("0.9.0-20070713.230317-1"));
+ assertEquals("0.9.0", "0.9.0");
+ }
+
+}