diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6b0b1270 --- /dev/null +++ b/LICENSE @@ -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/NOTICE b/NOTICE new file mode 100644 index 00000000..b56c2784 --- /dev/null +++ b/NOTICE @@ -0,0 +1,6 @@ +Apache XBean +Copyright 2005-2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + diff --git a/classpath/m2-readme.txt b/classpath/m2-readme.txt deleted file mode 100644 index 3750464a..00000000 --- a/classpath/m2-readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -Trying Maven 2.x ----------------- - -If you want to try m2 then simply run the mavenizer.sh script which -will produce an "m2" directory. Move into the "m2" directory and - -m2 install - -That should compile, test, package and install diff --git a/classpath/maven.xml b/classpath/maven.xml deleted file mode 100644 index 0872ff3b..00000000 --- a/classpath/maven.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/classpath/mavenizer.sh b/classpath/mavenizer.sh deleted file mode 100755 index 0c9696c3..00000000 --- a/classpath/mavenizer.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -DIR=m2 - -rm -rf $DIR > /dev/null 2>&1 -mkdir $DIR - -cp -r src $DIR -cp pom.xml $DIR - -( - cd $DIR - - find . -name '\.svn' -exec rm -rf {} \; - - ( - cd src - - mkdir main ; mv java main - - ( cd test ; mkdir java ; mv org java ) - - ) - -) diff --git a/classpath/pom.xml b/classpath/pom.xml deleted file mode 100644 index dabacde2..00000000 --- a/classpath/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - 4.0.0 - - XBean :: ClassPath - xbean-classpath - xbean - 2.0-alpha-1-SNAPSHOT - - XBean - http://xbean.org - - - - - openejb - OpenEJB Repo - http://www.openejb.org/maven - legacy - - - - http://www.xbean.org/ - - - - repo1 - Maven Central Repository - scp://repo1.maven.org/home/projects/maven/repository-staging/to-ibiblio/maven2 - - - website - scp://minotaur.apache.org/www/maven.apache.org/maven2/ - - - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - David Blevins - dblevins - dblevins@visi.com - - Dude - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.4 - 1.4 - - - - - - - diff --git a/classpath/project.properties b/classpath/project.properties deleted file mode 100644 index 07280f09..00000000 --- a/classpath/project.properties +++ /dev/null @@ -1,34 +0,0 @@ -### -# Copyright 2005 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. -### -maven.repo.remote=http://www.xbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven - -maven.compile.source=1.4 -maven.compile.target=1.4 -maven.compile.deprecation=true -maven.compile.debug=true -maven.compile.optimize=true - -maven.remote.group=xbean -maven.username=${user.name} -maven.repo.central=beaver.codehaus.org -maven.repo.central.directory=/dist - -maven.javadoc.source=1.4 -maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/ -maven.javadoc.additionalparam=-linksource - -maven.site.deploy.method=rsync -maven.site.deploy.clean=true diff --git a/classpath/project.xml b/classpath/project.xml deleted file mode 100644 index 668108f0..00000000 --- a/classpath/project.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - 3 - - XBean :: ClassPath - xbean-classpath - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - XBean: Classpath and classloader manipulation library - - - Classpath and classloader manipulation library - - - http://www.xbean.org/ - - www.xbean.org - /home/projects/xbean/public_html/maven - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - - - - - - - - src/java - src/test - - - **/*Test.java - - - - - - maven-jxr-plugin - maven-javadoc-plugin - maven-junit-report-plugin - - - - diff --git a/classpath/src/test/org/xbean/classpath/SystemClassPathTest.java b/classpath/src/test/org/xbean/classpath/SystemClassPathTest.java deleted file mode 100644 index 2082c584..00000000 --- a/classpath/src/test/org/xbean/classpath/SystemClassPathTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.xbean.classpath; - -/** - * @version $Revision$ $Date$ - */ - -import junit.framework.*; -import org.xbean.classpath.SystemClassPath; - -import java.net.URL; - -public class SystemClassPathTest extends TestCase { - - public void testAddJarToPath() throws Exception { - SystemClassPath systemClassPath = new SystemClassPath(); - - ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - - try { - systemClassLoader.loadClass("groovy.lang.GroovyShell"); - fail("Class already exists"); - } catch (ClassNotFoundException e) { - // this should fail - } - - - URL groovyJar = new URL("http://www.ibiblio.org/maven/groovy/jars/groovy-SNAPSHOT.jar"); - systemClassPath.addJarToPath(groovyJar); - - try { - systemClassLoader.loadClass("groovy.lang.GroovyShell"); - } catch (ClassNotFoundException e) { - // this should fail pass - fail("Class already exists"); - } - } -} \ No newline at end of file diff --git a/kernel/converting-gbean.apt b/kernel/converting-gbean.apt deleted file mode 100644 index d93fc0d1..00000000 --- a/kernel/converting-gbean.apt +++ /dev/null @@ -1,54 +0,0 @@ - ----- - Converting xbean.org from m1 to m2 - ----- - Jason van Zyl - ----- - - What follows is a little description of the the process that I go through - when converting an m1 project to an m2 project. In this example I'm going to - use xbean.org, Dain Sundstrom's [yada yada yada] ... - - Usually when I start looking at converting a project from m1 to m2 I take a - quick look at the directory structure because the standard layout we - decided upon for m2 is slightly different from layout most common in m1 - and it's understandable that folks might not want to change their layout. - I talked it over with Dain and because SVN makes it so easy to shuffle things - around he decided it would be ok to use the m2 standard. Using the m2 - standards does make things easier because the standards for m2 are - encapsulated in the Super POM that all m2 project inherit from. You can sort of - think of the Super POM in Maven as the analog of <<>> in Java. - - - pitfalls in using the default structure - - - resources in the same directories as sources - - - some things we don't have in m2 right now are a clover plugin - - Updating the POM - - - change pomVersion to modelVersion and 3 to 4.0.0 - - - the id element is deprecated an calculated in m2, so i changed the - id element to artifactId - - - currentVersion to version - - - change the version from 1.0-SNAPSHOT to 1.0-alpha-1-SNAPSHOT - - - package element no longer exists, it will be the job of plugins - - - remove the siteAddress and siteDirectory elemement and replace it - with the distributionManagement element - - - removed the entire build element because we are using m2 standards - which are encapsulated in the Super POM. - - - remove the reports for now as we don't have many - -Annoyances - - The single biggest annoyance with converting from m1 to m2 is the fact that - often times people have not made POMs for artifacts we have entered into the - repository. With m2 the build will not work if artifacts do not have the - accompanying POMs. - diff --git a/kernel/m2-readme.txt b/kernel/m2-readme.txt deleted file mode 100644 index 3750464a..00000000 --- a/kernel/m2-readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -Trying Maven 2.x ----------------- - -If you want to try m2 then simply run the mavenizer.sh script which -will produce an "m2" directory. Move into the "m2" directory and - -m2 install - -That should compile, test, package and install diff --git a/kernel/maven.xml b/kernel/maven.xml deleted file mode 100644 index 0872ff3b..00000000 --- a/kernel/maven.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/kernel/mavenizer.sh b/kernel/mavenizer.sh deleted file mode 100755 index 0c9696c3..00000000 --- a/kernel/mavenizer.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -DIR=m2 - -rm -rf $DIR > /dev/null 2>&1 -mkdir $DIR - -cp -r src $DIR -cp pom.xml $DIR - -( - cd $DIR - - find . -name '\.svn' -exec rm -rf {} \; - - ( - cd src - - mkdir main ; mv java main - - ( cd test ; mkdir java ; mv org java ) - - ) - -) diff --git a/kernel/pom.xml b/kernel/pom.xml deleted file mode 100644 index d4735575..00000000 --- a/kernel/pom.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - 4.0.0 - - XBean :: Kernel - xbean-kernel - xbean - 2.0-alpha-1-SNAPSHOT - - XBean - http://xbean.org - - - - - openejb - OpenEJB Repo - http://www.openejb.org/maven - legacy - - - - - XBean is a kernel architecture and server for ioc containers such as spring. - - - http://www.xbean.org/ - - - - repo1 - Maven Central Repository - scp://repo1.maven.org/home/projects/maven/repository-staging/to-ibiblio/maven2 - - - website - scp://minotaur.apache.org/www/maven.apache.org/maven2/ - - - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - Dain Sundstrom - dain - dain@iq80.com - - Despot - - - - - - - backport-util-concurrent - backport-util-concurrent - 2.0_01_pd - - - junit - junit - 3.8.1 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.4 - 1.4 - - - - - - - diff --git a/kernel/project.properties b/kernel/project.properties deleted file mode 100644 index 07280f09..00000000 --- a/kernel/project.properties +++ /dev/null @@ -1,34 +0,0 @@ -### -# Copyright 2005 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. -### -maven.repo.remote=http://www.xbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven - -maven.compile.source=1.4 -maven.compile.target=1.4 -maven.compile.deprecation=true -maven.compile.debug=true -maven.compile.optimize=true - -maven.remote.group=xbean -maven.username=${user.name} -maven.repo.central=beaver.codehaus.org -maven.repo.central.directory=/dist - -maven.javadoc.source=1.4 -maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/ -maven.javadoc.additionalparam=-linksource - -maven.site.deploy.method=rsync -maven.site.deploy.clean=true diff --git a/kernel/project.xml b/kernel/project.xml deleted file mode 100644 index 7be8e828..00000000 --- a/kernel/project.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - 3 - - XBean :: Kernel - xbean-kernel - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - XBean: kernel for containers - - - XBean is a kernel architecture and server for ioc containers such as spring. - - - http://www.xbean.org/ - - www.xbean.org - /home/projects/xbean/public_html/maven - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - backport-util-concurrent - backport-util-concurrent - 2.0_01_pd - - - - - src/java - src/test - - - **/*Test.java - - - - - - - - - - - - - - - maven-jxr-plugin - maven-javadoc-plugin - maven-junit-report-plugin - - - - - - - diff --git a/kernel/src/java/org/xbean/kernel/AbstractServiceFactory.java b/kernel/src/java/org/xbean/kernel/AbstractServiceFactory.java deleted file mode 100644 index 19010589..00000000 --- a/kernel/src/java/org/xbean/kernel/AbstractServiceFactory.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; -import java.util.Collections; -import java.util.HashSet; - -/** - * AbstractServiceFactory is an implementation of ServiceFactory that handles all of the mundane issues. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public abstract class AbstractServiceFactory implements ServiceFactory { - private boolean enabled = true; - private final Set startConditions = new HashSet(); - private final Set stopConditions = new HashSet(); - - /** - * {@inheritDoc} - */ - public synchronized boolean isEnabled() { - return enabled; - } - - /** - * {@inheritDoc} - */ - public synchronized void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - /** - * {@inheritDoc} - */ - public synchronized Set getStartConditions() { - return Collections.unmodifiableSet(new HashSet(startConditions)); - } - - /** - * {@inheritDoc} - */ - public synchronized void addStartCondition(ServiceCondition startCondition) throws NullPointerException { - if (startCondition == null) throw new NullPointerException("startCondition is null"); - startConditions.add(startCondition); - } - - /** - * {@inheritDoc} - */ - public synchronized void removeStartCondition(ServiceCondition startCondition) throws NullPointerException { - if (startCondition == null) throw new NullPointerException("startCondition is null"); - startConditions.remove(startCondition); - } - - /** - * {@inheritDoc} - */ - public synchronized Set getStopConditions() { - return Collections.unmodifiableSet(new HashSet(stopConditions)); - } - - /** - * {@inheritDoc} - */ - public synchronized void addStopCondition(ServiceCondition stopCondition) throws NullPointerException { - if (stopCondition == null) throw new NullPointerException("stopCondition is null"); - stopConditions.add(stopCondition); - } - - /** - * {@inheritDoc} - */ - public synchronized void removeStopCondition(ServiceCondition stopCondition) throws NullPointerException { - if (stopCondition == null) throw new NullPointerException("stopCondition is null"); - stopConditions.remove(stopCondition); - } -} diff --git a/kernel/src/java/org/xbean/kernel/ForcedStopException.java b/kernel/src/java/org/xbean/kernel/ForcedStopException.java deleted file mode 100644 index 88fbda2c..00000000 --- a/kernel/src/java/org/xbean/kernel/ForcedStopException.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Collections; -import java.util.Set; - -/** - * Signafies that a StopStrategies would like the kernel to ignore any unsatified stop conditions and continue to - * destroy the service. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ForcedStopException extends Exception { - private final ServiceName serviceName; - private final Set unsatisfiedConditions; - - /** - * Creates a ForcedStopException for the specified service name. - * - * @param serviceName the name of the service that is to be forceably stopped - * @param unsatisfiedConditions the unsatisfied conditions that will be ignored - */ - public ForcedStopException(ServiceName serviceName, Set unsatisfiedConditions) { - super("Forced stop and ignored unsatisfied conditons:" + - " serviceName=" + serviceName + - ", unsatisfiedConditions=" + unsatisfiedConditions); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (unsatisfiedConditions == null) throw new NullPointerException("unsatisfiedConditions is null"); - this.serviceName = serviceName; - this.unsatisfiedConditions = Collections.unmodifiableSet(unsatisfiedConditions); - } - - /** - * Gets the name of the service that is to be forceably stopped. - * - * @return the service name - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the conditions that were unsatified when the exception was thrown. - * - * @return the unsatified conditions that were ignored - */ - public Set getUnsatisfiedConditions() { - return unsatisfiedConditions; - } -} diff --git a/kernel/src/java/org/xbean/kernel/IllegalServiceStateException.java b/kernel/src/java/org/xbean/kernel/IllegalServiceStateException.java deleted file mode 100644 index 16107b62..00000000 --- a/kernel/src/java/org/xbean/kernel/IllegalServiceStateException.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Indicates an operation was called on a service in a state that does not allow that operation to be called. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class IllegalServiceStateException extends Exception { - private final ServiceName serviceName; - - /** - * Creates an IllegalServiceStateException. - * - * @param message information about why the service is in an illegal state - * @param serviceName the name of the service - */ - public IllegalServiceStateException(String message, ServiceName serviceName) { - super(message + ": " + serviceName); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - this.serviceName = serviceName; - } - - /** - * Gets the name of the service that caused this exception. - * - * @return the service name - */ - public ServiceName getServiceName() { - return serviceName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/InvalidServiceTypeException.java b/kernel/src/java/org/xbean/kernel/InvalidServiceTypeException.java deleted file mode 100644 index e82cf409..00000000 --- a/kernel/src/java/org/xbean/kernel/InvalidServiceTypeException.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Indicates that the service factory returned an object from the createService method that is not an instance of every - * declared type. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class InvalidServiceTypeException extends Exception { - private final ServiceName serviceName; - private final Class expectedType; - private final Class serviceType; - - /** - * Creates an InvalidServiceType caused by the service with the specified name, which returned an object from the - * createService method of the specified type that is not an instance of the expected type. - * @param serviceName the name of the service that returned an object of the wrong type - * @param expectedType the type that was expected - * @param serviceType the actual type of the service returned from the factory - */ - // todo add servicefacotory to the parameters - public InvalidServiceTypeException(ServiceName serviceName, Class expectedType, Class serviceType) { - super("Expected service type " + expectedType.getName() + ", but service factory created a " + - serviceType.getName() + " for service " + serviceName); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (expectedType == null) throw new NullPointerException("expectedType is null"); - if (serviceType == null) throw new NullPointerException("serviceType is null"); - this.serviceName = serviceName; - this.expectedType = expectedType; - this.serviceType = serviceType; - } - - /** - * Gets the name of the service that returned an object of the wrong type. - * @return the name of the service that returned an object of the wrong type - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the type that was expected. - * @return the type that was expected - */ - public Class getExpectedType() { - return expectedType; - } - - /** - * Gets the actual type of the service returned from the factory. - * @return the actual type of the service returned from the factory - */ - public Class getServiceType() { - return serviceType; - } -} diff --git a/kernel/src/java/org/xbean/kernel/Kernel.java b/kernel/src/java/org/xbean/kernel/Kernel.java deleted file mode 100644 index 1a8999fe..00000000 --- a/kernel/src/java/org/xbean/kernel/Kernel.java +++ /dev/null @@ -1,369 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.List; - -/** - * This iterface defines the API for managing and monitoring service life-cycle. A kernel can be constructed with the - * following code: - *

- * Kernel kernel = KernelFactory.newInstance().createKernel(name);
- * 
- * Services can be registered, unregistered, started and - * stopped. The lifecycle model is loosly based on the J2ee Management Specification (JSR 77). All lifecycle - * transitions are broadcasted via a ServiceMonitor. - *

- * Each kernel must have a name that is unique with in the KernelFactory (there should only be one KernelFactory per - * VM but class loader tricks can result in several KernelFactory) - *

- * This class is loosely based on the J2ee management MEJB and JMX MBeanServer interfaces. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface Kernel { - /** - * Destroys this kernel. This method causes all services to be stopped and unregistered. - */ - void destroy(); - - /** - * Waits for the kernel to be destroyed. - */ - void waitForDestruction(); - - /** - * Gets the running status of the kernel. Services can not be registered or started on a stopped kernel. - * - * @return true if the kernel is running; false otherwise - */ - boolean isRunning(); - - /** - * Gets the unique name of this kernel within the KernelFactory. - * - * @return the unique name of this kernel - */ - String getKernelName(); - - /** - * Registers a service with this kernel. If the service is restartable, it will enter the server in the - * STOPPED state. If a service is not restartable, the kernel will assure that all - * dependencies are satisfied and service will enter the kernel in the RUNNING state. If a - * dependency for a non-restartable service is not immediately satisfiable, this method will throw a - * ServiceRegistrationException. - * - * @param serviceName the unique name of the service in the kernel - * @param serviceFactory the factory used to create the service - * @param classLoader the class loader to use for this service - * @throws ServiceAlreadyExistsException if service is already registered with the specified name - * @throws ServiceRegistrationException if the service is not restartable and an error occured while starting the service - */ - void registerService(ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException, ServiceRegistrationException; - - /** - * Unregisters a service from this kernel. The kernel will attempt to stop the service using the - * SYNCHRONOUS stop strategy, but if it can not stop the service a - * ServiceRegistrationException will be thrown containing an UnsatisfiedConditionsException. - * - * @param serviceName the unique name of the service in the kernel - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws ServiceRegistrationException if the service could not be stopped - */ - void unregisterService(ServiceName serviceName) throws ServiceNotFoundException, ServiceRegistrationException; - - /** - * Unregisters a service from this kernel. The kernel will attempt to stop the service using the specified stop - * strategy, but if it can not stop the service a ServiceRegistrationException will be thrown containing - * either an UnsatisfiedConditionsException or a IllegalServiceStateException. - * - * @param serviceName the unique name of the service in the kernel - * @param stopStrategy the strategy that determines how unsatisfied conditions are handled - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws ServiceRegistrationException if the service could not be stopped - */ - void unregisterService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, ServiceRegistrationException; - - /** - * Determines if there is a service registered under the specified name. - * - * @param serviceName the unique name of the service - * @return true if there is a service registered with the specified name; false otherwise - */ - boolean isRegistered(ServiceName serviceName); - - /** - * Gets the ServiceState of the specified service. If the service is not restartable, this method will - * always return RUNNING. - * - * @param serviceName the unique name of the service in the kernel - * @return the curren ServiceState of the service - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - ServiceState getServiceState(ServiceName serviceName) throws ServiceNotFoundException; - - /** - * Gets the time the specified service entered the RUNNING state since the epoch - * (January 1, 1970, 00:00:00) in milliseconds. If the service is in the STOPPED or - * STARTING states, this method will return 0. - * - * @param serviceName the unique name of the service in the kernel - * @return the time the service started in milliseconds since January 1, 1970, 00:00:00 - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - long getServiceStartTime(ServiceName serviceName) throws ServiceNotFoundException; - - /** - * Immediately starts the service using the SYNCHRONOUS start strategy. Any exception throw - * from service constuction is throw directly from this method. If a start condition can not be immediately - * satisfied, a UnsatisfiedConditionsException will be thrown. If a service already in the - * RUNNING state, or is not restartable, this method is a noop. If the service - * is in the STOPPING state an IllegalServiceStateException will be thrown. If the - * service is disabled, this method will throw an IllegalServiceStateException. - *

- * This method has no effect on as service that is not restartable. - * - * @param serviceName the unique name of the service to start - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws IllegalServiceStateException if the service is restartable and is in the STOPPING state or if the - * service is disabled - * @throws UnsatisfiedConditionsException if some of the start conditions can not be immediately satisfied - * @throws Exception if service construction threw an Exception - */ - void startService(ServiceName serviceName) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception; - - /** - * Immediately starts the service using the specified start strategy. - *

- * The start strategy determines how any exception thrown from service constuction is handled including throwing - * the exception directly from this method. - *

- * The start strategy determines what to do if a start condition can not be immediately satisfied. Possibilities - * include throwing an UnsatisfiedConditionsException, blocking, leaving the service in the - * RUNNING state, or unregistering the service. - *

- * If a service already in the RUNNING state, or is not restartable, this method is a noop. - * If the service is in the STOPPING state an IllegalServiceStateException will be - * thrown. If the service is disabled, this method will throw an IllegalServiceStateException. - *

- * This method has no effect on as service that is not restartable. - * - * @param serviceName the unique name of the service to start - * @param startStrategy the strategy that determines how unsatisfied conditions and construction exceptions are handled - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws IllegalServiceStateException if the service is restartable and is in the STOPPING state or if the - * service is disabled - * @throws UnsatisfiedConditionsException if some of the start conditions can not be immediately satisfied - * @throws Exception if service construction threw an Exception - */ - void startService(ServiceName serviceName, StartStrategy startStrategy) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception; - - /** - * Immediately starts the service, and if the start ultimately completes successfully, all services owned by the - * specified service, all services that are owned by those services, and so on, will be started using the - * startServiceRecursive(ServiceName) method. - * - * @param serviceName the unique name of the service to start recursively - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws IllegalServiceStateException if the service is restartable and is in the STOPPING state or if the - * service is disabled - * @throws UnsatisfiedConditionsException if some of the start conditions can not be immediately satisfied - * @throws Exception if service construction threw an Exception - */ - void startServiceRecursive(ServiceName serviceName) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception; - - /** - * Immediately starts the service, and if the start ultimately completes successfully, all services owned by the - * specified service, all services that are owned by those services, and so on, will be started using the - * startServiceRecursive(ServiceName, StartStrategy) method. - * - * @param serviceName the unique name of the service to start recursively - * @param startStrategy the strategy that determines how unsatisfied conditions and construction exceptions are handled - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws IllegalServiceStateException if the service is restartable and is in the STOPPING state or if the - * service is disabled - * @throws UnsatisfiedConditionsException if some of the start conditions can not be immediately satisfied - * @throws Exception if service construction threw an Exception - */ - void startServiceRecursive(ServiceName serviceName, StartStrategy startStrategy) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception; - - /** - * Immediately stops the service using the SYNCHRONOUS stop strategy. If a stop condition can - * not be immediately satisfied, an UnsatisfiedConditionsException will be thrown. If a service already in - * the STOPPED state, this method is a noop. - *

- * If the service is not restartable, this method only attempts to satify the stop conditions. This is useful for - * stopping all dependent services of a non-restartable service before unregistering the service. - * - * @param serviceName the unique name of the service to stop - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - void stopService(ServiceName serviceName) throws ServiceNotFoundException, UnsatisfiedConditionsException; - - /** - * Immediately stops the service using the specified stop strategy. If a stop condition can not be immediately - * satisfied, an UnsatisfiedConditionsException will be thrown. If a service already in the - * STOPPED state, this method is a noop. - *

- * If the service is not restartable, this method only attempts to satify the stop conditions. This is useful for - * stopping all dependent services of a non-restartable service before unregistering the service. - * - * @param serviceName the unique name of the service to stop - * @param stopStrategy the strategy that determines how unsatisfied conditions are handled - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - void stopService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, UnsatisfiedConditionsException; - - /** - * Determines if the service can be instantiated in a kernel. A disabled restartable service can not be - * started. This method is equivalent to: - *

-     *     kernel.getServiceFactory(serviceName).isEnabled();
-     * 
- *

- * - * @param serviceName the unique name of the service - * @return true if the service factory is enabled; false otherwise - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - boolean isServiceEnabled(ServiceName serviceName) throws ServiceNotFoundException; - - /** - * Sets the enabled status of a service. A disabled restartable service can not be started. This state has - * no effect on a service that is already started, but if a running service is disabled, it can not be restarted. - * This method is equivalent to: - *

-     *     kernel.getServiceFactory(serviceName).setEnabled(enabled);
-     * 
- *

- * - * @param serviceName the unique name of the service - * @param enabled the new enabled state of this factory - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - void setServiceEnabled(ServiceName serviceName, boolean enabled) throws ServiceNotFoundException; - - /** - * Gets the service registered under the specified name. If the service is not in the RUNNING, - * or STARTING state this method will throw an IllegalArgumentException. - * - * @param serviceName the unique name of the service - * @return the service associated with the specified name - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws IllegalArgumentException if the service is not in the RUNNING, or STARTING state - */ - Object getService(ServiceName serviceName) throws ServiceNotFoundException, IllegalArgumentException; - - /** - * Gets the first running service registered with the kernel that is an instance of the specified type. If no - * running services are instances of the specified type, null is returned. - * - * @param type the of the desired service - * @return the first registered service that is an instance of the specified type and is running - */ - Object getService(Class type); - - /** - * Gets the all of running service registered with the kernel that are an instances of the specified type. If no - * running services are instances of the specified type, an empty list is returned - * - * @param type the of the desired service - * @return the registered services that are instances of the specified type and are running - */ - List getServices(Class type); - - /** - * Gets the service factory registered under the specified name. - * - * @param serviceName the unique name of the service - * @return the service factory associated with the specified name - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - ServiceFactory getServiceFactory(ServiceName serviceName) throws ServiceNotFoundException; - - /** - * Gets the first service factory registered with the kernel that creates an instance of the specified type. - * If no service factories create an instance of the specified type, null is returned. - * - * @param type the of the desired service - * @return the first service factory registered with the kernel that creates an instance of the specified type - */ - ServiceFactory getServiceFactory(Class type); - - /** - * Gets the all of the service factories registered with the kernel that create an instances of the specified type. - * If no service factories create an instance of the specified type, an empty list is returned. - * - * @param type the of the desired service - * @return the registered services that are instances of the specified type and are running - */ - List getServiceFactories(Class type); - - /** - * Gets the class loader associated with the specifed service. - * - * @param serviceName the unique name of the service - * @return the class loader associated with the specified name - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - ClassLoader getClassLoaderFor(ServiceName serviceName) throws ServiceNotFoundException; - - /** - * Adds a kernel monitor. - * - * @param kernelMonitor the kernel monitor to add - */ - void addKernelMonitor(KernelMonitor kernelMonitor); - - /** - * Removes a kernel monitor. - * - * @param kernelMonitor the kernel monitor to remove - */ - void removeKernelMonitor(KernelMonitor kernelMonitor); - - /** - * Adds a service monitor for all services registered with the kernel. This method is equivalent to: - *

-     *     addServiceMonitor(serviceMonitor, null);
-     * 
- *

- * Note: the order in which service monitors are notified is not specified. - * - * @param serviceMonitor the service monitor to add - */ - void addServiceMonitor(ServiceMonitor serviceMonitor); - - /** - * Adds a service monitor for a specific service. - *

- * Note: the order in which service monitors are notified is not specified. - * - * @param serviceMonitor the service monitor to add - * @param serviceName the unique name of the service to monitor or null to monitor all services - */ - void addServiceMonitor(ServiceMonitor serviceMonitor, ServiceName serviceName); - - /** - * Removes a service monitor. - * - * @param serviceMonitor the service monitor to remove - */ - void removeServiceMonitor(ServiceMonitor serviceMonitor); -} diff --git a/kernel/src/java/org/xbean/kernel/KernelAlreadyExistsException.java b/kernel/src/java/org/xbean/kernel/KernelAlreadyExistsException.java deleted file mode 100644 index 1c74fa31..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelAlreadyExistsException.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Indicates that a kernel is already registerd with the KernelFactory under the specified name. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelAlreadyExistsException extends RuntimeException { - private final String name; - - /** - * Creates a KernelAlreadyExistsException using the specified name. - * - * @param name the name of the kernel that was alredy registered - */ - public KernelAlreadyExistsException(String name) { - super("A kernel is already registered with the name " + name); - if (name == null) throw new NullPointerException("name is null"); - this.name = name; - } - - /** - * Gets the name of the kernel that already existed. - * - * @return the name of the kernel that already existed. - */ - public String getName() { - return name; - } -} diff --git a/kernel/src/java/org/xbean/kernel/KernelErrorsError.java b/kernel/src/java/org/xbean/kernel/KernelErrorsError.java deleted file mode 100644 index 86163f9c..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelErrorsError.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.io.PrintStream; -import java.io.PrintWriter; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -/** - * Groups a collection of errors from a set of work so they maybe be thrown together from the kernel. This is used - * when the kernel does aggregate work on somthing that shouldn't fail such as when notifying kernel monitors or - * destroying the kernel. This allows the kernel to preform all required work and then throw any errors as a single - * exception object. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelErrorsError extends Error { - private final List errors; - - /** - * Creates an Errors error containing the list of errors. - * - * @param errors the errors - */ - public KernelErrorsError(List errors) { - if (errors == null) throw new NullPointerException("errors is null"); - if (errors.isEmpty()) throw new IllegalArgumentException("errors is empty"); - for (ListIterator iterator = errors.listIterator(); iterator.hasNext();) { - Object error = iterator.next(); - if (error == null) { - throw new IllegalArgumentException("Errors element " + iterator.previousIndex() + " is null"); - } - if (!(error instanceof Error)) { - throw new IllegalArgumentException("Errors element " + iterator.previousIndex() + - " is not an instance of java.lang.Error " + error.getClass() + ": " + error); - } - } - - this.errors = Collections.unmodifiableList(errors); - } - - /** - * Gets the errors that casued this error. - * - * @return the errors that casued this error - */ - public List getErrors() { - return errors; - } - - public String getMessage() { - StringBuffer message = new StringBuffer(); - message.append(errors.size() + " Error(s) occured ["); - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Error error = (Error) iterator.next(); - message.append('\"').append(error.getMessage()).append('\"'); - if (iterator.hasNext()) { - message.append(", "); - } - } - return message.append("]").toString(); - } - - public String getLocalizedMessage() { - StringBuffer message = new StringBuffer(); - message.append(errors.size() + " Error(s) occured ["); - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Error error = (Error) iterator.next(); - message.append('\"').append(error.getLocalizedMessage()).append('\"'); - if (iterator.hasNext()) { - message.append(", "); - } - } - return message.append("]").toString(); - } - - public void printStackTrace(PrintStream stream) { - synchronized (stream) { - stream.println(this); - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Error error = (Error) iterator.next(); - error.printStackTrace(stream); - } - } - } - - public void printStackTrace(PrintWriter writer) { - synchronized (writer) { - writer.println(this); - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Error error = (Error) iterator.next(); - error.printStackTrace(writer); - } - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/KernelFactory.java b/kernel/src/java/org/xbean/kernel/KernelFactory.java deleted file mode 100644 index b8f04fa0..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelFactory.java +++ /dev/null @@ -1,193 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; -import org.xbean.kernel.standard.StandardKernelFactory; - -/** - * The Kernel factory is used to construct and locate Kernels. This class is loosly based on the SAXParserFactory and - * the JMX MBeanServerFactory. To constuct a kernel use the following: - *

- * Kernel kernel = KernelFactory.newInstance().createKernel(name);
- * 
- * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public abstract class KernelFactory { - /** - * The name of the system property and META-INF/services used to locate the kernel factory class. - */ - public static final String KERNEL_FACTORY_KEY = KernelFactory.class.getName(); - - private static final ConcurrentHashMap kernels = new ConcurrentHashMap(1); - - /** - * Gets the kernel registered under the specified name. If no kernel is registered with the specified name, null - * is returned. - * - * @param name the name of the kernel to return - * @return the kernel or null if no kernel is registered under the specified name - */ - public static Kernel getKernel(String name) { - if (name == null) throw new NullPointerException("name is null"); - return (Kernel) kernels.get(name); - } - - /** - * Gets a map of the existing kernels by kernel name. - * - * @return the existing kernels by kernel name. - */ - public static Map getKernels() { - return new HashMap(kernels); - } - - /** - * Creates a kernel with the specified name. This method will attempt to locate a KernelFactory implementation - * using the following procedure - * - * The factory class is loaded and constucted using the thread context class loader, if present, or the class - * loader of this class. - * - * @return the kernel factory implementation - * @throws KernelFactoryError if the specified kernel factory can not be created - */ - public static KernelFactory newInstance() throws KernelFactoryError { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader == null) { - classLoader = KernelFactory.class.getClassLoader(); - } - - // System property - try { - String kernelFactoryName = System.getProperty(KERNEL_FACTORY_KEY); - if (kernelFactoryName != null) { - return createKernelFactory(kernelFactoryName, classLoader); - } - } catch (SecurityException se) { - } - - // Jar Service Specification - http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html - String serviceId = "META-INF/services/" + KERNEL_FACTORY_KEY; - InputStream inputStream = null; - try { - inputStream = classLoader.getResourceAsStream(serviceId); - if (inputStream != null) { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - String kernelFactoryName = reader.readLine(); - reader.close(); - - if (kernelFactoryName != null && kernelFactoryName.length() > 0) { - return createKernelFactory(kernelFactoryName, classLoader); - } - } - } catch (Exception ignored) { - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException ignored) { - } - inputStream = null; - } - } - - // Default is the standard kernel - return new StandardKernelFactory(); - } - - /** - * Removes the kernel instance from the internal kernel registry. This method should only be called by the kernel - * instance itself during destruction. - * @param kernel the kernel to destroy - * @throws KernelFactoryError if the kernel is still running - */ - public static void destroyInstance(Kernel kernel) throws KernelFactoryError { - if (kernel.isRunning()) { - throw new KernelFactoryError("Kernel is running: name" + kernel.getKernelName()); - } - - kernels.remove(kernel.getKernelName(), kernel); - } - - private static KernelFactory createKernelFactory(String className, ClassLoader classLoader) throws KernelFactoryError { - try { - return (KernelFactory) classLoader.loadClass(className).newInstance(); - } catch (ClassCastException e) { - throw new KernelFactoryError("Kernel factory class does not implement KernelFactory: " + className); - } catch (ClassNotFoundException e) { - throw new KernelFactoryError("Kernel factory class not found: " + className); - } catch (Exception e) { - throw new KernelFactoryError("Unable to instantiate kernel factory class: " + className, e); - } - } - - /** - * Creates a new kernel instance and registers it with the static KernelFactory registry. This allows the kernel - * to be retrieved from the {@link KernelFactory#getKernel(String)} method. - * - * @param name the name of the kernel to create - * @return the new kernel - * @throws KernelAlreadyExistsException is a kernel already exists with the specified name - */ - public final Kernel createKernel(String name) throws KernelAlreadyExistsException { - if (name == null) throw new NullPointerException("name is null"); - - // quick check to see if a kernel already registerd wit the name - if (kernels.containsKey(name)) { - throw new KernelAlreadyExistsException(name); - } - - // create the kernel -- this may be an unnecessary construction, but it shouldn't be a big deal - Kernel kernel = createKernelInternal(name); - - // register the kernel, checking if someone snuck in an registered a kernel while we were creating ours - if (kernels.putIfAbsent(name, kernel) != null) { - throw new KernelAlreadyExistsException(name); - } - - return kernel; - } - - /** - * Creates the actual kernel instance which will be registerd in the KernelFactory. - * - * @param name the kernel name - * @return a new kernel instance - */ - protected abstract Kernel createKernelInternal(String name); -} diff --git a/kernel/src/java/org/xbean/kernel/KernelFactoryError.java b/kernel/src/java/org/xbean/kernel/KernelFactoryError.java deleted file mode 100644 index d986f50e..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelFactoryError.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * A problem occured while creating or using the kernel factory. This error indicates that the kernel factory is - * misconfigured or there is a programming error in the use of the kernel factory. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelFactoryError extends Error { - /** - * Creates a KernelFactoryError using the specified message. - * - * @param message information about the cause of this error - */ - public KernelFactoryError(String message) { - super(message); - } - - /** - * Creates a KernelFactoryError using the specified message and cause. - * - * @param message information about the cause of this error - * @param cause the cause of this error - */ - public KernelFactoryError(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/kernel/src/java/org/xbean/kernel/KernelMonitor.java b/kernel/src/java/org/xbean/kernel/KernelMonitor.java deleted file mode 100644 index 9ecc6eed..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelMonitor.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * This interface defines the interface used to monitor kernel events. A KernelMonitor can be registered with the - * kernel using the {@link Kernel#addKernelMonitor(KernelMonitor)} method. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface KernelMonitor { - /** - * An error occured with notifiying a service monitor. - * - * @param serviceMonitor the monitor that threw the exception - * @param serviceEvent the event that was being processed - * @param throwable the exception that was thrown - */ - void serviceNotificationError(ServiceMonitor serviceMonitor, ServiceEvent serviceEvent, Throwable throwable); -} diff --git a/kernel/src/java/org/xbean/kernel/KernelOperationInterruptedException.java b/kernel/src/java/org/xbean/kernel/KernelOperationInterruptedException.java deleted file mode 100644 index f8a12e04..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelOperationInterruptedException.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Signifies that a thread carrying out a kernel operation was interrupted. The kernel will always leave the - * system in a stable state before returning to the caller. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelOperationInterruptedException extends RuntimeException { - private final ServiceName serviceName; - private final String operationName; - - /** - * Created a KernelOperationInterruptedException for the specified operation on the specified service. - * - * @param cause the {@link InterruptedException} that casused the kernel operation to be interrupted - * @param serviceName the name of the service for which the operation was interrupted - * @param operationName the name of the operation that was interrupted - */ - public KernelOperationInterruptedException(InterruptedException cause, ServiceName serviceName, String operationName) { - super(cause); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (operationName == null) throw new NullPointerException("operationName is null"); - this.serviceName = serviceName; - this.operationName = operationName; - } - - /** - * Created a KernelOperationInterruptedException with a custom message. - * - * @param message a custom message for this exception - * @param cause the {@link InterruptedException} that casused the kernel operation to be interrupted - * @param serviceName the name of the service for which the operation was interrupted - * @param operationName the name of the operation that was interrupted - */ - public KernelOperationInterruptedException(String message, InterruptedException cause, ServiceName serviceName, String operationName) { - super(message, cause); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (operationName == null) throw new NullPointerException("operationName is null"); - this.serviceName = serviceName; - this.operationName = operationName; - } - - /** - * Gets the name of the service for which the operation was interrupted. - * - * @return the name of the service for which the operation was interrupted - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the name of the operation that was interrupted. - * - * @return the name of the operation that was interrupted - */ - public String getOperationName() { - return operationName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/KernelOperationTimoutException.java b/kernel/src/java/org/xbean/kernel/KernelOperationTimoutException.java deleted file mode 100644 index 3cadfce6..00000000 --- a/kernel/src/java/org/xbean/kernel/KernelOperationTimoutException.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Signifies that a kernel operation timed out before it could be completed. The kernel will always leave the - * system in a stable state before returning to the caller. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelOperationTimoutException extends RuntimeException { - private final ServiceName serviceName; - private final String operationName; - - /** - * Created a KernelOperationTimoutException for the specified operation on the specified service. - * - * @param serviceName the name of the service for which the operation timed out - * @param operationName the name of the operation that timed out - */ - public KernelOperationTimoutException(ServiceName serviceName, String operationName) { - super("Kernel operation timed out: serviceName=" + serviceName + ", operationName=" + operationName); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (operationName == null) throw new NullPointerException("operationName is null"); - this.serviceName = serviceName; - this.operationName = operationName; - } - - /** - * Created a KernelOperationTimoutException using the specified custom message. - * - * @param message a custom message for this exception - * @param serviceName the name of the service for which the operation timed out - * @param operationName the name of the operation that timed out - */ - public KernelOperationTimoutException(String message, ServiceName serviceName, String operationName) { - super(message); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (operationName == null) throw new NullPointerException("operationName is null"); - this.serviceName = serviceName; - this.operationName = operationName; - } - - /** - * Gets the name of the service for which the operation timed out. - * - * @return the name of the service for which the operation timed out - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the name of the operation that timed out. - * - * @return the name of the operation that timed out - */ - public String getOperationName() { - return operationName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/NullServiceMonitor.java b/kernel/src/java/org/xbean/kernel/NullServiceMonitor.java deleted file mode 100644 index ae721372..00000000 --- a/kernel/src/java/org/xbean/kernel/NullServiceMonitor.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * The NullServiceMonitor is a simple implementation of ServiceMonitor containing a noop implementaion of - * each callback. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class NullServiceMonitor implements ServiceMonitor { - /** - * {@inheritDoc} - */ - public void serviceRegistered(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceStarting(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStart(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceStartError(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceRunning(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceStopping(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStop(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceStopError(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceStopped(ServiceEvent serviceEvent) { - } - - /** - * {@inheritDoc} - */ - public void serviceUnregistered(ServiceEvent serviceEvent) { - } -} diff --git a/kernel/src/java/org/xbean/kernel/RunningServiceCondition.java b/kernel/src/java/org/xbean/kernel/RunningServiceCondition.java deleted file mode 100644 index 764d0d92..00000000 --- a/kernel/src/java/org/xbean/kernel/RunningServiceCondition.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * This condition that requires another service be in the RUNNING state to be satisfied. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class RunningServiceCondition implements ServiceCondition { - private final ServiceName dependency; - private final boolean ownedRelationship; - private final boolean stopOnServiceShutdown; - private final DependencyServiceMonitor serviceMonitor = new DependencyServiceMonitor(); - - private ServiceConditionContext context; - private boolean satisfied = true; - private StoppedServiceCondition stoppedServiceCondition; - - /** - * Creates a condition that requires the specified service be in the RUNNING state to be satisfied. - * - * @param dependency the service that must be running - * @param ownedRelationship if true the condition will register the relationship - * @param stopOnServiceShutdown if the our service should be stopped when the specified service shutsdown - */ - public RunningServiceCondition(ServiceName dependency, boolean ownedRelationship, boolean stopOnServiceShutdown) { - if (dependency == null) throw new NullPointerException("dependency is null"); - this.dependency = dependency; - this.ownedRelationship = ownedRelationship; - this.stopOnServiceShutdown = stopOnServiceShutdown; - } - - /** - * {@inheritDoc} - */ - public synchronized void initialize(ServiceConditionContext context) { - if (context == null) throw new NullPointerException("context is null"); - - // if we have no been destroyed, destroy not - if (this.context != null) { - destroy(); - } - - this.context = context; - - satisfied = false; - context.getKernel().addServiceMonitor(serviceMonitor, dependency); - if (ownedRelationship) { - // todo register owned relationship - } - - if (stopOnServiceShutdown) { - stoppedServiceCondition = new StoppedServiceCondition(context.getServiceName()); - } - } - - /** - * {@inheritDoc} - */ - public synchronized boolean isSatisfied() { - if (context == null) { - // we are not initialized so default to true - return true; - } - - if (!satisfied) { - try { - // grab a synchronized lock on the service factory to assure that the state doesn't change while - // adding the dependency.... the kernel will grab the same lock when getting the stop dependencies - ServiceFactory serviceFactory = context.getKernel().getServiceFactory(dependency); - synchronized (serviceFactory) { - if (context.getKernel().getService(dependency) == ServiceState.RUNNING) { - if (stopOnServiceShutdown) { - serviceFactory.addStopCondition(stoppedServiceCondition); - } - satisfied = true; - context.getKernel().removeServiceMonitor(serviceMonitor); - } - } - } catch (ServiceNotFoundException ignored) { - // service hasn't registered yet - } - } - return satisfied; - } - - /** - * {@inheritDoc} - */ - public synchronized void destroy() { - if (context == null) { - // we are already destroyed - return; - } - - context.getKernel().removeServiceMonitor(serviceMonitor); - context = null; - satisfied = true; - if (ownedRelationship) { - // todo unregister owned relationship - } - if (stopOnServiceShutdown) { - stoppedServiceCondition.destroy(); - stoppedServiceCondition = null; - } - } - - private class DependencyServiceMonitor extends NullServiceMonitor { - public void serviceRunning(ServiceEvent serviceEvent) { - synchronized (RunningServiceCondition.this) { - if (context != null) { - // we aren't running anymore - return; - } - - if (!satisfied) { - return; - } - - if (isSatisfied()) { - context.setSatisfied(); - } - } - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceAlreadyExistsException.java b/kernel/src/java/org/xbean/kernel/ServiceAlreadyExistsException.java deleted file mode 100644 index f0e84dea..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceAlreadyExistsException.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Signifies that an attempt was made to register a service using a name that already has a service registered. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceAlreadyExistsException extends Exception { - private final ServiceName serviceName; - - /** - * Creates a ServiceAlreadyExistsException for the specified service name. - * - * @param serviceName the name of the service that already exists - */ - public ServiceAlreadyExistsException(ServiceName serviceName) { - if (serviceName == null) throw new NullPointerException("name is null"); - this.serviceName = serviceName; - } - - /** - * Gets the name of the service that caused this exception. - * - * @return the service name - */ - public ServiceName getServiceName() { - return serviceName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceCondition.java b/kernel/src/java/org/xbean/kernel/ServiceCondition.java deleted file mode 100644 index 53841b92..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceCondition.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * A ServiceContion represents a prerequsite for a service to start or stop. A condition can be added to a service with - * the {@link ServiceFactory#addStartCondition(ServiceCondition)} or - * {@link ServiceFactory#addStopCondition(ServiceCondition)} methods. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceCondition { - /** - * Initializes the condition. The conition is now allowed reserve unique resources and start threads. - * mehtod should never block the thread nor should it throw any exceptions. - *

- * Note: this method is called from within a critical lock within the kernel, so do not block the thread or - * call back into the kernel. This method should never throw an exception. - * - * @param context context information for this condition - */ - void initialize(ServiceConditionContext context); - - /** - * Gets statisfied state of this conditon. Once a condition returns true from this method it is assumed to be satisfied until - * destroyed and reinitialized. - *

- * Note: this method is called from within a critical lock within the kernel, so do not block the thread or - * call back into the kernel. This method should never throw an exception. - * - * @return true if this condition is satisfied; false otherwise - */ - boolean isSatisfied(); - - /** - * Destroys the condition. The condition must release all resources and stop any started threads. - *

- * Note: this method is called from within a critical lock within the kernel, so do not block the thread or - * call back into the kernel. This method should never throw an exception. - */ - void destroy(); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceConditionContext.java b/kernel/src/java/org/xbean/kernel/ServiceConditionContext.java deleted file mode 100644 index f53eaa79..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceConditionContext.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * The ServiceConditionContext contains context information available to a service condition and a method to notify - * the kernel if a service condition is satisified asynchronously. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceConditionContext { - /** - * Gets the kernel in which the service is registered. - * - * @return the kernel in which the service is registered - */ - Kernel getKernel(); - - /** - * Gets the unique name of the service. - * - * @return the unique name of the service - */ - ServiceName getServiceName(); - - /** - * Gets the class loader for the service. - * - * @return the class loader for the service - */ - ClassLoader getClassLoader(); - - /** - * Used to notify the container that the condition has been satisfied asychronously. - */ - void setSatisfied(); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceContext.java b/kernel/src/java/org/xbean/kernel/ServiceContext.java deleted file mode 100644 index 1a3c5e70..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceContext.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * This class contains context information available to a service factory during service construction and destruction. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceContext { - /** - * Gets the kernel in which this service is registered. - * - * @return the kernel in which this service is registered - */ - Kernel getKernel(); - - /** - * Gets the unique name of the service in the kernel. - * - * @return the unique name of this service in the kernel - */ - ServiceName getServiceName(); - - /** - * Gets the class loader for this service. - * - * @return the class loader for this service - */ - ClassLoader getClassLoader(); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceContextThreadLocal.java b/kernel/src/java/org/xbean/kernel/ServiceContextThreadLocal.java deleted file mode 100644 index 4ba06ed7..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceContextThreadLocal.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * ServiceContextThreadLocal carries the ServiceContext on the Thread during service construction and destruction. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public final class ServiceContextThreadLocal { - private ServiceContextThreadLocal() { - } - - private static final ThreadLocal THREAD_LOCAL = new ThreadLocal(); - - /** - * Gets the ServiceContext associated with the current thread. - * @return the ServiceContext associated with the current thread - */ - public static ServiceContext get() { - return (ServiceContext) THREAD_LOCAL.get(); - } - - /** - * Assocates the specified ServiceContext with the current thread. - * @param serviceContext the service context to associate with the current thread - */ - public static void set(ServiceContext serviceContext) { - THREAD_LOCAL.set(serviceContext); - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceEvent.java b/kernel/src/java/org/xbean/kernel/ServiceEvent.java deleted file mode 100644 index e3e68783..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceEvent.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * This class holds information about a service event. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceEvent { - private final long eventId; - private final Kernel kernel; - private final ServiceName serviceName; - private final ServiceFactory serviceFactory; - private final ClassLoader classLoader; - private final Object service; - private final Throwable cause; - private final Set unsatisfiedConditions; - - /** - * Creates a service event. - * - * @param eventId the sequence number for this event - * @param kernel the kernel in which the service is registered - * @param serviceName the name of the service - * @param serviceFactory the factory for the service - * @param classLoader the class loader for the service - * @param service the service instance if it exists - * @param cause the exception that caused the event if this is an exception event - * @param unsatisfiedConditions the unsatified conditions that caused the event if this is a waiting event - */ - public ServiceEvent(long eventId, Kernel kernel, ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader, Object service, Throwable cause, Set unsatisfiedConditions) { - if (kernel == null) throw new NullPointerException("kernel is null"); - if (serviceName == null) throw new NullPointerException("name is null"); - if (serviceFactory == null) throw new NullPointerException("serviceFactory is null"); - if (classLoader == null) throw new NullPointerException("classLoader is null"); - if (unsatisfiedConditions != null && cause != null) throw new IllegalArgumentException("Either unsatisfiedConditions or cause must be null"); - if (cause != null && service != null) throw new IllegalArgumentException("A ServiceEvent can not carry both a cause and a service"); - this.eventId = eventId; - this.kernel = kernel; - this.serviceName = serviceName; - this.serviceFactory = serviceFactory; - this.classLoader = classLoader; - this.service = service; - this.cause = cause; - this.unsatisfiedConditions = unsatisfiedConditions; - } - - /** - * Gets the sequence number for this event. A service gaurentees that events will occur in increasing order with out - * skipping any numbers. - * - * @return the sequence number for this event - */ - public long getEventId() { - return eventId; - } - - /** - * Gets the kernel in which the service is registered. - * - * @return the kernel in which the servce is registerd - */ - public Kernel getKernel() { - return kernel; - } - - /** - * Gets the name of the service. - * - * @return the name of the service - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the service factory for the service. - * - * @return the service factory for the service - */ - public ServiceFactory getServiceFactory() { - return serviceFactory; - } - - /** - * Gets the class loader for the service. - * - * @return the class loader for the service - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Gets the service instance or null if the service doesn't exist. - * - * @return the service instance - */ - public Object getService() { - return service; - } - - /** - * Gets the error that was thrown during startup or shutdown. This is available only in error events. - * - * @return the error - */ - public Throwable getCause() { - return cause; - } - - /** - * Gets the unsatified dependencies that cause the service to wait. This is available only in waiting events. - * - * @return the unsatified dependencies that cause the service to wait - */ - public Set getUnsatisfiedConditions() { - return unsatisfiedConditions; - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceFactory.java b/kernel/src/java/org/xbean/kernel/ServiceFactory.java deleted file mode 100644 index b8659973..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceFactory.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * A service factory is responsible for construction and destruction of a single service. A service factory provides - * the kernel the start conditions, stopped conditions, owned services and enabled status of the service. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceFactory { - /** - * Gets the types of the service this service factory will create. These types is used to index the service within - * the kernel. It is a start error to return an object from create service that is not an instance of every type. - * This is the only type used to index the service, so if the service factory returns a subclass of this type from - * createService, the subtypes will now be reflected in the index. - * - * @return the type of the service this service factory will create - */ - Class[] getTypes(); - - /** - * A restartable service can be started and stopped repeatedly in the kernel. A service that is not restartable - * immediately enters the RUNNING state when registered with the kernel, and can not be started or stopped. - * - * @return true if this service can be started and stopped; false otherwise - */ - boolean isRestartable(); - - /** - * Determines if the service can be instantiated in a kernel. A disabled restartable service can not be - * started and a disabled non-restartable service can not be loaded into a kernel. - * - * @return true if the service factory is enabled; false otherwise - */ - boolean isEnabled(); - - /** - * Sets the enabled status of this service factory. A disabled restartable service can not be - * started and a disabled non-restartable service can not be loaded into a kernel. - * - * @param enabled the new enabled state of this factory - */ - void setEnabled(boolean enabled); - - /** - * Get an unmodifable snapshot of the conditions that must be satisfied before this service can be started. - * - * @return the start conditions of this service - */ - Set getStartConditions(); - - /** - * Adds start condition to this service. - * - * @param startCondition the new start condition - * @throws NullPointerException if startCondition is null - */ - void addStartCondition(ServiceCondition startCondition) throws NullPointerException; - - /** - * Removes a start condition from this service. - * - * @param startCondition the start condition to remove - * @throws NullPointerException if startCondition is null - */ - void removeStartCondition(ServiceCondition startCondition) throws NullPointerException; - - /** - * Get an unmodifable snapshot of the conditions that must be satisfied before this service can be stopped. - * - * @return the stop conditions of this service - */ - Set getStopConditions(); - - /** - * Adds stop condition to this service. - * - * @param stopCondition the new stop condition - * @throws NullPointerException if stopCondition is null - */ - void addStopCondition(ServiceCondition stopCondition) throws NullPointerException; - - /** - * Removes a stop condition from this service. - * - * @param stopCondition the stop condition to remove - * @throws NullPointerException if stopCondition is null - */ - void removeStopCondition(ServiceCondition stopCondition) throws NullPointerException; - - /** - * Gets the names of services owned by this service. This information is used for the startRecursive method on the - * kernel. When a servcie is started with startRecursive all owned services will be started with startRecursive. - * - * @return the names of the services owned by this service - */ - Set getOwnedServices(); - - /** - * Creates the service instance. - * - * @param serviceContext context information for the new service - * @return the service instance - * @throws Exception if a problem occurs during construction - */ - Object createService(ServiceContext serviceContext) throws Exception; - - /** - * Destroys the service instance. - * - * @param serviceContext the context information for the service - */ - void destroyService(ServiceContext serviceContext); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceMonitor.java b/kernel/src/java/org/xbean/kernel/ServiceMonitor.java deleted file mode 100644 index d1eeed98..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceMonitor.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * This interface is used to monitor service lifecycle events. A ServiceMonitor can be registered with a kernel using - * {@link Kernel#addServiceMonitor(ServiceMonitor)} or {@link Kernel#addServiceMonitor(ServiceMonitor, ServiceName)}. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceMonitor { - /** - * A new service has been registered with the kernel. - * - * @param serviceEvent the event information - */ - void serviceRegistered(ServiceEvent serviceEvent); - - /** - * A service has entered the STARTING state. - * - * @param serviceEvent the event information - */ - void serviceStarting(ServiceEvent serviceEvent); - - /** - * A service is waiting to start because some start conditions are unsatified. - * - * @param serviceEvent the event information - * @see ServiceEvent#getUnsatisfiedConditions() - */ - void serviceWaitingToStart(ServiceEvent serviceEvent); - - /** - * An error occured while calling creating the service. - * - * @param serviceEvent the event information - * @see ServiceEvent#getCause() - */ - void serviceStartError(ServiceEvent serviceEvent); - - /** - * A service has entered the RUNNING state. - * - * @param serviceEvent the event information - */ - void serviceRunning(ServiceEvent serviceEvent); - - /** - * A service has entered the RUNNING state. - * - * @param serviceEvent the event information - */ - void serviceStopping(ServiceEvent serviceEvent); - - /** - * A service is waiting to stop because some stop condition is unsatified. - * - * @param serviceEvent the event information - * @see ServiceEvent#getUnsatisfiedConditions() - */ - void serviceWaitingToStop(ServiceEvent serviceEvent); - - /** - * An error occured while calling destroying the service. - * - * @param serviceEvent the event information - * @see ServiceEvent#getCause() - */ - void serviceStopError(ServiceEvent serviceEvent); - - /** - * A service has entered the STOPPED state. - * - * @param serviceEvent the event information - */ - void serviceStopped(ServiceEvent serviceEvent); - - /** - * A service has been unregistered from the kernel. - * - * @param serviceEvent the event information - */ - void serviceUnregistered(ServiceEvent serviceEvent); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceName.java b/kernel/src/java/org/xbean/kernel/ServiceName.java deleted file mode 100644 index 1d05537b..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceName.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * The immutable unique name of a service. A proper implementation of ServiceName must have a correct implementation of - * equals and hashCode. A ServiceName should have one constructor that takes a single String and the toString method - * should return a String that can be used in the String constructor. This means the following code should work: - *

- * Constructor constructor = serviceName.getClass().getConstructor(new Class[] {String.class});
- * ServiceName name = constructor.newInstance(new Object[] {serviceName.toString()});
- * 
- * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ServiceName { - /** - * A service name must properly implement hashCode. For example, - *

-     * public int hashCode() {
-     *     int result = 17;
-     *     result = 37 * result + integer;
-     *     result = 37 * result + (object == null ? 0 : object.hashCode());
-     *     return result;
-     * }
-     * 
- * - * @return the hash code - */ - int hashCode(); - - /** - * A service name must property implement equals. For example, - *

-     * public boolean equals(Object obj) {
-     *     if (!(obj instanceof MyServiceName)) {
-     *         return false;
-     *     }
-     *     MyServiceName name = (MyServiceName) obj;
-     *     return integer == name.integer &&
-     *             (object == null ? name.object == null : object.equals(name.object));
-     * }
-     * 
- * - * @param object some object - * @return true if the object is equivalent to this service name; false otherwise - */ - boolean equals(Object object); - - /** - * A service name should return a string from toString that can be used in a String constructor. - * - * @return the connonical form of this name - */ - String toString(); -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceNotFoundException.java b/kernel/src/java/org/xbean/kernel/ServiceNotFoundException.java deleted file mode 100644 index 21d240cc..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceNotFoundException.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * A service with the specified name was not found. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceNotFoundException extends Exception { - private final ServiceName serviceName; - - /** - * Creates a ServiceNotFoundException for the specified service name. - * - * @param serviceName the name of the service that was not found. - */ - public ServiceNotFoundException(ServiceName serviceName) { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - this.serviceName = serviceName; - } - - /** - * Gets the name of the service that was not found. - * - * @return the the name of the service that was not found - */ - public ServiceName getServiceName() { - return serviceName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceRegistrationException.java b/kernel/src/java/org/xbean/kernel/ServiceRegistrationException.java deleted file mode 100644 index 809cdfb8..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceRegistrationException.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * A problem occured while attempting to register or unregister an exception. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceRegistrationException extends Exception { - private final ServiceName serviceName; - - /** - * Creates a ServiceRegistrationException for the specified service caused by the specified Throwable. - * - * @param serviceName the name of the service that was being registered or unregistered. - * @param cause the reason the registeration problem occured - */ - public ServiceRegistrationException(ServiceName serviceName, Throwable cause) { - super(cause); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - this.serviceName = serviceName; - } - - /** - * Gets the name of the service that had a registration problem. - * - * @return the the name of the service that had a registration problem - */ - public ServiceName getServiceName() { - return serviceName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/ServiceState.java b/kernel/src/java/org/xbean/kernel/ServiceState.java deleted file mode 100644 index 8de272a4..00000000 --- a/kernel/src/java/org/xbean/kernel/ServiceState.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.io.Serializable; - -/** - * The state of services within the Kernel. The state model is directly adapted from the J2EE Management Specification - * (JSR 77) with the removal of the FAILED state. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public final class ServiceState implements Serializable { - private static final long serialVersionUID = -2629672602273580572L; - - /** - * This state indicates that the service is attempting to start but has not fully started yet. Normally, a service - * in this state is waiting for a required service to enter the RUNNING state. - */ - public static final ServiceState STARTING = new ServiceState((byte) 0, "STARTING"); - - /** - * This state indicates that the service is in the normal operational state. - */ - public static final ServiceState RUNNING = new ServiceState((byte) 1, "RUNNING"); - - /** - * This state indicates that the service is attempting to stop but has not fully stopped yet. Normally, a service - * in this state because another service is still usind this service. - */ - public static final ServiceState STOPPING = new ServiceState((byte) 2, "STOPPING"); - - /** - * This state indicates that the service is stopped and not operational. - */ - public static final ServiceState STOPPED = new ServiceState((byte) 3, "STOPPED"); - - /** - * A quick index for looking up service states. - */ - private static final ServiceState[] serviceStateIndex = new ServiceState[]{STARTING, RUNNING, STOPPING, STOPPED}; - - static { - for (int i = 0; i < serviceStateIndex.length; i++) { - ServiceState serviceState = serviceStateIndex[i]; - if (serviceState.getIndex() != i) { - throw new AssertionError(serviceState + " state index is " + serviceState.getIndex() + - ", but is located at index " + i + " in the serviceStateIndex"); - } - } - } - - /** - * Converts the state index into corresponding state name. - * - * @param state the state index - * @return the name of the state - * @throws IllegalArgumentException if the state index is not 0, 1, 2 or 3 - */ - public static ServiceState getServiceState(int state) throws IllegalArgumentException { - if (state < 0 || state >= serviceStateIndex.length) { - throw new IllegalArgumentException("Unknown state " + state); - } - return serviceStateIndex[state]; - } - - /** - * Converts the state name in the corresponding state index. This method performs a case insensitive comparison. - * - * @param state the state name - * @return the state index - * @throws IllegalArgumentException if the state index is not STARTING, RUNNING, STOPPING or FAILED - */ - public static ServiceState parseServiceState(String state) { - if (state == null) throw new NullPointerException("state is null"); - if (STARTING.toString().equalsIgnoreCase(state)) { - return STARTING; - } else if (RUNNING.toString().equalsIgnoreCase(state)) { - return RUNNING; - } else if (STOPPING.toString().equalsIgnoreCase(state)) { - return STOPPING; - } else if (STOPPED.toString().equalsIgnoreCase(state)) { - return STOPPED; - } else { - throw new IllegalArgumentException("Unknown state " + state); - } - } - - private final byte index; - private final transient String name; - - private ServiceState(byte index, String name) { - this.index = index; - this.name = name; - } - - /** - * Gets the unique index of this state. This index can be be used to retrieve this state instance using the - * getServiceState(int) method. - * - * @return the unique index of this state - */ - public int getIndex() { - return index; - } - - /** - * The unique name of this state. This uppercase name can be used to retrieve this state instance using the - * parseServiceState(String). - * - * @return the unique name of this state - */ - public String getName() { - return name; - } - - /** - * Returns the name of this state. - * - * @return the unique name of this state - */ - public String toString() { - return name; - } - - private Object readResolve() { - return serviceStateIndex[index]; - } -} diff --git a/kernel/src/java/org/xbean/kernel/StartStrategies.java b/kernel/src/java/org/xbean/kernel/StartStrategies.java deleted file mode 100644 index f05990f6..00000000 --- a/kernel/src/java/org/xbean/kernel/StartStrategies.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * This class contains the built-in common start startegies. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public final class StartStrategies { - private StartStrategies() { - } - - /** - * This strategy attempts to immedately start the service. When there are unsatisfied conditions, this strategy - * will leave the service in the STARTING state, and throw an UnsatisfiedConditionsException - * to the caller. When there is a start error, the service will be destroyed and the exception will be rethrown to - * the caller. - */ - public static final StartStrategy SYNCHRONOUS = new Synchronous(); - - private static class Synchronous implements StartStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws UnsatisfiedConditionsException { - throw new UnsatisfiedConditionsException("Unsatisfied start conditions", serviceName, conditions); - } - - public void startError(ServiceName serviceName, Throwable startError) throws Exception { - if (startError instanceof Exception) { - throw (Exception) startError; - } else if (startError instanceof Error) { - throw (Error) startError; - } else { - throw new AssertionError(startError); - } - } - } - - /** - * This strategy attempts to start the service asynchronously. When there are unsatisfied conditions, this strategy - * will leave the service in the STARTING state, and caller will not recieve any exceptions. - * When there is a start error the service will be destroyed adn the exception will be sent to the service montior. - * The caller will not recieve any start exception. - */ - public static final StartStrategy ASYNCHRONOUS = new Asynchronous(); - - private static class Asynchronous implements StartStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) { - return false; - } - - public void startError(ServiceName serviceName, Throwable startError) { - } - } - - /** - * This strategy wait until the service start. This strategy blocks until all unsatisfied conditons - * are satisfied. When there is a start error, the service will be destroyed and the exception will be rethrown to - * the caller. - */ - public static final StartStrategy BLOCK = new Block(); - - private static class Block implements StartStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) { - return true; - } - - public void startError(ServiceName serviceName, Throwable startError) throws Exception { - if (startError instanceof Exception) { - throw (Exception) startError; - } else if (startError instanceof Error) { - throw (Error) startError; - } else { - throw new AssertionError(startError); - } - } - } - - /** - * This strategy attempts to start the service immedately. When there are unsatisfied conditions or a start error - * the dervice will be destroyed and unregistered. In this case an UnsatisfiedConditionsException or - * the start error will be thrown to the caller. - */ - public static final StartStrategy UNREGISTER = new Unregister(); - - private static class Unregister implements StartStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws UnregisterServiceException { - UnsatisfiedConditionsException userException = new UnsatisfiedConditionsException("Unsatisfied start conditions", serviceName, conditions); - throw new UnregisterServiceException(serviceName, userException); - } - - public void startError(ServiceName serviceName, Throwable startError) throws UnregisterServiceException { - throw new UnregisterServiceException(serviceName, startError); - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/StartStrategy.java b/kernel/src/java/org/xbean/kernel/StartStrategy.java deleted file mode 100644 index 24190321..00000000 --- a/kernel/src/java/org/xbean/kernel/StartStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * The StartStrategy interface is used to assist the kernel in determining how to handle problems that occur while - * starting a service. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface StartStrategy { - /** - * Determines if the kernel should wait for the unsatified conditions to be satisfied. - * - * @param serviceName the name of the service that has the unsatisfied condtions - * @param conditions the unsatisfied condtions - * @return true if the kernel should wait for the conditions to be satisfied; false if the strategy would like - * silently leave the service in the starting state - * @throws UnsatisfiedConditionsException the the strategy would like to leave the service in the starting state - * and throw an exception the caller - * @throws UnregisterServiceException if the strategy would like to ignore the unsatisfied conditions and continue to - * destry the service - */ - boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws UnsatisfiedConditionsException, UnregisterServiceException; - - /** - * Handle the start error. The strategy can rethrow the exception, throw an {@link UnregisterServiceException}, or - * return. If this method rethrows the exception, the service will be destroyed and the exception will be thrown to - * the caller. If an UnregisterServiceException is thrown, the kernel will unregister the service and rethrow - * {@link UnregisterServiceException#getCause()}. If this method returns without throwing an exception, the kernel - * will pass the exception to the service monitor for processing and leave the service in the starting state. - * - * @param serviceName the name of the service that has the error - * @param startError the Exception or Error - * @throws UnregisterServiceException if the strategy would like the service to be destroyed and unregistered - * @throws Exception if the strategy would like to destroy the service and throw the exception to the caller - */ - void startError(ServiceName serviceName, Throwable startError) throws UnregisterServiceException, Exception; -} diff --git a/kernel/src/java/org/xbean/kernel/StaticServiceFactory.java b/kernel/src/java/org/xbean/kernel/StaticServiceFactory.java deleted file mode 100644 index 8bd134f1..00000000 --- a/kernel/src/java/org/xbean/kernel/StaticServiceFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Collections; -import java.util.Set; - -/** - * A basic service factory that always creates the supplied object. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StaticServiceFactory extends AbstractServiceFactory { - private final Object service; - - /** - * Creates a non-restartable service factory which will simply returns the specified service from the createService - * method. - * - * @param service the static to which this factory "creates" - * @throws NullPointerException if service is null - */ - public StaticServiceFactory(Object service) throws NullPointerException { - if (service == null) throw new NullPointerException("service is null"); - this.service = service; - } - - public Class[] getTypes() { - return new Class[]{service.getClass()}; - } - - /** - * {@inheritDoc} - */ - public boolean isRestartable() { - return false; - } - - /** - * {@inheritDoc} - */ - public Set getOwnedServices() { - return Collections.EMPTY_SET; - } - - /** - * Returns the static service instance. - * - * @param serviceContext ignored - * @return the static service instance - */ - public Object createService(ServiceContext serviceContext) { - return service; - } - - /** - * This method is a noop. - * - * @param serviceContext ignored - */ - public void destroyService(ServiceContext serviceContext) { - } -} diff --git a/kernel/src/java/org/xbean/kernel/StopStrategies.java b/kernel/src/java/org/xbean/kernel/StopStrategies.java deleted file mode 100644 index eba98cc5..00000000 --- a/kernel/src/java/org/xbean/kernel/StopStrategies.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * This class contains the built-in common stop startegies. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public final class StopStrategies { - private StopStrategies() { - } - - /** - * This strategy attempts to immedately stop the service. When there are unsatisfied conditions, this strategy - * will leave the service in the STOPPING state, and throw an UnsatisfiedConditionsException - * to the caller. - */ - public static final StopStrategy SYNCHRONOUS = new Synchronous(); - - private static class Synchronous implements StopStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws UnsatisfiedConditionsException { - throw new UnsatisfiedConditionsException("Unsatisfied stop conditions", serviceName, conditions); - } - - } - - /** - * This strategy attempts to stop the service asynchronously. When there are unsatisfied conditions, this strategy - * will leave the service in the STOPPING state, and caller will not recieve any exceptions. - */ - public static final StopStrategy ASYNCHRONOUS = new Asynchronous(); - - private static class Asynchronous implements StopStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) { - return false; - } - - } - - /** - * This strategy wait until the service stops. This strategy blocks until all unsatisfied conditons - * are satisfied. - */ - public static final StopStrategy BLOCK = new Block(); - - private static class Block implements StopStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) { - return true; - } - - } - - /** - * This strategy forceable stops the service. This strategy ignores all unsatisfied conditons. - */ - public static final StopStrategy FORCE = new Force(); - - private static class Force implements StopStrategy { - public boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws ForcedStopException { - throw new ForcedStopException(serviceName, conditions); - } - - } -} diff --git a/kernel/src/java/org/xbean/kernel/StopStrategy.java b/kernel/src/java/org/xbean/kernel/StopStrategy.java deleted file mode 100644 index bf129f76..00000000 --- a/kernel/src/java/org/xbean/kernel/StopStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; - -/** - * The StopStrategy interface is used to assist the kernel in determining how to handle problems that occur while - * stoping a service. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface StopStrategy { - /** - * Determines if the kernel should wait for the unsatified conditions to be satisfied. - * - * @param serviceName the name of the service that has the unsatisfied condtions - * @param conditions the unsatisfied condtions - * @return true if the kernel should wait for the conditions to be satisfied; false if the strategy would like - * silently leave the service in the stopping state - * @throws UnsatisfiedConditionsException the the strategy would like to leave the service in the stopping state - * and throw an exception the caller - * @throws ForcedStopException if the strategy would like to ignore the unsatisfied conditions and continue to - * destroy the service - */ - boolean waitForUnsatisfiedConditions(ServiceName serviceName, Set conditions) throws UnsatisfiedConditionsException, ForcedStopException; -} diff --git a/kernel/src/java/org/xbean/kernel/StoppedServiceCondition.java b/kernel/src/java/org/xbean/kernel/StoppedServiceCondition.java deleted file mode 100644 index f820dbb6..00000000 --- a/kernel/src/java/org/xbean/kernel/StoppedServiceCondition.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * This condition that requires another service be in the STOPPED state to be satisfied. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StoppedServiceCondition implements ServiceCondition { - private final ServiceName dependency; - private final DependencyServiceMonitor serviceMonitor = new DependencyServiceMonitor(); - - private ServiceConditionContext context; - private boolean satisfied = true; - - /** - * Creates a condition that requires the specified service be in the STOPPED state to be satisfied. - * - * @param dependency the service that must be stopped - */ - public StoppedServiceCondition(ServiceName dependency) { - if (dependency == null) throw new NullPointerException("dependency is null"); - this.dependency = dependency; - } - - /** - * {@inheritDoc} - */ - public synchronized void initialize(ServiceConditionContext context) { - if (context == null) throw new NullPointerException("context is null"); - - // if we have no been destroyed, destroy not - if (this.context != null) { - destroy(); - } - - this.context = context; - - satisfied = false; - context.getKernel().addServiceMonitor(serviceMonitor, dependency); - } - - /** - * {@inheritDoc} - */ - public synchronized boolean isSatisfied() { - if (context == null) { - // we are not initialized so default to true - return true; - } - - if (!satisfied) { - try { - if (context.getKernel().getService(dependency) == ServiceState.RUNNING) { - satisfied = true; - context.getKernel().removeServiceMonitor(serviceMonitor); - } - } catch (ServiceNotFoundException ignored) { - // service hasn't registered yet - } - } - return satisfied; - } - - /** - * {@inheritDoc} - */ - public synchronized void destroy() { - if (context == null) { - // we are already destroyed - return; - } - - context.getKernel().removeServiceMonitor(serviceMonitor); - context = null; - satisfied = true; - } - - private class DependencyServiceMonitor extends NullServiceMonitor { - public void serviceStopped(ServiceEvent serviceEvent) { - synchronized (StoppedServiceCondition.this) { - if (context != null) { - // we aren't running anymore - return; - } - - if (!satisfied) { - return; - } - - if (isSatisfied()) { - context.setSatisfied(); - } - } - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/StringServiceName.java b/kernel/src/java/org/xbean/kernel/StringServiceName.java deleted file mode 100644 index b414bab0..00000000 --- a/kernel/src/java/org/xbean/kernel/StringServiceName.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * A simple service name containing a single String. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StringServiceName implements ServiceName { - /** - * The strang name of the service. - */ - private final String name; - - /** - * Create a StringServiceName wrapping specified name. - * - * @param name the name of the service - */ - public StringServiceName(String name) { - if (name == null) throw new NullPointerException("name is null"); - if (name.length() == 0) throw new IllegalArgumentException("name must be atleast one character long"); - this.name = name; - } - - public int hashCode() { - return name.hashCode(); - } - - public boolean equals(Object obj) { - if (obj instanceof StringServiceName) { - StringServiceName stringServiceName = (StringServiceName) obj; - return name.equals(stringServiceName.name); - } - return false; - } - - public String toString() { - return name; - } -} diff --git a/kernel/src/java/org/xbean/kernel/UnregisterServiceException.java b/kernel/src/java/org/xbean/kernel/UnregisterServiceException.java deleted file mode 100644 index 1e2c0812..00000000 --- a/kernel/src/java/org/xbean/kernel/UnregisterServiceException.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -/** - * Signafies that there was a problem starting a service and the StartStrategies would like the kernel to - * unregister the service. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class UnregisterServiceException extends Exception { - private final ServiceName serviceName; - - /** - * Creates a UnregisterServiceException for the specified service name. - * - * @param serviceName the name of the service that is to be unregistered - * @param cause the reason we are unregistering the service - */ - public UnregisterServiceException(ServiceName serviceName, Throwable cause) { - super(cause); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - this.serviceName = serviceName; - } - - /** - * Gets the name of the service that is to be unregistered. - * - * @return the service name - */ - public ServiceName getServiceName() { - return serviceName; - } -} diff --git a/kernel/src/java/org/xbean/kernel/UnsatisfiedConditionsException.java b/kernel/src/java/org/xbean/kernel/UnsatisfiedConditionsException.java deleted file mode 100644 index 714ee659..00000000 --- a/kernel/src/java/org/xbean/kernel/UnsatisfiedConditionsException.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Collections; -import java.util.Set; - -/** - * Signifies that there were unsatified conditions during a start or stop operation. The service is left in the - * STARTING state or STOPPING state. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class UnsatisfiedConditionsException extends Exception { - private final ServiceName serviceName; - private final Set unsatisfiedConditions; - - /** - * Creates an UnsatisfiedConditionsException for the specified service and unsatisfied conditions. - * - * @param message information about the unsatisfied conditions - * @param serviceName the name of the service that has unsatisfied conditions - * @param unsatisfiedConditions the unsatisfied conditions - */ - public UnsatisfiedConditionsException(String message, ServiceName serviceName, Set unsatisfiedConditions) { - super(message + ": serviceName=" + serviceName + ": unsatisfiedConditions=" + unsatisfiedConditions); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (unsatisfiedConditions == null) throw new NullPointerException("unsatisfiedConditions is null"); - this.serviceName = serviceName; - this.unsatisfiedConditions = Collections.unmodifiableSet(unsatisfiedConditions); - } - - /** - * Gets the name of the service that has unsatisfied conditions. - * - * @return the service name - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the conditions that were unsatified when the exception was thrown. - * - * @return the unsatified conditions that were ignored - */ - public Set getUnsatisfiedConditions() { - return unsatisfiedConditions; - } -} diff --git a/kernel/src/java/org/xbean/kernel/package.html b/kernel/src/java/org/xbean/kernel/package.html deleted file mode 100644 index 67d6b6c9..00000000 --- a/kernel/src/java/org/xbean/kernel/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Defines the kernel interface for managing services and interfaces the kernel uses to interact with the services. - - \ No newline at end of file diff --git a/kernel/src/java/org/xbean/kernel/standard/AggregateCondition.java b/kernel/src/java/org/xbean/kernel/standard/AggregateCondition.java deleted file mode 100644 index 48c94392..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/AggregateCondition.java +++ /dev/null @@ -1,198 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.ArrayList; -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 edu.emory.mathcs.backport.java.util.concurrent.locks.Condition; -import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceCondition; -import org.xbean.kernel.ServiceName; - -/** - * Aggregates a set of ServiceConditions together so the ServiceManager can treat them as a single unit. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class AggregateCondition { - private final Kernel kernel; - private final ServiceName serviceName; - private final ClassLoader classLoader; - private final Lock lock; - private final Map conditions = new HashMap(); - private final Condition satisfiedSignal; - private boolean destroyed = false; - - /** - * Creates an aggregate condition. - * - * @param kernel the kernel in which the service is registered - * @param serviceName the name of the service - * @param classLoader the class loader for the service - * @param lock the lock for the service manager - * @param conditions the conditions - */ - public AggregateCondition(Kernel kernel, ServiceName serviceName, ClassLoader classLoader, Lock lock, Set conditions) { - this.kernel = kernel; - this.serviceName = serviceName; - this.classLoader = classLoader; - this.lock = lock; - satisfiedSignal = lock.newCondition(); - - // add the conditions to the registry - if (conditions == null) throw new NullPointerException("conditions is null"); - for (Iterator iterator = conditions.iterator(); iterator.hasNext();) { - ServiceCondition serviceCondition = (ServiceCondition) iterator.next(); - addCondition(serviceCondition); - } - } - - /** - * Gets a snapshot of the current conditions. - * - * @return a snapshot of the current conditions - */ - protected Set getConditions() { - return new HashSet(conditions.keySet()); - } - - /** - * Adds a new condition if not already registered. - * - * @param condition the new condition - */ - protected final void addCondition(ServiceCondition condition) { - if (!conditions.containsKey(condition)) { - StandardServiceConditionContext context = new StandardServiceConditionContext(kernel, serviceName, classLoader, lock, satisfiedSignal); - condition.initialize(context); - conditions.put(condition, context); - } - } - - /** - * Removes a condition from the registry if present. - * - * @param condition the condition to remove - */ - protected final void removeCondition(ServiceCondition condition) { - if (conditions.remove(condition) != null) { - condition.destroy(); - } - } - - /** - * Initializes the conditions. - */ - public void initialize() { - if (destroyed) throw new IllegalStateException("destroyed"); - - for (Iterator iterator = conditions.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - ServiceCondition condition = (ServiceCondition) entry.getKey(); - StandardServiceConditionContext context = (StandardServiceConditionContext) entry.getValue(); - condition.initialize(context); - } - } - - /** - * Gets the unsatisfied conditions. - * - * @return the unstatisfied conditions - */ - public Set getUnsatisfied() { - if (destroyed) throw new IllegalStateException("destroyed"); - - Set unsatisfied = new HashSet(); - for (Iterator iterator = conditions.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - ServiceCondition condition = (ServiceCondition) entry.getKey(); - StandardServiceConditionContext context = (StandardServiceConditionContext) entry.getValue(); - if (!context.isSatisfied()) { - if (condition.isSatisfied()) { - // the condition is satisfied - // record this fact in the context - context.setSatisfied(); - } else { - unsatisfied.add(condition); - } - } - } - - // notify anyone awaiting satisfaction - if (unsatisfied.isEmpty()) { - satisfiedSignal.signalAll(); - } - return unsatisfied; - } - - /** - * Gets the destroyed status. - * - * @return true if this AggregateCondition been destroyed; false otherwise - */ - public boolean isDestroyed() { - return destroyed; - } - - /** - * Destroys all condtions. - * - * @return a list of the Exceptions or Errors that occured while destroying the conditon objects. - */ - public List destroy() { - List stopErrors = new ArrayList(); - if (!destroyed) { - destroyed = true; - for (Iterator iterator = conditions.keySet().iterator(); iterator.hasNext();) { - ServiceCondition condition = (ServiceCondition) iterator.next(); - try { - condition.destroy(); - } catch (RuntimeException stopError) { - stopErrors.add(stopError); - } catch (Error stopError) { - stopErrors.add(stopError); - } - } - // notify anyone awaiting satisfaction - satisfiedSignal.signalAll(); - } - return stopErrors; - } - - /** - * Causes the current thread to wait until the conditons is satisfied. - * - * @throws InterruptedException if the thread is interrupted - */ - public void awaitSatisfaction() throws InterruptedException { - while (!destroyed) { - if (getUnsatisfied().isEmpty()) { - return; - } - satisfiedSignal.await(); - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/AsyncServiceMonitor.java b/kernel/src/java/org/xbean/kernel/standard/AsyncServiceMonitor.java deleted file mode 100644 index 6a01dbe8..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/AsyncServiceMonitor.java +++ /dev/null @@ -1,155 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import edu.emory.mathcs.backport.java.util.concurrent.Executor; -import org.xbean.kernel.ServiceEvent; -import org.xbean.kernel.ServiceMonitor; - -/** - * The AsyncServiceMonitor delivers service events to a delegate ServiceMonitor asynchronously using an executor. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class AsyncServiceMonitor implements ServiceMonitor { - private final ServiceMonitor delegate; - private final Executor executor; - - /** - * Creates a AsyncServiceMonitor which asynchronously delivers service events to specified delegate - * ServiceMonitor using the specified executor. - * - * @param delegate the service monitor that should recieve the asynchronous events - * @param executor the executor used to asynchronously deliver the events - */ - public AsyncServiceMonitor(ServiceMonitor delegate, Executor executor) { - this.delegate = delegate; - this.executor = executor; - } - - /** - * {@inheritDoc} - */ - public void serviceRegistered(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceRegistered(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceStarting(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceStarting(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStart(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceWaitingToStart(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceStartError(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceStartError(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceRunning(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceRunning(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceStopping(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceStopping(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStop(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceWaitingToStop(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceStopError(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceStopError(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceStopped(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceStopped(serviceEvent); - } - }); - } - - /** - * {@inheritDoc} - */ - public void serviceUnregistered(final ServiceEvent serviceEvent) { - executor.execute(new Runnable() { - public void run() { - delegate.serviceUnregistered(serviceEvent); - } - }); - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/KernelMonitorBroadcaster.java b/kernel/src/java/org/xbean/kernel/standard/KernelMonitorBroadcaster.java deleted file mode 100644 index daa10a1c..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/KernelMonitorBroadcaster.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; -import org.xbean.kernel.KernelMonitor; -import org.xbean.kernel.ServiceMonitor; -import org.xbean.kernel.ServiceEvent; -import org.xbean.kernel.KernelErrorsError; - -/** - * The KernelMonitorBroadcaster broadcasts kernel events to registered kernel monitors. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelMonitorBroadcaster implements KernelMonitor { - /** - * The monitors of kernel events. - */ - private final CopyOnWriteArrayList kernelMonitors = new CopyOnWriteArrayList(); - - /** - * Adds a kernel monitor. - * - * @param kernelMonitor the kernel monitor to add - */ - public void addKernelMonitor(KernelMonitor kernelMonitor) { - kernelMonitors.addIfAbsent(kernelMonitor); - } - - /** - * Removes a kernel monitor. - * - * @param kernelMonitor the kernel monitor to remove - */ - public void removeKernelMonitor(KernelMonitor kernelMonitor) { - kernelMonitors.remove(kernelMonitor); - } - - /** - * {@inheritDoc} - */ - public void serviceNotificationError(ServiceMonitor serviceMonitor, ServiceEvent serviceEvent, Throwable throwable) { - List errors = new ArrayList(); - for (Iterator iterator = kernelMonitors.iterator(); iterator.hasNext();) { - KernelMonitor kernelMonitor = (KernelMonitor) iterator.next(); - try { - kernelMonitor.serviceNotificationError(serviceMonitor, serviceEvent, throwable); - } catch (RuntimeException ignored) { - // ignore - we did our best to notify the world - } catch (Error e) { - errors.add(e); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/NonRestartableStopCondition.java b/kernel/src/java/org/xbean/kernel/standard/NonRestartableStopCondition.java deleted file mode 100644 index c154fd0b..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/NonRestartableStopCondition.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; - -import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceCondition; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; - -/** - * A special sub-class of AggregateCondition used to manage the stop conditions of a non-restartable service. This class - * will update stop conditions to reflect the stop conditions currently registered with the service factory, when the - * initialized or getUnsatisfied methods are called. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class NonRestartableStopCondition extends AggregateCondition { - private final ServiceFactory serviceFactory; - - /** - * Creates a NonRestartableStopCondition. - * - * @param kernel the kernel in which the service is registered - * @param serviceName the name of the service - * @param classLoader the class loader for the service - * @param lock the lock for the service manager - * @param serviceFactory the service factory for the service - */ - public NonRestartableStopCondition(Kernel kernel, ServiceName serviceName, ClassLoader classLoader, Lock lock, ServiceFactory serviceFactory) { - super(kernel, serviceName, classLoader, lock, Collections.EMPTY_SET); - this.serviceFactory = serviceFactory; - } - - /** - * Throws UnsupportedOperationException. Initialize is not a valid operation for a NonRestartableStopCondition - * - * @throws UnsupportedOperationException always - */ - public synchronized void initialize() throws UnsupportedOperationException { - throw new UnsupportedOperationException("initialize should never be called on a NonRestartableStopCondition"); - } - - /** - * {@inheritDoc} - */ - public synchronized Set getUnsatisfied() { - updateConditions(); - return super.getUnsatisfied(); - } - - private void updateConditions() { - if (isDestroyed()) throw new IllegalStateException("destroyed"); - - Set conditions = getConditions(); - - // add the new conditions - Set stopConditions = serviceFactory.getStopConditions(); - for (Iterator iterator = stopConditions.iterator(); iterator.hasNext();) { - ServiceCondition condition = (ServiceCondition) iterator.next(); - if (!conditions.contains(condition)) { - addCondition(condition); - } - } - - // remove the conditions that were dropped - for (Iterator iterator = conditions.iterator(); iterator.hasNext();) { - ServiceCondition serviceCondition = (ServiceCondition) iterator.next(); - if (!stopConditions.contains(serviceCondition)) { - removeCondition(serviceCondition); - } - } - } - -} diff --git a/kernel/src/java/org/xbean/kernel/standard/RegistryFutureTask.java b/kernel/src/java/org/xbean/kernel/standard/RegistryFutureTask.java deleted file mode 100644 index cb22328c..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/RegistryFutureTask.java +++ /dev/null @@ -1,142 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import edu.emory.mathcs.backport.java.util.concurrent.Callable; -import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.StopStrategy; - -/** - * RegistryFutureTask preforms service registration and unregistration in a FutureTask. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -class RegistryFutureTask extends FutureTask implements Comparable { - private final long serviceId; - private final ServiceName serviceName; - private final String taskType; - private Throwable throwable; - - static RegistryFutureTask createRegisterTask(ServiceManager serviceManager) { - RegisterCallable registerCallable = new RegisterCallable(serviceManager); - RegistryFutureTask registryFutureTask = new RegistryFutureTask(serviceManager.getServiceId(), - serviceManager.getServiceName(), - "RegisterServiceManager", - registerCallable); - return registryFutureTask; - } - - static RegistryFutureTask createUnregisterTask(ServiceManager serviceManager, StopStrategy stopStrategy) { - UnregisterCallable unregisterCallable = new UnregisterCallable(serviceManager, stopStrategy); - RegistryFutureTask registryFutureTask = new RegistryFutureTask(serviceManager.getServiceId(), - serviceManager.getServiceName(), - "UnregisterServiceManager", - unregisterCallable); - unregisterCallable.setRegistryFutureTask(registryFutureTask); - return registryFutureTask; - } - - private RegistryFutureTask(long serviceId, ServiceName serviceName, String taskType, Callable callable) { - super(callable); - this.serviceId = serviceId; - this.serviceName = serviceName; - this.taskType = taskType; - } - - public ServiceName getServiceName() { - return serviceName; - } - - public synchronized Throwable getThrowable() { - return throwable; - } - - private synchronized void setThrowable(Throwable throwable) { - this.throwable = throwable; - } - - public int hashCode() { - return (int) (serviceId ^ (serviceId >>> 32)); - } - - public boolean equals(Object o) { - if (o instanceof RegistryFutureTask) { - return serviceId == ((RegistryFutureTask) o).serviceId; - } - return false; - } - - public int compareTo(Object o) { - RegistryFutureTask registryFutureTask = (RegistryFutureTask) o; - - if (serviceId < registryFutureTask.serviceId) { - return -1; - } else if (serviceId > registryFutureTask.serviceId) { - return 1; - } else { - return 0; - } - } - - public String toString() { - return "[RegistryFutureTask: task=" + taskType + ", serviceName=" + serviceName + "]"; - } - - - private static class RegisterCallable implements Callable { - private final ServiceManager serviceManager; - - private RegisterCallable(ServiceManager serviceManager) { - this.serviceManager = serviceManager; - } - - public Object call() throws Exception { - serviceManager.initialize(); - return serviceManager; - } - } - - private static class UnregisterCallable implements Callable { - private final ServiceManager serviceManager; - private final StopStrategy stopStrategy; - private RegistryFutureTask registryFutureTask; - - private UnregisterCallable(ServiceManager serviceManager, StopStrategy stopStrategy) { - this.serviceManager = serviceManager; - this.stopStrategy = stopStrategy; - } - - public void setRegistryFutureTask(RegistryFutureTask registryFutureTask) { - this.registryFutureTask = registryFutureTask; - } - - public Object call() { - try { - serviceManager.destroy(stopStrategy); - return null; - } catch (Throwable e) { - // Destroy failed, save the exception so it can be rethrown from the unregister method - registryFutureTask.setThrowable(e); - - // return the service manager so the service remains registered - return serviceManager; - } - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/ServiceManager.java b/kernel/src/java/org/xbean/kernel/standard/ServiceManager.java deleted file mode 100644 index 02ae510e..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/ServiceManager.java +++ /dev/null @@ -1,734 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Collections; - -import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; -import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; -import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock; -import org.xbean.kernel.ForcedStopException; -import org.xbean.kernel.IllegalServiceStateException; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelOperationInterruptedException; -import org.xbean.kernel.KernelOperationTimoutException; -import org.xbean.kernel.ServiceCondition; -import org.xbean.kernel.ServiceEvent; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceMonitor; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceState; -import org.xbean.kernel.StartStrategies; -import org.xbean.kernel.StartStrategy; -import org.xbean.kernel.StopStrategy; -import org.xbean.kernel.UnregisterServiceException; -import org.xbean.kernel.UnsatisfiedConditionsException; -import org.xbean.kernel.InvalidServiceTypeException; - -/** - * The ServiceManager handles the life cycle of a single service. The manager is responsible for gaurenteeing that - * all start conditions have been satisfied before the service is constructed, and that all stop conditions have been - * satisfied before the service is destroyed. The ServiceManager can be started and stopped several times, but once - * destroyed no methods may be called. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceManager implements Comparable { - /** - * The kernel in which this service is registered. - */ - private final Kernel kernel; - - /** - * The unique id of this service in the kernel. - */ - private final long serviceId; - - /** - * The unique name of this service in the kernel. - */ - private final ServiceName serviceName; - - /** - * The factory used to create and destroy the service instance. - */ - private final ServiceFactory serviceFactory; - - /** - * The type of service this service manager will create. This value is cached from the serviceFactory.getT - */ - private final Set serviceTypes; - - /** - * The class loader for this service. - */ - private final ClassLoader classLoader; - - /** - * The monitor to which we fire service events. The ServiceManager requires an asynchronous monitor becuse events are - * fired from within the lock. This helps to reduce complexity but will cause more services to sit in the - * {@link ServiceState#STARTING} and {@link ServiceState#STOPPING} states since events are propagated in a separate - * thread. - */ - private final ServiceMonitor serviceMonitor; - - /** - * The service context given to the service factory. This contans a reference to the kernel, serviceName and - * classloader. - */ - private final StandardServiceContext standardServiceContext; - - /** - * Current state of this service. - */ - private volatile ServiceState state = ServiceState.STOPPED; - - /** - * The time the service was started or 0 if not started. - */ - private volatile long startTime; - - /** - * The {@link ServiceCondition) objects required to be ready before this service can be completely started. - */ - private AggregateCondition startCondition; - - /** - * The {@link ServiceCondition) objects required to be ready before this service can be completely stopped. - */ - private AggregateCondition stopCondition; - - /** - * The service instance. - */ - private volatile Object service; - - /** - * The single lock we use. - */ - private final ReentrantLock lock = new ReentrantLock(); - - /** - * The maximum duration to wait for the lock. - */ - private final long timeoutDuration; - - /** - * The unit of measure for the {@link #timeoutDuration}. - */ - private final TimeUnit timeoutUnits; - - /** - * The name of the operation for which the lock is held; this is used in the reentrant exception message. - */ - private String currentLockHolderOperation = "NOT-HELD"; - - /** - * Sequence number for service event objects. - */ - private final AtomicLong eventId = new AtomicLong(0); - - /** - * If true, when start is successful we will startRecusrive all of the services owned by this service. - */ - private boolean recursive = false; - - /** - * Creates a service manager for a single service. - * - * @param kernel the kernel in which this wraper will be registered - * @param serviceId the unique id of this service in the kernel - * @param serviceName the unique name of this service in the kernel - * @param serviceFactory the factory used to create and destroy the service instance - * @param classLoader the class loader for this service - * @param serviceMonitor the monitor of service events - * @param timeoutDuration the maximum duration to wait for a lock - * @param timeoutUnits the unit of measure for the timeoutDuration - */ - public ServiceManager(Kernel kernel, - long serviceId, - ServiceName serviceName, - ServiceFactory serviceFactory, - ClassLoader classLoader, - ServiceMonitor serviceMonitor, - long timeoutDuration, - TimeUnit timeoutUnits) { - - this.kernel = kernel; - this.serviceId = serviceId; - this.serviceName = serviceName; - this.serviceFactory = serviceFactory; - this.classLoader = classLoader; - this.serviceMonitor = serviceMonitor; - this.timeoutDuration = timeoutDuration; - this.timeoutUnits = timeoutUnits; - standardServiceContext = new StandardServiceContext(kernel, serviceName, classLoader); - serviceTypes = Collections.unmodifiableSet(new LinkedHashSet(Arrays.asList(serviceFactory.getTypes()))); - } - - /** - * Initializes the service. - * - * @throws IllegalServiceStateException if the service is not restartable and is disabled - * @throws UnsatisfiedConditionsException if the service is not restartable and there were unsatisfied start conditions - * @throws Exception if the service is not restartable and service construction threw an exception - * @see Kernel#registerService(ServiceName, ServiceFactory, ClassLoader) - */ - public void initialize() throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - if (!serviceFactory.isRestartable() && !serviceFactory.isEnabled()) { - throw new IllegalServiceStateException("A disabled non-restartable service factory can not be initalized", serviceName); - } - - serviceMonitor.serviceRegistered(createServiceEvent()); - - // if we are not restartable, we need to start immediately, otherwise we are not going to register this service - if (!serviceFactory.isRestartable()) { - try { - start(false, StartStrategies.UNREGISTER); - } catch (UnregisterServiceException e) { - serviceMonitor.serviceUnregistered(createServiceEvent()); - Throwable cause = e.getCause(); - if (cause instanceof Exception) { - throw (Exception) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new AssertionError(cause); - } - } - - // a non restartable service uses a special stop conditions object that picks up stop conditions as they - // are added. When the stop() method is called on a non-restartable service all of the stop conditions - // registered with the service factory are initialized (if not already initialized), and the isSatisfied - // method is called. This should cause the stop logic of a stop condition to fire. - lock("initialize"); - try { - stopCondition = new NonRestartableStopCondition(kernel, serviceName, classLoader, lock, serviceFactory); - } finally { - unlock(); - } - } - } - - /** - * Attempts to stop and destroy the service. - * - * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions - * @throws IllegalServiceStateException is the service did not stop - * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions - * @see Kernel#unregisterService(ServiceName, StopStrategy) - */ - public void destroy(StopStrategy stopStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException { - // if we are not restartable, we need to stop - try { - if (!stop(stopStrategy)) { - throw new IllegalServiceStateException("Service did not stop", serviceName); - } - } catch (UnsatisfiedConditionsException e) { - throw e; - } - - if (!serviceFactory.isRestartable()) { - lock("destroy"); - try { - if (state != ServiceState.STOPPED) { - state = ServiceState.STARTING; - serviceMonitor.serviceStopping(createServiceEvent()); - if (service != null) { - try { - // destroy the service - serviceFactory.destroyService(standardServiceContext); - } catch (Throwable e) { - serviceMonitor.serviceStopError(createErrorServiceEvent(e)); - } - } - - destroyAllConditions(serviceMonitor); - - service = null; - startTime = 0; - state = ServiceState.STOPPED; - serviceMonitor.serviceStopped(createServiceEvent()); - } - } finally { - unlock(); - } - } - - // cool we can unregistered - serviceMonitor.serviceUnregistered(createServiceEvent()); - } - - /** - * Gets the unique id of this service in the kernel. - * - * @return the unique id of this service in the kernel - */ - public long getServiceId() { - return serviceId; - } - - /** - * Gets the unique name of this service in the kernel. - * - * @return the unique name of this servce in the kernel - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * Gets the types of the service that will be managed by this service manager. - * @return the types of the service - */ - public Set getServiceTypes() { - return serviceTypes; - } - - /** - * Gets the factory used to create and destroy the service instance. - * - * @return the factory for the service instance - * @see Kernel#getServiceFactory(ServiceName) - */ - public ServiceFactory getServiceFactory() { - return serviceFactory; - } - - /** - * Gets the class loader for this service. This class loader is provided to the service factory in the - * ServiceContext object. - * - * @return the classloader for this service - * @see Kernel#getClassLoaderFor(ServiceName) - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Gets the service instance. - * - * @return the service instance - * @see Kernel#getService(ServiceName) - */ - public Object getService() { - return service; - } - - /** - * Gets the current state of this service. - * - * @return the current state of this service - * @see Kernel#getServiceState(ServiceName) - */ - public ServiceState getState() { - return state; - } - - /** - * Gets the time at which this service entered the STARTING state or 0 if the service is STOPPED. - * - * @return the start time or 0 if the service is stopped - * @see Kernel#getServiceStartTime(ServiceName) - */ - public long getStartTime() { - return startTime; - } - - /** - * Attempts to starts the service. - * - * @param recursive if start is successful should we start recursive the services owned by this servic - * @param startStrategy the strategy used to determine how to handle unsatisfied start conditions and start errors - * @throws IllegalServiceStateException if the service is in a state in which it can not be started - * @throws UnregisterServiceException if the kernel should unregister this service - * @throws UnsatisfiedConditionsException if there were unsatisfied start conditions - * @throws Exception it service creation threw an exception - * @see Kernel#startService(ServiceName) - * @see Kernel#startServiceRecursive(ServiceName) - */ - public void start(boolean recursive, StartStrategy startStrategy) throws IllegalServiceStateException, UnregisterServiceException, UnsatisfiedConditionsException, Exception { - // verify that it is possible to start this service in the current state before obtaining the lock - if (!verifyStartable(state)) { - if (recursive) { - startOwnedServices(startStrategy); - } - return; - } - - boolean shouldStartRecursive = false; - lock("start"); - try { - // update the recursive flag - this.recursive = this.recursive || recursive; - - Throwable startError = null; - try { - // - // Loop until all start conditions have been satified. The start strategy can break this loop. - // - boolean satisfied = false; - while (!satisfied) { - // do we still want to start? - if (!verifyStartable(state)) { - // assume someone else called startOwnedServices - return; - } - - // if we are in the STOPPED state, we need to move to the STARTING state - if (state == ServiceState.STOPPED) { - // we are now officially starting - state = ServiceState.STARTING; - serviceMonitor.serviceStarting(createServiceEvent()); - - // initialize the start conditions - startCondition = new AggregateCondition(kernel, serviceName, classLoader, lock, serviceFactory.getStartConditions()); - startCondition.initialize(); - } - - // are we satisfied? - Set unsatisfiedConditions = startCondition.getUnsatisfied(); - satisfied = unsatisfiedConditions.isEmpty(); - if (!satisfied) { - // if the stragegy wants us to wait for conditions to be satisfied, it will return true - if (startStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) { - // wait for satisfaction and loop - startCondition.awaitSatisfaction(); - } else { - // no wait, notify the monitor and exit - serviceMonitor.serviceWaitingToStart(createWaitingServiceEvent(unsatisfiedConditions)); - return; - } - } - } - - // we are ready to create the service - service = serviceFactory.createService(standardServiceContext); - - // verify that the service implements all of the types - if (service == null) { - throw new NullPointerException("Service factory return null from createService for service " + serviceName); - } - for (Iterator iterator = serviceTypes.iterator(); iterator.hasNext();) { - Class type = (Class) iterator.next(); - if (!type.isInstance(service)) { - throw new InvalidServiceTypeException(serviceName, type, service.getClass()); - } - } - - // success transition to running - startTime = System.currentTimeMillis(); - state = ServiceState.RUNNING; - serviceMonitor.serviceRunning(createServiceEvent()); - - // should we recursively start our children - shouldStartRecursive = this.recursive || recursive; - this.recursive = false; - } catch (UnsatisfiedConditionsException e) { - // thrown from waitForUnsatisfiedConditions - throw e; - } catch (IllegalServiceStateException e) { - // this can be thrown while awaiting satisfaction - throw e; - } catch (Exception e) { - startError = e; - } catch (Error e) { - startError = e; - } - - if (startError != null) { - try { - if (startError instanceof UnregisterServiceException) { - throw (UnregisterServiceException) startError; - } else { - // the strategy will normally rethrow the startError, but if it doesn't notify the service monitor - startStrategy.startError(serviceName, startError); - serviceMonitor.serviceStartError(createErrorServiceEvent(startError)); - } - } finally { - // we are now STOPPING - state = ServiceState.STOPPING; - serviceMonitor.serviceStopping(createServiceEvent()); - - // clean up the conditons - destroyAllConditions(serviceMonitor); - - // transition to the STOPPED state - service = null; - startTime = 0; - state = ServiceState.STOPPED; - serviceMonitor.serviceStopped(createServiceEvent()); - } - } - } finally { - unlock(); - } - - - // startRecursive all of the owned services - if (shouldStartRecursive) { - startOwnedServices(startStrategy); - } - } - - private void startOwnedServices(StartStrategy startStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - Set ownedServices = serviceFactory.getOwnedServices(); - if (ownedServices == null) throw new NullPointerException("serviceFactory.getOwnedServices() returned null"); - for (Iterator iterator = ownedServices.iterator(); iterator.hasNext();) { - ServiceName ownedService = (ServiceName) iterator.next(); - try { - kernel.startServiceRecursive(ownedService, startStrategy); - } catch (ServiceNotFoundException ignored) { - // this is ok -- service unregistered - } catch (IllegalServiceStateException ignored) { - // ownedService is disabled or stopping -- anyway we don't care - } - } - } - - /** - * Verifies that the service is startable. This can be used out side a lock to avoid unecessary locking. - * - * @param state the state of the service - * @return true if it is possible to start a service in the specifiec state - * @throws IllegalServiceStateException if it is illegal to start a service in the specified state - */ - private boolean verifyStartable(ServiceState state) throws IllegalServiceStateException { - // if we are alredy in the running state, there is nothing to do - if (state == ServiceState.RUNNING) { - return false; - } - - // if we are in the stopping states, that is an error - if (state == ServiceState.STOPPING) { - throw new IllegalServiceStateException("A stopping service can not be started", serviceName); - } - - // is this service enabled? - if (state == ServiceState.STOPPED && !serviceFactory.isEnabled()) { - throw new IllegalServiceStateException("Service is disabled", serviceName); - } - - return true; - } - - /** - * Attempts to stop the service. - * - * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions - * @return true if the service was sucessfully stopped; false otherwise - * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions - * @see Kernel#stopService(ServiceName) - */ - public boolean stop(StopStrategy stopStrategy) throws UnsatisfiedConditionsException { - // check that we aren't already stopped before attempting to acquire the lock - ServiceState initialState = state; - if (initialState == ServiceState.STOPPED) { - return true; - } - - lock("stop"); - try { - try { - // - // Loop until all stop conditions have been satified. The stop strategy can break this loop. - // - boolean satisfied = false; - while (!satisfied) { - // do we still want to stop? - if (state == ServiceState.STOPPED) { - return true; - } - - // if we are not the STOPPING state, transition to it - // we check on the stopConditions variable because non-restartable services preset this in the - // intialization method - if (stopCondition == null) { - // we are not officially stopping - serviceMonitor.serviceStopping(createServiceEvent()); - state = ServiceState.STOPPING; - - // initialize all of the stop conditions - stopCondition = new AggregateCondition(kernel, serviceName, classLoader, lock, serviceFactory.getStopConditions()); - stopCondition.initialize(); - } - - // are we satisfied? - Set unsatisfiedConditions = stopCondition.getUnsatisfied(); - satisfied = unsatisfiedConditions.isEmpty(); - if (!satisfied) { - // if the stragegy wants us to wait for conditions to be satisfied, it will return true - if (stopStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) { - // wait for satisfaction and loop - stopCondition.awaitSatisfaction(); - } else { - // no wait, notify the monitor and exit - serviceMonitor.serviceWaitingToStop(createWaitingServiceEvent(unsatisfiedConditions)); - return false; - } - } - } - } catch (UnsatisfiedConditionsException e) { - throw e; - } catch (ForcedStopException e) { - serviceMonitor.serviceStopError(createErrorServiceEvent(e)); - } catch (Exception e) { - serviceMonitor.serviceStopError(createErrorServiceEvent(e)); - } catch (Error e) { - serviceMonitor.serviceStopError(createErrorServiceEvent(e)); - } - - if (serviceFactory.isRestartable()) { - if (service != null) { - try { - // destroy the service - serviceFactory.destroyService(standardServiceContext); - } catch (Throwable e) { - serviceMonitor.serviceStopError(createErrorServiceEvent(e)); - } - } - - destroyAllConditions(serviceMonitor); - - service = null; - startTime = 0; - state = ServiceState.STOPPED; - serviceMonitor.serviceStopped(createServiceEvent()); - } - return true; - } finally { - unlock(); - } - } - - private void destroyAllConditions(ServiceMonitor monitor) { - if (!lock.isHeldByCurrentThread()) { - throw new IllegalStateException("Current thread must hold lock before calling destroyAllConditions"); - } - - if (startCondition != null) { - List errors = startCondition.destroy(); - // errors from destroying the start conditions are stop errors because destroy is only called while - // stopping the service - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Throwable stopError = (Throwable) iterator.next(); - monitor.serviceStopError(createErrorServiceEvent(stopError)); - } - startCondition = null; - } - if (stopCondition != null) { - List errors = stopCondition.destroy(); - for (Iterator iterator = errors.iterator(); iterator.hasNext();) { - Throwable stopError = (Throwable) iterator.next(); - monitor.serviceStopError(createErrorServiceEvent(stopError)); - } - stopCondition = null; - } - } - - /** - * Obtain the lock for the specified operation. - * - * @param operationName name of the operation that lock will be used for - this is only used for exception messages - * @throws IllegalStateException if thread tries to reenter while holding the lock - * @throws KernelOperationTimoutException if lock could not be obtained in {@link #timeoutDuration} {@link #timeoutUnits} - * @throws KernelOperationInterruptedException if the thread was interrupted while waiting for the lock - */ - private void lock(String operationName) throws IllegalStateException, KernelOperationTimoutException, KernelOperationInterruptedException { - if (lock.isHeldByCurrentThread()) { - throw new IllegalStateException("Current thread holds lock for " + currentLockHolderOperation + - " and lock can not be reacquired for " + operationName + " on " + serviceName); - } - - try { - if (!lock.tryLock(timeoutDuration, timeoutUnits)) { - throw new KernelOperationTimoutException("Could not obtain lock for " + operationName + " operation on " + - serviceName + " within " + timeoutDuration + " " + timeoutUnits.toString().toLowerCase(), - serviceName, - operationName); - } - currentLockHolderOperation = operationName; - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException("Interrupted while attempting to obtain lock for " + operationName + - " operation on " + serviceName, - e, - serviceName, - operationName); - - } - } - - /** - * Unlock the lock and clear the currentLockHolderOperation name. - */ - private void unlock() { - if (!lock.isHeldByCurrentThread()) { - throw new IllegalMonitorStateException("Not owner"); - } - - currentLockHolderOperation = "NOT-HELD"; - lock.unlock(); - } - - private ServiceEvent createServiceEvent() { - return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, classLoader, service, null, null); - } - - private ServiceEvent createWaitingServiceEvent(Set unsatisfiedConditions) { - return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, classLoader, service, null, unsatisfiedConditions); - } - - private ServiceEvent createErrorServiceEvent(Throwable cause) { - return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, classLoader, null, cause, null); - } - - public int hashCode() { - return (int) (serviceId ^ (serviceId >>> 32)); - } - - public boolean equals(Object o) { - if (o instanceof ServiceManager) { - return serviceId == ((ServiceManager)o).serviceId; - } - return false; - } - - public int compareTo(Object o) { - ServiceManager serviceManager = (ServiceManager) o; - - if (serviceId < serviceManager.serviceId) { - return -1; - } else if (serviceId > serviceManager.serviceId) { - return 1; - } else { - return 0; - } - } - - public String toString() { - return "[ServiceManager: serviceId=" + serviceId + ", serviceName=" + serviceName + ", state=" + state + "]"; - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/ServiceManagerFactory.java b/kernel/src/java/org/xbean/kernel/standard/ServiceManagerFactory.java deleted file mode 100644 index 4dc4998d..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/ServiceManagerFactory.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import edu.emory.mathcs.backport.java.util.concurrent.Executor; -import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; - -/** - * The ServiceManagerFactory handles the construction ServiceManagers. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceManagerFactory { - /** - * The kernel in which the service will be bound. - */ - private final Kernel kernel; - - /** - * This monitor broadcasts events to the listeners registered for service. - */ - private final ServiceMonitorBroadcaster serviceMonitor; - - /** - * Events service events are sent asynchronously using this executor. - */ - private final Executor serviceExecutor; - - /** - * The maximum duration to wait for a service event to complete. - */ - private final long timeoutDuration; - - /** - * The unit of measure for the {@link #timeoutDuration}. - */ - private final TimeUnit timeoutUnits; - - /** - * Creates a ServiceManagerFactory. - * - * @param kernel the kernel in which the service will be registered - * @param serviceMonitor the service monitor used for all services created by this factory - * @param serviceExecutor the executor available to the service manager - * @param timeoutDuration the maximum duration to wait for a service event to complete - * @param timeoutUnits the unit of measure for the timeoutDuration - */ - public ServiceManagerFactory(Kernel kernel, ServiceMonitorBroadcaster serviceMonitor, Executor serviceExecutor, long timeoutDuration, TimeUnit timeoutUnits) { - this.kernel = kernel; - this.serviceMonitor = serviceMonitor; - this.serviceExecutor = serviceExecutor; - this.timeoutDuration = timeoutDuration; - this.timeoutUnits = timeoutUnits; - } - - /** - * Creates a ServiceManager. - * - * @param serviceId the id of the service - * @param serviceName the name of the service - * @param serviceFactory the factory for the service - * @param classLoader the classloader for the service - * @return a new service manager - */ - public ServiceManager createServiceManager(long serviceId, ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader) { - return new ServiceManager(kernel, - serviceId, - serviceName, - serviceFactory, - classLoader, - new AsyncServiceMonitor(serviceMonitor, serviceExecutor), - timeoutDuration, - timeoutUnits); - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/ServiceManagerRegistry.java b/kernel/src/java/org/xbean/kernel/standard/ServiceManagerRegistry.java deleted file mode 100644 index e13051dc..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/ServiceManagerRegistry.java +++ /dev/null @@ -1,592 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException; -import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; -import org.xbean.kernel.IllegalServiceStateException; -import org.xbean.kernel.KernelErrorsError; -import org.xbean.kernel.KernelOperationInterruptedException; -import org.xbean.kernel.ServiceAlreadyExistsException; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceRegistrationException; -import org.xbean.kernel.StopStrategies; -import org.xbean.kernel.StopStrategy; -import org.xbean.kernel.UnsatisfiedConditionsException; - -/** - * The StandardServiceRegistry manages the registration of ServiceManagers for the kernel. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceManagerRegistry { - /** - * The sequence used for the serviceId assigned to service managers. - */ - private final AtomicLong serviceId = new AtomicLong(1); - - /** - * The factory used to create service managers. - */ - private final ServiceManagerFactory serviceManagerFactory; - - /** - * The registered service managers. - */ - private final Map serviceManagers = new HashMap(); - - /** - * The service managers indexed by the service type. This map is populated when a service enters the running state. - */ - private final Map serviceManagersByType = new HashMap(); - - /** - * Creates a ServiceManagerRegistry that uses the specified service manager factory to create new service managers. - * - * @param serviceManagerFactory the factory for new service managers - */ - public ServiceManagerRegistry(ServiceManagerFactory serviceManagerFactory) { - this.serviceManagerFactory = serviceManagerFactory; - } - - /** - * Stops and destroys all services service managers. This method will FORCE stop the services if necessary. - * - * @throws KernelErrorsError if any errors occur while stopping or destroying the service managers - */ - public void destroy() throws KernelErrorsError { - // we gather all errors that occur during shutdown and throw them as on huge exception - List errors = new ArrayList(); - - List managerFutures; - synchronized (serviceManagers) { - managerFutures = new ArrayList(serviceManagers.values()); - serviceManagers.clear(); - - } - - List managers = new ArrayList(managerFutures.size()); - for (Iterator iterator = managerFutures.iterator(); iterator.hasNext();) { - RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next(); - try { - managers.add(registryFutureTask.get()); - } catch (InterruptedException e) { - // ignore -- this should not happen - errors.add(new AssertionError(e)); - } catch (ExecutionException e) { - // good -- one less manager to deal with - } - } - - // Be nice and try to stop asynchronously - errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS)); - - // Be really nice and try to stop asynchronously again - errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS)); - - // We have been nice enough now nuke them - errors.addAll(stopAll(managers, StopStrategies.FORCE)); - - // All managers are gaurenteed to be destroyed now - for (Iterator iterator = managers.iterator(); iterator.hasNext();) { - ServiceManager serviceManager = (ServiceManager) iterator.next(); - try { - serviceManager.destroy(StopStrategies.FORCE); - } catch (UnsatisfiedConditionsException e) { - // this should not happen, because we force stopped - errors.add(new AssertionError(e)); - } catch (IllegalServiceStateException e) { - // this should not happen, because we force stopped - errors.add(new AssertionError(e)); - } catch (RuntimeException e) { - errors.add(new AssertionError(e)); - } catch (Error e) { - errors.add(new AssertionError(e)); - } - } - - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - private List stopAll(List managers, StopStrategy stopStrategy) { - List errors = new ArrayList(); - for (Iterator iterator = managers.iterator(); iterator.hasNext();) { - ServiceManager serviceManager = (ServiceManager) iterator.next(); - try { - serviceManager.stop(stopStrategy); - } catch (UnsatisfiedConditionsException e) { - // this should not happen in with an asynchronous strategy - errors.add(new AssertionError(e)); - } catch (RuntimeException e) { - errors.add(new AssertionError(e)); - } catch (Error e) { - errors.add(new AssertionError(e)); - } - } - return errors; - } - - /** - * Determines if there is a service registered under the specified name. - * - * @param serviceName the unique name of the service - * @return true if there is a service registered with the specified name; false otherwise - */ - public boolean isRegistered(ServiceName serviceName) { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - - RegistryFutureTask registryFutureTask; - synchronized (serviceManagers) { - registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName); - } - try { - // the service is registered if we have a non-null future value - return registryFutureTask != null && registryFutureTask.get() != null; - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "isRegistered"); - } catch (ExecutionException e) { - return false; - } - } - - /** - * Gets the service manager registered under the specified name. - * - * @param serviceName the unique name of the service - * @return the ServiceManager - * @throws ServiceNotFoundException if there is no service registered under the specified name - */ - public ServiceManager getServiceManager(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - - RegistryFutureTask registryFutureTask; - synchronized (serviceManagers) { - registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName); - } - - // this service has no future - if (registryFutureTask == null) { - throw new ServiceNotFoundException(serviceName); - } - - try { - ServiceManager serviceManager = (ServiceManager) registryFutureTask.get(); - if (serviceManager == null) { - throw new ServiceNotFoundException(serviceName); - } - return serviceManager; - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "getServiceManager"); - } catch (ExecutionException e) { - // registration threw an exception which means it didn't register - throw new ServiceNotFoundException(serviceName); - } - } - - /** - * Gets the first registered service manager that creates an instance of the specified type, or null if no service - * managers create an instance of the specified type. - * - * @param type the of the desired service - * @return the first registered service manager that creates an instance of the specified type, or null if none found - */ - public ServiceManager getServiceManager(Class type) { - SortedSet serviceManagerFutures = getServiceManagerFutures(type); - for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) { - RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next(); - try { - ServiceManager serviceManager = (ServiceManager) registryFutureTask.get(); - if (serviceManager != null) { - return serviceManager; - } - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)"); - } catch (ExecutionException ignored) { - // registration threw an exception which means it didn't register - } - } - return null; - } - - /** - * Gets all service managers that create an instances of the specified type, or an empty list if no service - * managers create an instance of the specified type. - * - * @param type the of the desired service managers - * @return all service managers that create an instances of the specified type, or an empty list if none found - */ - public List getServiceManagers(Class type) { - SortedSet serviceManagerFutures = getServiceManagerFutures(type); - List serviceManagers = new ArrayList(serviceManagerFutures.size()); - for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) { - RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next(); - try { - ServiceManager serviceManager = (ServiceManager) registryFutureTask.get(); - if (serviceManager != null) { - serviceManagers.add(serviceManager); - } - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)"); - } catch (ExecutionException ignored) { - // registration threw an exception which means it didn't register - } - } - return serviceManagers; - } - - /** - * Gets the first registed and running service that is an instance of the specified type, or null if no instances - * of the specified type are running. - * - * @param type the of the desired service - * @return the first registed and running service that is an instance of the specified type or null if none found - */ - public synchronized Object getService(Class type) { - SortedSet serviceManagerFutures = getServiceManagerFutures(type); - for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) { - RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next(); - try { - ServiceManager serviceManager = (ServiceManager) registryFutureTask.get(); - if (serviceManager != null) { - Object service = serviceManager.getService(); - if (service != null) { - return service; - } - } - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getService(java.lang.Class)"); - } catch (ExecutionException ignored) { - // registration threw an exception which means it didn't register - } - } - return null; - } - - /** - * Gets the all of running service that are an instances of the specified type, or an empty list if no instances - * of the specified type are running. - * - * @param type the of the desired service - * @return the all of running service that are an instances of the specified type, or an empty list if none found - */ - public synchronized List getServices(Class type) { - List serviceManagers = getServiceManagers(type); - List services = new ArrayList(serviceManagers.size()); - for (Iterator iterator = serviceManagers.iterator(); iterator.hasNext();) { - ServiceManager serviceManager = (ServiceManager) iterator.next(); - if (serviceManager != null) { - Object service = serviceManager.getService(); - if (service != null) { - services.add(service); - } - } - } - return services; - } - - private SortedSet getServiceManagerFutures(Class type) { - SortedSet serviceManagerFutures; - synchronized (serviceManagers) { - serviceManagerFutures = (SortedSet) serviceManagersByType.get(type); - if (serviceManagerFutures != null) { - serviceManagerFutures = new TreeSet(serviceManagerFutures); - } else { - serviceManagerFutures = new TreeSet(); - } - } - return serviceManagerFutures; - } - - /** - * Creates a ServiceManager and registers it under the specified name. If the service is restartable, it will - * enter the server in the STOPPED state. If a service is not restartable, the service manager will assure that all - * dependencies are satisfied and service will immediately enter in the RUNNING state. If a - * dependency for a non-restartable service is not immediately satisfiable, this method will throw a - * ServiceRegistrationException. - * - * @param serviceName the unique name of the service - * @param serviceFactory the factory used to create the service - * @param classLoader the class loader to use for this service - * @throws ServiceAlreadyExistsException if service is already registered with the specified name - * @throws ServiceRegistrationException if the service is not restartable and an error occured while starting the service - */ - public void registerService(ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException, ServiceRegistrationException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (serviceFactory == null) throw new NullPointerException("serviceFactory is null"); - if (classLoader == null) throw new NullPointerException("classLoader is null"); - - if (!serviceFactory.isEnabled()) { - throw new ServiceRegistrationException(serviceName, - new IllegalServiceStateException("A disabled non-restartable service factory can not be registered", serviceName)); - } - - RegistryFutureTask registrationTask = null; - - // - // This loop will continue until we put our registrationTask in the serviceManagers map. If at any point, - // we discover that there is already a service registered under the specified service name, we will throw - // a ServiceAlreadyExistsException exiting this method. - // - while (registrationTask == null) { - RegistryFutureTask existingRegistration; - synchronized (serviceManagers) { - existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName); - - // if we do not have an existing registration or the existing registration task is complete - // we can create the new registration task; otherwise we need to wait for the existing registration to - // finish out side of the synchronized lock on serviceManagers. - if (existingRegistration == null || existingRegistration.isDone()) { - // if we have a valid existing registration, throw a ServiceAlreadyExistsException - if (existingRegistration != null) { - try { - boolean alreadyRegistered = (existingRegistration.get() != null); - if (alreadyRegistered) { - throw new ServiceAlreadyExistsException(serviceName); - } - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "registerService"); - } catch (ExecutionException e) { - // the previous registration threw an exception.. we can continure as normal - } - } - - // we are ready to register our serviceManager - existingRegistration = null; - ServiceManager serviceManager = serviceManagerFactory.createServiceManager(serviceId.getAndIncrement(), - serviceName, - serviceFactory, - classLoader); - registrationTask = RegistryFutureTask.createRegisterTask(serviceManager); - serviceManagers.put(serviceName, registrationTask); - addTypeIndex(serviceManager, registrationTask); - } - } - - // If there is an unfinished exiting registration task, wait until it is done executing - if (existingRegistration != null) { - try { - existingRegistration.get(); - // we don't throw an error here because we want to check in the synchronized block that this - // future is still registered in the serviceManagers map - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "registerService"); - } catch (ExecutionException e) { - // good - } - } - } - - // run our registration task and check the results - registrationTask.run(); - try { - // if initialization completed successfully, this method will not throw an exception - registrationTask.get(); - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "registerService"); - } catch (ExecutionException e) { - // registration failed, remove our task - synchronized (serviceManagers) { - // make sure our task is still the registered one - if (serviceManagers.get(serviceName) == registrationTask) { - serviceManagers.remove(serviceName); - removeTypeIndex(registrationTask); - } - } - throw new ServiceRegistrationException(serviceName, e.getCause()); - } - } - - /** - * Stops and destorys the ServiceManager and then unregisters it. The ServiceManagerRegistry will attempt to stop - * the service using the specified stop strategy, but if the service can not be stopped a - * ServiceRegistrationException will be thrown containing either an UnsatisfiedConditionsException or an - * IllegalServiceStateException. - * - * @param serviceName the unique name of the service - * @param stopStrategy the strategy that determines how unsatisfied conditions are handled - * @throws ServiceNotFoundException if there is no service registered under the specified name - * @throws ServiceRegistrationException if the service could not be stopped - */ - public void unregisterService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, ServiceRegistrationException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (stopStrategy == null) throw new NullPointerException("stopStrategy is null"); - - RegistryFutureTask unregistrationTask = null; - - // - // This loop will continue until we put our unregistrationTask in the serviceManagers map. If at any point, - // we discover that there actually is not a service registered under the specified service name, we will throw - // a ServiceNotFoundException exiting this method. - // - while (unregistrationTask == null) { - RegistryFutureTask existingRegistration; - synchronized (serviceManagers) { - existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName); - if (existingRegistration == null) { - throw new ServiceNotFoundException(serviceName); - } - - // if existing registration is done running, we can destroy it - if (existingRegistration.isDone()) { - ServiceManager serviceManager = null; - try { - serviceManager = (ServiceManager) existingRegistration.get(); - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "unregisterService"); - } catch (ExecutionException e) { - // good - } - - // if there isn't a registered manager that is an exception - if (serviceManager == null) { - throw new ServiceNotFoundException(serviceName); - } - - // we are ready to register our serviceManager - existingRegistration = null; - unregistrationTask = RegistryFutureTask.createUnregisterTask(serviceManager, stopStrategy); - serviceManagers.put(serviceName, unregistrationTask); - addTypeIndex(serviceManager, unregistrationTask); - } - } - - - // If there is an unfinished exiting registration task, wait until it is done executing - if (existingRegistration != null) { - try { - existingRegistration.get(); - // we don't throw an error here because we want to check in the synchronized block that this - // future is still registered in the serviceManagers map - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "unregisterService"); - } catch (ExecutionException e) { - // good - } - } - } - - unregistrationTask.run(); - try { - // if get returns any value other then null, the unregistration failed - if (unregistrationTask.get() == null) { - // unregistration was successful, remove the furuture object - synchronized (serviceManagers) { - // make sure our task is still the registered one - if (serviceManagers.get(serviceName) == unregistrationTask) { - serviceManagers.remove(serviceName); - removeTypeIndex(unregistrationTask); - } - } - } else { - synchronized (unregistrationTask) { - // the root exception is contained in the exception handle - throw new ServiceRegistrationException(serviceName, unregistrationTask.getThrowable()); - } - } - } catch (InterruptedException e) { - throw new KernelOperationInterruptedException(e, serviceName, "unregisterService"); - } catch (ExecutionException e) { - // this won't happen - throw new AssertionError(e); - } - } - - private void addTypeIndex(ServiceManager serviceManager, RegistryFutureTask registryFutureTask) { - if (serviceManager == null) throw new NullPointerException("serviceManager is null"); - if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null"); - - Set allTypes = new LinkedHashSet(); - for (Iterator iterator = serviceManager.getServiceTypes().iterator(); iterator.hasNext();) { - Class serviceType = (Class) iterator.next(); - - if (serviceType.isArray()) { - throw new IllegalArgumentException("Service is an array: serviceName=" + serviceManager.getServiceName() + - ", serviceType=" + serviceManager.getServiceTypes()); - } - - allTypes.add(serviceType); - allTypes.addAll(getAllSuperClasses(serviceType)); - allTypes.addAll(getAllInterfaces(serviceType)); - } - - synchronized (serviceManagers) { - for (Iterator iterator = allTypes.iterator(); iterator.hasNext();) { - Class type = (Class) iterator.next(); - Set futureServiceManagers = (Set) serviceManagersByType.get(type); - if (futureServiceManagers == null) { - futureServiceManagers = new TreeSet(); - serviceManagersByType.put(type, futureServiceManagers); - } - futureServiceManagers.add(registryFutureTask); - } - } - } - - private void removeTypeIndex(RegistryFutureTask registryFutureTask) { - if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null"); - synchronized (serviceManagers) { - for (Iterator iterator = serviceManagersByType.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - Set serviceManagers = (Set) entry.getValue(); - serviceManagers.remove(registryFutureTask); - if (serviceManagers.isEmpty()) { - iterator.remove(); - } - } - } - } - - private static Set getAllSuperClasses(Class clazz) { - Set allSuperClasses = new LinkedHashSet(); - for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) { - allSuperClasses.add(superClass); - } - return allSuperClasses; - } - - private static Set getAllInterfaces(Class clazz) { - Set allInterfaces = new LinkedHashSet(); - LinkedList stack = new LinkedList(); - stack.addAll(Arrays.asList(clazz.getInterfaces())); - while (!stack.isEmpty()) { - Class intf = (Class) stack.removeFirst(); - if (!allInterfaces.contains(intf)) { - allInterfaces.add(intf); - stack.addAll(Arrays.asList(intf.getInterfaces())); - } - } - return allInterfaces; - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/ServiceMonitorBroadcaster.java b/kernel/src/java/org/xbean/kernel/standard/ServiceMonitorBroadcaster.java deleted file mode 100644 index ac7f5b4a..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/ServiceMonitorBroadcaster.java +++ /dev/null @@ -1,325 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.xbean.kernel.ServiceMonitor; -import org.xbean.kernel.KernelMonitor; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceEvent; -import org.xbean.kernel.KernelErrorsError; - -/** - * The ServiceMonitorBroadcaster broadcasts kernel events to registered service monitors. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceMonitorBroadcaster implements ServiceMonitor { - /** - * The monitors for service events. - */ - private final Map serviceMonitors = new LinkedHashMap(); - - /** - * The monitor we notify when we get an exception from a service monitor. - */ - private final KernelMonitor kernelMonitor; - - /** - * Creates a ServiceMonitorBroadcaster that notifies the specified kernel monitor when an error occurs while - * notifying the registered service monitors. - * - * @param kernelMonitor the monitor to notify when an error occurs while notifying the registered service monitors - */ - public ServiceMonitorBroadcaster(KernelMonitor kernelMonitor) { - if (kernelMonitor == null) throw new NullPointerException("kernelMonitor is null"); - this.kernelMonitor = kernelMonitor; - } - - /** - * Adds a service monitor for a specific service, or if the specified service name is null, a global monitor. - *

- * Note: the order in which service monitors are notified is not specified. - * - * @param serviceMonitor the service monitor to add - * @param serviceName the unique name of the service to monitor or null to monitor all services - */ - public void addServiceMonitor(ServiceMonitor serviceMonitor, ServiceName serviceName) { - if (serviceMonitor == null) throw new NullPointerException("serviceMonitor is null"); - synchronized (serviceMonitors) { - Set monitors = (Set) serviceMonitors.get(serviceName); - if (monitors == null) { - monitors = new LinkedHashSet(); - serviceMonitors.put(serviceName, monitors); - } - monitors.add(serviceMonitor); - } - } - - /** - * Removes a service monitor. - * - * @param serviceMonitor the service monitor to remove - */ - public void removeServiceMonitor(ServiceMonitor serviceMonitor) { - if (serviceMonitor == null) throw new NullPointerException("serviceMonitor is null"); - synchronized (serviceMonitors) { - for (Iterator iterator = serviceMonitors.values().iterator(); iterator.hasNext();) { - Set monitors = (Set) iterator.next(); - monitors.remove(serviceMonitor); - if (monitors.isEmpty()) { - iterator.remove(); - } - } - } - } - - /** - * Gets the service monitors registered to recieve events for the specified service. This will include all global - * monitors and service specific monitors. - * - * @param serviceName the name of the service - * @return the monitors registerd to recieve events for the specified service - */ - private Set getServiceMonitors(ServiceName serviceName) { - synchronized (serviceMonitors) { - Set monitors = new LinkedHashSet(); - Set globalMonitors = (Set) serviceMonitors.get(null); - if (globalMonitors != null) { - monitors.addAll(globalMonitors); - } - Set specificMonitors = (Set) serviceMonitors.get(serviceName); - if (specificMonitors != null) { - monitors.addAll(specificMonitors); - } - return monitors; - } - } - - /** - * {@inheritDoc} - */ - public void serviceRegistered(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceRegistered(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceStarting(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceStarting(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStart(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceWaitingToStart(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceStartError(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceStartError(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceRunning(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceRunning(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceStopping(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceStopping(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceWaitingToStop(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceWaitingToStop(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceStopError(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceStopError(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceStopped(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceStopped(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - /** - * {@inheritDoc} - */ - public void serviceUnregistered(ServiceEvent serviceEvent) { - List errors = new ArrayList(); - Set serviceMonitors = getServiceMonitors(serviceEvent.getServiceName()); - for (Iterator iterator = serviceMonitors.iterator(); iterator.hasNext();) { - ServiceMonitor serviceMonitor = (ServiceMonitor) iterator.next(); - try { - serviceMonitor.serviceUnregistered(serviceEvent); - } catch (Throwable e) { - errors.addAll(fireServiceNotificationError(serviceMonitor, serviceEvent, e)); - } - } - if (!errors.isEmpty()) { - throw new KernelErrorsError(errors); - } - } - - private List fireServiceNotificationError(ServiceMonitor serviceMonitor, ServiceEvent serviceEvent, Throwable throwable) { - try { - kernelMonitor.serviceNotificationError(serviceMonitor, serviceEvent, throwable); - } catch (RuntimeException ignored) { - // ignore - we did our best to notify the world - } catch (KernelErrorsError e) { - return e.getErrors(); - } catch (Error e) { - return Collections.singletonList(e); - } - return Collections.EMPTY_LIST; - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/StandardKernel.java b/kernel/src/java/org/xbean/kernel/standard/StandardKernel.java deleted file mode 100644 index 4105bf51..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/StandardKernel.java +++ /dev/null @@ -1,495 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.util.List; -import java.util.ArrayList; -import java.util.Iterator; - -import edu.emory.mathcs.backport.java.util.concurrent.Executor; -import edu.emory.mathcs.backport.java.util.concurrent.Executors; -import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; -import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; -import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock; -import edu.emory.mathcs.backport.java.util.concurrent.locks.Condition; -import org.xbean.kernel.IllegalServiceStateException; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelErrorsError; -import org.xbean.kernel.KernelMonitor; -import org.xbean.kernel.ServiceAlreadyExistsException; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceMonitor; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceRegistrationException; -import org.xbean.kernel.ServiceState; -import org.xbean.kernel.StartStrategies; -import org.xbean.kernel.StartStrategy; -import org.xbean.kernel.StopStrategies; -import org.xbean.kernel.StopStrategy; -import org.xbean.kernel.UnregisterServiceException; -import org.xbean.kernel.UnsatisfiedConditionsException; -import org.xbean.kernel.KernelFactory; - -/** - * The standard kernel implementation. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StandardKernel implements Kernel { - /** - * The unique name of this kernel. - */ - private final String kernelName; - - /** - * The registered service managers. - */ - private final ServiceManagerRegistry serviceManagerRegistry; - - /** - * Tracks and broadcasts kernel evnents to the registered listeners. - */ - private final KernelMonitorBroadcaster kernelMonitor = new KernelMonitorBroadcaster(); - - /** - * This monitor broadcasts events to the listeners registered for service. - */ - private final ServiceMonitorBroadcaster serviceMonitor = new ServiceMonitorBroadcaster(kernelMonitor); - - /** - * If true, the kernel is still running. - */ - private boolean running = true; - - /** - * Lock that should be acquired before accessing the running boolean flag. - */ - private final Lock destroyLock = new ReentrantLock(); - - /** - * The condition that is notified when the kernel has been destroyed. - */ - private final Condition destroyCondition = destroyLock.newCondition(); - - /** - * Creates the service managers with handle service lifecycle. - */ - private ServiceManagerFactory serviceManagerFactory; - - /** - * Creates a kernel using the specified name. - * - * @param kernelName the unique name of this kernel - */ - public StandardKernel(String kernelName) { - this(kernelName, Executors.newCachedThreadPool(), 30, TimeUnit.SECONDS); - } - - /** - * Creates a kernel using the specified name. - * - * @param kernelName the unique name of this kernel - * @param serviceExecutor the executor to use for asynchronous service operations - * @param timeoutDuration the maximum duration to wait for a service event to complete - * @param timeoutUnits the unit of measure for the timeoutDuration - */ - public StandardKernel(String kernelName, Executor serviceExecutor, long timeoutDuration, TimeUnit timeoutUnits) { - if (kernelName == null) throw new NullPointerException("kernelName is null"); - if (kernelName.length() ==0) throw new IllegalArgumentException("kernelName must be atleast one character long"); - if (serviceExecutor == null) throw new NullPointerException("serviceExecutor is null"); - if (timeoutUnits == null) throw new NullPointerException("timeoutUnits is null"); - - this.kernelName = kernelName; - serviceManagerFactory = new ServiceManagerFactory(this, serviceMonitor, serviceExecutor, timeoutDuration, timeoutUnits); - serviceManagerRegistry = new ServiceManagerRegistry(serviceManagerFactory); - } - - /** - * {@inheritDoc} - */ - public void destroy() throws KernelErrorsError { - destroyLock.lock(); - try { - // if we are already stopped simply return - if (!running) { - return; - } - running = false; - } finally { - destroyLock.unlock(); - } - - // destroy all services - serviceManagerRegistry.destroy(); - - // remove this kernel from the kernel factory registry - KernelFactory.destroyInstance(this); - - // notify threads waiting for destroy to complete - destroyLock.lock(); - try { - destroyCondition.signalAll(); - } finally { - destroyLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - public void waitForDestruction() { - destroyLock.lock(); - try { - // if we are already stopped simply return - if (!running) { - return; - } - - // wait until destroy completes - destroyCondition.awaitUninterruptibly(); - } finally { - destroyLock.unlock(); - } - - } - - /** - * {@inheritDoc} - */ - public boolean isRunning() { - destroyLock.lock(); - try { - return running; - } finally { - destroyLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - public String getKernelName() { - return kernelName; - } - - /** - * {@inheritDoc} - */ - public void registerService(ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException, ServiceRegistrationException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (serviceFactory == null) throw new NullPointerException("serviceFactory is null"); - if (classLoader == null) throw new NullPointerException("classLoader is null"); - if (!isRunning()) { - throw new ServiceRegistrationException(serviceName, new IllegalStateException("Kernel is destroyed")); - } - - serviceManagerRegistry.registerService(serviceName, serviceFactory, classLoader); - } - - /** - * {@inheritDoc} - */ - public void unregisterService(ServiceName serviceName) throws ServiceNotFoundException, ServiceRegistrationException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - unregisterService(serviceName, StopStrategies.SYNCHRONOUS); - } - - /** - * {@inheritDoc} - */ - public void unregisterService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, ServiceRegistrationException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (stopStrategy == null) throw new NullPointerException("stopStrategy is null"); - if (!isRunning()) { - return; - } - - serviceManagerRegistry.unregisterService(serviceName, stopStrategy); - } - - /** - * {@inheritDoc} - */ - public boolean isRegistered(ServiceName serviceName) { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (!isRunning()) { - return false; - } - - return serviceManagerRegistry.isRegistered(serviceName); - } - - /** - * {@inheritDoc} - */ - public ServiceState getServiceState(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - return serviceManager.getState(); - } - - /** - * {@inheritDoc} - */ - public long getServiceStartTime(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - return serviceManager.getStartTime(); - } - - /** - * {@inheritDoc} - */ - public void startService(ServiceName serviceName) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - startService(serviceName, false, StartStrategies.SYNCHRONOUS); - } - - /** - * {@inheritDoc} - */ - public void startService(ServiceName serviceName, StartStrategy startStrategy) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (startStrategy == null) throw new NullPointerException("startStrategy is null"); - startService(serviceName, false, startStrategy); - } - - /** - * {@inheritDoc} - */ - public void startServiceRecursive(ServiceName serviceName) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - startService(serviceName, true, StartStrategies.SYNCHRONOUS); - } - - /** - * {@inheritDoc} - */ - public void startServiceRecursive(ServiceName serviceName, StartStrategy startStrategy) throws ServiceNotFoundException, IllegalServiceStateException, UnsatisfiedConditionsException, Exception { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (startStrategy == null) throw new NullPointerException("startStrategy is null"); - startService(serviceName, true, startStrategy); - } - - private void startService(ServiceName serviceName, boolean recursive, StartStrategy startStrategy) throws Exception { - if (startStrategy == null) throw new NullPointerException("startStrategy is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - try { - serviceManager.start(recursive, startStrategy); - } catch (UnregisterServiceException e) { - try { - unregisterService(serviceName, StopStrategies.FORCE); - } catch (ServiceNotFoundException ignored) { - // that is weird, but what ever - } catch (ServiceRegistrationException ignored) { - // we are alredy throwing an exception so ignore this one - } - Throwable cause = e.getCause(); - if (cause instanceof Exception) { - throw (Exception) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new AssertionError(cause); - } - } - } - - /** - * {@inheritDoc} - */ - public void stopService(ServiceName serviceName) throws ServiceNotFoundException, UnsatisfiedConditionsException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - stopService(serviceName, StopStrategies.SYNCHRONOUS); - } - - /** - * {@inheritDoc} - */ - public void stopService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, UnsatisfiedConditionsException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (stopStrategy == null) throw new NullPointerException("stopStrategy is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - serviceManager.stop(stopStrategy); - } - - /** - * {@inheritDoc} - */ - public boolean isServiceEnabled(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - ServiceFactory serviceFactory = serviceManager.getServiceFactory(); - return serviceFactory.isEnabled(); - } - - /** - * {@inheritDoc} - */ - public void setServiceEnabled(ServiceName serviceName, boolean enabled) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - ServiceFactory serviceFactory = serviceManager.getServiceFactory(); - serviceFactory.setEnabled(enabled); - } - - /** - * {@inheritDoc} - */ - public Object getService(ServiceName serviceName) throws ServiceNotFoundException, IllegalArgumentException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - return serviceManager.getService(); - } - - /** - * {@inheritDoc} - */ - public Object getService(Class type) { - if (type == null) throw new NullPointerException("type is null"); - if (!isRunning()) { - return null; - } - - Object service = serviceManagerRegistry.getService(type); - return service; - } - - /** - * {@inheritDoc} - */ - public List getServices(Class type) { - if (type == null) throw new NullPointerException("type is null"); - if (!isRunning()) { - return null; - } - - List services = serviceManagerRegistry.getServices(type); - return services; - } - - /** - * {@inheritDoc} - */ - public ServiceFactory getServiceFactory(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - return serviceManager.getServiceFactory(); - } - - /** - * {@inheritDoc} - */ - public ServiceFactory getServiceFactory(Class type) { - if (type == null) throw new NullPointerException("type is null"); - if (!isRunning()) { - return null; - } - - ServiceManager serviceManager = serviceManagerRegistry.getServiceManager(type); - return serviceManager.getServiceFactory(); - } - - /** - * {@inheritDoc} - */ - public List getServiceFactories(Class type) { - if (type == null) throw new NullPointerException("type is null"); - if (!isRunning()) { - return null; - } - - List serviceManagers = serviceManagerRegistry.getServiceManagers(type); - List serviceFactories = new ArrayList(serviceManagers.size()); - for (Iterator iterator = serviceManagers.iterator(); iterator.hasNext();) { - ServiceManager serviceManager = (ServiceManager) iterator.next(); - serviceFactories.add(serviceManager.getServiceFactory()); - } - return serviceFactories; - } - - /** - * {@inheritDoc} - */ - public ClassLoader getClassLoaderFor(ServiceName serviceName) throws ServiceNotFoundException { - if (serviceName == null) throw new NullPointerException("serviceName is null"); - ServiceManager serviceManager = getServiceManager(serviceName); - return serviceManager.getClassLoader(); - } - - private ServiceManager getServiceManager(ServiceName serviceName) throws ServiceNotFoundException { - if (!isRunning()) { - throw new ServiceNotFoundException(serviceName); - } - - ServiceManager serviceManager = serviceManagerRegistry.getServiceManager(serviceName); - return serviceManager; - } - - /** - * {@inheritDoc} - */ - public void addKernelMonitor(KernelMonitor kernelMonitor) { - if (kernelMonitor == null) throw new NullPointerException("kernelMonitor is null"); - if (!isRunning()) { - throw new IllegalStateException("Kernel is stopped"); - } - this.kernelMonitor.addKernelMonitor(kernelMonitor); - } - - /** - * {@inheritDoc} - */ - public void removeKernelMonitor(KernelMonitor kernelMonitor) { - if (kernelMonitor == null) throw new NullPointerException("kernelMonitor is null"); - this.kernelMonitor.removeKernelMonitor(kernelMonitor); - } - - /** - * {@inheritDoc} - */ - public void addServiceMonitor(ServiceMonitor serviceMonitor) { - if (serviceMonitor == null) throw new NullPointerException("serviceMonitor is null"); - if (!isRunning()) { - throw new IllegalStateException("Kernel is stopped"); - } - addServiceMonitor(serviceMonitor, null); - } - - /** - * {@inheritDoc} - */ - public void addServiceMonitor(ServiceMonitor serviceMonitor, ServiceName serviceName) { - if (serviceMonitor == null) throw new NullPointerException("serviceMonitor is null"); - if (serviceName == null) throw new NullPointerException("serviceName is null"); - if (!isRunning()) { - throw new IllegalStateException("Kernel is stopped"); - } - this.serviceMonitor.addServiceMonitor(serviceMonitor, serviceName); - } - - /** - * {@inheritDoc} - */ - public void removeServiceMonitor(ServiceMonitor serviceMonitor) { - if (serviceMonitor == null) throw new NullPointerException("serviceMonitor is null"); - this.serviceMonitor.removeServiceMonitor(serviceMonitor); - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/StandardKernelFactory.java b/kernel/src/java/org/xbean/kernel/standard/StandardKernelFactory.java deleted file mode 100644 index 56344aa5..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/StandardKernelFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelFactory; - -/** - * The kernel factory for StandardKernel instances. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StandardKernelFactory extends KernelFactory { - /** - * {@inheritDoc} - */ - protected Kernel createKernelInternal(String name) { - if (name == null) throw new NullPointerException("name is null"); - return new StandardKernel(name); - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/StandardServiceConditionContext.java b/kernel/src/java/org/xbean/kernel/standard/StandardServiceConditionContext.java deleted file mode 100644 index 16acae8e..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/StandardServiceConditionContext.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import edu.emory.mathcs.backport.java.util.concurrent.locks.Condition; -import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceConditionContext; -import org.xbean.kernel.ServiceName; - -/** - * This is the service context used by the service manager. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StandardServiceConditionContext implements ServiceConditionContext { - /** - * The kernel in which the service is registered. - */ - private final Kernel kernel; - - /** - * The unique name of the service in the kernel. - */ - private final ServiceName serviceName; - - /** - * The class loader for the service. - */ - private final ClassLoader classLoader; - - /** - * The lock that must be acquired before signaling the condition. - */ - private final Lock lock; - - /** - * The condition to signal when the {@link #setSatisfied()} method is called. - */ - private final Condition condition; - - /** - * Has this condition been satisfied? Once satisfied a condition is always considered satisfied. - */ - private boolean satisfied = false; - - /** - * Creates a service context for the specified service. - * - * @param kernel the kernel in which the service is registered - * @param serviceName the name of the service - * @param classLoader the class loader for the service - * @param lock the lock for the service manager - * @param condition the condition that should be notified when the {@link #setSatisfied()} method is called - */ - public StandardServiceConditionContext(Kernel kernel, ServiceName serviceName, ClassLoader classLoader, Lock lock, Condition condition) { - this.kernel = kernel; - this.serviceName = serviceName; - this.classLoader = classLoader; - this.lock = lock; - this.condition = condition; - } - - /** - * {@inheritDoc} - */ - public Kernel getKernel() { - return kernel; - } - - /** - * {@inheritDoc} - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * {@inheritDoc} - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Gets the satisfied status of this condition. Once satisfied a condition is considered satisfied until destroyed - * and reinitialized. The ServiceManager uses the StandardServiceConditionContext to track the status of conditions - * so it will call setSatisfied() when the condition returns true from isSatisfied(). - * - * @return satisfied status of this condition - */ - public boolean isSatisfied() { - return satisfied; - } - - /** - * {@inheritDoc} - */ - public void setSatisfied() { - lock.lock(); - try { - if (!satisfied) { - satisfied = true; - condition.signalAll(); - } - } finally { - lock.unlock(); - } - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/StandardServiceContext.java b/kernel/src/java/org/xbean/kernel/standard/StandardServiceContext.java deleted file mode 100644 index 140b1bd2..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/StandardServiceContext.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; - -/** - * The standard service context implementation. This is passed to the service factory in the - * {@link ServiceFactory#createService(ServiceContext)} and {@link ServiceFactory#destroyService(ServiceContext)} - * methods. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StandardServiceContext implements ServiceContext { - private final Kernel kernel; - private final ServiceName serviceName; - private final ClassLoader classLoader; - - /** - * Creates the standard service context implementation. - * - * @param kernel the kernel in which the service is registered - * @param serviceName the name of the service - * @param classLoader the class loader for the service - */ - public StandardServiceContext(Kernel kernel, ServiceName serviceName, ClassLoader classLoader) { - this.kernel = kernel; - this.serviceName = serviceName; - this.classLoader = classLoader; - } - - /** - * {@inheritDoc} - */ - public Kernel getKernel() { - return kernel; - } - - /** - * {@inheritDoc} - */ - public ServiceName getServiceName() { - return serviceName; - } - - /** - * {@inheritDoc} - */ - public ClassLoader getClassLoader() { - return classLoader; - } -} diff --git a/kernel/src/java/org/xbean/kernel/standard/package.html b/kernel/src/java/org/xbean/kernel/standard/package.html deleted file mode 100644 index eac9184c..00000000 --- a/kernel/src/java/org/xbean/kernel/standard/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Provides the standard kernel implementation. The classes contined in this package should be considered private -implementation details of XBean, and should not be used directly. - - \ No newline at end of file diff --git a/kernel/src/test/org/xbean/kernel/KernelMonitorBroadcasterTest.java b/kernel/src/test/org/xbean/kernel/KernelMonitorBroadcasterTest.java deleted file mode 100644 index 2b97c381..00000000 --- a/kernel/src/test/org/xbean/kernel/KernelMonitorBroadcasterTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import junit.framework.TestCase; -import org.xbean.kernel.standard.StandardKernel; -import org.xbean.kernel.standard.KernelMonitorBroadcaster; - -/** - * Tests the KernelMonitorBroadcaster. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelMonitorBroadcasterTest extends TestCase { - private static final ServiceMonitor SERVICE_MONITOR = new NullServiceMonitor(); - private static final ServiceEvent SERVICE_EVENT = new ServiceEvent(0, - new StandardKernel("test"), - new StringServiceName("service-name"), - new StaticServiceFactory(new Object()), - ClassLoader.getSystemClassLoader(), - null, - null, - null); - private static final Throwable THROWABLE = new Throwable("test throwable"); - - private static final int MONITOR_COUNT = 4; - private MockKernelMonitor[] kernelMonitors = new MockKernelMonitor[MONITOR_COUNT]; - private KernelMonitorBroadcaster kernelMonitorBroadcaster = new KernelMonitorBroadcaster(); - - protected void setUp() throws Exception { - super.setUp(); - for (int i = 0; i < kernelMonitors.length; i++) { - kernelMonitors[i] = new MockKernelMonitor("monitor-" + i); - kernelMonitorBroadcaster.addKernelMonitor(kernelMonitors[i]); - } - } - - /** - * Test that events are fired to all registered monitors. - * Strategy: - *

- */ - public void testFireEvent() { - kernelMonitorBroadcaster.serviceNotificationError(SERVICE_MONITOR, SERVICE_EVENT, THROWABLE); - assertNotificationCorrect(); - } - - private void assertNotificationCorrect() { - for (int i = 0; i < kernelMonitors.length; i++) { - MockKernelMonitor kernelMonitor = kernelMonitors[i]; - assertTrue(kernelMonitor.wasNotificationCalled()); - } - } - - /** - * Test that if a monitor is added more then once it only recieves the event once. - * Strategy: - * - */ - public void testDoubleAdd() { - kernelMonitorBroadcaster.addKernelMonitor(kernelMonitors[1]); - kernelMonitorBroadcaster.addKernelMonitor(kernelMonitors[1]); - kernelMonitorBroadcaster.addKernelMonitor(kernelMonitors[1]); - - kernelMonitorBroadcaster.serviceNotificationError(SERVICE_MONITOR, SERVICE_EVENT, THROWABLE); - - // note the mock monitor asserts that it is only called once before requreing a reset - assertNotificationCorrect(); - } - - /** - * Test that events are not fired to a monitor that has been removed. - * Strategy: - * - */ - public void testRemove() { - kernelMonitorBroadcaster.serviceNotificationError(SERVICE_MONITOR, SERVICE_EVENT, THROWABLE); - - assertNotificationCorrect(); - - for (int i = 0; i < kernelMonitors.length; i++) { - MockKernelMonitor kernelMonitor = kernelMonitors[i]; - kernelMonitor.rest(); - } - - kernelMonitorBroadcaster.removeKernelMonitor(kernelMonitors[1]); - - kernelMonitorBroadcaster.serviceNotificationError(SERVICE_MONITOR, SERVICE_EVENT, THROWABLE); - - for (int i = 0; i < kernelMonitors.length; i++) { - MockKernelMonitor kernelMonitor = kernelMonitors[i]; - if (i == 1) { - assertFalse(kernelMonitor.wasNotificationCalled()); - } else { - assertTrue(kernelMonitor.wasNotificationCalled()); - } - } - } - - /** - * Tests that no exceptions are thrown if an attempt is made to remove a monitor not registered. - */ - public void testRemoveUnassociated() { - kernelMonitorBroadcaster.removeKernelMonitor(new MockKernelMonitor("unassociated monitor")); - } - - private static class MockKernelMonitor implements KernelMonitor { - private final String name; - private boolean notificationCalled = false; - - private MockKernelMonitor(String name) { - this.name = name; - } - - public String toString() { - return name; - } - - private void rest() { - notificationCalled = false; - } - - public boolean wasNotificationCalled() { - return notificationCalled; - } - - public void serviceNotificationError(ServiceMonitor serviceMonitor, ServiceEvent serviceEvent, Throwable throwable) { - assertFalse(notificationCalled); - notificationCalled = true; - assertSame(SERVICE_MONITOR, serviceMonitor); - assertSame(SERVICE_EVENT, serviceEvent); - assertSame(THROWABLE, throwable); - } - } -} \ No newline at end of file diff --git a/kernel/src/test/org/xbean/kernel/ServiceStateTest.java b/kernel/src/test/org/xbean/kernel/ServiceStateTest.java deleted file mode 100644 index dd66aa4d..00000000 --- a/kernel/src/test/org/xbean/kernel/ServiceStateTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.rmi.MarshalledObject; - -import junit.framework.TestCase; - -/** - * Tests that the ServiceState constants are consistent. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceStateTest extends TestCase { - /** - * Tests that the constances are .equals to them selves and no other constants. - */ - public void testEquals() { - assertTrue(ServiceState.STARTING.equals(ServiceState.STARTING)); - assertFalse(ServiceState.STARTING.equals(ServiceState.RUNNING)); - assertFalse(ServiceState.STARTING.equals(ServiceState.STOPPING)); - assertFalse(ServiceState.STARTING.equals(ServiceState.STOPPED)); - - assertFalse(ServiceState.RUNNING.equals(ServiceState.STARTING)); - assertTrue(ServiceState.RUNNING.equals(ServiceState.RUNNING)); - assertFalse(ServiceState.RUNNING.equals(ServiceState.STOPPING)); - assertFalse(ServiceState.RUNNING.equals(ServiceState.STOPPED)); - - assertFalse(ServiceState.STOPPING.equals(ServiceState.STARTING)); - assertFalse(ServiceState.STOPPING.equals(ServiceState.RUNNING)); - assertTrue(ServiceState.STOPPING.equals(ServiceState.STOPPING)); - assertFalse(ServiceState.STOPPING.equals(ServiceState.STOPPED)); - - assertFalse(ServiceState.STOPPED.equals(ServiceState.STARTING)); - assertFalse(ServiceState.STOPPED.equals(ServiceState.RUNNING)); - assertFalse(ServiceState.STOPPED.equals(ServiceState.STOPPING)); - assertTrue(ServiceState.STOPPED.equals(ServiceState.STOPPED)); - } - - /** - * Tests that the constants create a hashCode that is equal to their own hashCode an no other constants hashCode. - */ - public void testHashCode() { - assertTrue(ServiceState.STARTING.hashCode() == ServiceState.STARTING.hashCode()); - assertFalse(ServiceState.STARTING.hashCode() == ServiceState.RUNNING.hashCode()); - assertFalse(ServiceState.STARTING.hashCode() == ServiceState.STOPPING.hashCode()); - assertFalse(ServiceState.STARTING.hashCode() == ServiceState.STOPPED.hashCode()); - - assertFalse(ServiceState.RUNNING.hashCode() == ServiceState.STARTING.hashCode()); - assertTrue(ServiceState.RUNNING.hashCode() == ServiceState.RUNNING.hashCode()); - assertFalse(ServiceState.RUNNING.hashCode() == ServiceState.STOPPING.hashCode()); - assertFalse(ServiceState.RUNNING.hashCode() == ServiceState.STOPPED.hashCode()); - - assertFalse(ServiceState.STOPPING.hashCode() == ServiceState.STARTING.hashCode()); - assertFalse(ServiceState.STOPPING.hashCode() == ServiceState.RUNNING.hashCode()); - assertTrue(ServiceState.STOPPING.hashCode() == ServiceState.STOPPING.hashCode()); - assertFalse(ServiceState.STOPPING.hashCode() == ServiceState.STOPPED.hashCode()); - - assertFalse(ServiceState.STOPPED.hashCode() == ServiceState.STARTING.hashCode()); - assertFalse(ServiceState.STOPPED.hashCode() == ServiceState.RUNNING.hashCode()); - assertFalse(ServiceState.STOPPED.hashCode() == ServiceState.STOPPING.hashCode()); - assertTrue(ServiceState.STOPPED.hashCode() == ServiceState.STOPPED.hashCode()); - } - - /** - * Tests that getServiceState returns the same constant as the constant from which the index was gotten, and that - * getServiceState throws an exception if an attempt is make to get an unknown constant. - */ - public void testGetServiceState() { - assertSame(ServiceState.STARTING, ServiceState.getServiceState(ServiceState.STARTING.getIndex())); - assertSame(ServiceState.RUNNING, ServiceState.getServiceState(ServiceState.RUNNING.getIndex())); - assertSame(ServiceState.STOPPING, ServiceState.getServiceState(ServiceState.STOPPING.getIndex())); - assertSame(ServiceState.STOPPED, ServiceState.getServiceState(ServiceState.STOPPED.getIndex())); - - try { - ServiceState.getServiceState(ServiceState.STOPPED.getIndex() + 1); - fail("ServiceState.getServiceState(ServiceState.STOPPED.getIndex() + 1) should have thrown and exception"); - } catch (IllegalArgumentException expected) { - // expected - } - - try { - ServiceState.getServiceState(ServiceState.STARTING.getIndex() - 1); - fail("ServiceState.getServiceState(ServiceState.STARTING.getIndex() - 1) should have thrown and exception"); - } catch (IllegalArgumentException expected) { - // expected - } - } - - /** - * Tests that parseServiceState returns the same state when called with getName on a state, that it throws an exception - * for an unknown state and that the parsing is done using a case insensitive match. - */ - public void testParseServiceState() { - assertSame(ServiceState.STARTING, ServiceState.parseServiceState(ServiceState.STARTING.getName())); - assertSame(ServiceState.RUNNING, ServiceState.parseServiceState(ServiceState.RUNNING.getName())); - assertSame(ServiceState.STOPPING, ServiceState.parseServiceState(ServiceState.STOPPING.getName())); - assertSame(ServiceState.STOPPED, ServiceState.parseServiceState(ServiceState.STOPPED.getName())); - - try { - ServiceState.parseServiceState("donkey"); - fail("ServiceState.parseServiceState(\"donkey\") should have thrown and exception"); - } catch (IllegalArgumentException expected) { - // expected - } - - assertSame(ServiceState.STARTING, ServiceState.parseServiceState("StARting")); - assertSame(ServiceState.RUNNING, ServiceState.parseServiceState("running")); - assertSame(ServiceState.STOPPING, ServiceState.parseServiceState("stoPPing")); - assertSame(ServiceState.STOPPED, ServiceState.parseServiceState("StoppeD")); - } - - /** - * Tests that when a state is serialized an deserialized it returns the same state object. This test shoudl assure - * that there is only one state instance with a specific index in existant at one time. - * @throws Exception if a problem occurs - */ - public void testSerialization() throws Exception { - assertSame(ServiceState.STARTING, copyServiceState(ServiceState.STARTING)); - assertSame(ServiceState.RUNNING, copyServiceState(ServiceState.RUNNING)); - assertSame(ServiceState.STOPPING, copyServiceState(ServiceState.STOPPING)); - assertSame(ServiceState.STOPPED, copyServiceState(ServiceState.STOPPED)); - } - - private ServiceState copyServiceState(ServiceState original) throws Exception { - MarshalledObject marshalledObject = new MarshalledObject(original); - return (ServiceState) marshalledObject.get(); - } -} diff --git a/kernel/src/test/org/xbean/kernel/StaticServiceFactoryTest.java b/kernel/src/test/org/xbean/kernel/StaticServiceFactoryTest.java deleted file mode 100644 index 29d75a8f..00000000 --- a/kernel/src/test/org/xbean/kernel/StaticServiceFactoryTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import java.util.Set; -import java.util.TreeSet; - -import junit.framework.TestCase; - -/** - * Tests the StaticServiceFactory. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StaticServiceFactoryTest extends TestCase { - private static final Object SERVICE = new TreeSet(); - private static final ServiceContext SERVICE_CONTEXT = new MockServiceContext(); - private static final ServiceCondition START_CONDITION = new MockStartCondition(); - private static final ServiceCondition STOP_CONDITION = new MockStopCondition(); - - /** - * Tests that the constructor works when called with an Object and fails when called with null. - */ - public void testConstructor() { - new StaticServiceFactory(SERVICE); - try { - new StaticServiceFactory(null); - fail("new StaticServiceFactory(null) should have thrown a NullPointerException"); - } catch (NullPointerException expected) { - } - } - - /** - * Tests that create service returns the same object that was passed to the constructor. - */ - public void testCreateService() { - assertSame(SERVICE, new StaticServiceFactory(SERVICE).createService(SERVICE_CONTEXT)); - } - - /** - * Tests that the service factory is not restartable. - */ - public void testIsRestartable() { - assertEquals(false, new StaticServiceFactory(SERVICE).isRestartable()); - } - - /** - * Tests that geting and setting the enalbe flag. - */ - public void testEnabled() { - StaticServiceFactory serviceFactory = new StaticServiceFactory(SERVICE); - assertEquals(true, serviceFactory.isEnabled()); - serviceFactory.setEnabled(false); - assertEquals(false, serviceFactory.isEnabled()); - serviceFactory.setEnabled(true); - assertEquals(true, serviceFactory.isEnabled()); - } - - /** - * Tests getting and setting start and stop conditions. - */ - public void testConditions() { - StaticServiceFactory serviceFactory = new StaticServiceFactory(SERVICE); - - // get the dependency set - Set dependencies = serviceFactory.getStartConditions(); - assertNotNull(dependencies); - // it should be initially empty - assertTrue(dependencies.isEmpty()); - - serviceFactory.addStartCondition(START_CONDITION); - // old dependency set should still be empty... it is a snapshot - assertTrue(dependencies.isEmpty()); - - // get a new dependency set - dependencies = serviceFactory.getStartConditions(); - assertNotNull(dependencies); - // should have our dependency in it - assertEquals(1, dependencies.size()); - assertTrue(dependencies.contains(START_CONDITION)); - - try { - dependencies.clear(); - fail("dependencies.clear() should have thrown an Exception"); - } catch (Exception expected) { - } - - // get the dependency set - dependencies = serviceFactory.getStopConditions(); - assertNotNull(dependencies); - // it should be initially empty - assertTrue(dependencies.isEmpty()); - - serviceFactory.addStopCondition(STOP_CONDITION); - // old dependency set should still be empty... it is a snapshot - assertTrue(dependencies.isEmpty()); - - // get a new dependency set - dependencies = serviceFactory.getStopConditions(); - assertNotNull(dependencies); - // should have our dependency in it - assertEquals(1, dependencies.size()); - assertTrue(dependencies.contains(STOP_CONDITION)); - - try { - dependencies.clear(); - fail("dependencies.clear() should have thrown an Exception"); - } catch (Exception expected) { - } - } - - /** - * Tests that getTypes returns an array containing a single class which is the class of the service passed to the constuctor. - */ - public void testGetTypes() { - StaticServiceFactory serviceFactory = new StaticServiceFactory(SERVICE); - Class[] types = serviceFactory.getTypes(); - assertNotNull(types); - assertEquals(1, types.length); - assertSame(SERVICE.getClass(), types[0]); - } - - private static class MockServiceContext implements ServiceContext { - public Kernel getKernel() { - throw new UnsupportedOperationException(); - } - - public ServiceName getServiceName() { - throw new UnsupportedOperationException(); - } - - public ClassLoader getClassLoader() { - throw new UnsupportedOperationException(); - } - } - - private static class MockStartCondition implements ServiceCondition { - public void initialize(ServiceConditionContext context) { - throw new UnsupportedOperationException(); - } - - public boolean isSatisfied() { - throw new UnsupportedOperationException(); - } - - public void destroy() { - throw new UnsupportedOperationException(); - } - } - - private static class MockStopCondition implements ServiceCondition { - public void initialize(ServiceConditionContext context) { - throw new UnsupportedOperationException(); - } - - public boolean isSatisfied() { - throw new UnsupportedOperationException(); - } - - public void destroy() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/kernel/src/test/org/xbean/kernel/StringServiceNameTest.java b/kernel/src/test/org/xbean/kernel/StringServiceNameTest.java deleted file mode 100644 index 4e277cc5..00000000 --- a/kernel/src/test/org/xbean/kernel/StringServiceNameTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel; - -import junit.framework.TestCase; - -/** - * Tests StringServiceName. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StringServiceNameTest extends TestCase { - /** - * Tests that the constuctor works when passed a string and fails when passed null. - */ - public void testConstructor() { - new StringServiceName("foo"); - - try { - new StringServiceName(null); - fail("new StringServiceName(null) should have thrown NullPointerException"); - } catch (NullPointerException expected) { - } - } - - /** - * Tests that toString returns equivalent String as the one passed to the construcor. - */ - public void testToString() { - assertEquals("foo", new StringServiceName("foo").toString()); - } - - /** - * Tests that equals works when comparing two names created with equivalent strings, and fails when not. - */ - public void testEquals() { - assertEquals(new StringServiceName("foo"), new StringServiceName("foo")); - assertFalse(new StringServiceName("bar").equals(new StringServiceName("foo"))); - } - - /** - * Tests that hashCode creates the same value when used on two names created with equivalent strings, and fails when not. - */ - public void testHashCode() { - assertEquals(new StringServiceName("foo").hashCode(), new StringServiceName("foo").hashCode()); - assertFalse(new StringServiceName("bar").hashCode() == new StringServiceName("foo").hashCode()); - } -} diff --git a/kernel/src/test/org/xbean/kernel/standard/ServiceManagerRegistryTest.java b/kernel/src/test/org/xbean/kernel/standard/ServiceManagerRegistryTest.java deleted file mode 100644 index 46d407f3..00000000 --- a/kernel/src/test/org/xbean/kernel/standard/ServiceManagerRegistryTest.java +++ /dev/null @@ -1,897 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.HashSet; -import java.util.Arrays; -import java.util.AbstractCollection; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.SortedSet; -import java.util.TreeSet; -import java.io.Serializable; - -import edu.emory.mathcs.backport.java.util.concurrent.Callable; -import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; -import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException; -import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; -import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; -import junit.framework.TestCase; -import org.xbean.kernel.IllegalServiceStateException; -import org.xbean.kernel.KernelErrorsError; -import org.xbean.kernel.ServiceAlreadyExistsException; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceRegistrationException; -import org.xbean.kernel.StaticServiceFactory; -import org.xbean.kernel.StopStrategies; -import org.xbean.kernel.StopStrategy; -import org.xbean.kernel.StringServiceName; -import org.xbean.kernel.UnsatisfiedConditionsException; -import org.xbean.kernel.NullServiceMonitor; - -/** - * Test the ServiceManagerRegistry. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceManagerRegistryTest extends TestCase { - private static final int TIMEOUT_DURATION = 5; - private static final TimeUnit TIMEOUT_UNITS = TimeUnit.SECONDS; - - private static final StringServiceName SERVICE_NAME = new StringServiceName("Service"); - private static final StaticServiceFactory SERVICE_FACTORY = new StaticServiceFactory(new Object()); - private static final ClassLoader CLASS_LOADER = new URLClassLoader(new URL[0]); - private static final Class[] EXPECTED_TYPES = new Class[] { - TreeSet.class, - AbstractSet.class, - AbstractCollection.class, - Object.class, - SortedSet.class, - Set.class, - Collection.class, - Cloneable.class, - Serializable.class, - List.class - }; - private final MockServiceManager serviceManager = new MockServiceManager(); - private final MockServiceManagerFactory serviceManagerFactory = new MockServiceManagerFactory(); - private final ServiceManagerRegistry registry = new ServiceManagerRegistry(serviceManagerFactory); - - /** - * Tests the initial state of the registry. - */ - public void testInitialState() { - assertFalse(registry.isRegistered(SERVICE_NAME)); - try { - assertNull(registry.getServiceManager(SERVICE_NAME)); - fail("should have thrown an exception"); - } catch (ServiceNotFoundException expected) { - // expected - assertEquals(SERVICE_NAME, expected.getServiceName()); - } - assertFalse(serviceManager.isInitializeCalled()); - assertFalse(serviceManager.isDestroyCalled()); - } - - /** - * Test the registration and unregistration. - * Strategy: - * - */ - public void testNotificationRuntimeException() { - throwable = new RuntimeException("notification exception"); - fireAllEvents(); - } - - /** - * Test that when a service monitor throws an Error the kernel monitor is notified. - * Strategy: - *
  • - * Set all monitors to throw an Error - *
  • - * Fire all events and verify - *
  • - */ - public void testNotificationError() { - throwable = new Error("notification error"); - fireAllEvents(); - } - - /** - * Tests that when a kernel monitor throws a RuntimeException that it is ignored. - *
  • - * Set all service monitors to throw a RuntimeException - *
  • - * Set kernel monitor to throw a RuntimeException - *
  • - * Fire all events and verify - *
  • - */ - public void testKernelMonitorException() { - throwable = new RuntimeException("notification exception"); - kernelThrowable = new RuntimeException("kernel exception"); - fireAllEvents(); - } - - /** - * Tests that when a kernel monitor throws an Error that it is propagated back to call in a KernelErrorsError. - *
  • - * Set all service monitors to throw a RuntimeException - *
  • - * Set kernel monitor to throw an Error - *
  • - * Fire all events and verify - *
  • - */ - public void testKernelMonitorError() { - throwable = new RuntimeException("notification exception"); - kernelThrowable = new Error("kernel error"); - fireAllEvents(); - } - - private void fireAllEvents() { - for (int serviceId = 0; serviceId < serviceEvent.length; serviceId++) { - for (int j = 0; j < NOTIFICATION_TYPES.length; j++) { - int notificationType = NOTIFICATION_TYPES[j]; - initMonitors(serviceId, throwable); - fireEvent(serviceId, notificationType); - assertNotificationCorrect(serviceId, notificationType); - } - } - } - - private void initMonitors(int serviceId, Throwable throwable) { - if (throwable != null) { - kernelMonitor.init(expectedMonitors[serviceId], serviceEvent[serviceId], throwable, kernelThrowable); - } else { - kernelMonitor.init(Collections.EMPTY_SET, null, null, null); - - } - for (Iterator iterator = expectedMonitors[serviceId].iterator(); iterator.hasNext();) { - ((MockServiceMonitor) iterator.next()).init(serviceEvent[serviceId], throwable); - } - for (Iterator iterator = unexpectedMonitors[serviceId].iterator(); iterator.hasNext();) { - ((MockServiceMonitor) iterator.next()).init(null, throwable); - } - } - - private void fireEvent(int serviceId, int notificationType) { - try { - if (notificationType == SERVICE_REGISTERED) { - serviceMonitorBroadcaster.serviceRegistered(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_STARTING) { - serviceMonitorBroadcaster.serviceStarting(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_WAITING_TO_START) { - serviceMonitorBroadcaster.serviceWaitingToStart(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_START_ERROR) { - serviceMonitorBroadcaster.serviceStartError(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_RUNNING) { - serviceMonitorBroadcaster.serviceRunning(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_STOPPING) { - serviceMonitorBroadcaster.serviceStopping(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_WAITING_TO_STOP) { - serviceMonitorBroadcaster.serviceWaitingToStop(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_STOP_ERROR) { - serviceMonitorBroadcaster.serviceStopError(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_STOPPED) { - serviceMonitorBroadcaster.serviceStopped(serviceEvent[serviceId]); - } else if (notificationType == SERVICE_UNREGISTERED) { - serviceMonitorBroadcaster.serviceUnregistered(serviceEvent[serviceId]); - } else { - fail("Unknown notification type " + notificationType); - } - } catch (KernelErrorsError e) { - assertEquals(e.getErrors().size(), expectedMonitors[serviceId].size()); - for (Iterator iterator = e.getErrors().iterator(); iterator.hasNext();) { - assertEquals(kernelThrowable, iterator.next()); - } - } - } - - private void assertNotificationCorrect(int serviceId, int notificationType) { - for (Iterator iterator = expectedMonitors[serviceId].iterator(); iterator.hasNext();) { - MockServiceMonitor mockServiceMonitor = (MockServiceMonitor) iterator.next(); - assertEquals(notificationType, mockServiceMonitor.getCalled()); - } - for (Iterator iterator = unexpectedMonitors[serviceId].iterator(); iterator.hasNext();) { - MockServiceMonitor mockServiceMonitor = (MockServiceMonitor) iterator.next(); - assertEquals(0, mockServiceMonitor.getCalled()); - } - assertTrue("Unfired service errors " + kernelMonitor.getExpectedServiceMonitors(), kernelMonitor.getExpectedServiceMonitors().isEmpty()); - } - - protected void setUp() throws Exception { - super.setUp(); - - // serviceNames - for (int i = 0; i < serviceName.length; i++) { - serviceName[i] = new StringServiceName("service-" + i); - } - - // serviceEvents - for (int i = 0; i < serviceEvent.length; i++) { - serviceEvent[i] = createEvent(serviceName[i]); - } - - // all monitors - Set allMonitors = new LinkedHashSet(); - - // global - globalMonitors = createMonitors(2, "global"); - allMonitors.addAll(Arrays.asList(globalMonitors)); - for (int i = 0; i < globalMonitors.length; i++) { - MockServiceMonitor gloalMonitor = globalMonitors[i]; - serviceMonitorBroadcaster.addServiceMonitor(gloalMonitor, null); - } - - // service specific - serviceMonitors = new MockServiceMonitor[SERVICE_COUNT][]; - for (int i = 0; i < serviceMonitors.length; i++) { - serviceMonitors[i] = createMonitors(i, "service" + i); - allMonitors.addAll(Arrays.asList(serviceMonitors[i])); - for (int j = 0; j < serviceMonitors[i].length; j++) { - MockServiceMonitor serviceMonitor = serviceMonitors[i][j]; - serviceMonitorBroadcaster.addServiceMonitor(serviceMonitor, serviceName[i]); - } - } - - // even numbered services - evenMonitor = new MockServiceMonitor("even"); - allMonitors.add(evenMonitor); - for (int i = 0; i < serviceName.length; i++) { - if (i % 2 == 0) { - serviceMonitorBroadcaster.addServiceMonitor(evenMonitor, serviceName[i]); - } - - } - - // odd numbered services - oddMonitor = new MockServiceMonitor("odd"); - allMonitors.add(oddMonitor); - for (int i = 0; i < serviceName.length; i++) { - if (i % 2 != 0) { - serviceMonitorBroadcaster.addServiceMonitor(oddMonitor, serviceName[i]); - } - - } - - // expected monitors - for (int i = 0; i < expectedMonitors.length; i++) { - expectedMonitors[i] = new LinkedHashSet(); - expectedMonitors[i].addAll(Arrays.asList(globalMonitors)); - expectedMonitors[i].addAll(Arrays.asList(serviceMonitors[i])); - if (i % 2 == 0) { - expectedMonitors[i].add(evenMonitor); - } else { - expectedMonitors[i].add(oddMonitor); - } - } - - // unexpected monitors - for (int i = 0; i < unexpectedMonitors.length; i++) { - unexpectedMonitors[i] = new LinkedHashSet(allMonitors); - unexpectedMonitors[i].removeAll(expectedMonitors[i]); - } - } - - private MockServiceMonitor[] createMonitors(int count, String name) { - MockServiceMonitor[] gloalMonitors = new MockServiceMonitor[count]; - for (int i = 0; i < gloalMonitors.length; i++) { - gloalMonitors[i] = new MockServiceMonitor(name + "-" + i); - } - return gloalMonitors; - } - - private static ServiceEvent createEvent(ServiceName serviceName) { - return new ServiceEvent(0, - KERNEL, - serviceName, - SERVICE_FACTORY, - SYSTEM_CLASS_LOADER, - null, - null, - null); - } - - private static class MockServiceMonitor implements ServiceMonitor { - private final String name; - private int called; - private ServiceEvent expectedEvent; - private Throwable throwable; - - private MockServiceMonitor(String name) { - this.name = name; - } - - public String toString() { - return name; - } - - private void init(ServiceEvent expectedEvent, Throwable throwable) { - this.expectedEvent = expectedEvent; - this.throwable = throwable; - called = 0; - } - - public int getCalled() { - return called; - } - - public void serviceRegistered(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_REGISTERED) == 0); - called |= SERVICE_REGISTERED; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceStarting(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_STARTING) == 0); - called |= SERVICE_STARTING; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceWaitingToStart(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_WAITING_TO_START) == 0); - called |= SERVICE_WAITING_TO_START; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceStartError(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_START_ERROR) == 0); - called |= SERVICE_START_ERROR; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceRunning(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_RUNNING) == 0); - called |= SERVICE_RUNNING; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceStopping(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_STOPPING) == 0); - called |= SERVICE_STOPPING; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceWaitingToStop(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_WAITING_TO_STOP) == 0); - called |= SERVICE_WAITING_TO_STOP; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceStopError(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_STOP_ERROR) == 0); - called |= SERVICE_STOP_ERROR; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceStopped(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_STOPPED) == 0); - called |= SERVICE_STOPPED; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - public void serviceUnregistered(ServiceEvent serviceEvent) { - assertTrue("alread called", (called & SERVICE_UNREGISTERED) == 0); - called |= SERVICE_UNREGISTERED; - assertNotNull("call not expected", expectedEvent); - assertSame("wrong event object", expectedEvent, serviceEvent); - throwIfNecessary(); - } - - private void throwIfNecessary() { - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; - } else if (throwable instanceof Error) { - throw (Error) throwable; - } else { - assertNull(throwable); - } - } - } - - private static class MockKernelMonitor implements KernelMonitor { - private Set expectedServiceMonitors; - private ServiceEvent expectedEvent; - private Throwable expectedThrowable; - private Throwable kernelThrowable; - - private void init(Set expectedServiceMonitors, ServiceEvent expectedEvent, Throwable expectedThrowable, Throwable kernelThrowable) { - this.expectedServiceMonitors = new LinkedHashSet(expectedServiceMonitors); - this.expectedEvent = expectedEvent; - this.expectedThrowable = expectedThrowable; - this.kernelThrowable = kernelThrowable; - } - - public Set getExpectedServiceMonitors() { - return expectedServiceMonitors; - } - - public void serviceNotificationError(ServiceMonitor serviceMonitor, ServiceEvent serviceEvent, Throwable throwable) { - assertNotNull(expectedEvent); - assertTrue(expectedServiceMonitors.contains(serviceMonitor)); - expectedServiceMonitors.remove(serviceMonitor); - assertSame(expectedEvent, serviceEvent); - assertSame(expectedThrowable, throwable); - throwIfNecessary(); - } - - private void throwIfNecessary() { - if (kernelThrowable instanceof RuntimeException) { - throw (RuntimeException) kernelThrowable; - } else if (kernelThrowable instanceof Error) { - throw (Error) kernelThrowable; - } else { - assertNull(kernelThrowable); - } - } - } -} diff --git a/kernel/src/test/org/xbean/kernel/standard/StandardKernelTest.java b/kernel/src/test/org/xbean/kernel/standard/StandardKernelTest.java deleted file mode 100644 index f89dd133..00000000 --- a/kernel/src/test/org/xbean/kernel/standard/StandardKernelTest.java +++ /dev/null @@ -1,257 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.kernel.standard; - -import java.net.URL; -import java.net.URLClassLoader; - -import junit.framework.TestCase; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceState; -import org.xbean.kernel.StartStrategies; -import org.xbean.kernel.StaticServiceFactory; -import org.xbean.kernel.StopStrategies; -import org.xbean.kernel.StringServiceName; - -/** - * Tests the StandardKernel. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class StandardKernelTest extends TestCase { - private static final Object SERVICE = new Object(); - private static final String KERNEL_NAME = "test"; - private final Kernel kernel = new StandardKernel(KERNEL_NAME); - private final StringServiceName serviceName = new StringServiceName("Service"); -// private final StringServiceName ownedServiceName = new StringServiceName("OwnedService"); - private final MockServiceFactory serviceFactory = new MockServiceFactory(); - private final ClassLoader classLoader = new URLClassLoader(new URL[0]); - - /** - * Tests the initial state of the kernel is as expected. - * @throws Exception if a problem occurs - */ - public void testInitialState() throws Exception { - assertEquals(KERNEL_NAME, kernel.getKernelName()); - assertTrue(kernel.isRunning()); - - assertFalse(kernel.isRegistered(serviceName)); - try { - kernel.getServiceFactory(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getClassLoaderFor(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getServiceState(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getServiceStartTime(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.isServiceEnabled(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startService(serviceName, StartStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startServiceRecursive(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startServiceRecursive(serviceName, StartStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.stopService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.stopService(serviceName, StopStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - - kernel.destroy(); - assertEquals(KERNEL_NAME, kernel.getKernelName()); - assertFalse(kernel.isRunning()); - - assertFalse(kernel.isRegistered(serviceName)); - try { - kernel.getServiceFactory(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getClassLoaderFor(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getServiceState(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.getServiceStartTime(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.isServiceEnabled(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startService(serviceName, StartStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startServiceRecursive(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.startServiceRecursive(serviceName, StartStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.stopService(serviceName); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - try { - kernel.stopService(serviceName, StopStrategies.ASYNCHRONOUS); - fail("expected exception"); - } catch (ServiceNotFoundException e) { - // expected - assertSame(serviceName, e.getServiceName()); - } - } - - /** - * Test the life cycle of a simple service. - * @throws Exception if a problem occurs - */ - public void testSimpleLifecycle() throws Exception { - kernel.registerService(serviceName, serviceFactory, classLoader); - assertTrue(kernel.isRegistered(serviceName)); - assertSame(serviceFactory, kernel.getServiceFactory(serviceName)); - assertSame(classLoader, kernel.getClassLoaderFor(serviceName)); - assertSame(ServiceState.STOPPED, kernel.getServiceState(serviceName)); - assertNull(kernel.getService(serviceName)); - assertEquals(0, kernel.getServiceStartTime(serviceName)); - assertTrue(kernel.isServiceEnabled(serviceName)); - } - - private static class MockServiceFactory extends StaticServiceFactory { - private boolean restartable = true; - - private MockServiceFactory() throws NullPointerException { - super(SERVICE); - } - - public boolean isRestartable() { - return restartable; - } - } -} diff --git a/maven-xbean-plugin/pom.xml b/maven-xbean-plugin/pom.xml new file mode 100644 index 00000000..d926fe05 --- /dev/null +++ b/maven-xbean-plugin/pom.xml @@ -0,0 +1,115 @@ + + + + + + + 4.0.0 + + + xbean + org.apache.xbean + 3.12 + + + maven-xbean-plugin + Apache XBean :: Maven Plugin + maven-plugin + + + + + ${project.groupId} + xbean-spring + ${project.version} + + + + org.springframework + spring-beans + 2.0.5 + + + + org.springframework + spring-context + 2.0.5 + + + + org.apache.maven + maven-project + 2.0 + + + + org.codehaus.plexus + plexus-archiver + 1.0-alpha-5 + + + + org.apache.maven + maven-archiver + 2.0 + + + + org.apache.maven + maven-plugin-api + 2.0 + + + + ant + ant + true + + + + com.thoughtworks.qdox + qdox + + + + org.codehaus.plexus + plexus-utils + 1.1 + + + + org.apache.maven + maven-artifact + 2.0 + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 2.2 + + + + + diff --git a/maven-xbean-plugin/src/main/java/org/apache/xbean/maven/XBeanMojo.java b/maven-xbean-plugin/src/main/java/org/apache/xbean/maven/XBeanMojo.java new file mode 100644 index 00000000..0a3c88e2 --- /dev/null +++ b/maven-xbean-plugin/src/main/java/org/apache/xbean/maven/XBeanMojo.java @@ -0,0 +1,288 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.maven; + +import java.beans.PropertyEditorManager; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.apache.tools.ant.BuildException; +import org.apache.xbean.spring.generator.DocumentationGenerator; +import org.apache.xbean.spring.generator.GeneratorPlugin; +import org.apache.xbean.spring.generator.LogFacade; +import org.apache.xbean.spring.generator.MappingLoader; +import org.apache.xbean.spring.generator.NamespaceMapping; +import org.apache.xbean.spring.generator.QdoxMappingLoader; +import org.apache.xbean.spring.generator.WikiDocumentationGenerator; +import org.apache.xbean.spring.generator.XmlMetadataGenerator; +import org.apache.xbean.spring.generator.XsdGenerator; + +/** + * @author Guillaume Nodet + * @version $Id: GenerateApplicationXmlMojo.java 314956 2005-10-12 16:27:15Z brett $ + * @goal mapping + * @description Creates xbean mapping file + * @phase generate-sources + * @requiresDependencyResolution compile + */ +public class XBeanMojo extends AbstractMojo implements LogFacade { + + /** + * @parameter expression="${project}" + * @required + */ + private MavenProject project; + + /** + * Maven ProjectHelper + * + * @component + */ + protected MavenProjectHelper projectHelper; + + /** + * @parameter + * @required + */ + private String namespace; + + /** + * @parameter expression="${basedir}/src/main/java" + * @required + */ + private File srcDir; + + /** + * @parameter + */ + private List includes; + + /** + * @parameter + */ + private List classPathIncludes; + + /** + * @parameter + */ + private String excludedClasses; + + /** + * @parameter expression="${basedir}/target/xbean/" + * @required + */ + private File outputDir; + + /** + * @parameter + */ + private File schema; + + /** + * @parameter expression="org.apache.xbean.spring.context.impl" + */ + private String propertyEditorPaths; + + /** + * @parameter schemaAsArtifact + */ + private boolean schemaAsArtifact = true; + + /** + * @parameter + */ + private boolean generateSpringSchemasFile = true; + + /** + * @parameter + */ + private boolean generateSpringHandlersFile = true; + + /** + * @parameter + */ + private boolean strictXsdOrder = true; + + /** + * A list of additional GeneratorPlugins that should get used executed + * when generating output. + * + * @parameter + */ + private List generatorPlugins = Collections.emptyList(); + + public void execute() throws MojoExecutionException, MojoFailureException { + getLog().debug( " ======= XBeanMojo settings =======" ); + getLog().debug( "namespace[" + namespace + "]" ); + getLog().debug( "srcDir[" + srcDir + "]" ); + getLog().debug( "schema[" + schema + "]" ); + getLog().debug( "excludedClasses[" + excludedClasses + "]"); + getLog().debug( "outputDir[" + outputDir + "]" ); + getLog().debug( "propertyEditorPaths[" + propertyEditorPaths + "]" ); + getLog().debug( "schemaAsArtifact[" + schemaAsArtifact + "]"); + getLog().debug( "generateSpringSchemasFile[" + generateSpringSchemasFile + "]"); + getLog().debug( "generateSpringHandlersFile[" + generateSpringHandlersFile + "]"); + + if (schema == null) { + schema = new File(outputDir, project.getArtifactId() + ".xsd"); + } + + if (propertyEditorPaths != null) { + List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath())); + for (StringTokenizer paths = new StringTokenizer(propertyEditorPaths, " ,"); paths.hasMoreElements(); ) { + //StringTokenizer implements Enumeration, not Enumeration !! + editorSearchPath.add((String) paths.nextElement()); + } + PropertyEditorManager.setEditorSearchPath( editorSearchPath.toArray(new String[editorSearchPath.size()])); + } + + ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + try { + schema.getParentFile().mkdirs(); + + String[] excludedClasses = null; + if (this.excludedClasses != null) { + excludedClasses = this.excludedClasses.split(" *, *"); + } + Set dependencies = project.getDependencyArtifacts(); + List sourceJars = new ArrayList(); + sourceJars.add(srcDir); + if( includes !=null ) { + for (String src : includes) { + sourceJars.add(new File(src)); + } + } + for (Artifact dependency : dependencies) { + if ("sources".equals(dependency.getClassifier())) { + File file = dependency.getFile(); + sourceJars.add(file); + } + } + File[] srcJars = sourceJars.toArray(new File[sourceJars.size()]); + MappingLoader mappingLoader = new QdoxMappingLoader(namespace, srcJars, excludedClasses); + GeneratorPlugin[] plugins = new GeneratorPlugin[]{ + new XmlMetadataGenerator(outputDir.getAbsolutePath(), schema, generateSpringSchemasFile, generateSpringHandlersFile), + new DocumentationGenerator(schema), + new XsdGenerator(schema, strictXsdOrder), + new WikiDocumentationGenerator(schema), + }; + + // load the mappings + Thread.currentThread().setContextClassLoader(getClassLoader()); + Set namespaces = mappingLoader.loadNamespaces(); + if (namespaces.isEmpty()) { + System.out.println("Warning: no namespaces found!"); + } + + // generate the files + for (NamespaceMapping namespaceMapping : namespaces) { + for (GeneratorPlugin plugin : plugins) { + plugin.setLog(this); + plugin.generate(namespaceMapping); + } + for (GeneratorPlugin plugin : generatorPlugins) { + plugin.setLog(this); + plugin.generate(namespaceMapping); + } + } + + // Attach them as artifacts + if (schemaAsArtifact) { + projectHelper.attachArtifact(project, "xsd", null, schema); + projectHelper.attachArtifact(project, "html", "schema", new File(schema.getAbsolutePath() + ".html")); + } + + Resource res = new Resource(); + res.setDirectory(outputDir.toString()); + project.addResource(res); + + log("...done."); + } catch (Exception e) { + throw new BuildException(e); + } finally { + Thread.currentThread().setContextClassLoader(oldCL); + } + } + + public void log(String message) { + getLog().info(message); + } + + public void log(String message, int level) { + getLog().info(message); + } + + protected URLClassLoader getClassLoader() throws MojoExecutionException { + try { + Set urls = new HashSet(); + + URL mainClasses = new File(project.getBuild().getOutputDirectory()) + .toURL(); + getLog().debug("Adding to classpath : " + mainClasses); + urls.add(mainClasses); + + URL testClasses = new File(project.getBuild() + .getTestOutputDirectory()).toURL(); + getLog().debug("Adding to classpath : " + testClasses); + urls.add(testClasses); + + Set dependencies = project.getArtifacts(); + Iterator iter = dependencies.iterator(); + for (Artifact classPathElement : dependencies) { + getLog().debug( + "Adding artifact: " + classPathElement.getFile() + + " to classpath"); + urls.add(classPathElement.getFile().toURL()); + } + + if( classPathIncludes!=null ) { + for (String include : classPathIncludes) { + final URL url = new File(include).toURL(); + getLog().debug("Adding to classpath : " + url); + urls.add(url); + } + } + + URLClassLoader appClassloader = new URLClassLoader(urls.toArray(new URL[urls.size()]), + this.getClass().getClassLoader()); + return appClassloader; + } catch (MalformedURLException e) { + throw new MojoExecutionException( + "Error during setting up classpath", e); + } + } + +} diff --git a/maven-xbean-plugin/src/site/site.xml b/maven-xbean-plugin/src/site/site.xml new file mode 100644 index 00000000..e00978f7 --- /dev/null +++ b/maven-xbean-plugin/src/site/site.xml @@ -0,0 +1,37 @@ + + + + + + + + + + ${parentProject} + + ${modules} + + ${reports} + + + + + + diff --git a/maven.xml b/maven.xml deleted file mode 100644 index 9c0de314..00000000 --- a/maven.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..523e5672 --- /dev/null +++ b/pom.xml @@ -0,0 +1,441 @@ + + + + + + + + 4.0.0 + + + org.apache.geronimo.genesis + genesis-java5-flava + 2.0 + + + org.apache.xbean + xbean + Apache XBean + pom + 2005 + + 3.12 + + + XBean is a plugin based server architecture. + + + + scm:svn:http://svn.apache.org/repos/asf/geronimo/xbean/tags/xbean-3.12 + scm:svn:https://svn.apache.org/repos/asf/geronimo/xbean/tags/xbean-3.12 + http://svn.apache.org/viewvc/geronimo/xbean/tags/xbean-3.12 + + + http://geronimo.apache.org/maven/${siteId}/${project.version} + + + + xbean-website + ${staging.siteURL}/${siteId}/${project.version} + + + + + xbean + + UTF-8 + + + + + jira + http://issues.apache.org/jira/browse/XBEAN + + + + + xbean developers + mailto:xbean-dev-subscribe@geronimo.apache.org + mailto:xbean-dev-unsubscribe@xbean.org + + + xbean users + mailto:xbean-user-subscribe@geronimo.apache.org + mailto:xbean-user-unsubscribe@geronimo.apache.org + + + xbean scm + mailto:xbean-scm-subscribe@geronimo.apache.org + mailto:xbean-scm-unsubscribe@geronimo.apache.org + + + + + + chirino + Hiram Chirino + + Committer + + -5 + + + dain + Dain Sundstrom + dain@iq80.com + + Committer + + -8 + + + dblevins + David Blevins + dblevins@visi.com + + Committer + + -8 + + + jstrachan + James Strachan + + Committer + + -8 + + + jvanzyl + Jason van Zyl + + Committer + + -8 + + + maguro + Alan D. Cabrera + + Committer + + -8 + + + gnodet + Guillaume Nodet + + Committer + + +1 + + + jlaskowski + Jacek Laskowski + jacek@laskowski.net.pl + + Committer + + +1 + + + djencks + David Jencks + + Committer + + -8 + + + + + + + + org.apache.xbean + xbean-classloader + ${project.version} + + + org.apache.xbean + xbean-classpath + ${project.version} + + + org.apache.xbean + xbean-bundleutils + ${project.version} + + + org.apache.xbean + xbean-finder + ${project.version} + + + org.apache.xbean + xbean-naming + ${project.version} + + + org.apache.xbean + xbean-reflect + ${project.version} + + + org.apache.xbean + xbean-blueprint + ${project.version} + + + org.apache.xbean + xbean-spring + ${project.version} + + + org.apache.xbean + xbean-telnet + ${project.version} + + + org.apache.xbean + xbean-asm-shaded + ${project.version} + + + org.apache.xbean + xbean-finder-shaded + ${project.version} + + + + + + ant + ant + 1.6.5 + + + + cglib + cglib-nodep + 2.1_2 + + + + commons-beanutils + commons-beanutils + 1.7.0 + + + + commons-logging + commons-logging + 1.0.3 + + + + groovy + groovy + 1.0-jsr-03 + + + + mx4j + mx4j + 3.0.1 + + + + org.springframework + spring-beans + 2.5.6 + + + + org.springframework + spring-context + 2.5.6 + + + + org.springframework + spring-web + 2.5.6 + + + + com.thoughtworks.qdox + qdox + 1.6.3 + + + + org.slf4j + slf4j-api + 1.5.11 + + + + + + + + junit + junit + 4.8.2 + test + + + + + + install + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.3.2 + + + org.apache.xbean + maven-xbean-plugin + ${project.version} + + + org.apache.felix + maven-bundle-plugin + 2.3.4 + true + + + ${project.url} + org.apache.xbean.*;version=${project.version} + + + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + xbean-classloader + xbean-classpath + xbean-bundleutils + xbean-finder + xbean-naming + xbean-reflect + xbean-blueprint + xbean-spring + xbean-telnet + maven-xbean-plugin + xbean-asm-shaded + xbean-finder-shaded + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.5 + + 128m + 512 + true + true + false + 1.5 + + true + + + http://java.sun.com/j2se/1.5.0/docs/api/ + http://java.sun.com/j2se/1.4.2/docs/api/ + http://java.sun.com/j2se/1.3/docs/api/ + + + http://java.sun.com/j2ee/1.4/docs/api/ + http://java.sun.com/j2ee/sdk_1.3/techdocs/api/ + + + http://jakarta.apache.org/commons/collections/apidocs + http://jakarta.apache.org/commons/logging/apidocs/ + http://logging.apache.org/log4j/docs/api/ + http://jakarta.apache.org/regexp/apidocs/ + http://jakarta.apache.org/velocity/api/ + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 2.4 + + 1.5 + + + + + org.codehaus.mojo + jxr-maven-plugin + 2.0-beta-1 + + + + org.codehaus.mojo + surefire-report-maven-plugin + 2.0-beta-1 + + + + + + diff --git a/project.properties b/project.properties deleted file mode 100644 index ee39ee17..00000000 --- a/project.properties +++ /dev/null @@ -1 +0,0 @@ -maven.multiproject.include=*/project.xml \ No newline at end of file diff --git a/project.xml b/project.xml deleted file mode 100644 index 954d0e38..00000000 --- a/project.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - 3 - - XBean - xbean - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - diff --git a/server/maven.xml b/server/maven.xml deleted file mode 100644 index f848a205..00000000 --- a/server/maven.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/server/project.properties b/server/project.properties deleted file mode 100644 index 07280f09..00000000 --- a/server/project.properties +++ /dev/null @@ -1,34 +0,0 @@ -### -# Copyright 2005 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. -### -maven.repo.remote=http://www.xbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven - -maven.compile.source=1.4 -maven.compile.target=1.4 -maven.compile.deprecation=true -maven.compile.debug=true -maven.compile.optimize=true - -maven.remote.group=xbean -maven.username=${user.name} -maven.repo.central=beaver.codehaus.org -maven.repo.central.directory=/dist - -maven.javadoc.source=1.4 -maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/ -maven.javadoc.additionalparam=-linksource - -maven.site.deploy.method=rsync -maven.site.deploy.clean=true diff --git a/server/project.xml b/server/project.xml deleted file mode 100644 index 8ab83e89..00000000 --- a/server/project.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - 3 - - XBean :: Server - xbean-server - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - XBean: generic server components - - - The XBean server module contains generic services useful in a server environment. - - - http://www.xbean.org/ - - www.xbean.org - /home/projects/xbean/public_html/maven - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - backport175 - backport175 - 1.0 - - - backport-util-concurrent - backport-util-concurrent - 2.0_01_pd - - - cglib - cglib-nodep - 2.1_2 - test - - - mx4j - mx4j - 3.0.1 - - - springframework - spring - 1.2.4 - - - xbean - xbean-kernel - 2.0-SNAPSHOT - - - xbean - xbean-spring - 2.0-SNAPSHOT - - - - - src/java - - - src/resources - - - src/test - - - **/*Test.java - - - - - - - - - - - - - - - maven-jxr-plugin - maven-javadoc-plugin - maven-junit-report-plugin - - - - - - - diff --git a/server/src/java/org/xbean/server/annotation/AnnotationProvider.java b/server/src/java/org/xbean/server/annotation/AnnotationProvider.java deleted file mode 100644 index adefac45..00000000 --- a/server/src/java/org/xbean/server/annotation/AnnotationProvider.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * AnnotationProvider is a simple abstraction over the various annotatiom systems used in Java 1.4 and default provider - * in Java 5. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface AnnotationProvider { - /** - * Gets the type of the specified annotation or null if not an recognized annotation. - * @param annotation the annotation instance - * @return the type of the annotation or null if not an recognized annotation - */ - Class getAnnotationType(Object annotation); - - /** - * Determines if the specific class had been annotated with the specified annotation. - * @param annotation the annotation type to test - * @param target the class to test - * @return true if the class has been annotated with the specified annotation - */ - boolean isAnnotationPresent(Class annotation, Class target); - - /** - * Gets all of the annotations on the specified class. - * @param target the class from which the annotations should be retrieved - * @return the annotations on the specified class - */ - Object[] getAnnotations(Class target); - - /** - * Gets a specific annotation on the specified class. - * @param annotation the annotation to retrieve from the class - * @param target the class from which the annotation should be retrieved - * @return an instance of the specified annotation or null if the class has not have the annotated - */ - Object getAnnotation(Class annotation, Class target); - - /** - * Determines if the specific method had been annotated with the specified annotation. - * @param annotation the annotation type to test - * @param target the method to test - * @return true if the method has been annotated with the specified annotation - */ - boolean isAnnotationPresent(Class annotation, Method target); - - /** - * Gets all of the annotations on the specified method. - * @param target the method from which the annotations should be retrieved - * @return the annotations on the specified method - */ - Object[] getAnnotations(Method target); - - /** - * Gets a specific annotation on the specified method. - * @param annotation the annotation to retrieve from the method - * @param target the method from which the annotation should be retrieved - * @return an instance of the specified annotation or null if the method has not have the annotated - */ - Object getAnnotation(Class annotation, Method target); - - /** - * Gets all of the annotations for each parameter of the specified method. - * @param target the method from which the parameter annotations should be retrieved - * @return the annotations on the parameters specified method - */ - Object[][] getParameterAnnotations(Method target); - - /** - * Determines if the specific constructor had been annotated with the specified annotation. - * @param annotation the annotation type to test - * @param target the constructor to test - * @return true if the constructor has been annotated with the specified annotation - */ - boolean isAnnotationPresent(Class annotation, Constructor target); - - /** - * Gets all of the annotations on the specified constructor. - * @param target the constructor from which the annotations should be retrieved - * @return the annotations on the specified constructor - */ - Object[] getAnnotations(Constructor target); - - /** - * Gets a specific annotation on the specified constructor. - * @param annotation the annotation to retrieve from the constructor - * @param target the constructor from which the annotation should be retrieved - * @return an instance of the specified annotation or null if the constructor has not have the annotated - */ - Object getAnnotation(Class annotation, Constructor target); - - /** - * Gets all of the annotations for each parameter of the specified constructor. - * @param target the constructor from which the parameter annotations should be retrieved - * @return the annotations on the parameters specified constructor - */ - Object[][] getParameterAnnotations(Constructor target); - - /** - * Determines if the specific field had been annotated with the specified annotation. - * @param annotation the annotation type to test - * @param target the field to test - * @return true if the field has been annotated with the specified annotation - */ - boolean isAnnotationPresent(Class annotation, Field target); - - /** - * Gets all of the annotations on the specified field. - * @param target the field from which the annotations should be retrieved - * @return the annotations on the specified field - */ - Object[] getAnnotations(Field target); - - /** - * Gets a specific annotation on the specified field. - * @param annotation the annotation to retrieve from the field - * @param target the field from which the annotation should be retrieved - * @return an instance of the specified annotation or null if the field has not have the annotated - */ - Object getAnnotation(Class annotation, Field target); -} diff --git a/server/src/java/org/xbean/server/annotation/Backport175AnnotationProvider.java b/server/src/java/org/xbean/server/annotation/Backport175AnnotationProvider.java deleted file mode 100644 index f278f62d..00000000 --- a/server/src/java/org/xbean/server/annotation/Backport175AnnotationProvider.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import org.codehaus.backport175.reader.Annotation; -import org.codehaus.backport175.reader.Annotations; - -/** - * Annotation provider for backport 175. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class Backport175AnnotationProvider implements AnnotationProvider { - private static final Annotation[][] NO_PARAMETER_ANNOTATIONS = new Annotation[0][0]; - - /** - * {@inheritDoc} - */ - public Class getAnnotationType(Object annotation) { - if (annotation instanceof Annotation) { - return ((Annotation) annotation).annotationType(); - } - return null; - } - - /** - * {@inheritDoc} - */ - public boolean isAnnotationPresent(Class annotation, Class target) { - return Annotations.isAnnotationPresent(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[] getAnnotations(Class target) { - return Annotations.getAnnotations(target); - } - - /** - * {@inheritDoc} - */ - public Object getAnnotation(Class annotation, Class target) { - return Annotations.getAnnotation(annotation, target); - } - - /** - * {@inheritDoc} - */ - public boolean isAnnotationPresent(Class annotation, Method target) { - return Annotations.isAnnotationPresent(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[] getAnnotations(Method method) { - return Annotations.getAnnotations(method); - } - - /** - * {@inheritDoc} - */ - public Object getAnnotation(Class annotation, Method target) { - return Annotations.getAnnotation(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[][] getParameterAnnotations(Method target) { - // backport 175 does not support parameter annotations - return NO_PARAMETER_ANNOTATIONS; - } - - /** - * {@inheritDoc} - */ - public boolean isAnnotationPresent(Class annotation, Constructor target) { - return Annotations.isAnnotationPresent(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[] getAnnotations(Constructor target) { - return Annotations.getAnnotations(target); - } - - /** - * {@inheritDoc} - */ - public Object getAnnotation(Class annotation, Constructor target) { - return Annotations.getAnnotation(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[][] getParameterAnnotations(Constructor target) { - // backport 175 does not support parameter annotations - return NO_PARAMETER_ANNOTATIONS; - } - - /** - * {@inheritDoc} - */ - public boolean isAnnotationPresent(Class annotation, Field target) { - return Annotations.isAnnotationPresent(annotation, target); - } - - /** - * {@inheritDoc} - */ - public Object[] getAnnotations(Field field) { - return Annotations.getAnnotations(field); - } - - /** - * {@inheritDoc} - */ - public Object getAnnotation(Class annotation, Field target) { - return Annotations.getAnnotation(annotation, target); - } -} diff --git a/server/src/java/org/xbean/server/annotation/DefaultConstructor.java b/server/src/java/org/xbean/server/annotation/DefaultConstructor.java deleted file mode 100644 index 90e33c55..00000000 --- a/server/src/java/org/xbean/server/annotation/DefaultConstructor.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -/** - * DefaultConstructor is an annotation to select the default constructor for a class. This annotation informs the - * server that specific constructor should be used instead of the default no argument constrctor. The system will - * provide Java default values for an constructor argument that is not specified. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface DefaultConstructor { -} diff --git a/server/src/java/org/xbean/server/annotation/ParameterNames.java b/server/src/java/org/xbean/server/annotation/ParameterNames.java deleted file mode 100644 index e7661bf8..00000000 --- a/server/src/java/org/xbean/server/annotation/ParameterNames.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -/** - * ParameterNames is an annotation to provide the names of constructor and method names to the server. Parameter names - * can be used during constructor injection instead of the standard index based approach. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface ParameterNames { - /** - * The names of the constructor or method parameters. - * @return names of the constructor or method parameters. - */ - String[] names(); -} diff --git a/server/src/java/org/xbean/server/classloader/DestroyableClassLoader.java b/server/src/java/org/xbean/server/classloader/DestroyableClassLoader.java deleted file mode 100644 index 49dd72d6..00000000 --- a/server/src/java/org/xbean/server/classloader/DestroyableClassLoader.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.classloader; - -/** - * DestroyableClassLoader is a mixin interface for a ClassLoader that add a destroy method to propertly cleanup a - * classloader then dereferenced by the server. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface DestroyableClassLoader { - /** - * Destroys the clasloader releasing all resources. After this mehtod is called, the class loader will no longer - * load any classes or resources. - */ - void destroy(); -} diff --git a/server/src/java/org/xbean/server/classloader/JarFileClassLoader.java b/server/src/java/org/xbean/server/classloader/JarFileClassLoader.java deleted file mode 100644 index 2f4b3584..00000000 --- a/server/src/java/org/xbean/server/classloader/JarFileClassLoader.java +++ /dev/null @@ -1,411 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.classloader; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLStreamHandlerFactory; -import java.security.CodeSource; -import java.security.cert.Certificate; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -/** - * The JarFileClassLoader that loads classes and resources from a list of JarFiles. This method is simmilar to URLClassLoader - * except it properly closes JarFiles when the classloader is destroyed so that the file read lock will be released, and - * the jar file can be modified and deleted. - *

    - * Note: This implementation currently does not work reliably on windows, since the jar URL handler included with the Sun JavaVM - * holds a read lock on the JarFile, and this lock is not released when the jar url is dereferenced. To fix this a - * replacement for the jar url handler must be written. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class JarFileClassLoader extends MultiParentClassLoader { - private static final URL[] EMPTY_URLS = new URL[0]; - private final Object lock = new Object(); - private final LinkedHashMap classPath = new LinkedHashMap(); - private boolean destroyed = false; - - /** - * Creates a JarFileClassLoader that is a child of the system class loader. - * @param name the name of this class loader - * @param urls a list of URLs from which classes and resources should be loaded - */ - public JarFileClassLoader(String name, URL[] urls) { - super(name, EMPTY_URLS); - addURLs(urls); - } - - /** - * Creates a JarFileClassLoader that is a child of the specified class loader. - * @param name the name of this class loader - * @param urls a list of URLs from which classes and resources should be loaded - * @param parent the parent of this class loader - */ - public JarFileClassLoader(String name, URL[] urls, ClassLoader parent) { - this(name, urls, new ClassLoader[] {parent}); - } - - /** - * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory - * for accessing the urls.. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parent the parent of this class loader - * @param factory the URLStreamHandlerFactory used to access the urls - */ - public JarFileClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { - this(name, urls, new ClassLoader[] {parent}, factory); - } - - /** - * Creates a named class loader as a child of the specified parents. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parents the parents of this class loader - */ - public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents) { - super(name, EMPTY_URLS, parents); - addURLs(urls); - } - - /** - * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory - * for accessing the urls.. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parents the parents of this class loader - * @param factory the URLStreamHandlerFactory used to access the urls - */ - public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) { - super(name, EMPTY_URLS, parents, factory); - addURLs(urls); - } - - /** - * {@inheritDoc} - */ - public URL[] getURLs() { - return (URL[]) classPath.keySet().toArray(new URL[classPath.keySet().size()]); - } - - /** - * {@inheritDoc} - */ - protected void addURL(URL url) { - addURLs(Collections.singletonList(url)); - } - - /** - * Adds an array of urls to the end of this class loader. - * @param urls the URLs to add - */ - protected void addURLs(URL[] urls) { - addURLs(Arrays.asList(urls)); - } - - /** - * Adds a list of urls to the end of this class loader. - * @param urls the URLs to add - */ - protected void addURLs(List urls) { - LinkedList locationStack = new LinkedList(urls); - try { - while (!locationStack.isEmpty()) { - URL url = (URL) locationStack.removeFirst(); - - if (!"file".equals(url.getProtocol())) { - // download the jar - throw new Error("Only local file jars are supported " + url); - } - - String path = url.getPath(); - if (classPath.containsKey(path)) { - continue; - } - - File file = new File(path); - if (!file.canRead()) { - // can't read file... - continue; - } - - // open the jar file - JarFile jarFile; - try { - jarFile = new JarFile(file); - } catch (IOException e) { - // can't seem to open the file - continue; - } - classPath.put(url, jarFile); - - // push the manifest classpath on the stack (make sure to maintain the order) - Manifest manifest = null; - try { - manifest = jarFile.getManifest(); - } catch (IOException ignored) { - } - - if (manifest != null) { - Attributes mainAttributes = manifest.getMainAttributes(); - String manifestClassPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH); - if (manifestClassPath != null) { - LinkedList classPathUrls = new LinkedList(); - for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) { - String entry = tokenizer.nextToken(); - File parentDir = file.getParentFile(); - File entryFile = new File(parentDir, entry); - // manifest entries are optional... if they aren't there it is ok - if (entryFile.canRead()) { - classPathUrls.addLast(entryFile.getAbsolutePath()); - } - } - locationStack.addAll(0, classPathUrls); - } - } - } - } catch (Error e) { - destroy(); - throw e; - } - } - - /** - * {@inheritDoc} - */ - public void destroy() { - synchronized (lock) { - if (destroyed) { - return; - } - destroyed = true; - for (Iterator iterator = classPath.values().iterator(); iterator.hasNext();) { - JarFile jarFile = (JarFile) iterator.next(); - try { - jarFile.close(); - } catch (IOException ignored) { - } - } - classPath.clear(); - } - super.destroy(); - } - - /** - * {@inheritDoc} - */ - public URL findResource(String resourceName) { - URL jarUrl = null; - synchronized (lock) { - if (destroyed) { - return null; - } - for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && jarUrl == null;) { - Map.Entry entry = (Map.Entry) iterator.next(); - JarFile jarFile = (JarFile) entry.getValue(); - JarEntry jarEntry = jarFile.getJarEntry(resourceName); - if (jarEntry != null && !jarEntry.isDirectory()) { - jarUrl = (URL) entry.getKey(); - } - } - } - - - try { - String urlString = "jar:" + jarUrl + "!/" + resourceName; - return new URL(jarUrl, urlString); - } catch (MalformedURLException e) { - return null; - } - } - - /** - * {@inheritDoc} - */ - public Enumeration findResources(String resourceName) throws IOException { - List resources = new ArrayList(); - List superResources = Collections.list(super.findResources(resourceName)); - resources.addAll(superResources); - - synchronized (lock) { - if (destroyed) { - return Collections.enumeration(Collections.EMPTY_LIST); - } - for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - JarFile jarFile = (JarFile) entry.getValue(); - JarEntry jarEntry = jarFile.getJarEntry(resourceName); - if (jarEntry != null && !jarEntry.isDirectory()) { - try { - URL url = (URL) entry.getKey(); - String urlString = "jar:" + url + "!/" + resourceName; - resources.add(new URL(url, urlString)); - } catch (MalformedURLException e) { - } - } - } - } - - return Collections.enumeration(resources); - } - - /** - * {@inheritDoc} - */ - protected Class findClass(String className) throws ClassNotFoundException { - SecurityManager securityManager = System.getSecurityManager(); - if (securityManager != null) { - String packageName; - int packageEnd = className.lastIndexOf('.'); - if (packageEnd >= 0) { - packageName = className.substring(0, packageEnd); - securityManager.checkPackageDefinition(packageName); - } - } - - Certificate[] certificates = null; - URL jarUrl = null; - Manifest manifest = null; - byte[] bytes; - synchronized (lock) { - if (destroyed) { - throw new ClassNotFoundException("Class loader has been destroyed: " + className); - } - - try { - String entryName = className.replace('.', '/') + ".class"; - InputStream inputStream = null; - for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && inputStream == null;) { - Map.Entry entry = (Map.Entry) iterator.next(); - jarUrl = (URL) entry.getKey(); - JarFile jarFile = (JarFile) entry.getValue(); - JarEntry jarEntry = jarFile.getJarEntry(entryName); - if (jarEntry != null && !jarEntry.isDirectory()) { - inputStream = jarFile.getInputStream(jarEntry); - certificates = jarEntry.getCertificates(); - manifest = jarFile.getManifest(); - } - } - if (inputStream == null) { - throw new ClassNotFoundException(className); - } - - try { - byte[] buffer = new byte[4096]; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { - out.write(buffer, 0, count); - } - bytes = out.toByteArray(); - } finally { - inputStream.close(); - } - } catch (IOException e) { - throw new ClassNotFoundException(className, e); - } - } - - definePackage(className, jarUrl, manifest); - CodeSource codeSource = new CodeSource(jarUrl, certificates); - Class clazz = defineClass(className, bytes, 0, bytes.length, codeSource); - return clazz; - } - - private void definePackage(String className, URL jarUrl, Manifest manifest) { - int packageEnd = className.lastIndexOf('.'); - if (packageEnd < 0) { - return; - } - - String packageName = className.substring(0, packageEnd); - String packagePath = packageName.replace('.', '/') + "/"; - - Attributes packageAttributes = null; - Attributes mainAttributes = null; - if (manifest != null) { - packageAttributes = manifest.getAttributes(packagePath); - mainAttributes = manifest.getMainAttributes(); - } - Package pkg = getPackage(packageName); - if (pkg != null) { - if (pkg.isSealed()) { - if (!pkg.isSealed(jarUrl)) { - throw new SecurityException("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl); - } - } else { - if (isSealed(packageAttributes, mainAttributes)) { - throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl); - } - } - } else { - String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes); - String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes); - String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes); - String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes); - String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes); - String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes); - - URL sealBase = null; - if (isSealed(packageAttributes, mainAttributes)) { - sealBase = jarUrl; - } - - definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); - } - } - - private String getAttribute(Attributes.Name name, Attributes packageAttributes, Attributes mainAttributes) { - if (packageAttributes != null) { - String value = packageAttributes.getValue(name); - if (value != null) { - return value; - } - } - if (mainAttributes != null) { - return mainAttributes.getValue(name); - } - return null; - } - - private boolean isSealed(Attributes packageAttributes, Attributes mainAttributes) { - String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes); - if (sealed == null) { - return false; - } - return "true".equalsIgnoreCase(sealed); - } -} \ No newline at end of file diff --git a/server/src/java/org/xbean/server/classloader/MultiParentClassLoader.java b/server/src/java/org/xbean/server/classloader/MultiParentClassLoader.java deleted file mode 100644 index dccf57fe..00000000 --- a/server/src/java/org/xbean/server/classloader/MultiParentClassLoader.java +++ /dev/null @@ -1,193 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.classloader; - -import java.io.IOException; -import java.net.URL; -import java.net.URLStreamHandlerFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; - -/** - * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class - * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced - * with a operation that checks each parent in order. This getParent method of this class will always return null, - * which may be interperated by the calling code to mean that this class loader is a direct child of the system class - * loader. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class MultiParentClassLoader extends NamedClassLoader { - private final ClassLoader[] parents; - - /** - * Creates a named class loader with no parents. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - */ - public MultiParentClassLoader(String name, URL[] urls) { - super(name, urls); - parents = new ClassLoader[0]; - } - - /** - * Creates a named class loader as a child of the specified parent. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parent the parent of this class loader - */ - public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent) { - this(name, urls, new ClassLoader[] {parent}); - } - - /** - * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory - * for accessing the urls.. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parent the parent of this class loader - * @param factory the URLStreamHandlerFactory used to access the urls - */ - public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { - this(name, urls, new ClassLoader[] {parent}, factory); - } - - /** - * Creates a named class loader as a child of the specified parents. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parents the parents of this class loader - */ - public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents) { - super(name, urls); - this.parents = copyParents(parents); - } - - /** - * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory - * for accessing the urls.. - * @param name the name of this class loader - * @param urls the urls from which this class loader will classes and resources - * @param parents the parents of this class loader - * @param factory the URLStreamHandlerFactory used to access the urls - */ - public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) { - super(name, urls, null, factory); - this.parents = copyParents(parents); - } - - private static ClassLoader[] copyParents(ClassLoader[] parents) { - ClassLoader[] newParentsArray = new ClassLoader[parents.length]; - for (int i = 0; i < parents.length; i++) { - ClassLoader parent = parents[i]; - if (parent == null) { - throw new NullPointerException("parent[" + i + "] is null"); - } - newParentsArray[i] = parent; - } - return newParentsArray; - } - - /** - * Gets the parents of this class loader. - * @return the parents of this class loader - */ - public ClassLoader[] getParents() { - return parents; - } - - /** - * {@inheritDoc} - */ - protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - Class clazz = findLoadedClass(name); - for (int i = 0; i < parents.length && clazz == null; i++) { - ClassLoader parent = parents[i]; - try { - clazz = parent.loadClass(name); - } catch (ClassNotFoundException ignored) { - // this parent didn't have the class; try the next one - } - } - - if (clazz == null) { - // parents didn't have the class; attempt to load from my urls - return super.loadClass(name, resolve); - } else { - // we found the class; resolve it if requested - if (resolve) { - resolveClass(clazz); - } - return clazz; - } - } - - /** - * {@inheritDoc} - */ - public URL getResource(String name) { - URL url = null; - for (int i = 0; i < parents.length && url == null; i++) { - ClassLoader parent = parents[i]; - url = parent.getResource(name); - } - - if (url == null) { - // parents didn't have the resource; attempt to load it from my urls - return super.getResource(name); - } else { - return url; - } - } - - /** - * {@inheritDoc} - */ - public Enumeration findResources(String name) throws IOException { - List resources = new ArrayList(); - - // Add resources from all parents - for (int i = 0; i < parents.length; i++) { - ClassLoader parent = parents[i]; - List parentResources = Collections.list(parent.getResources(name)); - resources.addAll(parentResources); - } - - // Add the resources from my urls - List myResources = Collections.list(super.findResources(name)); - resources.addAll(myResources); - - // return an enumeration over the list - return Collections.enumeration(resources); - } - - /** - * {@inheritDoc} - */ - public String toString() { - return "[" + getClass().getName() + ":" + - " name=" + getName() + - " urls=" + Arrays.asList(getURLs()) + - " parents=" + Arrays.asList(parents) + - "]"; - } -} diff --git a/server/src/java/org/xbean/server/loader/LoadAllMain.java b/server/src/java/org/xbean/server/loader/LoadAllMain.java deleted file mode 100644 index 5044af03..00000000 --- a/server/src/java/org/xbean/server/loader/LoadAllMain.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.loader; - -import java.util.List; -import java.util.Iterator; - -import org.xbean.server.main.Main; -import org.xbean.server.main.FatalStartupError; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceName; - -/** - * LoadAllMain loads all configurations specified in the arguments passed to main. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class LoadAllMain implements Main { - private Kernel kernel; - private Main next; - - /** - * Gets the kernel in which configurations are loaded. - * @return the kernel in which configurations are loaded - */ - public Kernel getKernel() { - return kernel; - } - - /** - * Sets the kernel in which configurations are loaded. - * @param kernel the kernel in which configurations are loaded - */ - public void setKernel(Kernel kernel) { - this.kernel = kernel; - } - - /** - * Gets the next main instance to call. - * @return the next main instance to call - */ - public Main getNext() { - return next; - } - - /** - * Sets the next main instance to call. - * @param next the next main instance to call - */ - public void setNext(Main next) { - this.next = next; - } - - /** - * Loads all configurations specified in the args and call the next main instance with no arguments. - * @param args the configurations to load - */ - public void main(String[] args) { - for (int i = 0; i < args.length; i++) { - String location = args[i]; - load(location); - } - - if (next != null) { - next.main(new String[0]); - } - } - - private void load(String location) { - List loaders = kernel.getServices(Loader.class); - for (Iterator iterator = loaders.iterator(); iterator.hasNext();) { - Loader loader = (Loader) iterator.next(); - try { - ServiceName configurationName = loader.load(location); - if (configurationName != null) { - kernel.startServiceRecursive(configurationName); - return; - } - } catch (Exception e) { - throw new FatalStartupError("Error loading '" + location + "' with " + loader, e); - } - } - String message = "No loaders were able to load '" + location + "' : Available loaders "; - for (Iterator iterator = loaders.iterator(); iterator.hasNext();) { - message += iterator.next(); - if (iterator.hasNext()) { - message += ", "; - } - } - throw new FatalStartupError(message); - } -} diff --git a/server/src/java/org/xbean/server/loader/Loader.java b/server/src/java/org/xbean/server/loader/Loader.java deleted file mode 100644 index b14a8107..00000000 --- a/server/src/java/org/xbean/server/loader/Loader.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.loader; - -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.Kernel; - -/** - * Loaders load configurations into a Kernel. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface Loader { - /** - * Gets the kernel in which configuraitons are loaded. - * @return the kernel in which configurations are loaded - */ - Kernel getKernel(); - - /** - * Loads the specified configuration into the kernel. The location passed to this method is specific loader - * implementation. It is important to remember that a loader only loads the configuration into the kernel and - * does not start the configuration. - * - * @param location the location of the configuration - * @return the service name of the configuration added to the kernel - * @throws Exception if a problem occurs while loading the configuration - */ - ServiceName load(String location) throws Exception; -} diff --git a/server/src/java/org/xbean/server/main/FatalStartupError.java b/server/src/java/org/xbean/server/main/FatalStartupError.java deleted file mode 100644 index 070892ff..00000000 --- a/server/src/java/org/xbean/server/main/FatalStartupError.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.main; - -/** - * Indicates that a fatal error occured while starting the server. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class FatalStartupError extends Error { - /** - * The default exit code assigned to new exception if an exit code is not provided. - */ - public static final int DEFAULT_EXIT_CODE = 3; - - private final int exitCode; - - /** - * Creates a FatalStartupError containing the specified message and the DEFAULT_EXIT_CODE. - * @param message a descrption of the cause of the error - */ - public FatalStartupError(String message) { - this(message, DEFAULT_EXIT_CODE); - } - - /** - * Creates a FatalStartupError containing the specified message and exit code. - * @param message a descrption of the cause of the error - * @param exitCode the exit code that should be passed to System.exit(int) - */ - public FatalStartupError(String message, int exitCode) { - super(message); - this.exitCode = exitCode; - } - - /** - * Creates a FatalStartupError containing the specified message, cause by exception, and the DEFAULT_EXIT_CODE. - * @param message a descrption of the cause of the error - * @param cause the cause of this exception - */ - public FatalStartupError(String message, Throwable cause) { - this(message, DEFAULT_EXIT_CODE, cause); - } - - /** - * Creates a FatalStartupError containing the specified message, cause by exception, and the specified exit code. - * @param message a descrption of the cause of the error - * @param exitCode the exit code that should be passed to System.exit(int) - * @param cause the cause of this exception - */ - public FatalStartupError(String message, int exitCode, Throwable cause) { - super(message, cause); - this.exitCode = exitCode; - } - - /** - * Gets the number that should be passed to System.exit(int) when the virtual machine is halted. - * @return the exit code that should be passed to System.exit(int) - */ - public int getExitCode() { - return exitCode; - } -} diff --git a/server/src/java/org/xbean/server/main/KernelMain.java b/server/src/java/org/xbean/server/main/KernelMain.java deleted file mode 100644 index 7dc5621b..00000000 --- a/server/src/java/org/xbean/server/main/KernelMain.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.main; - -import java.util.Map; -import java.util.Collections; -import java.util.Iterator; - -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.StringServiceName; -import org.xbean.kernel.StaticServiceFactory; - -/** - * KernelMain is the standard entry point class used for a server. It will initalize a kernel with a set of services - * and can optional hold the thread of execution until the kernel or virtual machine is destroyed. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelMain implements Main { - private static final String DEFAULT_KERNEL_NAME = "xbean"; - - private Kernel kernel; - private ClassLoader classLoader; - private Map services = Collections.EMPTY_MAP; - private boolean daemon = true; - private Main next; - - /** - * Gets the kernel that will be initialized in the main method. If the kernel is null, a new kernel will be created - * and initialized in the main method. - * @return the kernel that will be initialized in the main method - */ - public Kernel getKernel() { - return kernel; - } - - /** - * Sets the kernel to be initialized in the main method. - * @param kernel the kernel to initialize in the main method - */ - public void setKernel(Kernel kernel) { - this.kernel = kernel; - } - - /** - * Gets the class loader which is used as the thread context class loader during the main method. - * @return the class loader which is used as the thread context class loader during the main method - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Sets the class loader to use as the thread context class loader during the main method. - * @param classLoader the class loader to use as the thread context class loader during the main method - */ - public void setClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - /** - * Gets the services to be registered with the kernel during the main method. - * @return the services to be mounted added to the kernel during the main method - */ - public Map getServices() { - return services; - } - - /** - * Sets the services to be registered with the kernel during the main method. - * @param services the services to be registered with the kernel during the main method - */ - public void setServices(Map services) { - this.services = services; - } - - /** - * Determines if the main method should hold the thread until the kernel is destroyed. - * @return true if the main method should hold the thread until the kernel is destroyed; false otherwise - */ - public boolean isDaemon() { - return daemon; - } - - /** - * Sets the main method to hold the thread until the kernel is destroyed. - * @param daemon true if the main method should hold the thread until the kernel is destroyed - */ - public void setDaemon(boolean daemon) { - this.daemon = daemon; - } - - /** - * Gets the next main to call after the kernel has been initialized, but before destroying the kernel. - * @return the next main to call after the kernel has been initialized - */ - public Main getNext() { - return next; - } - - /** - * Sets the next main to call after the kernel has been initialized. - * @param next the next main to call after the kernel has been initialized - */ - public void setNext(Main next) { - this.next = next; - } - - /** - * Registers the services with the kernel, calls the next main, optionally holds the thread until the kernel is - * destroyed, and then destroys the kernel. - * @param args the arguments passed the next main - */ - public void main(String[] args) { - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - try { - // create a default kernel if necessary - if (kernel == null) { - kernel = KernelFactory.newInstance().createKernel(DEFAULT_KERNEL_NAME); - } - - boolean failed = false; - try { - // bind the bootstrap services - for (Iterator iterator = services.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - String name = (String) entry.getKey(); - Object service = entry.getValue(); - - try { - ServiceName serviceName = new StringServiceName(name); - kernel.registerService(serviceName, new StaticServiceFactory(service), classLoader); - kernel.startService(serviceName); - } catch (Exception e) { - throw new FatalStartupError("Unable to bind bootstrap service '" + name + "' into the kernel", e); - } - } - - // if we have a child main class call it - if (next != null) { - next.main(args); - } - - // if we are a daemon we wait here until the server stops - if (daemon) { - // add our shutdown hook - Runtime.getRuntime().addShutdownHook(new DestroyKernelThread(kernel)); - - // wait for the kernel to be destroyed - kernel.waitForDestruction(); - } - } catch (RuntimeException e) { - failed = true; - throw e; - } catch (Error e) { - failed = true; - throw e; - } finally { - try { - kernel.destroy(); - } catch (Exception e) { - // if we are not alredy throwing an exception, throw a new exception - if (!failed) { - throw new FatalStartupError("Exception while shutting down kernel", e); - } - } - } - } finally { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - private static class DestroyKernelThread extends Thread { - private final Kernel kernel; - - private DestroyKernelThread(Kernel kernel) { - super("Destroy Kernel Shutdown Hook"); - this.kernel = kernel; - } - - public void run() { - kernel.destroy(); - } - } -} diff --git a/server/src/java/org/xbean/server/main/Main.java b/server/src/java/org/xbean/server/main/Main.java deleted file mode 100644 index a5f563a9..00000000 --- a/server/src/java/org/xbean/server/main/Main.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.main; - -/** - * Main is centeral entry point for the server. This should used instead of public static void Main(String[] args). - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface Main { - /** - * The main entry point method. - * @param args arguments to the main entry point - */ - void main(String[] args); -} diff --git a/server/src/java/org/xbean/server/propertyeditor/InetAddressEditor.java b/server/src/java/org/xbean/server/propertyeditor/InetAddressEditor.java deleted file mode 100644 index 9a649df2..00000000 --- a/server/src/java/org/xbean/server/propertyeditor/InetAddressEditor.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.propertyeditor; - -import java.beans.PropertyEditorSupport; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * InetAddressEditor is a java beans property editor that can convert an InetAddreass to and from a String. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class InetAddressEditor extends PropertyEditorSupport { - /** - * Converts the specified string value into an InetAddress and stores the value in this instance. - * @param value the string to convert into an InetAddress - * @throws IllegalArgumentException if the specified string value is not a valid InetAddress - */ - public void setAsText(String value) throws IllegalArgumentException { - try { - setValue(InetAddress.getByName(value)); - } catch (UnknownHostException e) { - throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); - } - } - - /** - * Converts the stored InetAddress value into a String. - * @return the string form of the current InetAddress value - * @throws NullPointerException if the current InetAddress is null - */ - public String getAsText() throws NullPointerException { - InetAddress inetAddress = (InetAddress) getValue(); - if (inetAddress == null) { - throw new NullPointerException("Current InetAddress value is null"); - } - String text = inetAddress.toString(); - return text; - } -} \ No newline at end of file diff --git a/server/src/java/org/xbean/server/propertyeditor/ObjectNameEditor.java b/server/src/java/org/xbean/server/propertyeditor/ObjectNameEditor.java deleted file mode 100644 index 47047464..00000000 --- a/server/src/java/org/xbean/server/propertyeditor/ObjectNameEditor.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.propertyeditor; - -import java.beans.PropertyEditorSupport; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -/** - * ObjectNameEditor is a java beans property editor that can convert an ObjectName to and from a String. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ObjectNameEditor extends PropertyEditorSupport { - /** - * Converts the specified string value into an ObjectName and stores the value in this instance. - * @param value the string to convert into an ObjectName - * @throws IllegalArgumentException if the specified string value is not a valid ObjectName - */ - public void setAsText(String value) throws IllegalArgumentException { - try { - setValue(new ObjectName(value)); - } catch (MalformedObjectNameException e) { - throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); - } - } - - /** - * Converts the stored ObjectName value into a String. - * @return the canonical string form of the current ObjectName value - * @throws NullPointerException if the current ObjectName is null - */ - public String getAsText() { - ObjectName objectName = (ObjectName) getValue(); - if (objectName == null) { - throw new NullPointerException("Current ObjectName value is null"); - } - String text = objectName.getCanonicalName(); - return text; - } -} \ No newline at end of file diff --git a/server/src/java/org/xbean/server/propertyeditor/URIEditor.java b/server/src/java/org/xbean/server/propertyeditor/URIEditor.java deleted file mode 100644 index 5cc11887..00000000 --- a/server/src/java/org/xbean/server/propertyeditor/URIEditor.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.propertyeditor; - -import java.beans.PropertyEditorSupport; -import java.net.URI; -import java.net.URISyntaxException; - -/** - * URIEditor is a java beans property editor that can convert an URI to and from a String. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class URIEditor extends PropertyEditorSupport { - /** - * Converts the specified string value into an URI and stores the value in this instance. - * @param value the string to convert into an URI - * @throws IllegalArgumentException if the specified string value is not a valid URI - */ - public void setAsText(String value) throws IllegalArgumentException { - try { - setValue(new URI(value)); - } catch (URISyntaxException e) { - throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); - } - } - - /** - * Converts the stored URI value into a String. - * @return the string form of the current URI value - * @throws NullPointerException if the current URI is null - */ - public String getAsText() { - URI uri = (URI) getValue(); - if (uri == null) { - throw new NullPointerException("Current URI value is null"); - } - String text = uri.toString(); - return text; - } -} \ No newline at end of file diff --git a/server/src/java/org/xbean/server/repository/FileSystemRepository.java b/server/src/java/org/xbean/server/repository/FileSystemRepository.java deleted file mode 100644 index 1db1efbf..00000000 --- a/server/src/java/org/xbean/server/repository/FileSystemRepository.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.repository; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; - -/** - * FileSystemRepository maps resource ids to a directory on the local file system. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class FileSystemRepository implements Repository { - private File root; - - /** - * Creates a new repository without a specified root directory. This repository is not usable until the root - * directory is specified. - */ - public FileSystemRepository() { - } - - /** - * Creates a new repository using the specified root directory. - * @param root the root directory from which resources are resolved - */ - public FileSystemRepository(File root) { - this.root = root; - } - - /** - * Gets the root directory from which resources are resolved. - * @return the root directory of this repository - */ - public File getRoot() { - return root; - } - - /** - * Sets the root directory of this repository. - * Note: the setting of the root directory is not synchronized and is expected to be called immediately after the - * default constructor in the same thread. - * @param root the new root directory from which resources are to be resolved - */ - public void setRoot(File root) { - this.root = root; - } - - /** - * Gets location of the resource realitive to the root directory. This method simply resolves the location against - * the root directory using root.toURI().resolve(location). - * @param location the location of the resource - * @return the absolute location of the resource or null if the root directory does not contain a readable file at - * the specified location - */ - public URL getResource(String location) { - File root = this.root; - if (root == null) { - throw new NullPointerException("root directory is null"); - } - - URI uri = root.toURI().resolve(location); - File file = new File(uri); - - if (!file.canRead()) { - return null; - } - - try { - return file.toURL(); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Malformed resource " + uri); - } - } -} diff --git a/server/src/java/org/xbean/server/repository/Repository.java b/server/src/java/org/xbean/server/repository/Repository.java deleted file mode 100644 index 37c73eae..00000000 --- a/server/src/java/org/xbean/server/repository/Repository.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.repository; - -import java.net.URL; - -/** - * Repository is used to locate resources by id. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface Repository { - /** - * Gets a URL to the specified resource. - * @param id the id of the resource - * @return the location of the resource or null if the repository does not contain the resource - */ - URL getResource(String id); -} diff --git a/server/src/java/org/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java b/server/src/java/org/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java deleted file mode 100644 index cad4bed8..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; - -import org.xbean.server.classloader.MultiParentClassLoader; -import org.xbean.server.repository.Repository; -import org.xbean.spring.context.SpringXmlPreprocessor; -import org.xbean.spring.context.SpringApplicationContext; -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -/** - * ClassLoaderXmlPreprocessor extracts a ClassLoader definition from the xml document, builds a class loader, assigns - * the class loader to the application context and xml reader, and removes the classpath element from document. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ClassLoaderXmlPreprocessor implements SpringXmlPreprocessor { - private final Repository repository; - - /** - * Creates a ClassLoaderXmlPreprocessor that uses the specified repository to resolve the class path locations. - * @param repository the repository used to resolve the class path locations - */ - public ClassLoaderXmlPreprocessor(Repository repository) { - this.repository = repository; - } - - /** - * Extracts a ClassLoader definition from the xml document, builds a class loader, assigns - * the class loader to the application context and xml reader, and removes the classpath element from document. - * - * @param applicationContext the application context on which the class loader will be set - * @param reader the xml reader on which the class loader will be set - * @param document the xml document to inspect - */ - public void preprocess(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) { - // determine the classLoader - ClassLoader classLoader; - NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath"); - if (classpathElements.getLength() < 1) { - classLoader = getClass().getClassLoader(); - } else if (classpathElements.getLength() > 1) { - throw new FatalBeanException("Expected only classpath element but found " + classpathElements.getLength()); - } else { - Element classpathElement = (Element) classpathElements.item(0); - - // build the classpath - List classpath = new ArrayList(); - NodeList locations = classpathElement.getElementsByTagName("location"); - for (int i = 0; i < locations.getLength(); i++) { - Element locationElement = (Element) locations.item(i); - - String location = ((Text) locationElement.getFirstChild()).getData().trim(); - classpath.add(location); - } - - // convert the paths to URLS - URL[] urls = new URL[classpath.size()]; - for (ListIterator iterator = classpath.listIterator(); iterator.hasNext();) { - String location = (String) iterator.next(); - urls[iterator.previousIndex()] = repository.getResource(location); - } - - // create the classloader - classLoader = new MultiParentClassLoader(applicationContext.getDisplayName(), urls, getClass().getClassLoader()); - - // remove the classpath element so Spring doesn't get confused - document.getDocumentElement().removeChild(classpathElement); - } - - // assign the class loader to the xml reader and the application context - reader.setBeanClassLoader(classLoader); - applicationContext.setClassLoader(classLoader); - } -} diff --git a/server/src/java/org/xbean/server/spring/configuration/LifecycleDetector.java b/server/src/java/org/xbean/server/spring/configuration/LifecycleDetector.java deleted file mode 100644 index 2e1292cb..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/LifecycleDetector.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.xbean.spring.util.SpringVisitor; -import org.xbean.spring.util.AbstractSpringVisitor; - -/** - * LifecycleDetector is a Spring configuration post processor that automatically sets the init and destroy methods - * on the bean definition based on the type of the bean class. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class LifecycleDetector implements BeanFactoryPostProcessor { - private final Map lifecycleMap = new LinkedHashMap(); - - /** - * Gets the lifecycle interfaces used by this detectors. - * @return the lifecycle interfaces used by this detectors - */ - public List getLifecycleInterfaces() { - List lifecycleInterfaces = new LinkedList(); - for (Iterator iterator = lifecycleMap.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - Class type = (Class) entry.getKey(); - LifecycleMethods lifecycleMethods = (LifecycleMethods) entry.getValue(); - lifecycleInterfaces.add(new LifecycleInfo(type, lifecycleMethods.initMethodName, lifecycleMethods.destroyMethodName)); - } - return lifecycleInterfaces; - } - - /** - * Sets the lifecycle interfaces used by this detectors. - * @param lifecycleInterfaces the lifecycle interfaces used by this detectors - */ - public void setLifecycleInterfaces(List lifecycleInterfaces) { - lifecycleMap.clear(); - for (Iterator iterator = lifecycleInterfaces.iterator(); iterator.hasNext();) { - addLifecycleInterface((LifecycleInfo) iterator.next()); - } - } - - /** - * Adds a lifecycle interface to this detector. - * @param type the lifecycle interface - * @param initMethodName the name of the init method - * @param destroyMethodName the name of the destroy method - */ - public void addLifecycleInterface(Class type, String initMethodName, String destroyMethodName) { - lifecycleMap.put(type, new LifecycleMethods(initMethodName, destroyMethodName)); - } - - /** - * Adds a lifecycle interface to this detector. - * @param lifecycleInfo the lifecycle interfface info - */ - public void addLifecycleInterface(LifecycleInfo lifecycleInfo) { - lifecycleMap.put(lifecycleInfo.getType(), - new LifecycleMethods(lifecycleInfo.getInitMethodName(), lifecycleInfo.getDestroyMethodName())); - } - - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - SpringVisitor visitor = new AbstractSpringVisitor() { - public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException { - super.visitBeanDefinition(beanDefinition, data); - - if (!(beanDefinition instanceof RootBeanDefinition)) { - return; - } - RootBeanDefinition rootBeanDefinition = ((RootBeanDefinition) beanDefinition); - Class beanType = rootBeanDefinition.getBeanClass(); - for (Iterator iterator = lifecycleMap.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - Class lifecycleInterface = (Class) entry.getKey(); - LifecycleMethods value = (LifecycleMethods) entry.getValue(); - if (lifecycleInterface.isAssignableFrom(beanType)) { - if (rootBeanDefinition.getInitMethodName() == null) { - rootBeanDefinition.setInitMethodName(value.initMethodName); - } - if (rootBeanDefinition.getDestroyMethodName() == null) { - rootBeanDefinition.setDestroyMethodName(value.destroyMethodName); - } - if (rootBeanDefinition.getInitMethodName() != null && rootBeanDefinition.getDestroyMethodName() != null) { - return; - } - } - } - } - }; - visitor.visitBeanFactory(beanFactory, null); - } - - private static class LifecycleMethods { - private String initMethodName; - private String destroyMethodName; - - public LifecycleMethods(String initMethodName, String destroyMethodName) { - this.initMethodName = initMethodName; - this.destroyMethodName = destroyMethodName; - } - } -} diff --git a/server/src/java/org/xbean/server/spring/configuration/LifecycleInfo.java b/server/src/java/org/xbean/server/spring/configuration/LifecycleInfo.java deleted file mode 100644 index bfb33549..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/LifecycleInfo.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -/** - * LifecycleInfo defines the init and destroy method names for a lifecycle interface. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class LifecycleInfo { - private Class type; - private String initMethodName; - private String destroyMethodName; - - /** - * Creates a new empty lifecycle info. Note, this instance is unusable until the type, inti method name and - * destroy method name are set. - */ - public LifecycleInfo() { - } - - /** - * Creates a new lifecycle info object for the specified interface and defining the init and destroy method names. - * @param type the lifecycle interface - * @param initMethodName the init method name - * @param destroyMethodName the destroy method name - */ - public LifecycleInfo(Class type, String initMethodName, String destroyMethodName) { - this.type = type; - this.initMethodName = initMethodName; - this.destroyMethodName = destroyMethodName; - } - - /** - * Gets the lifecycle interface type. - * @return the lifecycle interface type - */ - public Class getType() { - return type; - } - - /** - * Sets the lifecycle interface type. - * @param type the lifecycle interface type - */ - public void setType(Class type) { - this.type = type; - } - - /** - * Gets the init method name. - * @return the init method name - */ - public String getInitMethodName() { - return initMethodName; - } - - /** - * Sets the init method name. - * @param initMethodName the init method name - */ - public void setInitMethodName(String initMethodName) { - this.initMethodName = initMethodName; - } - - /** - * Gets the destroy method name. - * @return the destroy method name - */ - public String getDestroyMethodName() { - return destroyMethodName; - } - - /** - * Sets the destroy method name. - * @param destroyMethodName the destroy method name - */ - public void setDestroyMethodName(String destroyMethodName) { - this.destroyMethodName = destroyMethodName; - } -} diff --git a/server/src/java/org/xbean/server/spring/configuration/NamedConstructorArgs.java b/server/src/java/org/xbean/server/spring/configuration/NamedConstructorArgs.java deleted file mode 100644 index 5db1b3f6..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/NamedConstructorArgs.java +++ /dev/null @@ -1,339 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -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.LinkedList; -import java.util.Arrays; -import java.lang.reflect.Constructor; - -import org.springframework.beans.BeansException; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.FactoryBean; -import org.xbean.server.annotation.AnnotationProvider; -import org.xbean.server.annotation.DefaultConstructor; -import org.xbean.server.annotation.ParameterNames; -import org.xbean.spring.util.SpringVisitor; -import org.xbean.spring.util.AbstractSpringVisitor; - -/** - * NamedConstructorArgs is a BeanFactoryPostProcessor that converts property declarations into indexed constructor args - * based on the the constructor parameter names annotation. This process first selctes a constructor and then fills in - * the constructor arguments from the properties defined in the bean definition. If a property is not defined in the - * bean definition, first the defaultValues map is checked for a value and if a value is not present a Java default - * value is provided for the constructor argument (e.g. numbers are assigned 0 and objects are assigned null). - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class NamedConstructorArgs implements BeanFactoryPostProcessor { - private AnnotationProvider annotationProvider; - private Map defaultValues = new HashMap(); - - /** - * Creates an empty NamedConstructorArgs. Note this class is not usable until an annotation provider is assigned. - */ - public NamedConstructorArgs() { - } - - /** - * Creates a NamedConstructorArgs that uses the specified annotation provider to select the default constructor - * and to determine the constructor argument names. - * @param annotationProvider the annotation provieder used to determine parameter names - */ - public NamedConstructorArgs(AnnotationProvider annotationProvider) { - this.annotationProvider = annotationProvider; - } - - /** - * Gets the annotation provider which is used to select the default constructor and to determine the constructor - * argument names. - * @return the annotation provider - */ - public AnnotationProvider getAnnotationProvider() { - return annotationProvider; - } - - /** - * Sets the annotation provider which is used to select the default constructor and to determine the constructor - * argument names. Note it is expected that this method is called immedately after a constructor using the same - * thread, therefore the setting of the annotation provider is not protected with a synchronized block. - * @param annotationProvider the new annotation provider - */ - public void setAnnotationProvider(AnnotationProvider annotationProvider) { - this.annotationProvider = annotationProvider; - } - - /** - * Gets the default values that are assigned to constructor arguments without a defined value. - * @return the default values that are assigned to constructor arguments without a defined value - */ - public List getDefaultValues() { - List values = new LinkedList(); - for (Iterator iterator = defaultValues.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - PropertyKey key = (PropertyKey) entry.getKey(); - Object value = entry.getValue(); - values.add(new DefaultProperty(key.name, key.type, value)); - } - return values; - } - - /** - * Sets the default values that are assigned to constructor arguments without a defined value. - * @param defaultValues the values that are assigned to constructor arguments without a defined value - */ - public void setDefaultValues(List defaultValues) { - this.defaultValues.clear(); - for (Iterator iterator = defaultValues.iterator(); iterator.hasNext();) { - addDefaultValue((DefaultProperty) iterator.next()); - } - } - - /** - * Adds a default value for a property with the specified name and type. - * @param name the name of the property - * @param type the type of the property - * @param value the default value for a property with the specified name and type - */ - public void addDefaultValue(String name, Class type, Object value) { - defaultValues.put(new PropertyKey(name, type), value); - } - - /** - * Adds a defautl value for a property. - * @param defaultProperty the default property information - */ - private void addDefaultValue(DefaultProperty defaultProperty) { - defaultValues.put(new PropertyKey(defaultProperty.getName(), defaultProperty.getType()), defaultProperty.getValue()); - } - - /** - * Finds all bean definitions and where possble assigns the indexed constructor argument values from the defined - * and default property values. - * @param beanFactory the bean factory to inspect - * @throws BeansException if the parameter name annotation for a constructor does not contain the same number of - * parameter names as the constructor - */ - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - SpringVisitor visitor = new AbstractSpringVisitor() { - public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException { - super.visitBeanDefinition(beanDefinition, data); - - if (!(beanDefinition instanceof RootBeanDefinition)) { - return; - } - - RootBeanDefinition rootBeanDefinition = ((RootBeanDefinition) beanDefinition); - processParameters(rootBeanDefinition); - - } - }; - visitor.visitBeanFactory(beanFactory, null); - } - - private void processParameters(RootBeanDefinition rootBeanDefinition) throws BeansException { - ConstructorArgumentValues constructorArgumentValues = rootBeanDefinition.getConstructorArgumentValues(); - - // if this bean already has constructor arguments defined, don't mess with them - if (constructorArgumentValues.getArgumentCount() > 0) { - return; - } - - // try to get a list of constructor arg names to use - ConstructorInfo constructorInfo = selectConstructor(rootBeanDefinition); - if (constructorInfo == null) { - return; - } - - // remove each named property and add an indexed constructor arg - MutablePropertyValues propertyValues = rootBeanDefinition.getPropertyValues(); - String[] parameterNames = constructorInfo.parameterNames; - Class[] parameterTypes = constructorInfo.constructor.getParameterTypes(); - for (int i = 0; i < parameterNames.length; i++) { - String parameterName = parameterNames[i]; - Class parameterType = parameterTypes[i]; - - PropertyValue propertyValue = propertyValues.getPropertyValue(parameterName); - if (propertyValue != null) { - propertyValues.removePropertyValue(parameterName); - constructorArgumentValues.addIndexedArgumentValue(i, propertyValue.getValue(), parameterType.getName()); - } else { - Object defaultValue = defaultValues.get(new PropertyKey(parameterName, parameterType)); - if (defaultValue == null) { - defaultValue = DEFAULT_VALUE.get(parameterType); - } - if (defaultValue instanceof FactoryBean) { - try { - defaultValue = ((FactoryBean)defaultValue).getObject(); - } catch (Exception e) { - throw new FatalBeanException("Unable to get object value from bean factory", e); - } - } - constructorArgumentValues.addIndexedArgumentValue(i, defaultValue, parameterType.getName()); - } - } - - // todo set any usable default values on the bean definition - } - - private ConstructorInfo selectConstructor(RootBeanDefinition rootBeanDefinition) { - Class beanType = rootBeanDefinition.getBeanClass(); - - // get a set containing the names of the defined properties - Set definedProperties = new HashSet(); - PropertyValue[] values = rootBeanDefinition.getPropertyValues().getPropertyValues(); - for (int i = 0; i < values.length; i++) { - definedProperties.add(values[i].getName()); - } - - // get the constructors sorted by longest arg length first - List constructors = new ArrayList(Arrays.asList(beanType.getConstructors())); - Collections.sort(constructors, new ArgLengthComparator()); - - // if a constructor has been annotated as the default constructor we always use that constructor - for (Iterator iterator = constructors.iterator(); iterator.hasNext();) { - Constructor constructor = (Constructor) iterator.next(); - if (annotationProvider.isAnnotationPresent(DefaultConstructor.class, constructor)) { - return new ConstructorInfo(constructor); - } - } - - // try to find a constructor for which we have all of the properties defined - for (Iterator iterator = constructors.iterator(); iterator.hasNext();) { - Constructor constructor = (Constructor) iterator.next(); - ConstructorInfo constructorInfo = new ConstructorInfo(constructor); - if (isUsableConstructor(constructorInfo, definedProperties)) { - return constructorInfo; - } - } - return null; - } - - private boolean isUsableConstructor(ConstructorInfo constructorInfo, Set definedProperties) { - // if we don't have parameter names this is not the constructor we are looking for - String[] parameterNames = constructorInfo.parameterNames; - if (parameterNames == null) { - return false; - } - - Class[] parameterTypes = constructorInfo.constructor.getParameterTypes(); - for (int i = 0; i < parameterNames.length; i++) { - String parameterName = parameterNames[i]; - Class parameterType = parameterTypes[i]; - - // can we satify this property using a defined property or default property - if (!definedProperties.contains(parameterName) && !defaultValues.containsKey(new PropertyKey(parameterName, parameterType))) { - return false; - } - } - - return true; - } - - private class ConstructorInfo { - private final Constructor constructor; - private final String[] parameterNames; - - public ConstructorInfo(Constructor constructor) { - this.constructor = constructor; - ParameterNames parameterNames = (ParameterNames) annotationProvider.getAnnotation(ParameterNames.class, constructor); - - // verify that we have enough parameter names - String[] names = parameterNames.names(); - int expectedParameterCount = constructor.getParameterTypes().length; - if (names != null && names.length != expectedParameterCount) { - throw new FatalBeanException("Excpected " + expectedParameterCount + " parameter names for constructor but only got " + - names.length + ": " + constructor.toString()); - } - if (expectedParameterCount == 0) { - names = new String[0]; - } - - this.parameterNames = names; - } - } - - private static class ArgLengthComparator implements Comparator { - public int compare(Object o1, Object o2) { - Constructor constructor1 = (Constructor) o1; - Constructor constructor2 = (Constructor) o2; - return constructor2.getParameterTypes().length - constructor1.getParameterTypes().length; - } - } - - private static class PropertyKey { - private final String name; - private final Class type; - - public PropertyKey(String name, Class type) { - this.name = name; - this.type = type; - } - - public boolean equals(Object object) { - if (!(object instanceof PropertyKey)) { - return false; - } - - PropertyKey defaultProperty = (PropertyKey) object; - return name.equals(defaultProperty.name) && type.equals(type); - } - - public int hashCode() { - int result = 17; - result = 37 * result + name.hashCode(); - result = 37 * result + type.hashCode(); - return result; - } - - public String toString() { - return "[" + name + " " + type + "]"; - } - } - - private static final Map DEFAULT_VALUE; - static { - Map temp = new HashMap(); - temp.put(Boolean.TYPE, Boolean.FALSE); - temp.put(Byte.TYPE, new Byte((byte) 0)); - temp.put(Character.TYPE, new Character((char) 0)); - temp.put(Short.TYPE, new Short((short) 0)); - temp.put(Integer.TYPE, new Integer(0)); - temp.put(Long.TYPE, new Long(0)); - temp.put(Float.TYPE, new Float(0)); - temp.put(Double.TYPE, new Double(0)); - - DEFAULT_VALUE = Collections.unmodifiableMap(temp); - } -} diff --git a/server/src/java/org/xbean/server/spring/configuration/SpringConfiguration.java b/server/src/java/org/xbean/server/spring/configuration/SpringConfiguration.java deleted file mode 100644 index bd767583..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/SpringConfiguration.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceAlreadyExistsException; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.ServiceNotFoundException; -import org.xbean.kernel.ServiceRegistrationException; -import org.xbean.kernel.StaticServiceFactory; -import org.xbean.kernel.StringServiceName; -import org.springframework.beans.BeansException; -import org.xbean.spring.context.SpringApplicationContext; -import org.xbean.server.spring.loader.SpringLoader; - -/** - * SpringConfiguration that registers and unregisters services that have been defined in a SpringApplicationContext. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringConfiguration { - private final SpringApplicationContext applicationContext; - private final Map serviceFactories; - private final Kernel kernel; - - /** - * Creates a SpringConfiguration that registers and unregisters services that have been defined in a Spring ApplicationContext. - * @param applicationContext the application context from which services are registered - * @param kernel the kernel in which services are registered and unregistered - * @throws Exception if a problem occurs while registering the services fromt he application context - */ - public SpringConfiguration(SpringApplicationContext applicationContext, Kernel kernel) throws Exception { - this.applicationContext = applicationContext; - this.kernel = kernel; - - ClassLoader classLoader = getClassLoader(applicationContext); - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - - // read the configuration file from source - applicationContext.refresh(); - - try { - - // build a map from bean name to service name - Map serviceNameIndex = buildServiceNameIndex(applicationContext); - - // Use Spring to create all of the beans - Map factories = new HashMap(serviceNameIndex.size()); - for (Iterator iterator = serviceNameIndex.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - String beanName = (String) entry.getKey(); - ServiceName serviceName = (ServiceName) entry.getValue(); - - Object bean = applicationContext.getBean(beanName); - StaticServiceFactory serviceFactory = new StaticServiceFactory(bean); - factories.put(serviceName, serviceFactory); - } - serviceFactories = Collections.unmodifiableMap(factories); - - // register each bean with the kernel - for (Iterator iterator = serviceFactories.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - ServiceName serviceName = (ServiceName) entry.getKey(); - StaticServiceFactory serviceFactory = (StaticServiceFactory) entry.getValue(); - kernel.registerService(serviceName, serviceFactory, classLoader); - } - - } catch (ServiceAlreadyExistsException e) { - destroy(); - throw e; - } catch (ServiceRegistrationException e) { - destroy(); - throw e; - } finally { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - /** - * Gets the unique identifier of this configuration. - * @return the unique identifier of this configuration - */ - public String getId() { - return applicationContext.getDisplayName(); - } - - /** - * Gets the service factories for the services defined in this configuration by ServiceName. - * @return the service factories for the services defined in this configuration by ServiceName - */ - public Map getServiceFactories() { - return serviceFactories; - } - - /** - * Unregisters all of the services registered with the kernel in the constructor. - */ - public void destroy() { - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(getClassLoader(applicationContext)); - try { - for (Iterator iterator = serviceFactories.keySet().iterator(); iterator.hasNext();) { - ServiceName serviceName = (ServiceName) iterator.next(); - try { - kernel.unregisterService(serviceName); - } catch (ServiceNotFoundException e) { - // Doesn't matter - } catch (ServiceRegistrationException e) { - // todo ignored... - } - } - - applicationContext.close(); - } finally { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - private static Map buildServiceNameIndex(SpringApplicationContext applicationContext) { - String[] beanNames = applicationContext.getBeanDefinitionNames(); - Map serviceNameIndex = new HashMap(beanNames.length); - for (int i = 0; i < beanNames.length; i++) { - String beanName = beanNames[i]; - ServiceName serviceName = new StringServiceName(beanName); - serviceNameIndex.put(beanName, serviceName); - } - return serviceNameIndex; - } - - private static ClassLoader getClassLoader(SpringApplicationContext applicationContext) { - ClassLoader classLoader = applicationContext.getClassLoader(); - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - if (classLoader == null) { - classLoader = SpringLoader.class.getClassLoader(); - } - return classLoader; - } -} diff --git a/server/src/java/org/xbean/server/spring/configuration/SpringConfigurationServiceFactory.java b/server/src/java/org/xbean/server/spring/configuration/SpringConfigurationServiceFactory.java deleted file mode 100644 index dfc318b8..00000000 --- a/server/src/java/org/xbean/server/spring/configuration/SpringConfigurationServiceFactory.java +++ /dev/null @@ -1,183 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.configuration; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.HashSet; -import java.util.Collections; - -import org.xbean.kernel.AbstractServiceFactory; -import org.xbean.kernel.ServiceCondition; -import org.xbean.kernel.ServiceConditionContext; -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.spring.context.SpringApplicationContext; - -/** - * SpringConfigurationServiceFactory is manages the creation and destruction of a SpringConfiguration. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringConfigurationServiceFactory extends AbstractServiceFactory { - private final SpringApplicationContext applicationContext; - private final ConfigurationStopCondition configurationStopCondition; - private SpringConfiguration springConfiguration; - - /** - * Creates a SpringConfigurationServiceFactory that wraps the specified application context. - * @param applicationContext the application context for this configuration - */ - public SpringConfigurationServiceFactory(SpringApplicationContext applicationContext) { - this.applicationContext = applicationContext; - configurationStopCondition = new ConfigurationStopCondition(); - addStopCondition(configurationStopCondition); - } - - /** - * Gets the unique id if this configuration. - * @return the unique id if this configuration - */ - public String getId() { - return applicationContext.getDisplayName(); - } - - /** - * Gets the application context wrapped by this configuration. Use caution when modifiying this context as it can - * effect the running state of services. - * @return the application context wrapped by this configuration - */ - public SpringApplicationContext getApplicationContext() { - return applicationContext; - } - - /** - * {@inheritDoc} - */ - public Class[] getTypes() { - return new Class[]{SpringConfiguration.class}; - } - - /** - * SpringConfigurationServiceFactory is restartable so this method always returns true. - * @return true - */ - public boolean isRestartable() { - return true; - } - - /** - * Gets the ServiceNames of the services defined in the application context if the configuration has been started, - * otherwise this method returns an empty set. - * - * @return the ServiceNames of the services defined in the application context if the configuration has been started - */ - public Set getOwnedServices() { - if (springConfiguration != null) { - return new HashSet(springConfiguration.getServiceFactories().keySet()); - } - return Collections.EMPTY_SET; - } - - /** - * {@inheritDoc} - */ - public Object createService(ServiceContext serviceContext) throws Exception { - springConfiguration = new SpringConfiguration(applicationContext, serviceContext.getKernel()); - - // add owned service stop conditions - Set ownedServices = springConfiguration.getServiceFactories().keySet(); - for (Iterator iterator = springConfiguration.getServiceFactories().entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - ServiceName serviceName = (ServiceName) entry.getKey(); - ServiceFactory serviceFactory = (ServiceFactory) entry.getValue(); - if (ownedServices.contains(serviceName)) { - serviceFactory.addStopCondition(configurationStopCondition.createOwnedServiceStopCondition()); - } - } - - return springConfiguration; - } - - /** - * {@inheritDoc} - */ - public void destroyService(ServiceContext serviceContext) { - if (springConfiguration != null) { - springConfiguration.destroy(); - springConfiguration = null; - } - } - - private static class ConfigurationStopCondition implements ServiceCondition { - private final List ownedServiceConditions = new ArrayList(); - - public synchronized void initialize(ServiceConditionContext context) { - for (Iterator iterator = ownedServiceConditions.iterator(); iterator.hasNext();) { - OwnedServiceCondition ownedServiceCondition = (OwnedServiceCondition) iterator.next(); - ownedServiceCondition.setSatisfied(); - } - } - - public boolean isSatisfied() { - return true; - } - - public synchronized void destroy() { - for (Iterator iterator = ownedServiceConditions.iterator(); iterator.hasNext();) { - OwnedServiceCondition ownedServiceCondition = (OwnedServiceCondition) iterator.next(); - ownedServiceCondition.setSatisfied(); - } - } - - public ServiceCondition createOwnedServiceStopCondition() { - ServiceCondition ownedServiceCondition = new OwnedServiceCondition(); - ownedServiceConditions.add(ownedServiceCondition); - return ownedServiceCondition; - } - - private static class OwnedServiceCondition implements ServiceCondition { - private boolean satisfied = false; - private ServiceConditionContext context; - - public synchronized void initialize(ServiceConditionContext context) { - this.context = context; - } - - public synchronized boolean isSatisfied() { - return satisfied; - } - - private void setSatisfied() { - this.satisfied = true; - if (context != null) { - context.setSatisfied(); - } - } - - public synchronized void destroy() { - context = null; - } - } - } -} diff --git a/server/src/java/org/xbean/server/spring/loader/SpringLoader.java b/server/src/java/org/xbean/server/spring/loader/SpringLoader.java deleted file mode 100644 index c4c6153c..00000000 --- a/server/src/java/org/xbean/server/spring/loader/SpringLoader.java +++ /dev/null @@ -1,147 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.loader; - -import java.io.File; -import java.util.List; -import java.util.Collections; - -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.StringServiceName; -import org.xbean.server.loader.Loader; -import org.xbean.server.spring.configuration.SpringConfigurationServiceFactory; -import org.xbean.spring.context.FileSystemXmlApplicationContext; -import org.xbean.spring.context.SpringApplicationContext; - - -/** - * SpringLoader loads Spring xml configurations into a Kernel. This service uses the XBean version of - * FileSystemXmlApplicationContext so custom XML extensions are available. This loader also support the specification - * of SpringXmlPreprocessors and BeanFactoryPostProcessors to apply to the configuration. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringLoader implements Loader { - private Kernel kernel; - private File baseDir = new File(".").getAbsoluteFile(); - private List beanFactoryPostProcessors = Collections.EMPTY_LIST; - private List xmlPreprocessors = Collections.EMPTY_LIST; - - /** - * Creates an empty SpringLoader. Note this loader is not usable until a kernel is specified. - */ - public SpringLoader() { - } - - /** - * {@inheritDoc} - */ - public Kernel getKernel() { - return kernel; - } - - /** - * Sets the kernel in which configurations are loaded. - * @param kernel the kernel in which configurations are loaded - */ - public void setKernel(Kernel kernel) { - this.kernel = kernel; - } - - /** - * Gets the BeanFactoryPostProcessors to apply to the configuration. - * @return the BeanFactoryPostProcessors to apply to the configuration - */ - public List getBeanFactoryPostProcessors() { - return beanFactoryPostProcessors; - } - - /** - * Sets the BeanFactoryPostProcessors to apply to the configuration. - * @param beanFactoryPostProcessors the BeanFactoryPostProcessors to apply to the configuration - */ - public void setBeanFactoryPostProcessors(List beanFactoryPostProcessors) { - this.beanFactoryPostProcessors = beanFactoryPostProcessors; - } - - /** - * Gets the base directory from which configuration locations are resolved. - * @return the base directory from which configuration locations are resolved - */ - public File getBaseDir() { - return baseDir; - } - - /** - * Sets the base directory from which configuration locations are resolved. - * @param baseDir the base directory from which configuration locations are resolved - */ - public void setBaseDir(File baseDir) { - this.baseDir = baseDir; - } - - /** - * Gets the SpringXmlPreprocessors applied to the configuration. - * @return the SpringXmlPreprocessors applied to the configuration - */ - public List getXmlPreprocessors() { - return xmlPreprocessors; - } - - /** - * Sets the SpringXmlPreprocessors applied to the configuration. - * @param xmlPreprocessors the SpringXmlPreprocessors applied to the configuration - */ - public void setXmlPreprocessors(List xmlPreprocessors) { - this.xmlPreprocessors = xmlPreprocessors; - } - - /** - * Loads the specified configuration into the kernel. The location specifies a file relative to the baseDir using - * baseDir.toURI().resolve(location).getPath() + ".xml". This service uses the XBean version of - * FileSystemXmlApplicationContext so custom XML extensions are available. - * @param location the location of the configuration file relative to the base directory without the .xml extension - * @return the name of the SpringConfiguration service for this location - * @throws Exception if a problem occurs while loading the Spring configuration file - */ - public ServiceName load(String location) throws Exception { - String resolvedLocation = baseDir.toURI().resolve(location).getPath(); - String configLocation = "/" + resolvedLocation + ".xml"; - SpringApplicationContext applicationContext = new FileSystemXmlApplicationContext( - new String[] {configLocation}, - false, - xmlPreprocessors); - applicationContext.setDisplayName(location); - - ClassLoader classLoader = applicationContext.getClassLoader(); - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - if (classLoader == null) { - classLoader = SpringLoader.class.getClassLoader(); - } - - ServiceName serviceName = new StringServiceName("configuration:" + location); - ServiceFactory springConfigurationServiceFactory = new SpringConfigurationServiceFactory(applicationContext); - kernel.registerService(serviceName, springConfigurationServiceFactory, classLoader); - return serviceName; - } -} diff --git a/server/src/java/org/xbean/server/spring/main/SpringBootstrap.java b/server/src/java/org/xbean/server/spring/main/SpringBootstrap.java deleted file mode 100644 index 514af375..00000000 --- a/server/src/java/org/xbean/server/spring/main/SpringBootstrap.java +++ /dev/null @@ -1,275 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.main; - -import java.beans.PropertyEditorManager; -import java.io.File; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -import org.xbean.server.main.FatalStartupError; -import org.xbean.server.main.Main; -import org.xbean.spring.context.ClassPathXmlApplicationContext; -import org.xbean.spring.context.FileSystemXmlApplicationContext; -import org.xbean.spring.context.SpringApplicationContext; - -/** - * SpringBootstrap is the main class used by a Spring based server. This class uses the following strategies to determine - * the configuration file to load: - * - * Command line parameter --bootstrap FILE - * Manifest entry XBean-Bootstrap in the startup jar - * META-INF/xbean-bootstrap.xml - * - * This class atempts to first load the configuration file from the local file system and if that fails it attempts to - * load it from the classpath. - * - * SpringBootstrap expects the configuration to contain a service with the id "main" which is an implementation of - * org.xbean.server.main.Main. - * - * This class will set the system property xbean.base.dir to the directory containing the startup jar if the property - * has not alredy been set (on the command line). - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringBootstrap { - private static final String XBEAN_BOOTSTRAP_MANIFEST = "XBean-Bootstrap"; - private static final String BOOTSTRAP_FLAG = "--bootstrap"; - private static final String DEFAULT_BOOTSTRAP = "META-INF/xbean-bootstrap.xml"; - private static final List DEFAULT_PROPERTY_EDITOR_PATHS = Collections.singletonList("org.xbean.server.propertyeditor"); - - private String configurationFile; - private String[] mainArguments; - private List propertyEditorPaths = DEFAULT_PROPERTY_EDITOR_PATHS; - private String serverBaseDirectory; - - /** - * Initializes and boots the server using the supplied arguments. If an error is thrown from the boot method, - * this method will pring the error to standard error along with the stack trace and exit with the exit specified - * in the FatalStartupError or exit code 9 if the error was not a FatalStartupError. - * @param args the arguments used to start the server - */ - public static void main(String[] args) { - SpringBootstrap springBootstrap = new SpringBootstrap(); - springBootstrap.initialize(args); - - try { - springBootstrap.boot(); - } catch (FatalStartupError e) { - System.err.println(e.getMessage()); - if (e.getCause() != null) { - e.getCause().printStackTrace(); - } - System.exit(e.getExitCode()); - } catch (Throwable e) { - System.err.println("Unknown error"); - e.printStackTrace(); - System.exit(9); - } - } - - /** - * Gets the configuration file from which the main instance is loaded. - * @return the configuration file from which the main instance is loaded - */ - public String getConfigurationFile() { - return configurationFile; - } - - /** - * Sets the configuration file from which the main instance is loaded. - * @param configurationFile the configuration file from which the main instance is loaded - */ - public void setConfigurationFile(String configurationFile) { - this.configurationFile = configurationFile; - } - - /** - * Gets the arguments passed to the main instance. - * @return the arguments passed to the main instance - */ - public String[] getMainArguments() { - return mainArguments; - } - - /** - * Sets the arguments passed to the main instance. - * @param mainArguments the arguments passed to the main instance - */ - public void setMainArguments(String[] mainArguments) { - this.mainArguments = mainArguments; - } - - /** - * Gets the paths that are appended to the system property editors search path. - * @return the paths that are appended to the system property editors search path - */ - public List getPropertyEditorPaths() { - return propertyEditorPaths; - } - - /** - * Sets the paths that are appended to the system property editors search path. - * @param propertyEditorPaths the paths that are appended to the system property editors search path - */ - public void setPropertyEditorPaths(List propertyEditorPaths) { - this.propertyEditorPaths = propertyEditorPaths; - } - - /** - * Gets the base directory of the server. - * @return the base directory of the server - */ - public String getServerBaseDirectory() { - return serverBaseDirectory; - } - - /** - * Sets the base directory of the server. - * @param serverBaseDirectory the base directory of the server - */ - public void setServerBaseDirectory(String serverBaseDirectory) { - this.serverBaseDirectory = serverBaseDirectory; - } - - /** - * Determines the configuration file and server base directory. - * @param args the arguments passed to main - */ - public void initialize(String[] args) { - // check if bootstrap configuration was specified on the command line - if (args.length > 1 && BOOTSTRAP_FLAG.equals(args[0])) { - configurationFile = args[1]; - this.mainArguments = new String[args.length - 2]; - System.arraycopy(args, 2, this.mainArguments, 0, args.length); - } else { - configurationFile = DEFAULT_BOOTSTRAP; - this.mainArguments = args; - } - - // Determine the xbean installation directory - // guess from the location of the jar - URL url = SpringBootstrap.class.getClassLoader().getResource("META-INF/startup-jar"); - if (url != null) { - try { - JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); - url = jarConnection.getJarFileURL(); - - URI baseURI = new URI(url.toString()).resolve(".."); - serverBaseDirectory = new File(baseURI).getAbsolutePath(); - - Manifest manifest; - manifest = jarConnection.getManifest(); - Attributes mainAttributes = manifest.getMainAttributes(); - if (configurationFile == null) { - configurationFile = mainAttributes.getValue(XBEAN_BOOTSTRAP_MANIFEST); - } - } catch (Exception e) { - System.err.println("Could not determine xbean installation directory"); - e.printStackTrace(); - System.exit(9); - return; - } - } else { - String dir = System.getProperty("xbean.base.dir", System.getProperty("user.dir")); - serverBaseDirectory = new File(dir).getAbsolutePath(); - } - } - - /** - * Loads the main instance from the configuration file. - * @return the main instance - */ - public Main loadMain() { - if (serverBaseDirectory == null) { - throw new NullPointerException("serverBaseDirectory is null"); - - } - File baseDirectory = new File(serverBaseDirectory); - if (!baseDirectory.isDirectory()) { - throw new IllegalArgumentException("serverBaseDirectory is not a directory: " + serverBaseDirectory); - - } - if (configurationFile == null) { - throw new NullPointerException("configurationFile is null"); - - } - - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(SpringBootstrap.class.getClassLoader()); - try { - // add our property editors into the system - if (propertyEditorPaths != null && !propertyEditorPaths.isEmpty()) { - List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath())); - editorSearchPath.addAll(propertyEditorPaths); - PropertyEditorManager.setEditorSearchPath((String[]) editorSearchPath.toArray(new String[editorSearchPath.size()])); - } - - // set the server base directory system property - System.setProperty("xbean.base.dir", baseDirectory.getAbsolutePath()); - - // load the configuration file - SpringApplicationContext factory; - File file = new File(baseDirectory.toURI().resolve(configurationFile)); - if (file.canRead()) { - try { - // configuration file is on the local file system - factory = new FileSystemXmlApplicationContext(file.toURL().toString()); - } catch (MalformedURLException e) { - throw new FatalStartupError("Error creating url for bootstrap file", e); - } - } else { - // assume it is a classpath resource - factory = new ClassPathXmlApplicationContext(configurationFile); - } - - // get the main service from the configuration file - String[] names = factory.getBeanNamesForType(Main.class); - Main main = null; - if (names.length == 0) { - throw new FatalStartupError("No bean of type: " + Main.class.getName() + " found in the bootstrap.xml", 10); - } - main = (Main) factory.getBean(names[0]); - return main; - } - finally { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - /** - * Loads the main instance from the Spring configuration file and executes it. - */ - public void boot() { - // load the main instance - Main main = loadMain(); - - // start it up - main.main(mainArguments); - - } -} diff --git a/server/src/java/org/xbean/server/spring/reference/ClassLoaderReference.java b/server/src/java/org/xbean/server/spring/reference/ClassLoaderReference.java deleted file mode 100644 index c486865f..00000000 --- a/server/src/java/org/xbean/server/spring/reference/ClassLoaderReference.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.reference; - -import java.io.Serializable; - -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceContextThreadLocal; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.RootBeanDefinition; - -/** - * ClassLoaderReference is a the class loader in the ServiceContextThreadLocal. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ClassLoaderReference implements FactoryBean, Serializable { - /** - * Creates a bean definition for ServiceContextThreadLocal. - * @return a bean definition for ServiceContextThreadLocal - */ - public static BeanDefinitionHolder createBeanDefinition() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(ClassLoaderReference.class, 0); - return new BeanDefinitionHolder(beanDefinition, ClassLoaderReference.class.getName()); - } - - public final Class getObjectType() { - return ClassLoader.class; - } - - public synchronized final Object getObject() { - ServiceContext serviceContext = ServiceContextThreadLocal.get(); - if (serviceContext == null) { - throw new IllegalStateException("Service context has not been set"); - } - return serviceContext.getClassLoader(); - } - - public boolean isSingleton() { - return true; - } -} diff --git a/server/src/java/org/xbean/server/spring/reference/KernelReference.java b/server/src/java/org/xbean/server/spring/reference/KernelReference.java deleted file mode 100644 index 4f87212c..00000000 --- a/server/src/java/org/xbean/server/spring/reference/KernelReference.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.reference; - -import java.io.Serializable; - -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.FactoryBean; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceContextThreadLocal; - - -/** - * KernelReference is a the kernel in the ServiceContextThreadLocal. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class KernelReference implements FactoryBean, Serializable { - /** - * Creates a bean definition for KernelReference. - * @return a bean definition for KernelReference - */ - public static BeanDefinitionHolder createBeanDefinition() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(KernelReference.class, 0); - return new BeanDefinitionHolder(beanDefinition, KernelReference.class.getName()); - } - - public final Class getObjectType() { - return Kernel.class; - } - - public synchronized final Object getObject() { - ServiceContext serviceContext = ServiceContextThreadLocal.get(); - if (serviceContext == null) { - throw new IllegalStateException("Service context has not been set"); - } - return serviceContext.getKernel(); - } - - public boolean isSingleton() { - return true; - } -} diff --git a/server/src/java/org/xbean/server/spring/reference/ServiceNameReference.java b/server/src/java/org/xbean/server/spring/reference/ServiceNameReference.java deleted file mode 100644 index 8ddd1782..00000000 --- a/server/src/java/org/xbean/server/spring/reference/ServiceNameReference.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.reference; - -import java.io.Serializable; - -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceContextThreadLocal; -import org.xbean.kernel.ServiceName; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.RootBeanDefinition; - -/** - * ServiceNameReference is a the ServiceName in the ServiceContextThreadLocal. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceNameReference implements FactoryBean, Serializable { - /** - * Creates a bean definition for ServiceNameReference. - * @return a bean definition for ServiceNameReference - */ - public static BeanDefinitionHolder createBeanDefinition() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(ServiceNameReference.class, 0); - return new BeanDefinitionHolder(beanDefinition, ServiceNameReference.class.getName()); - } - - public final Class getObjectType() { - return ServiceName.class; - } - - public synchronized final Object getObject() { - ServiceContext serviceContext = ServiceContextThreadLocal.get(); - if (serviceContext == null) { - throw new IllegalStateException("Service context has not been set"); - } - return serviceContext.getServiceName(); - } - - public boolean isSingleton() { - return true; - } -} diff --git a/server/src/java/org/xbean/server/spring/reference/ServiceNameStringReference.java b/server/src/java/org/xbean/server/spring/reference/ServiceNameStringReference.java deleted file mode 100644 index 112de1ac..00000000 --- a/server/src/java/org/xbean/server/spring/reference/ServiceNameStringReference.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.reference; - -import java.io.Serializable; - -import org.xbean.kernel.ServiceContext; -import org.xbean.kernel.ServiceContextThreadLocal; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.RootBeanDefinition; - -/** - * ServiceNameStringReference is a the ServiceName as a string in the ServiceContextThreadLocal. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ServiceNameStringReference implements FactoryBean, Serializable { - /** - * Creates a bean definition for ServiceNameStringReference. - * @return a bean definition for ServiceNameStringReference - */ - public static BeanDefinitionHolder createBeanDefinition() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(ServiceNameStringReference.class, 0); - return new BeanDefinitionHolder(beanDefinition, ServiceNameStringReference.class.getName()); - } - - public final Class getObjectType() { - return String.class; - } - - public synchronized final Object getObject() { - ServiceContext serviceContext = ServiceContextThreadLocal.get(); - if (serviceContext == null) { - throw new IllegalStateException("Service context has not been set"); - } - return serviceContext.getServiceName().toString(); - } - - public boolean isSingleton() { - return true; - } -} diff --git a/server/src/resources/META-INF/xbean-bootstrap.xml b/server/src/resources/META-INF/xbean-bootstrap.xml deleted file mode 100644 index c9e6ae42..00000000 --- a/server/src/resources/META-INF/xbean-bootstrap.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/server/src/test/org/xbean/server/annotation/AnnotationTest.java b/server/src/test/org/xbean/server/annotation/AnnotationTest.java deleted file mode 100644 index 1576db82..00000000 --- a/server/src/test/org/xbean/server/annotation/AnnotationTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -import junit.framework.TestCase; - -/** - * Tests the annotation provider system. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class AnnotationTest extends TestCase { - /** - * Tests the backport 175 annotation provider. - */ - public void testRead175() { - AnnotationProvider annotationProvider = new Backport175AnnotationProvider(); - assertTrue(annotationProvider.isAnnotationPresent(Description.class, PizzaService.class)); - Object annotation = annotationProvider.getAnnotation(Description.class, PizzaService.class); - assertNotNull(annotation); - assertSame(Description.class, annotationProvider.getAnnotationType(annotation)); - Description description = (Description) annotation; - assertEquals("pizza making service", description.value()); - assertEquals("english", description.language()); - } -} diff --git a/server/src/test/org/xbean/server/annotation/Description.java b/server/src/test/org/xbean/server/annotation/Description.java deleted file mode 100644 index 222b3a32..00000000 --- a/server/src/test/org/xbean/server/annotation/Description.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -/** - * Simple description annotation used to test the annotation system. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface Description { - /** - * Gets the language of the description. - * @return the language of the description - * - * @org.codehaus.backport175.DefaultValue ("english") - */ - String language(); - - /** - * Gets the description. - * @return the description - */ - String value(); -} diff --git a/server/src/test/org/xbean/server/annotation/PizzaService.java b/server/src/test/org/xbean/server/annotation/PizzaService.java deleted file mode 100644 index 8eae1394..00000000 --- a/server/src/test/org/xbean/server/annotation/PizzaService.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.annotation; - -/** - * A sample pizza service. - * @org.xbean.server.annotation.Description ("pizza making service") - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class PizzaService { - private String topping; - private String cheese; - private int size; - private double price; - - /** - * Gets the cheese type. - * @return the cheese type - * - * @org.xbean.server.annotation.Description ("type of cheese") - */ - public String getCheese() { - return cheese; - } - - /** - * Sets the cheese type. - * @param cheese the cheese type - */ - public void setCheese(String cheese) { - this.cheese = cheese; - } - - /** - * Gets the price. - * @return the price - * - * @org.xbean.server.annotation.Description ("price in dollars") - */ - public double getPrice() { - return price; - } - - /** - * Sets the price. - * @param price the price - */ - public void setPrice(double price) { - this.price = price; - } - - /** - * Gets the size. - * @return the size - * - * @org.xbean.server.annotation.Description ("size of the pizza in inches") - */ - public int getSize() { - return size; - } - - /** - * Sets the size. - * @param size the size - */ - public void setSize(int size) { - this.size = size; - } - - /** - * Gets the toppings. - * @return the toppings - * - * @org.xbean.server.annotation.Description ("pizza topping") - */ - public String getTopping() { - return topping; - } - - /** - * Sets the toppings. - * @param topping the toppings - */ - public void setTopping(String topping) { - this.topping = topping; - } -} diff --git a/server/src/test/org/xbean/server/classloader/JarFileClassLoaderTest.java b/server/src/test/org/xbean/server/classloader/JarFileClassLoaderTest.java deleted file mode 100644 index d711627b..00000000 --- a/server/src/test/org/xbean/server/classloader/JarFileClassLoaderTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.classloader; - -import java.net.URL; - -/** - * Test the JarFileClassLoader. - * - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class JarFileClassLoaderTest extends MultiParentClassLoaderTest { - protected MultiParentClassLoader createClassLoader(String name, URL[] urls, ClassLoader[] parents) { - return new JarFileClassLoader(name, urls, parents); - } -} diff --git a/server/src/test/org/xbean/server/spring/loader/SpringLoaderTest.java b/server/src/test/org/xbean/server/spring/loader/SpringLoaderTest.java deleted file mode 100644 index 20a7ad91..00000000 --- a/server/src/test/org/xbean/server/spring/loader/SpringLoaderTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.loader; - -import junit.framework.TestCase; -import net.sf.cglib.core.DefaultGeneratorStrategy; -import net.sf.cglib.core.NamingPolicy; -import net.sf.cglib.core.Predicate; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.NoOp; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelFactory; -import org.xbean.kernel.ServiceName; -import org.xbean.kernel.StringServiceName; -import org.xbean.server.repository.FileSystemRepository; -import org.xbean.server.spring.configuration.ClassLoaderXmlPreprocessor; -import org.xbean.server.spring.configuration.SpringConfigurationServiceFactory; -import org.xbean.spring.context.SpringApplicationContext; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Collections; -import java.util.List; -import java.util.SortedSet; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringLoaderTest extends TestCase { - private static final String CLASS_NAME = "TestClass"; - private static final String ENTRY_NAME = "foo"; - private static final String ENTRY_VALUE = "bar"; - private File jarFile; - - public void testLoad() throws Exception{ - Kernel kernel = KernelFactory.newInstance().createKernel("test"); - - try { - File baseDir = new File("src/test/org/xbean/server/spring/loader/").getAbsoluteFile(); - System.setProperty("xbean.base.dir", baseDir.getAbsolutePath()); - - FileSystemRepository repository = new FileSystemRepository(new File(".").getAbsoluteFile()); - ClassLoaderXmlPreprocessor classLoaderXmlPreprocessor = new ClassLoaderXmlPreprocessor(repository); - List xmlPreprocessors = Collections.singletonList(classLoaderXmlPreprocessor); - - SpringLoader springLoader = new SpringLoader(); - springLoader.setKernel(kernel); - springLoader.setBaseDir(baseDir); - springLoader.setXmlPreprocessors(xmlPreprocessors); - ServiceName configurationName = springLoader.load("classpath-xbean"); - - kernel.startService(configurationName); - - Object testService = kernel.getService(new StringServiceName("test")); - assertEquals("TestClass", testService.getClass().getName()); - assertTrue(testService instanceof SortedSet); - } finally { - kernel.destroy(); - } - } - - public void testReload() throws Exception{ - Kernel kernel = KernelFactory.newInstance().createKernel("test"); - - try { - File baseDir = new File("src/test/org/xbean/server/spring/loader/").getAbsoluteFile(); - System.setProperty("xbean.base.dir", baseDir.getAbsolutePath()); - - FileSystemRepository repository = new FileSystemRepository(new File(".").getAbsoluteFile()); - ClassLoaderXmlPreprocessor classLoaderXmlPreprocessor = new ClassLoaderXmlPreprocessor(repository); - List xmlPreprocessors = Collections.singletonList(classLoaderXmlPreprocessor); - - SpringLoader springLoader = new SpringLoader(); - springLoader.setKernel(kernel); - springLoader.setBaseDir(baseDir); - springLoader.setXmlPreprocessors(xmlPreprocessors); - ServiceName configurationName = springLoader.load("classpath-xbean"); - SpringConfigurationServiceFactory serviceFactory = (SpringConfigurationServiceFactory) kernel.getServiceFactory(configurationName); - SpringApplicationContext applicationContext = serviceFactory.getApplicationContext(); - - kernel.startService(configurationName); - - Object testService = kernel.getService(new StringServiceName("test")); - assertEquals("TestClass", testService.getClass().getName()); - assertEquals(applicationContext.getClassLoader(), testService.getClass().getClassLoader()); - assertTrue(testService instanceof SortedSet); - - kernel.stopService(configurationName); - kernel.startService(configurationName); - - Object newTestService = kernel.getService(new StringServiceName("test")); - assertEquals("TestClass", newTestService.getClass().getName()); - assertEquals(applicationContext.getClassLoader(), newTestService.getClass().getClassLoader()); - assertTrue(newTestService instanceof SortedSet); - - assertNotSame("test service should be a new instance", - testService, newTestService); - - assertNotSame("test service should have been loaded from a different classloader", - testService.getClass().getClassLoader(), newTestService.getClass().getClassLoader()); - } finally { - kernel.destroy(); - } - } - - protected void setUp() throws Exception { - super.setUp(); - jarFile = createJarFile(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - jarFile.delete(); - } - - private static File createJarFile() throws IOException { - File file = new File("target/SpringLoaderTest.jar"); - - FileOutputStream out = new FileOutputStream(file); - JarOutputStream jarOut = new JarOutputStream(out); - - jarOut.putNextEntry(new JarEntry(CLASS_NAME + ".class")); - jarOut.write(createClass(CLASS_NAME)); - - jarOut.putNextEntry(new JarEntry(ENTRY_NAME)); - jarOut.write(ENTRY_VALUE.getBytes()); - - jarOut.close(); - out.close(); - - return file; - } - - private static byte[] createClass(final String name) { - Enhancer enhancer = new Enhancer(); - enhancer.setNamingPolicy(new NamingPolicy() { - public String getClassName(String prefix, String source, Object key, Predicate names) { - return name; - } - }); - enhancer.setClassLoader(new URLClassLoader(new URL[0])); - enhancer.setSuperclass(Object.class); - enhancer.setInterfaces(new Class[]{SortedSet.class}); - enhancer.setCallbackTypes(new Class[]{NoOp.class}); - enhancer.setUseFactory(false); - ByteCode byteCode = new ByteCode(); - enhancer.setStrategy(byteCode); - enhancer.createClass(); - - return byteCode.getByteCode(); - } - - private static class ByteCode extends DefaultGeneratorStrategy { - private byte[] byteCode; - - public byte[] transform(byte[] byteCode) { - this.byteCode = byteCode; - return byteCode; - } - - public byte[] getByteCode() { - return byteCode; - } - } -} diff --git a/server/src/test/org/xbean/server/spring/loader/classpath-xbean.xml b/server/src/test/org/xbean/server/spring/loader/classpath-xbean.xml deleted file mode 100644 index 4c0f0d94..00000000 --- a/server/src/test/org/xbean/server/spring/loader/classpath-xbean.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - target/SpringLoaderTest.jar - - - - - - - - - diff --git a/server/src/test/org/xbean/server/spring/main/SpringBootstrapTest.java b/server/src/test/org/xbean/server/spring/main/SpringBootstrapTest.java deleted file mode 100644 index 6b84f005..00000000 --- a/server/src/test/org/xbean/server/spring/main/SpringBootstrapTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.server.spring.main; - -import junit.framework.TestCase; -import org.xbean.kernel.Kernel; -import org.xbean.kernel.KernelFactory; -import org.xbean.server.main.KernelMain; -import org.xbean.server.spring.main.SpringBootstrap; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SpringBootstrapTest extends TestCase { - private SpringBootstrap springBootstrap; - - public void testClasspathBootstrap() throws Exception{ - springBootstrap.setConfigurationFile("META-INF/xbean-bootstrap.xml"); - springBootstrap.setServerBaseDirectory("."); - assertBootable(springBootstrap); - } - - public void testFileBootstrap() throws Exception{ - springBootstrap.setConfigurationFile("src/resources/META-INF/xbean-bootstrap.xml"); - springBootstrap.setServerBaseDirectory("."); - assertBootable(springBootstrap); - } - - private static void assertBootable(SpringBootstrap springBootstrap) { - // load the main instance - KernelMain main = (KernelMain) springBootstrap.loadMain(); - - // we don't want to start a daemon - main.setDaemon(false); - - // verify a kernel was registered - Kernel kernel = (Kernel) KernelFactory.getKernels().values().iterator().next(); - assertEquals(kernel, KernelFactory.getKernels().values().iterator().next()); - assertEquals(kernel, KernelFactory.getKernel(kernel.getKernelName())); - - // now boot the main instance - main.main(new String[0]); - - // verify the kernel is destroyed (the kernel main destroys the kernel on exit) - assertFalse(kernel.isRunning()); - - // verify a kernel was unregistered - assertFalse(KernelFactory.getKernels().values().iterator().hasNext()); - assertNull(KernelFactory.getKernel(kernel.getKernelName())); - } - - protected void setUp() throws Exception { - super.setUp(); - springBootstrap = new SpringBootstrap(); - } -} diff --git a/spring/.classpath b/spring/.classpath deleted file mode 100644 index 0d4b568e..00000000 --- a/spring/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/spring/.project b/spring/.project deleted file mode 100644 index 523480a9..00000000 --- a/spring/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - - xbean-spring - XBean Spring Adaptor adds some enhancements to Spring such as custom XML languages. - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - \ No newline at end of file diff --git a/spring/README.txt b/spring/README.txt deleted file mode 100644 index 826ba9f0..00000000 --- a/spring/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -This module contains the Spring additions for XBean which - -* provide a custom XML extension mechanism, allowing more concise XML to be used - along with XSDs so that they can be more easily validated with off the shelf XML tooling - -* LDAP enhancements to Spring's configuration mechanism. diff --git a/spring/maven.xml b/spring/maven.xml deleted file mode 100644 index 1adc935d..00000000 --- a/spring/maven.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring/project.properties b/spring/project.properties deleted file mode 100644 index 07280f09..00000000 --- a/spring/project.properties +++ /dev/null @@ -1,34 +0,0 @@ -### -# Copyright 2005 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. -### -maven.repo.remote=http://www.xbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven - -maven.compile.source=1.4 -maven.compile.target=1.4 -maven.compile.deprecation=true -maven.compile.debug=true -maven.compile.optimize=true - -maven.remote.group=xbean -maven.username=${user.name} -maven.repo.central=beaver.codehaus.org -maven.repo.central.directory=/dist - -maven.javadoc.source=1.4 -maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/ -maven.javadoc.additionalparam=-linksource - -maven.site.deploy.method=rsync -maven.site.deploy.clean=true diff --git a/spring/project.xml b/spring/project.xml deleted file mode 100644 index f935f3cc..00000000 --- a/spring/project.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - - 3 - - XBean :: Spring - xbean-spring - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - XBean: Spring Adaptor - - - XBean Spring Adaptor adds some enhancements to Spring such as custom - XML languages. - - - http://www.xbean.org/ - - www.xbean.org - /home/projects/xbean/public_html/maven - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - commons-logging - commons-logging - 1.0.3 - - - - springframework - spring - 1.2.4 - - - - - stax - stax-api - 1.0 - - - - - mx4j - mx4j-jmx - 2.1.1 - - - - - annogen - annogen - 0.1.0 - - - ant - ant - 1.6.2 - - - - - junit - junit - 3.8.1 - - - - - src/java - - - src/resources - - - - src/test - - - - src/test - - META-INF/**/* - **/*.x* - - - - - **/*Test.java - - - - - - - - - - - - - - - maven-jxr-plugin - maven-javadoc-plugin - maven-junit-report-plugin - - - - - - - diff --git a/spring/src/java/org/xbean/spring/context/ResourceXmlApplicationContext.java b/spring/src/java/org/xbean/spring/context/ResourceXmlApplicationContext.java deleted file mode 100644 index 3d0d942c..00000000 --- a/spring/src/java/org/xbean/spring/context/ResourceXmlApplicationContext.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.support.AbstractXmlApplicationContext; -import org.springframework.core.io.Resource; -import org.xbean.spring.context.impl.XBeanXmlBeanDefinitionReader; - -/** - * An XBean version of a regular Spring ApplicationContext which takes a - * {@link Resource} as a parameter to load the application context - * - * @author James Strachan - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class ResourceXmlApplicationContext extends AbstractXmlApplicationContext implements SpringApplicationContext { - private final List xmlPreprocessors; - private final Resource resource; - - /** - * Creates a ResourceXmlApplicationContext which loads the configuration from the specified Resource. - * @param resource the resource from which the configuration is loaded - */ - public ResourceXmlApplicationContext(Resource resource) { - super(); - this.xmlPreprocessors = Collections.EMPTY_LIST; - this.resource = resource; - refresh(); - } - - /** - * Creates a ResourceXmlApplicationContext which loads the configuration from the specified Resource. - * @param resource the resource from which the configuration is loaded - * @param xmlPreprocessors the SpringXmlPreprocessors to apply before passing the xml to Spring for processing - */ - public ResourceXmlApplicationContext(Resource resource, List xmlPreprocessors) { - super(); - this.xmlPreprocessors = xmlPreprocessors; - this.resource = resource; - refresh(); - } - - protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { - XmlBeanDefinitionReader beanDefinitionReader = new XBeanXmlBeanDefinitionReader(this, beanFactory, xmlPreprocessors); - - initBeanDefinitionReader(beanDefinitionReader); - - loadBeanDefinitions(beanDefinitionReader); - } - - /** - * {@inheritDoc} - */ - protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { - reader.loadBeanDefinitions(resource); - } - - /** - * {@inheritDoc} - */ - protected String[] getConfigLocations() { - return null; - } -} diff --git a/spring/src/java/org/xbean/spring/context/SpringXmlPreprocessor.java b/spring/src/java/org/xbean/spring/context/SpringXmlPreprocessor.java deleted file mode 100644 index e1cdd0ba..00000000 --- a/spring/src/java/org/xbean/spring/context/SpringXmlPreprocessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.spring.context; - -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.w3c.dom.Document; - -/** - * SpringXmlPreprocessor preprocesses the xml Document before it is passed to Spring for processing. - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public interface SpringXmlPreprocessor { - /** - * Preprocesses the xml document which is being loaded by the specified application context and is being read by the - * specified xml reader. - * @param applicationContext the application context which is being loaded - * @param reader the xml reader that read the document - * @param document the xml document to read - */ - public void preprocess(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document); -} diff --git a/spring/src/java/org/xbean/spring/context/impl/PropertyEditorHelper.java b/spring/src/java/org/xbean/spring/context/impl/PropertyEditorHelper.java deleted file mode 100644 index 25598a11..00000000 --- a/spring/src/java/org/xbean/spring/context/impl/PropertyEditorHelper.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context.impl; - -import javax.management.ObjectName; - -import java.beans.PropertyEditorManager; -import java.net.URI; - -/** - * A helper method to register some custom editors - * - * @version $Revision: 1.1 $ - */ -public class PropertyEditorHelper { - - public static void registerCustomEditors() { - PropertyEditorManager.registerEditor(URI.class, URIEditor.class); - PropertyEditorManager.registerEditor(ObjectName.class, ObjectNameEditor.class); - } - -} diff --git a/spring/src/java/org/xbean/spring/context/impl/QNameHelper.java b/spring/src/java/org/xbean/spring/context/impl/QNameHelper.java deleted file mode 100644 index ce3d30d0..00000000 --- a/spring/src/java/org/xbean/spring/context/impl/QNameHelper.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context.impl; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import javax.xml.namespace.QName; - -/** - * - * @version $Revision: 1.1 $ - */ -public class QNameHelper { - private static final Log log = LogFactory.getLog(QNameHelper.class); - - public static QName createQName(Element element, String qualifiedName) { - int index = qualifiedName.indexOf(':'); - if (index >= 0) { - String prefix = qualifiedName.substring(0, index); - String localName = qualifiedName.substring(index + 1); - String uri = recursiveGetAttributeValue(element, "xmlns:" + prefix); - return new QName(uri, localName, prefix); - } - else { - String uri = recursiveGetAttributeValue(element, "xmlns"); - if (uri != null) { - return new QName(uri, qualifiedName); - } - return new QName(qualifiedName); - } - } - - /** - * Recursive method to find a given attribute value - */ - public static String recursiveGetAttributeValue(Element element, String attributeName) { - String answer = null; - try { - answer = element.getAttribute(attributeName); - } - catch (Exception e) { - if (log.isTraceEnabled()) { - log.trace("Caught exception looking up attribute: " + attributeName + " on element: " + element + ". Cause: " + e, e); - } - } - if (answer == null || answer.length() == 0) { - Node parentNode = element.getParentNode(); - if (parentNode instanceof Element) { - return recursiveGetAttributeValue((Element) parentNode, attributeName); - } - } - return answer; - } -} diff --git a/spring/src/java/org/xbean/spring/context/impl/XBeanXmlBeanDefinitionParser.java b/spring/src/java/org/xbean/spring/context/impl/XBeanXmlBeanDefinitionParser.java deleted file mode 100644 index a9eeda0d..00000000 --- a/spring/src/java/org/xbean/spring/context/impl/XBeanXmlBeanDefinitionParser.java +++ /dev/null @@ -1,522 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context.impl; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.DefaultXmlBeanDefinitionParser; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; -import org.springframework.context.support.AbstractApplicationContext; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -import javax.xml.namespace.QName; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; - -/** - * An enhanced XML parser capable of handling custom XML schemas. - * - * @author James Strachan - * @version $Id$ - * @since 2.0 - */ -public class XBeanXmlBeanDefinitionParser extends DefaultXmlBeanDefinitionParser { - - private static final Log log = LogFactory.getLog(XBeanXmlBeanDefinitionParser.class); - - /** - * All the reserved Spring XML element names which cannot be overloaded by - * an XML extension - */ - protected static final String[] RESERVED_ELEMENT_NAMES = { "beans", DESCRIPTION_ELEMENT, IMPORT_ELEMENT, - ALIAS_ELEMENT, BEAN_ELEMENT, CONSTRUCTOR_ARG_ELEMENT, PROPERTY_ELEMENT, LOOKUP_METHOD_ELEMENT, - REPLACED_METHOD_ELEMENT, ARG_TYPE_ELEMENT, REF_ELEMENT, IDREF_ELEMENT, VALUE_ELEMENT, NULL_ELEMENT, - LIST_ELEMENT, SET_ELEMENT, MAP_ELEMENT, ENTRY_ELEMENT, KEY_ELEMENT, PROPS_ELEMENT, PROP_ELEMENT }; - - protected static final String[] RESERVED_BEAN_ATTRIBUTE_NAMES = { ID_ATTRIBUTE, NAME_ATTRIBUTE, CLASS_ATTRIBUTE, - PARENT_ATTRIBUTE, DEPENDS_ON_ATTRIBUTE, FACTORY_METHOD_ATTRIBUTE, FACTORY_BEAN_ATTRIBUTE, - DEPENDENCY_CHECK_ATTRIBUTE, AUTOWIRE_ATTRIBUTE, INIT_METHOD_ATTRIBUTE, DESTROY_METHOD_ATTRIBUTE, - ABSTRACT_ATTRIBUTE, SINGLETON_ATTRIBUTE, LAZY_INIT_ATTRIBUTE }; - - private static final String JAVA_PACKAGE_PREFIX = "java://"; - - private static final String BEAN_REFERENCE_SUFFIX = "-ref"; - - private Set reservedElementNames = new HashSet(Arrays.asList(RESERVED_ELEMENT_NAMES)); - private Set reservedBeanAttributeNames = new HashSet(Arrays.asList(RESERVED_BEAN_ATTRIBUTE_NAMES)); - protected final NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(); - - /** - * Configures the XmlBeanDefinitionReader to work nicely with extensible XML - * using this reader implementation. - */ - public static void configure(AbstractApplicationContext context, XmlBeanDefinitionReader reader) { - reader.setValidating(false); - reader.setNamespaceAware(true); - reader.setParserClass(XBeanXmlBeanDefinitionParser.class); - } - - /** - * Registers whatever custom editors we need - */ - public static void registerCustomEditors(DefaultListableBeanFactory beanFactory) { - PropertyEditorHelper.registerCustomEditors(); - } - - /** - * Parses the non-standard XML element as a Spring bean definition - */ - protected BeanDefinitionHolder parseBeanFromExtensionElement(Element element) { - String uri = element.getNamespaceURI(); - String localName = element.getLocalName(); - - MappingMetaData metadata = findNamespaceProperties(uri, localName); - if (metadata != null) { - // lets see if we configured the localName to a bean class - String className = metadata.getClassName(localName); - if (className != null) { - // lets assume the class name == the package name plus the - element.setAttributeNS(null, "class", className); - BeanDefinitionHolder definition = parseBeanDefinitionElement(element, false); - addAttributeProperties(definition, metadata, className, element); - addNestedPropertyElements(definition, metadata, className, element); - addInlinedPropertiesFile(definition, metadata, className, element); - coerceNamespaceAwarePropertyValues(definition, element); - declareLifecycleMethods(definition, metadata, element); - namedConstructorArgs.processParameters(definition, metadata); - return definition; - } - } - return null; - } - - /** - * Parses attribute names and values as being bean property expressions - */ - protected void addAttributeProperties(BeanDefinitionHolder definition, MappingMetaData metadata, String className, - Element element) { - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, size = attributes.getLength(); i < size; i++) { - Attr attribute = (Attr) attributes.item(i); - String uri = attribute.getNamespaceURI(); - String localName = attribute.getLocalName(); - - if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) { - continue; - } - - // we could use namespaced attributes to differentiate real spring - // attributes from namespace-specific attributes - if (((isEmpty(uri)) && !reservedBeanAttributeNames.contains(localName)) - || (!isEmpty(uri) && !uri.equals("http://www.w3.org/2000/xmlns/"))) { - addAttributeProperty(definition, metadata, element, attribute); - } - } - } - - protected void addAttributeProperty(BeanDefinitionHolder definition, MappingMetaData metadata, Element element, - Attr attribute) { - String localName = attribute.getName(); - String value = attribute.getValue(); - if (value != null) { - if (localName.endsWith(BEAN_REFERENCE_SUFFIX)) { - localName = localName.substring(0, localName.length() - BEAN_REFERENCE_SUFFIX.length()); - String propertyName = metadata.getPropertyName(element.getLocalName(), localName); - if (propertyName != null) { - definition.getBeanDefinition().getPropertyValues().addPropertyValue(propertyName, - new RuntimeBeanReference(value)); - } - } - else { - String propertyName = metadata.getPropertyName(element.getLocalName(), localName); - if (propertyName != null) { - definition.getBeanDefinition().getPropertyValues().addPropertyValue(propertyName, value); - } - } - } - } - - /** - * Lets iterate through the children of this element and create any nested - * child properties - */ - protected void addNestedPropertyElements(BeanDefinitionHolder definition, MappingMetaData metadata, - String className, Element element) { - NodeList nl = element.getChildNodes(); - for (int i = 0; i < nl.getLength(); i++) { - Node node = nl.item(i); - if (node instanceof Element) { - Element childElement = (Element) node; - String uri = childElement.getNamespaceURI(); - String localName = childElement.getLocalName(); - - if (!isEmpty(uri) || !reservedElementNames.contains(localName)) { - // we could be one of the following - // * the child element maps to a tag with inner - // tags being the bean - // * the child element maps to a tag with - // inner tags being the contents of the list - // * the child element maps to a tag and is the - // bean tag too - Object value = null; - String propertyName = metadata.getNestedListProperty(element.getLocalName(), localName); - if (propertyName != null) { - value = parseListElement(childElement, propertyName); - } - else { - propertyName = metadata.getNestedProperty(element.getLocalName(), localName); - if (propertyName != null) { - // lets find the first child bean that parses fine - value = parseChildExtensionBean(childElement); - } - } - if (propertyName == null) { - value = tryParseNestedPropertyViaIntrospection(metadata, className, childElement); - propertyName = localName; - } - if (value != null) { - definition.getBeanDefinition().getPropertyValues().addPropertyValue(propertyName, value); - } - } - } - } - } - - /** - * Attempts to use introspection to parse the nested property element. - */ - protected Object tryParseNestedPropertyViaIntrospection(MappingMetaData metadata, String className, Element element) { - BeanInfo beanInfo = getBeanInfo(className); - String localName = element.getLocalName(); - if (beanInfo != null) { - PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); - for (int i = 0; i < descriptors.length; i++) { - PropertyDescriptor descriptor = descriptors[i]; - if (descriptor.getWriteMethod() != null) { - String name = descriptor.getName(); - if (name.equals(localName)) { - return parseNestedPropertyViaIntrospection(metadata, className, element, descriptor); - } - } - } - } - return null; - } - - /** - * Parses a Properties file from the text node inside an element and adds - * the contents as properties of this bean. Only valid for elements - * containing a single text node and no sub-elements - */ - protected void addInlinedPropertiesFile(BeanDefinitionHolder definition, MappingMetaData metadata, - String className, Element element) { - NodeList childNodes = element.getChildNodes(); - if (childNodes.getLength() == 1 && childNodes.item(0) instanceof Text) { - Text text = (Text) childNodes.item(0); - ByteArrayInputStream in = new ByteArrayInputStream(text.getData().getBytes()); - Properties properties = new Properties(); - try { - properties.load(in); - } - catch (IOException e) { - return; - } - Enumeration enumeration = properties.propertyNames(); - while (enumeration.hasMoreElements()) { - String name = (String) enumeration.nextElement(); - Object value = properties.getProperty(name); - definition.getBeanDefinition().getPropertyValues().addPropertyValue(name, value); - } - } - } - - /** - * Any namespace aware property values (such as QNames) need to be coerced - * while we still have access to the XML Element from which its value comes - - * so lets do that now before we trash the DOM and just have the bean - * definition. - */ - protected void coerceNamespaceAwarePropertyValues(BeanDefinitionHolder definitionHolder, Element element) { - BeanDefinition definition = definitionHolder.getBeanDefinition(); - if (definition instanceof AbstractBeanDefinition) { - AbstractBeanDefinition bd = (AbstractBeanDefinition) definition; - // lets check for any QName types - BeanInfo beanInfo = getBeanInfo(bd.getBeanClassName()); - if (beanInfo != null) { - PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); - for (int i = 0; i < descriptors.length; i++) { - PropertyDescriptor descriptor = descriptors[i]; - if (descriptor.getWriteMethod() != null - && descriptor.getPropertyType().isAssignableFrom(QName.class)) { - String name = descriptor.getName(); - MutablePropertyValues propertyValues = bd.getPropertyValues(); - PropertyValue propertyValue = propertyValues.getPropertyValue(name); - if (propertyValue != null) { - Object value = propertyValue.getValue(); - if (value instanceof String) { - propertyValues.removePropertyValue(propertyValue); - propertyValues.addPropertyValue(name, QNameHelper.createQName(element, (String) value)); - } - } - } - } - } - } - } - - - protected BeanInfo getBeanInfo(String className) throws BeanDefinitionStoreException { - BeanInfo info = null; - Class type = null; - try { - type = loadClass(className); - } - catch (ClassNotFoundException e) { - throw new BeanDefinitionStoreException("Failed to load type: " + className + ". Reason: " + e, e); - } - try { - info = Introspector.getBeanInfo(type); - } - catch (IntrospectionException e) { - throw new BeanDefinitionStoreException("Failed to introspect type: " + className + ". Reason: " + e, e); - } - return info; - } - - /** - * Attempts to use introspection to parse the nested property element. - */ - protected Object parseNestedPropertyViaIntrospection(MappingMetaData metadata, String className, Element element, - PropertyDescriptor descriptor) { - String name = descriptor.getName(); - if (isCollection(descriptor.getPropertyType())) { - return parseListElement(element, name); - } - else { - return parseChildExtensionBean(element); - } - } - - /** - * Returns true if the given type is a collection type or an array - */ - protected boolean isCollection(Class type) { - return type.isArray() || Collection.class.isAssignableFrom(type); - } - - /** - * Iterates the children of this element to find the first nested bean - */ - protected Object parseChildExtensionBean(Element element) { - NodeList nl = element.getChildNodes(); - for (int i = 0; i < nl.getLength(); i++) { - Node node = nl.item(i); - if (node instanceof Element) { - Element childElement = (Element) node; - String uri = childElement.getNamespaceURI(); - String localName = childElement.getLocalName(); - - if (!isEmpty(uri) || !reservedElementNames.contains(localName)) { - Object value = parseBeanFromExtensionElement(childElement); - if (value != null) { - return value; - } - } - else if (isEmpty(uri)) { - if (BEAN_ELEMENT.equals(localName)) { - return parseBeanDefinitionElement(childElement, true); - } - } - } - } - return null; - } - - /** - * Uses META-INF/services discovery to find a Properties file with the XML - * marshaling configuration - * - * @param namespaceURI - * the namespace URI of the element - * @param localName - * the local name of the element - * @return the properties configuration of the namespace or null if none - * could be found - */ - protected MappingMetaData findNamespaceProperties(String namespaceURI, String localName) { - // lets look for the magic prefix - if (namespaceURI != null && namespaceURI.startsWith(JAVA_PACKAGE_PREFIX)) { - String packageName = namespaceURI.substring(JAVA_PACKAGE_PREFIX.length()); - return new MappingMetaData(packageName); - } - - String uri = NamespaceHelper.createDiscoveryPathName(namespaceURI, localName); - InputStream in = loadResource(uri); - if (in == null) { - if (namespaceURI != null && namespaceURI.length() > 0) { - in = loadResource(NamespaceHelper.createDiscoveryPathName(namespaceURI)); - } - } - - if (in != null) { - try { - Properties properties = new Properties(); - properties.load(in); - return new MappingMetaData(properties); - } - catch (IOException e) { - log.warn("Failed to load resource from uri: " + uri, e); - } - } - return null; - } - - /** - * Loads the resource from the given URI - */ - protected InputStream loadResource(String uri) { - // lets try the thread context class loader first - InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(uri); - if (in == null) { - in = getClass().getClassLoader().getResourceAsStream(uri); - if (in == null) { - logger.debug("Could not find resource: " + uri); - } - } - return in; - } - - /** - * Attempts to load the class on the current thread context class loader or - * the class loader which loaded us - */ - protected Class loadClass(String name) throws ClassNotFoundException { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (contextClassLoader != null) { - try { - return contextClassLoader.loadClass(name); - } - catch (ClassNotFoundException e) { - } - } - return getClass().getClassLoader().loadClass(name); - } - - protected boolean isEmpty(String uri) { - return uri == null || uri.length() == 0; - } - - protected void declareLifecycleMethods(BeanDefinitionHolder definitionHolder, MappingMetaData metaData, Element element) { - BeanDefinition definition = definitionHolder.getBeanDefinition(); - if (definition instanceof AbstractBeanDefinition) { - AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definition; - if (beanDefinition.getInitMethodName() == null) { - beanDefinition.setInitMethodName(metaData.getInitMethodName(element.getLocalName())); - } - if (beanDefinition.getDestroyMethodName() == null) { - beanDefinition.setDestroyMethodName(metaData.getDestroyMethodName(element.getLocalName())); - } - if (beanDefinition.getFactoryMethodName() == null) { - beanDefinition.setFactoryMethodName(metaData.getFactoryMethodName(element.getLocalName())); - } - } - } - - // ------------------------------------------------------------------------- - // - // TODO we could apply the following patches into the Spring code - - // though who knows if it'll ever make it into a release! :) - // - // ------------------------------------------------------------------------- - protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { - int beanDefinitionCount = 0; - NodeList nl = root.getChildNodes(); - for (int i = 0; i < nl.getLength(); i++) { - Node node = nl.item(i); - if (node instanceof Element) { - Element ele = (Element) node; - if (IMPORT_ELEMENT.equals(node.getNodeName())) { - importBeanDefinitionResource(ele); - } - else if (ALIAS_ELEMENT.equals(node.getNodeName())) { - String name = ele.getAttribute(NAME_ATTRIBUTE); - String alias = ele.getAttribute(ALIAS_ATTRIBUTE); - getBeanDefinitionReader().getBeanFactory().registerAlias(name, alias); - } - else if (BEAN_ELEMENT.equals(node.getNodeName())) { - beanDefinitionCount++; - BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); - BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getBeanDefinitionReader() - .getBeanFactory()); - } - else { - BeanDefinitionHolder bdHolder = parseBeanFromExtensionElement(ele); - if (bdHolder != null) { - beanDefinitionCount++; - BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getBeanDefinitionReader() - .getBeanFactory()); - } - else { - log.debug("Ignoring unknown element namespace: " + ele.getNamespaceURI() + " localName: " - + ele.getLocalName()); - } - } - } - } - return beanDefinitionCount; - } - - protected Object parsePropertySubElement(Element element, String beanName) throws BeanDefinitionStoreException { - String uri = element.getNamespaceURI(); - String localName = element.getLocalName(); - - if (!isEmpty(uri) || !reservedElementNames.contains(localName)) { - Object answer = parseBeanFromExtensionElement(element); - if (answer != null) { - return answer; - } - } - return super.parsePropertySubElement(element, beanName); - } -} diff --git a/spring/src/java/org/xbean/spring/task/SchemaElement.java b/spring/src/java/org/xbean/spring/task/SchemaElement.java deleted file mode 100644 index 21b9e4cb..00000000 --- a/spring/src/java/org/xbean/spring/task/SchemaElement.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.task; - -import org.codehaus.jam.JClass; - -/** - * Represents an element in the schema - * - * @version $Revision: 1.1 $ - */ -public class SchemaElement implements Comparable { - - private final JClass type; - private final String localName; - private final String namespace; - - public SchemaElement(JClass type, String localName, String namespace) { - this.type = type; - this.localName = localName; - this.namespace = namespace; - } - - public String getLocalName() { - return localName; - } - - public String getNamespace() { - return namespace; - } - - public JClass getType() { - return type; - } - - public int compareTo(Object that) { - if (that instanceof SchemaElement) { - SchemaElement thatElement = (SchemaElement) that; - return localName.compareTo(thatElement.localName); - } - return getClass().getName().compareTo(that.getClass().getName()); - } -} diff --git a/spring/src/java/org/xbean/spring/task/SchemaGenerateTask.java b/spring/src/java/org/xbean/spring/task/SchemaGenerateTask.java deleted file mode 100644 index 88a71684..00000000 --- a/spring/src/java/org/xbean/spring/task/SchemaGenerateTask.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.xbean.spring.task; - -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.taskdefs.MatchingTask; -import org.apache.tools.ant.types.Path; -import org.apache.tools.ant.types.Reference; -import org.codehaus.jam.JClass; -import org.codehaus.jam.JamService; -import org.codehaus.jam.JamServiceFactory; -import org.codehaus.jam.JamServiceParams; - -import java.io.File; - -/** - * An Ant task for executing Gram scripts, which are Groovy scripts executed on - * the JAM context. - * - * @version $Revision: 1.2 $ - */ -public class SchemaGenerateTask extends MatchingTask { - - private String namespace; - private Path srcDir = null; - private Path mToolpath = null; - private Path mClasspath = null; - private String mIncludes = "**/*.java"; - private File destFile = new File("target/classes/activemq.xsd"); - private String metaInfDir = "target/classes/"; - - public File getDestFile() { - return destFile; - } - - public void setDestFile(File scenariosFile) { - this.destFile = scenariosFile; - } - - public String getMetaInfDir() { - return metaInfDir; - } - - public void setMetaInfDir(String metaInfDir) { - this.metaInfDir = metaInfDir; - } - - public String getNamespace() { - return namespace; - } - - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - public void setSrcDir(Path srcDir) { - this.srcDir = srcDir; - } - - public void setToolpath(Path path) { - if (mToolpath == null) { - mToolpath = path; - } - else { - mToolpath.append(path); - } - } - - public void setToolpathRef(Reference r) { - createToolpath().setRefid(r); - } - - public Path createToolpath() { - if (mToolpath == null) { - mToolpath = new Path(getProject()); - } - return mToolpath.createPath(); - } - - public void setClasspath(Path path) { - if (mClasspath == null) { - mClasspath = path; - } - else { - mClasspath.append(path); - } - } - - public void setClasspathRef(Reference r) { - createClasspath().setRefid(r); - } - - public Path createClasspath() { - if (mClasspath == null) { - mClasspath = new Path(getProject()); - } - return mClasspath.createPath(); - } - - public void execute() throws BuildException { - if (namespace == null) { - throw new BuildException("'namespace' must be specified"); - } - if (srcDir == null) { - throw new BuildException("'srcDir' must be specified"); - } - if (destFile == null) { - throw new BuildException("'destFile' must be specified"); - } - JamServiceFactory jamServiceFactory = JamServiceFactory.getInstance(); - JamServiceParams serviceParams = jamServiceFactory.createServiceParams(); - if (mToolpath != null) { - File[] tcp = path2files(mToolpath); - for (int i = 0; i < tcp.length; i++) { - serviceParams.addToolClasspath(tcp[i]); - } - } - if (mClasspath != null) { - File[] cp = path2files(mClasspath); - for (int i = 0; i < cp.length; i++) { - serviceParams.addClasspath(cp[i]); - } - } - - try { - serviceParams.includeSourcePattern(path2files(srcDir), mIncludes); - JamService jam = jamServiceFactory.createService(serviceParams); - JClass[] classes = jam.getAllClasses(); - SchemaGenerator generator = new SchemaGenerator(classes, destFile, namespace, metaInfDir); - generator.generate(); - - log("...done."); - } - catch (Exception e) { - throw new BuildException(e); - } - } - - protected File[] path2files(Path path) { - String[] list = path.list(); - File[] out = new File[list.length]; - for (int i = 0; i < out.length; i++) { - out[i] = new File(list[i]).getAbsoluteFile(); - } - return out; - } -} diff --git a/spring/src/java/org/xbean/spring/task/SchemaGenerator.java b/spring/src/java/org/xbean/spring/task/SchemaGenerator.java deleted file mode 100644 index 48e4a228..00000000 --- a/spring/src/java/org/xbean/spring/task/SchemaGenerator.java +++ /dev/null @@ -1,597 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.task; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.codehaus.jam.JAnnotatedElement; -import org.codehaus.jam.JAnnotation; -import org.codehaus.jam.JAnnotationValue; -import org.codehaus.jam.JClass; -import org.codehaus.jam.JComment; -import org.codehaus.jam.JConstructor; -import org.codehaus.jam.JMethod; -import org.codehaus.jam.JParameter; -import org.codehaus.jam.JProperty; -import org.xbean.spring.context.impl.NamespaceHelper; -import org.xbean.spring.context.impl.PropertyEditorHelper; - -import java.beans.PropertyEditor; -import java.beans.PropertyEditorManager; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * - * @version $Revision: 1.1 $ - */ -public class SchemaGenerator { - public static final String XBEAN_ANNOTATION = "org.xbean.XBean"; - - private static final Log log = LogFactory.getLog(SchemaGenerator.class); - - private final JClass[] classes; - private final File destFile; - private final String defaultNamespace; - private String metaInfDir = "target/classes/"; - - private Map namespaces = new HashMap(); - private List allElements = new ArrayList(); - private SchemaElement rootElement; - private Map xsdTypeMap; - - public SchemaGenerator(JClass[] classes, File destFile, String defaultNamespace, String metaInfDir) { - this.classes = classes; - this.destFile = destFile; - this.defaultNamespace = defaultNamespace; - this.metaInfDir = metaInfDir; - - PropertyEditorHelper.registerCustomEditors(); - } - - public void generate() throws IOException { - loadModel(); - - for (Iterator iter = namespaces.entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = (Map.Entry) iter.next(); - String namespace = (String) entry.getKey(); - List elements = (List) entry.getValue(); - Collections.sort(elements); - if (namespace != null) { - generatePropertiesFile(namespace, elements); - } - generateDocumentation(namespace, elements); - generateSchema(namespace, elements); - } - - if (namespaces.isEmpty()) { - System.out.println("Warning: no namespaces found!"); - } - } - - // Documentation generation - // ------------------------------------------------------------------------- - protected void generatePropertiesFile(String namespace, List elements) throws IOException { - File file = new File(metaInfDir + NamespaceHelper.createDiscoveryPathName(namespace)); - file.getParentFile().mkdirs(); - System.out.println("Generating META-INF properties file: " + file + " for namespace: " + namespace); - PrintWriter out = new PrintWriter(new FileWriter(file)); - try { - generatePropertiesFile(out, namespace, elements); - } - finally { - out.close(); - } - } - - protected void generatePropertiesFile(PrintWriter out, String namespace, List elements) { - out.println("# NOTE: this file is autogenerated by XBeans"); - out.println(); - out.println("# beans"); - - for (Iterator iter = elements.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - out.println(element.getLocalName() + " = " + element.getType().getQualifiedName()); - - generatePropertiesFileConstructors(out, namespace, element); - } - } - - protected void generatePropertiesFileConstructors(PrintWriter out, String namespace, SchemaElement element) { - JClass type = element.getType(); - JConstructor[] constructors = type.getConstructors(); - for (int i = 0; i < constructors.length; i++) { - JConstructor constructor = constructors[i]; - generatePropertiesFileConstructor(out, namespace, element, constructor); - } - } - - protected void generatePropertiesFileConstructor(PrintWriter out, String namespace, SchemaElement element, JConstructor constructor) { - JParameter[] parameters = constructor.getParameters(); - if (parameters.length == 0) { - return; - } - out.print(element.getType().getQualifiedName()); - out.print("("); - for (int i = 0; i < parameters.length; i++) { - JParameter parameter = parameters[i]; - if (i > 0) { - out.print(","); - } - out.print(parameter.getType().getQualifiedName()); - } - out.print(").parameterNames ="); - for (int i = 0; i < parameters.length; i++) { - JParameter parameter = parameters[i]; - out.print(" "); - out.print(parameter.getSimpleName()); - } - out.println(); - } - - // Documentation generation - // ------------------------------------------------------------------------- - protected void generateDocumentation(String namespace, List elements) throws IOException { - // TODO can only handle 1 schema document so far... - File file = new File(destFile.getParentFile(), destFile.getName() + ".html"); - System.out.println("Generating HTML documentation file: " + file + " for namespace: " + namespace); - PrintWriter out = new PrintWriter(new FileWriter(file)); - try { - generateDocumentation(out, namespace, elements); - } - finally { - out.close(); - } - } - - private void generateDocumentation(PrintWriter out, String namespace, List elements) { - out.println(""); - out.println(""); - out.println(""); - out.println("Schema for namespace: " + namespace + ""); - out.println(""); - out.println(""); - out.println(""); - out.println(""); - out.println(); - out.println(""); - out.println(); - - if (rootElement != null) { - out.println("

    Root Element

    "); - out.println(""); - out.println(" "); - generateHtmlElementSummary(out, rootElement); - out.println("
    ElementDescriptionClass
    "); - out.println(); - } - - out.println("

    Element Summary

    "); - out.println(""); - out.println(" "); - for (Iterator iter = elements.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - generateHtmlElementSummary(out, element); - } - out.println("
    ElementDescriptionClass
    "); - out.println(); - out.println(); - - out.println("

    Element Detail

    "); - for (Iterator iter = elements.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - generateHtmlElementDetail(out, element); - } - - out.println(); - out.println(""); - out.println(""); - } - - protected void generateHtmlElementSummary(PrintWriter out, SchemaElement element) { - String localName = element.getLocalName(); - JClass type = element.getType(); - out.println(" " + localName + "" + getDescription(type) - + "" + type.getQualifiedName() + ""); - } - - protected void generateHtmlElementDetail(PrintWriter out, SchemaElement element) { - String localName = element.getLocalName(); - out.println("

    Element: " + localName + "

    "); - - out.println(""); - out.println(" "); - JClass type = element.getType(); - JProperty[] properties = type.getProperties(); - for (int i = 0; i < properties.length; i++) { - JProperty property = properties[i]; - if (!isValidProperty(property)) { - continue; - } - if (isSimpleType(property)) { - out.println(" "); - } - } - out.println("
    AttributeTypeDescription
    " + getPropertyXmlName(property) + "" + getXSDType(property) - + "" + getDescription(property) + "
    "); - - out.println(""); - out.println(" "); - for (int i = 0; i < properties.length; i++) { - JProperty property = properties[i]; - if (!isValidProperty(property)) { - continue; - } - if (!isSimpleType(property)) { - out.print(" "); - } - } - out.println("
    ElementTypeDescription
    " + getPropertyXmlName(property) + ""); - printComplexPropertyTypeDocumentation(out, property); - out.println("" + getDescription(property) + "
    "); - } - - protected void printComplexPropertyTypeDocumentation(PrintWriter out, JProperty property) { - JClass type = property.getType(); - String typeName = type.getQualifiedName(); - if (isCollection(type)) { - out.print(""); - } - else { - int counter = 0; - // lets find all the implementations of the type - List types = findImplementationsOf(type); - for (Iterator iter = types.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - if (counter++ > 0) { - out.print(" | "); - } - out.print("<" + element.getLocalName() + "/>"); - } - if (counter > 0) { - out.print(" | "); - } - out.print(""); - } - } - - protected List findImplementationsOf(JClass type) { - List answer = new ArrayList(); - for (Iterator iter = answer.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - if (isImplementationOf(type, element.getType())) { - answer.add(element); - } - } - return answer; - } - - // XSD generation - // ------------------------------------------------------------------------- - protected void generateSchema(String namespace, List elements) throws IOException { - // TODO can only handle 1 schema document so far... - File file = destFile; - System.out.println("Generating XSD file: " + file + " for namespace: " + namespace); - PrintWriter out = new PrintWriter(new FileWriter(file)); - try { - generateSchema(out, namespace, elements); - } - finally { - out.close(); - } - } - - protected void generateSchema(PrintWriter out, String namespace, List elements) { - out.println(""); - out.println(""); - out.println(); - out.println(""); - - for (Iterator iter = elements.iterator(); iter.hasNext();) { - SchemaElement element = (SchemaElement) iter.next(); - generateSchemaElement(out, element); - } - - out.println(); - out.println(""); - - String localName = element.getLocalName(); - - out.println(" "); - out.println(" "); - - JProperty[] properties = type.getProperties(); - int complexCount = 0; - for (int i = 0; i < properties.length; i++) { - JProperty property = properties[i]; - if (!isValidProperty(property)) { - continue; - } - if (isSimpleType(property)) { - generateSchemaElementSimpleProperty(out, element, property); - } - else { - complexCount++; - } - } - if (complexCount > 0) { - out.println(" "); - for (int i = 0; i < properties.length; i++) { - JProperty property = properties[i]; - if (!isValidProperty(property)) { - continue; - } - if (!isSimpleType(property)) { - generateSchemaElementComplexProperty(out, element, property); - } - } - } - out.println(" "); - out.println(" "); - out.println(); - } - - protected void generateSchemaElementSimpleProperty(PrintWriter out, SchemaElement element, JProperty property) { - out.println(" "); - } - - protected void generateSchemaElementComplexProperty(PrintWriter out, SchemaElement element, JProperty property) { - out.println(" "); - - // TODO expand the element declaration to list possible implementations - // and allow extension via xsd:any - } - - protected boolean isSimpleType(JProperty property) { - JClass type = property.getType(); - String name = type.getQualifiedName(); - if (type.isPrimitiveType()) { - return true; - } - if (name.equals("javax.xml.namespace.QName")) { - return true; - } - if (name.endsWith("]")) { - return false; - } - Class theClass; - try { - theClass = loadClass(name); - } - catch (ClassNotFoundException e) { - System.out.println("Warning, could not load class: " + name); - return false; - } - // lets see if we can find a property editor for this type - PropertyEditor editor = PropertyEditorManager.findEditor(theClass); - return editor != null; - } - - protected boolean isValidProperty(JProperty property) { - if ( !property.getSimpleName().equals("Class")) { - JMethod setter = property.getSetter(); - if (setter == null) { - return false; - } - JAnnotation annotation = setter.getAnnotation(XBEAN_ANNOTATION); - if (annotation != null) { - JAnnotationValue value = annotation.getValue("hide"); - if (value != null) { - return value.asBoolean(); - } - } - return true; - } - return false; - } - - protected String getPropertyXmlName(JAnnotatedElement element) { - String answer = element.getSimpleName(); - if (answer.length() > 0) { - answer = answer.substring(0, 1).toLowerCase() + answer.substring(1); - } - - // lets strip off the trailing Bean for *FactoryBean types by default - if (element instanceof JClass && answer.endsWith("FactoryBean")) { - answer = answer.substring(0, answer.length() - 4); - } - return answer; - } - - protected String getDescription(JClass type) { - return getCommentText(type); - } - - protected String getDescription(JProperty property) { - JMethod setter = property.getSetter(); - if (setter != null) { - return getCommentText(setter); - } - return ""; - } - - protected String getCommentText(JAnnotatedElement element) { - JAnnotation annotation = element.getAnnotation(XBEAN_ANNOTATION); - if (annotation != null) { - JAnnotationValue value = annotation.getValue("description"); - if (value != null) { - return value.asString(); - } - } - JComment comment = element.getComment(); - if (comment != null) { - return comment.getText(); - } - return ""; - } - - protected String getXSDType(JProperty property) { - if (xsdTypeMap == null) { - xsdTypeMap = new HashMap(); - loadXsdTypeMap(xsdTypeMap); - } - String typeName = property.getType().getQualifiedName(); - String answer = (String) xsdTypeMap.get(typeName); - if (answer == null) { - answer = "xsd:string"; - } - return answer; - } - - protected void loadXsdTypeMap(Map map) { - // TODO check these XSD types are right... - map.put(String.class.getName(), "xsd:string"); - map.put(Boolean.class.getName(), "xsd:boolean"); - map.put(boolean.class.getName(), "xsd:boolean"); - map.put(Byte.class.getName(), "xsd:byte"); - map.put(byte.class.getName(), "xsd:byte"); - map.put(Short.class.getName(), "xsd:short"); - map.put(short.class.getName(), "xsd:short"); - map.put(Integer.class.getName(), "xsd:integer"); - map.put(int.class.getName(), "xsd:integer"); - map.put(Long.class.getName(), "xsd:long"); - map.put(long.class.getName(), "xsd:long"); - map.put(Float.class.getName(), "xsd:float"); - map.put(float.class.getName(), "xsd:float"); - map.put(Double.class.getName(), "xsd:double"); - map.put(double.class.getName(), "xsd:double"); - map.put(java.util.Date.class.getName(), "xsd:date"); - map.put(java.sql.Date.class.getName(), "xsd:date"); - map.put("javax.xml.namespace.QName", "xsd:QName"); - } - - /** - * Attempts to load the class on the current thread context class loader or - * the class loader which loaded us - */ - protected Class loadClass(String name) throws ClassNotFoundException { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (contextClassLoader != null) { - try { - return contextClassLoader.loadClass(name); - } - catch (ClassNotFoundException e) { - } - } - return getClass().getClassLoader().loadClass(name); - } - - protected void loadModel() { - for (int i = 0; i < classes.length; i++) { - JClass type = classes[i]; - JAnnotation annotation = type.getAnnotation(XBEAN_ANNOTATION); - if (annotation != null) { - String localName = getStringValue(annotation, "element"); - if (localName == null) { - localName = getPropertyXmlName(type); - } - String namespace = getStringValue(annotation, "namespace"); - if (namespace == null) { - namespace = defaultNamespace; - } - boolean root = getBooleanValue(annotation, "rootElement"); - addXmlType(type, localName, namespace, root); - } - else { - log.debug("No XML annotation found for type: " + type.getQualifiedName()); - } - } - } - - protected void addXmlType(JClass type, String localName, String namespace, boolean root) { - List list = (List) namespaces.get(namespace); - if (list == null) { - list = new ArrayList(); - namespaces.put(namespace, list); - } - SchemaElement element = new SchemaElement(type, localName, namespace); - list.add(element); - allElements.add(element); - if (root) { - rootElement = element; - } - } - - protected boolean isCollection(JClass type) { - return type.isArrayType() || implementsInterface(type, Collection.class.getName()); - } - - protected boolean implementsInterface(JClass type, String interfaceClass) { - JClass[] interfaces = type.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - JClass anInterface = interfaces[i]; - if (anInterface.getQualifiedName().equals(interfaceClass)) { - return true; - } - } - JClass superclass = type.getSuperclass(); - if (superclass == null || superclass == type) { - return false; - } - else { - return implementsInterface(superclass, interfaceClass); - } - } - - protected boolean isImplementationOf(JClass type, JClass interfaceOrBaseClass) { - if (interfaceOrBaseClass.isInterface()) { - return implementsInterface(type, interfaceOrBaseClass.getQualifiedName()); - } - while (type != null) { - type = type.getSuperclass(); - if (type != null && type.getQualifiedName().equals(interfaceOrBaseClass.getQualifiedName())) { - return true; - } - } - return false; - } - - protected String getStringValue(JAnnotation annotation, String name) { - JAnnotationValue value = annotation.getValue(name); - if (value != null) { - return value.asString(); - } - return null; - } - - protected boolean getBooleanValue(JAnnotation annotation, String name) { - JAnnotationValue value = annotation.getValue(name); - if (value != null) { - return value.asBoolean(); - } - return false; - } -} diff --git a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza b/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza deleted file mode 100644 index 303e519d..00000000 --- a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza +++ /dev/null @@ -1,17 +0,0 @@ -# START SNIPPET: config - -# the default package that POJOs are in -package = org.xbean.spring.example - -# Mapping of XML Element localNames to classes -pizza = org.xbean.spring.example.PizzaService -restaurant = org.xbean.spring.example.RestaurantService - -# Mapping of XML Attributes to property names -pizza.myTopping = topping - -# Mapping of nested bean properties -restaurant.dinnerMenu.list = dinnerMenu -restaurant.favourite = favourite - -# END SNIPPET: config diff --git a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza-simple b/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza-simple deleted file mode 100644 index e0007fbe..00000000 --- a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/pizza-simple +++ /dev/null @@ -1,10 +0,0 @@ -# START SNIPPET: config - -# the default package that POJOs are in -package = org.xbean.spring.example - -# Mapping of XML Element localNames to classes -pizza = org.xbean.spring.example.PizzaService -restaurant = org.xbean.spring.example.RestaurantService - -# END SNIPPET: config diff --git a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/salad b/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/salad deleted file mode 100644 index 3c0fd292..00000000 --- a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/salad +++ /dev/null @@ -1,15 +0,0 @@ -# START SNIPPET: config - -# the default package that POJOs are in -package = org.xbean.spring.example - -# Mapping of XML Element localNames to classes -salad = org.xbean.spring.example.SaladService - -# Mapping of XML Attributes to property names -salad.addCroutons = crouton - -# Mapping of constructor argument names -org.xbean.spring.example.SaladService(java.lang.String,java.lang.String,boolean).parameterNames=dressing size crouton - -# END SNIPPET: config diff --git a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/soup b/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/soup deleted file mode 100644 index 62592990..00000000 --- a/spring/src/test/META-INF/services/org/xbean/spring/http/xbean.org/schemas/soup +++ /dev/null @@ -1,17 +0,0 @@ -# START SNIPPET: config - -# the default package that POJOs are in -package = org.xbean.spring.example - -# Mapping of XML Element localNames to classes -soup = org.xbean.spring.example.SoupService - -# Mapping of life-cycle methods -soup.initMethod = make -soup.destroyMethod = eat -soup.factoryMethod = newSoup - -# Mapping of constructor argument names -org.xbean.spring.example.SoupService.newSoup(java.lang.String).parameterNames=type - -# END SNIPPET: config diff --git a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanTest.java b/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanTest.java deleted file mode 100644 index ee9f0264..00000000 --- a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * @author James Strachan - * @version $Id$ - * @since 2.0 - */ -public class PizzaUsingXBeanTest extends PizzaUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/pizza-xbean.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWinBeanRefTest.java b/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWinBeanRefTest.java deleted file mode 100644 index 7f6c0163..00000000 --- a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWinBeanRefTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * - * @version $Revision: 1.1 $ - */ -public class PizzaUsingXBeanWinBeanRefTest extends PizzaUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/pizza-xbean-bean-ref.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithJavaNamespaceTest.java b/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithJavaNamespaceTest.java deleted file mode 100644 index 1815b77b..00000000 --- a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithJavaNamespaceTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * @author James Strachan - * @version $Id$ - * @since 2.0 - */ -public class PizzaUsingXBeanWithJavaNamespaceTest extends PizzaUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/pizza-xbean-java.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithPropertiesTextNodeTest.java b/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithPropertiesTextNodeTest.java deleted file mode 100644 index 249ecebf..00000000 --- a/spring/src/test/org/xbean/spring/context/PizzaUsingXBeanWithPropertiesTextNodeTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2005 David Blevins - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * - * @version $Revision: 1.1 $ - */ -public class PizzaUsingXBeanWithPropertiesTextNodeTest extends PizzaUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/pizza-xbean-properties.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/RestaurantUsingXBeanWithSimplerConfigTest.java b/spring/src/test/org/xbean/spring/context/RestaurantUsingXBeanWithSimplerConfigTest.java deleted file mode 100644 index 93f92a2c..00000000 --- a/spring/src/test/org/xbean/spring/context/RestaurantUsingXBeanWithSimplerConfigTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * @author James Strachan - * @version $Id$ - * @since 2.0 - */ -public class RestaurantUsingXBeanWithSimplerConfigTest extends RestaurantUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/restaurant-xbean.xml"); - } -} diff --git a/spring/src/test/org/xbean/spring/context/SaladUsingSpringTest.java b/spring/src/test/org/xbean/spring/context/SaladUsingSpringTest.java deleted file mode 100644 index 607acac1..00000000 --- a/spring/src/test/org/xbean/spring/context/SaladUsingSpringTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; -import org.xbean.spring.example.SaladService; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 1.0 - */ -public class SaladUsingSpringTest extends SpringTestSupport { - public void testSalad() throws Exception { - SaladService salad = (SaladService) getBean("saladService"); - - assertEquals("dressing", "Cesar", salad.getDressing()); - assertEquals("size", "Small", salad.getSize()); - assertEquals("crouton", true, salad.isCrouton()); - } - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/salad-normal.xml"); - } -} diff --git a/spring/src/test/org/xbean/spring/context/SaladUsingXBeanTest.java b/spring/src/test/org/xbean/spring/context/SaladUsingXBeanTest.java deleted file mode 100644 index b8cba278..00000000 --- a/spring/src/test/org/xbean/spring/context/SaladUsingXBeanTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SaladUsingXBeanTest extends SaladUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/salad-xbean.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/SoupUsingSpringTest.java b/spring/src/test/org/xbean/spring/context/SoupUsingSpringTest.java deleted file mode 100644 index 3ecd2d56..00000000 --- a/spring/src/test/org/xbean/spring/context/SoupUsingSpringTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; -import org.xbean.spring.example.SoupService; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 1.0 - */ -public class SoupUsingSpringTest extends SpringTestSupport { - private static final long time = System.currentTimeMillis(); - - public void testSoup() throws Exception { - SoupService soup = (SoupService) getBean("soupService"); - - assertEquals("type", "French Onion", soup.getType()); - assertTrue(soup.getCreateTime() >= time); - assertTrue(soup.exists()); - - context.close(); - assertFalse(soup.exists()); - } - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/soup-normal.xml"); - } -} diff --git a/spring/src/test/org/xbean/spring/context/SoupUsingXBeanTest.java b/spring/src/test/org/xbean/spring/context/SoupUsingXBeanTest.java deleted file mode 100644 index 77e3c01b..00000000 --- a/spring/src/test/org/xbean/spring/context/SoupUsingXBeanTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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.xbean.spring.context; - -import org.springframework.context.support.AbstractXmlApplicationContext; - -/** - * @author Dain Sundstrom - * @version $Id$ - * @since 2.0 - */ -public class SoupUsingXBeanTest extends SoupUsingSpringTest { - - protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/xbean/spring/context/soup-xbean.xml"); - } - -} diff --git a/spring/src/test/org/xbean/spring/context/pizza-normal.xml b/spring/src/test/org/xbean/spring/context/pizza-normal.xml deleted file mode 100644 index 7b528b92..00000000 --- a/spring/src/test/org/xbean/spring/context/pizza-normal.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/pizza-xbean-bean-ref.xml b/spring/src/test/org/xbean/spring/context/pizza-xbean-bean-ref.xml deleted file mode 100644 index b8b67e83..00000000 --- a/spring/src/test/org/xbean/spring/context/pizza-xbean-bean-ref.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Salami - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/pizza-xbean-java.xml b/spring/src/test/org/xbean/spring/context/pizza-xbean-java.xml deleted file mode 100644 index 6c9a8a16..00000000 --- a/spring/src/test/org/xbean/spring/context/pizza-xbean-java.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/pizza-xbean-properties.xml b/spring/src/test/org/xbean/spring/context/pizza-xbean-properties.xml deleted file mode 100644 index 5684a079..00000000 --- a/spring/src/test/org/xbean/spring/context/pizza-xbean-properties.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - topping Salami - cheese Edam - size 17 - - - - - diff --git a/spring/src/test/org/xbean/spring/context/pizza-xbean.xml b/spring/src/test/org/xbean/spring/context/pizza-xbean.xml deleted file mode 100644 index d84402c7..00000000 --- a/spring/src/test/org/xbean/spring/context/pizza-xbean.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/restaurant-normal.xml b/spring/src/test/org/xbean/spring/context/restaurant-normal.xml deleted file mode 100644 index d1a65490..00000000 --- a/spring/src/test/org/xbean/spring/context/restaurant-normal.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/restaurant-xbean-simple.xml b/spring/src/test/org/xbean/spring/context/restaurant-xbean-simple.xml deleted file mode 100644 index c4a55bce..00000000 --- a/spring/src/test/org/xbean/spring/context/restaurant-xbean-simple.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/restaurant-xbean.xml b/spring/src/test/org/xbean/spring/context/restaurant-xbean.xml deleted file mode 100644 index 3c72e5d6..00000000 --- a/spring/src/test/org/xbean/spring/context/restaurant-xbean.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/salad-normal.xml b/spring/src/test/org/xbean/spring/context/salad-normal.xml deleted file mode 100644 index 937a6386..00000000 --- a/spring/src/test/org/xbean/spring/context/salad-normal.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/salad-xbean.xml b/spring/src/test/org/xbean/spring/context/salad-xbean.xml deleted file mode 100644 index 09ae7610..00000000 --- a/spring/src/test/org/xbean/spring/context/salad-xbean.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/soup-normal.xml b/spring/src/test/org/xbean/spring/context/soup-normal.xml deleted file mode 100644 index ab821e8f..00000000 --- a/spring/src/test/org/xbean/spring/context/soup-normal.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/context/soup-xbean.xml b/spring/src/test/org/xbean/spring/context/soup-xbean.xml deleted file mode 100644 index e38ae7fa..00000000 --- a/spring/src/test/org/xbean/spring/context/soup-xbean.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/spring/src/test/org/xbean/spring/example/SaladService.java b/spring/src/test/org/xbean/spring/example/SaladService.java deleted file mode 100644 index 88ecab8d..00000000 --- a/spring/src/test/org/xbean/spring/example/SaladService.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright 2005 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.xbean.spring.example; - -/** - * @org.xbean.XBean - * - * @author Dain Sundstrom - * @version $Id$ - * @since 1.0 - */ - -// START SNIPPET: bean -public class SaladService { - private final String dressing; - private final String size; - private final boolean crouton; - - public SaladService(String dressing, String size, boolean crouton) { - this.dressing = dressing; - this.size = size; - this.crouton = crouton; - } - - public String getDressing() { - return dressing; - } - - public String getSize() { - return size; - } - - public boolean isCrouton() { - return crouton; - } -} -// END SNIPPET: bean diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 00000000..0c3092c2 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + ${modules} + + ${reports} + + + + + + diff --git a/telnet/m2-readme.txt b/telnet/m2-readme.txt deleted file mode 100644 index 3750464a..00000000 --- a/telnet/m2-readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -Trying Maven 2.x ----------------- - -If you want to try m2 then simply run the mavenizer.sh script which -will produce an "m2" directory. Move into the "m2" directory and - -m2 install - -That should compile, test, package and install diff --git a/telnet/maven.xml b/telnet/maven.xml deleted file mode 100644 index 0872ff3b..00000000 --- a/telnet/maven.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/telnet/mavenizer.sh b/telnet/mavenizer.sh deleted file mode 100755 index 0c9696c3..00000000 --- a/telnet/mavenizer.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -DIR=m2 - -rm -rf $DIR > /dev/null 2>&1 -mkdir $DIR - -cp -r src $DIR -cp pom.xml $DIR - -( - cd $DIR - - find . -name '\.svn' -exec rm -rf {} \; - - ( - cd src - - mkdir main ; mv java main - - ( cd test ; mkdir java ; mv org java ) - - ) - -) diff --git a/telnet/pom.xml b/telnet/pom.xml deleted file mode 100644 index 5a77d7e4..00000000 --- a/telnet/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - 4.0.0 - - XBean :: Telnet - xbean-telent - xbean - 2.0-alpha-1-SNAPSHOT - - XBean - http://xbean.org - - - - - openejb - OpenEJB Repo - http://www.openejb.org/maven - legacy - - - - http://www.xbean.org/ - - - - repo1 - Maven Central Repository - scp://repo1.maven.org/home/projects/maven/repository-staging/to-ibiblio/maven2 - - - website - scp://minotaur.apache.org/www/maven.apache.org/maven2/ - - - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - David Blevins - dblevins - dblevins@visi.com - - Dude - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.4 - 1.4 - - - - - - - diff --git a/telnet/project.properties b/telnet/project.properties deleted file mode 100644 index 07280f09..00000000 --- a/telnet/project.properties +++ /dev/null @@ -1,34 +0,0 @@ -### -# Copyright 2005 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. -### -maven.repo.remote=http://www.xbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven - -maven.compile.source=1.4 -maven.compile.target=1.4 -maven.compile.deprecation=true -maven.compile.debug=true -maven.compile.optimize=true - -maven.remote.group=xbean -maven.username=${user.name} -maven.repo.central=beaver.codehaus.org -maven.repo.central.directory=/dist - -maven.javadoc.source=1.4 -maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/ -maven.javadoc.additionalparam=-linksource - -maven.site.deploy.method=rsync -maven.site.deploy.clean=true diff --git a/telnet/project.xml b/telnet/project.xml deleted file mode 100644 index a75cadc1..00000000 --- a/telnet/project.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - 3 - - XBean :: Telnet - xbean-telnet - xbean - 2.0-SNAPSHOT - - XBean.org - http://xbean.org - - - org.xbean - - XBean: Telnet protocol and shell library - - - Telnet protocol and shell library - - - http://www.xbean.org/ - - www.xbean.org - /home/projects/xbean/public_html/maven - - - - xbean developers - mailto:dev-subscribe@xbean.org - mailto:dev-unsubscribe@xbean.org - - - xbean users - mailto:user-subscribe@xbean.org - mailto:user-unsubscribe@xbean.org - - - xbean source control messages - mailto:scm-subscribe@xbean.org - mailto:scm-unsubscribe@xbean.org - - - - - - groovy - groovy - 1.0-jsr-03 - - - - - src/java - src/test - - - **/*Test.java - - - - - - maven-jxr-plugin - maven-javadoc-plugin - maven-junit-report-plugin - - - - diff --git a/telnet/src/java/org/xbean/telnet/Exit.java b/telnet/src/java/org/xbean/telnet/Exit.java deleted file mode 100755 index 0d532d37..00000000 --- a/telnet/src/java/org/xbean/telnet/Exit.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * - * Copyright 2005 David Blevins - * - * 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.xbean.telnet; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; - -public class Exit extends Command { - public static void register() { - Command.register("exit", Exit.class); - } - - public void exec(String[] args, InputStream in, PrintStream out) throws IOException { - throw new UnsupportedOperationException(); - } -} diff --git a/telnet/src/java/org/xbean/telnet/Help.java b/telnet/src/java/org/xbean/telnet/Help.java deleted file mode 100755 index a4408342..00000000 --- a/telnet/src/java/org/xbean/telnet/Help.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * - * Copyright 2005 David Blevins - * - * 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.xbean.telnet; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Set; - -public class Help extends Command { - public static void register() { - Command.register("help", Help.class); - } - - public void exec(String[] args, InputStream in, PrintStream out) throws IOException { - HashMap hash = Command.commands; - Set set = hash.keySet(); - Iterator cmds = set.iterator(); - while (cmds.hasNext()) { - out.print(" " + cmds.next()); - out.println(""); - } - } -} diff --git a/telnet/src/test/org/xbean/telnet/TelnetShellTest.java b/telnet/src/test/org/xbean/telnet/TelnetShellTest.java deleted file mode 100644 index bf48962b..00000000 --- a/telnet/src/test/org/xbean/telnet/TelnetShellTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.xbean.telnet; - -import junit.framework.*; -import org.xbean.telnet.TelnetShell; - -import java.io.InputStream; -import java.io.PrintStream; -import java.io.IOException; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; - -public class TelnetShellTest extends TestCase { - - public void testService() throws Exception { - TestCommand.register(); - - TelnetShell telnetShell = new TelnetShell("acme"); - ByteArrayInputStream in = new ByteArrayInputStream("test hello world\r\n".getBytes()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - telnetShell.service(in, out); - - // TODO test case still needs work - System.out.println(new String(out.toByteArray())); - } - - public static class TestCommand extends Command { - public static void register() { - Command.register("test", TestCommand.class); - } - public void exec(String[] args, InputStream in, PrintStream out) throws IOException { - out.print(args[0].length()); - out.print(args[1].length()); - } - } -} \ No newline at end of file diff --git a/xbean-asm-shaded/pom.xml b/xbean-asm-shaded/pom.xml new file mode 100755 index 00000000..abaade3e --- /dev/null +++ b/xbean-asm-shaded/pom.xml @@ -0,0 +1,112 @@ + + + + + + + 4.0.0 + + xbean + org.apache.xbean + 3.12 + + xbean-asm-shaded + bundle + Apache XBean :: ASM shaded (repackaged) + + Repackaged and shaded asm jars + + + http://asm.ow2.org/license.html + + + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + org.apache.xbean.asm;version=3.1,org.apache.xbean.asm.signature;version=3.1,org.apache.xbean.asm.commons;version=3.1,org.apache.xbean.asm.tree;version=3.1 + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + org.objectweb.asm + org.apache.xbean.asm + + + + + + http://asm.ow2.org/license.html + org.apache.xbean.asm-shaded + ${xbean.osgi.export} + ${xbean.osgi.export} + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + + asm + asm + 3.2 + + + asm + asm-commons + 3.2 + + + diff --git a/xbean-asm-shaded/src/main/appended-resources/META-INF/LICENSE.vm b/xbean-asm-shaded/src/main/appended-resources/META-INF/LICENSE.vm new file mode 100644 index 00000000..2adb4128 --- /dev/null +++ b/xbean-asm-shaded/src/main/appended-resources/META-INF/LICENSE.vm @@ -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. +## +## $Rev$ $Date$ +## + +Copyright (c) 2000-2005 INRIA, France Telecom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + + + diff --git a/xbean-blueprint/pom.xml b/xbean-blueprint/pom.xml new file mode 100644 index 00000000..0eefeccd --- /dev/null +++ b/xbean-blueprint/pom.xml @@ -0,0 +1,192 @@ + + + + + + + 4.0.0 + + + xbean + org.apache.xbean + 3.12 + + + xbean-blueprint + bundle + Apache XBean :: OSGI Blueprint Namespace Handler + + xbean-blueprint provides a schema-driven namespace handler for Apache Aries Blueprint + + + 1.5.0 + + + + + + org.apache.aries.blueprint + org.apache.aries.blueprint.api + 1.0.0 + provided + + + org.apache.aries.blueprint + org.apache.aries.blueprint.cm + 1.0.0 + provided + + + + org.apache.commons + commons-jexl + 2.0 + + + commons-logging + commons-logging + + + + + + org.ops4j.pax.logging + pax-logging-api + ${pax.logging.version} + + + + org.osgi + org.osgi.core + 4.2.0 + provided + + + + org.osgi + org.osgi.compendium + 4.2.0 + provided + + + + org.ops4j.pax.logging + pax-logging-service + ${pax.logging.version} + + + log4j + log4j + + + provided + + + + com.thoughtworks.qdox + qdox + true + + + + ant + ant + true + + + + org.apache.geronimo.specs + geronimo-annotation_1.0_spec + 1.1 + provided + + + + + + + + src/test/resources + + + target/test-generated + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/BeerUsingXBeanNSTest.java + + + + + maven-antrun-plugin + + + process-classes + + run + + + + + + + + + + + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + com.thoughtworks.qdox*;resolution:=optional, + org.apache.tools.ant*;resolution:=optional, + javax.script*;resolution:=optional, + javax.script*;resolution:=optional, + org.apache.commons.jexl2*;resolution:=optional, + org.osgi.service.cm;version="[1.0,2.0)", + * + + + org.apache.aries.blueprint.compendium.cm + + + + + + + + diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/CmNamespaceHandler.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/CmNamespaceHandler.java new file mode 100644 index 00000000..67745c67 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/CmNamespaceHandler.java @@ -0,0 +1,533 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.blueprint.cm; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.NamespaceHandler; +import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.ext.impl.ExtNamespaceHandler; +import org.apache.aries.blueprint.mutable.MutableBeanMetadata; +import org.apache.aries.blueprint.mutable.MutableCollectionMetadata; +import org.apache.aries.blueprint.mutable.MutableComponentMetadata; +import org.apache.aries.blueprint.mutable.MutableIdRefMetadata; +import org.apache.aries.blueprint.mutable.MutableMapMetadata; +import org.apache.aries.blueprint.mutable.MutableRefMetadata; +import org.apache.aries.blueprint.mutable.MutableReferenceMetadata; +import org.apache.aries.blueprint.mutable.MutableValueMetadata; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.IdRefMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.RefMetadata; +import org.osgi.service.blueprint.reflect.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.osgi.service.cm.ConfigurationAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.CharacterData; +import org.w3c.dom.Comment; +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Modified from aries CmNamespaceHandler + * + * @version $Rev$ $Date$ + */ +public class CmNamespaceHandler implements NamespaceHandler { + + public static final String BLUEPRINT_NAMESPACE = "http://www.osgi.org/xmlns/blueprint/v1.0.0"; + public static final String XBEAN_CM_NAMESPACE = "http://xbean.apache.org/blueprint/xmlns/xbean-cm/v1.0.0"; + + public static final String PROPERTY_PLACEHOLDER_ELEMENT = "property-placeholder"; +// public static final String MANAGED_PROPERTIES_ELEMENT = "managed-properties"; +// public static final String MANAGED_SERVICE_FACTORY_ELEMENT = "managed-service-factory"; + public static final String CM_PROPERTIES_ELEMENT = "cm-properties"; + public static final String DEFAULT_PROPERTIES_ELEMENT = "default-properties"; + public static final String PROPERTY_ELEMENT = "property"; + public static final String INTERFACES_ELEMENT = "interfaces"; + public static final String VALUE_ELEMENT = "value"; + public static final String MANAGED_COMPONENT_ELEMENT = "managed-component"; + + public static final String ID_ATTRIBUTE = "id"; + public static final String PERSISTENT_ID_ATTRIBUTE = "persistent-id"; + public static final String PLACEHOLDER_PREFIX_ATTRIBUTE = "placeholder-prefix"; + public static final String PLACEHOLDER_SUFFIX_ATTRIBUTE = "placeholder-suffix"; + public static final String DEFAULTS_REF_ATTRIBUTE = "defaults-ref"; + public static final String UPDATE_STRATEGY_ATTRIBUTE = "update-strategy"; + public static final String UPDATE_METHOD_ATTRIBUTE = "update-method"; + public static final String FACTORY_PID_ATTRIBUTE = "factory-pid"; + public static final String AUTO_EXPORT_ATTRIBUTE = "auto-export"; + public static final String RANKING_ATTRIBUTE = "ranking"; + public static final String INTERFACE_ATTRIBUTE = "interface"; + public static final String UPDATE_ATTRIBUTE = "update"; + + public static final String AUTO_EXPORT_DISABLED = "disabled"; + public static final String AUTO_EXPORT_INTERFACES = "interfaces"; + public static final String AUTO_EXPORT_CLASS_HIERARCHY = "class-hierarchy"; + public static final String AUTO_EXPORT_ALL = "all-classes"; + public static final String AUTO_EXPORT_DEFAULT = AUTO_EXPORT_DISABLED; + public static final String RANKING_DEFAULT = "0"; + + private static final String MANAGED_OBJECT_MANAGER_NAME = "org.apache.aries.managedObjectManager"; + + private static final Logger LOGGER = LoggerFactory.getLogger(CmNamespaceHandler.class); + +// private final ConfigurationAdmin configAdmin; + + private int idCounter; + +// public CmNamespaceHandler(ConfigurationAdmin configAdmin) { +// this.configAdmin = configAdmin; +// } + + public URL getSchemaLocation(String namespace) { + return getClass().getResource("xbean-cm.xsd"); + } + + public Set getManagedClasses() { + return new HashSet(Arrays.asList( + JexlPropertyPlaceholder.class + )); + } + + public Metadata parse(Element element, ParserContext context) { + LOGGER.debug("Parsing element {{}}{}", element.getNamespaceURI(), element.getLocalName()); + ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry(); +// registerManagedObjectManager(context, registry); + if (nodeNameEquals(element, PROPERTY_PLACEHOLDER_ELEMENT)) { + return parsePropertyPlaceholder(context, element); +// } else if (nodeNameEquals(element, MANAGED_SERVICE_FACTORY_ELEMENT)) { +// return parseManagedServiceFactory(context, element); + } else { + throw new ComponentDefinitionException("Unsupported element: " + element.getNodeName()); + } + } + + public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) { + LOGGER.debug("Decorating node {{}}{}", node.getNamespaceURI(), node.getLocalName()); + ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry(); +// registerManagedObjectManager(context, registry); + if (node instanceof Element) { +// if (nodeNameEquals(node, MANAGED_PROPERTIES_ELEMENT)) { +// return decorateManagedProperties(context, (Element) node, component); +// } else +// if (nodeNameEquals(node, CM_PROPERTIES_ELEMENT)) { +// return decorateCmProperties(context, (Element) node, component); +// } else { + throw new ComponentDefinitionException("Unsupported element: " + node.getNodeName()); +// } + } else { + throw new ComponentDefinitionException("Illegal use of blueprint cm namespace"); + } + } + + private ComponentMetadata parsePropertyPlaceholder(ParserContext context, Element element) { + MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class); + metadata.setProcessor(true); + metadata.setId(getId(context, element)); + metadata.setScope(BeanMetadata.SCOPE_SINGLETON); + metadata.setRuntimeClass(JexlPropertyPlaceholder.class); + metadata.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); +// metadata.addProperty("configAdmin", createConfigAdminProxy(context)); + metadata.addProperty("configAdmin", createReference(context, ConfigurationAdmin.class.getName())); + metadata.addProperty("persistentId", createValue(context, element.getAttribute(PERSISTENT_ID_ATTRIBUTE))); +// metadata.addArgument(createRef(context, "blueprintContainer"), BlueprintContainer.class.getName(), 0); +// metadata.addArgument(createReference(context, ConfigurationAdmin.class.getName()), ConfigurationAdmin.class.getName(), 1); +// metadata.addArgument(createValue(context, element.getAttribute(PERSISTENT_ID_ATTRIBUTE)), String.class.getName(), 2); + String prefix = element.hasAttribute(PLACEHOLDER_PREFIX_ATTRIBUTE) + ? element.getAttribute(PLACEHOLDER_PREFIX_ATTRIBUTE) + : "${"; + metadata.addProperty("placeholderPrefix", createValue(context, prefix)); + String suffix = element.hasAttribute(PLACEHOLDER_SUFFIX_ATTRIBUTE) + ? element.getAttribute(PLACEHOLDER_SUFFIX_ATTRIBUTE) + : "}"; + metadata.addProperty("placeholderSuffix", createValue(context, suffix)); + String defaultsRef = element.hasAttribute(DEFAULTS_REF_ATTRIBUTE) ? element.getAttribute(DEFAULTS_REF_ATTRIBUTE) : null; + if (defaultsRef != null) { + metadata.addProperty("defaultProperties", createRef(context, defaultsRef)); + } + String ignoreMissingLocations = element.hasAttributeNS(ExtNamespaceHandler.BLUEPRINT_EXT_NAMESPACE_V1_0, ExtNamespaceHandler.IGNORE_MISSING_LOCATIONS_ATTRIBUTE) + ? element.getAttributeNS(ExtNamespaceHandler.BLUEPRINT_EXT_NAMESPACE_V1_0, ExtNamespaceHandler.IGNORE_MISSING_LOCATIONS_ATTRIBUTE) : null; + if (ignoreMissingLocations != null) { + metadata.addProperty("ignoreMissingLocations", createValue(context, ignoreMissingLocations)); + } + String systemProperties = element.hasAttributeNS(ExtNamespaceHandler.BLUEPRINT_EXT_NAMESPACE_V1_0, ExtNamespaceHandler.SYSTEM_PROPERTIES_ATTRIBUTE) + ? element.getAttributeNS(ExtNamespaceHandler.BLUEPRINT_EXT_NAMESPACE_V1_0, ExtNamespaceHandler.SYSTEM_PROPERTIES_ATTRIBUTE) : null; + if (systemProperties == null) { + systemProperties = ExtNamespaceHandler.SYSTEM_PROPERTIES_NEVER; + } + metadata.addProperty("systemProperties", createValue(context, systemProperties)); + // Parse elements + List locations = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (XBEAN_CM_NAMESPACE.equals(e.getNamespaceURI())) { + if (nodeNameEquals(e, DEFAULT_PROPERTIES_ELEMENT)) { + if (defaultsRef != null) { + throw new ComponentDefinitionException("Only one of " + DEFAULTS_REF_ATTRIBUTE + " attribute or " + DEFAULT_PROPERTIES_ELEMENT + " element is allowed"); + } + Metadata props = parseDefaultProperties(context, metadata, e); + metadata.addProperty("defaultProperties", props); + } + } else if (ExtNamespaceHandler.BLUEPRINT_EXT_NAMESPACE_V1_0.equals(e.getNamespaceURI())) { + if (nodeNameEquals(e, ExtNamespaceHandler.LOCATION_ELEMENT)) { + locations.add(getTextValue(e)); + } + } + } + } + if (!locations.isEmpty()) { + metadata.addProperty("locations", createList(context, locations)); + } + +// PlaceholdersUtils.validatePlaceholder(metadata, context.getComponentDefinitionRegistry()); + + return metadata; + } + + private Metadata parseDefaultProperties(ParserContext context, MutableBeanMetadata enclosingComponent, Element element) { + MutableMapMetadata props = context.createMetadata(MutableMapMetadata.class); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (XBEAN_CM_NAMESPACE.equals(e.getNamespaceURI())) { + if (nodeNameEquals(e, PROPERTY_ELEMENT)) { + BeanProperty prop = context.parseElement(BeanProperty.class, enclosingComponent, e); + props.addEntry(createValue(context, prop.getName(), String.class.getName()), prop.getValue()); + } + } + } + } + return props; + } + +// private ComponentMetadata parseManagedServiceFactory(ParserContext context, Element element) { +// String id = getId(context, element); +// +// MutableBeanMetadata factoryMetadata = context.createMetadata(MutableBeanMetadata.class); +// generateIdIfNeeded(context, factoryMetadata); +// factoryMetadata.addProperty("id", createValue(context, factoryMetadata.getId())); +// factoryMetadata.setScope(BeanMetadata.SCOPE_SINGLETON); +// factoryMetadata.setRuntimeClass(CmManagedServiceFactory.class); +// factoryMetadata.setInitMethod("init"); +// factoryMetadata.setDestroyMethod("destroy"); +// factoryMetadata.addProperty("configAdmin", createConfigAdminProxy(context)); +// factoryMetadata.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); +// factoryMetadata.addProperty("factoryPid", createValue(context, element.getAttribute(FACTORY_PID_ATTRIBUTE))); +// String autoExport = element.hasAttribute(AUTO_EXPORT_ATTRIBUTE) ? element.getAttribute(AUTO_EXPORT_ATTRIBUTE) : AUTO_EXPORT_DEFAULT; +// if (AUTO_EXPORT_DISABLED.equals(autoExport)) { +// autoExport = Integer.toString(ServiceMetadata.AUTO_EXPORT_DISABLED); +// } else if (AUTO_EXPORT_INTERFACES.equals(autoExport)) { +// autoExport = Integer.toString(ServiceMetadata.AUTO_EXPORT_INTERFACES); +// } else if (AUTO_EXPORT_CLASS_HIERARCHY.equals(autoExport)) { +// autoExport = Integer.toString(ServiceMetadata.AUTO_EXPORT_CLASS_HIERARCHY); +// } else if (AUTO_EXPORT_ALL.equals(autoExport)) { +// autoExport = Integer.toString(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES); +// } else { +// throw new ComponentDefinitionException("Illegal value (" + autoExport + ") for " + AUTO_EXPORT_ATTRIBUTE + " attribute"); +// } +// factoryMetadata.addProperty("autoExport", createValue(context, autoExport)); +// String ranking = element.hasAttribute(RANKING_ATTRIBUTE) ? element.getAttribute(RANKING_ATTRIBUTE) : RANKING_DEFAULT; +// factoryMetadata.addProperty("ranking", createValue(context, ranking)); +// +// List interfaces = null; +// if (element.hasAttribute(INTERFACE_ATTRIBUTE)) { +// interfaces = Collections.singletonList(element.getAttribute(INTERFACE_ATTRIBUTE)); +// factoryMetadata.addProperty("interfaces", createList(context, interfaces)); +// } +// +// Parser parser = getParser(context); +// +// // Parse elements +// List listeners = new ArrayList(); +// NodeList nl = element.getChildNodes(); +// for (int i = 0; i < nl.getLength(); i++) { +// Node node = nl.item(i); +// if (node instanceof Element) { +// Element e = (Element) node; +// if (isBlueprintNamespace(e.getNamespaceURI())) { +// if (nodeNameEquals(e, INTERFACES_ELEMENT)) { +// if (interfaces != null) { +// throw new ComponentDefinitionException("Only one of " + Parser.INTERFACE_ATTRIBUTE + " attribute or " + INTERFACES_ELEMENT + " element must be used"); +// } +// interfaces = parseInterfaceNames(e); +// factoryMetadata.addProperty("interfaces", createList(context, interfaces)); +// } else if (nodeNameEquals(e, Parser.SERVICE_PROPERTIES_ELEMENT)) { +// MapMetadata map = parser.parseServiceProperties(e, factoryMetadata); +// factoryMetadata.addProperty("serviceProperties", map); +// } else if (nodeNameEquals(e, Parser.REGISTRATION_LISTENER_ELEMENT)) { +// listeners.add(parser.parseRegistrationListener(e, factoryMetadata)); +// } +// } else if (XBEAN_CM_NAMESPACE.equals(e.getNamespaceURI())) { +// if (nodeNameEquals(e, MANAGED_COMPONENT_ELEMENT)) { +// MutableBeanMetadata managedComponent = context.parseElement(MutableBeanMetadata.class, null, e); +// generateIdIfNeeded(context, managedComponent); +// managedComponent.setScope(BeanMetadata.SCOPE_PROTOTYPE); +// // destroy-method on managed-component has different signature than on regular beans +// // so we'll handle it differently +// String destroyMethod = managedComponent.getDestroyMethod(); +// if (destroyMethod != null) { +// factoryMetadata.addProperty("componentDestroyMethod", createValue(context, destroyMethod)); +// managedComponent.setDestroyMethod(null); +// } +// context.getComponentDefinitionRegistry().registerComponentDefinition(managedComponent); +// factoryMetadata.addProperty("managedComponentName", createIdRef(context, managedComponent.getId())); +// } +// } +// } +// } +// +// MutableCollectionMetadata listenerCollection = context.createMetadata(MutableCollectionMetadata.class); +// listenerCollection.setCollectionClass(List.class); +// for (RegistrationListener listener : listeners) { +// MutableBeanMetadata bean = context.createMetadata(MutableBeanMetadata.class); +// bean.setRuntimeClass(ServiceListener.class); +// bean.addProperty("listener", listener.getListenerComponent()); +// bean.addProperty("registerMethod", createValue(context, listener.getRegistrationMethod())); +// bean.addProperty("unregisterMethod", createValue(context, listener.getUnregistrationMethod())); +// listenerCollection.addValue(bean); +// } +// factoryMetadata.addProperty("listeners", listenerCollection); +// +// context.getComponentDefinitionRegistry().registerComponentDefinition(factoryMetadata); +// +// MutableBeanMetadata mapMetadata = context.createMetadata(MutableBeanMetadata.class); +// mapMetadata.setScope(BeanMetadata.SCOPE_SINGLETON); +// mapMetadata.setId(id); +// mapMetadata.setFactoryComponent(createRef(context, factoryMetadata.getId())); +// mapMetadata.setFactoryMethod("getServiceMap"); +// return mapMetadata; +// } + +// private ComponentMetadata decorateCmProperties(ParserContext context, Element element, ComponentMetadata component) { +// generateIdIfNeeded(context, ((MutableComponentMetadata) component)); +// MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class); +// metadata.setProcessor(true); +// metadata.setId(getId(context, element)); +// metadata.setRuntimeClass(CmProperties.class); +// String persistentId = element.getAttribute(PERSISTENT_ID_ATTRIBUTE); +// // if persistentId is "" the cm-properties element in nested in managed-service-factory +// // and the configuration object will come from the factory. So we only really need to register +// // ManagedService if the persistentId is not an empty string. +// if (persistentId.length() > 0) { +// metadata.setInitMethod("init"); +// metadata.setDestroyMethod("destroy"); +// } +// metadata.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); +// metadata.addProperty("configAdmin", createConfigAdminProxy(context)); +// metadata.addProperty("managedObjectManager", createRef(context, MANAGED_OBJECT_MANAGER_NAME)); +// metadata.addProperty("persistentId", createValue(context, persistentId)); +// if (element.hasAttribute(UPDATE_ATTRIBUTE)) { +// metadata.addProperty("update", createValue(context, element.getAttribute(UPDATE_ATTRIBUTE))); +// } +// metadata.addProperty("serviceId", createIdRef(context, component.getId())); +// context.getComponentDefinitionRegistry().registerComponentDefinition(metadata); +// return component; +// } + +// private ComponentMetadata decorateManagedProperties(ParserContext context, Element element, ComponentMetadata component) { +// if (!(component instanceof MutableBeanMetadata)) { +// throw new ComponentDefinitionException("Element " + MANAGED_PROPERTIES_ELEMENT + " must be used inside a element"); +// } +// generateIdIfNeeded(context, ((MutableBeanMetadata) component)); +// MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class); +// metadata.setProcessor(true); +// metadata.setId(getId(context, element)); +// metadata.setRuntimeClass(CmManagedProperties.class); +// String persistentId = element.getAttribute(PERSISTENT_ID_ATTRIBUTE); +// // if persistentId is "" the managed properties element in nested in managed-service-factory +// // and the configuration object will come from the factory. So we only really need to register +// // ManagedService if the persistentId is not an empty string. +// if (persistentId.length() > 0) { +// metadata.setInitMethod("init"); +// metadata.setDestroyMethod("destroy"); +// } +// metadata.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); +// metadata.addProperty("configAdmin", createConfigAdminProxy(context)); +// metadata.addProperty("managedObjectManager", createRef(context, MANAGED_OBJECT_MANAGER_NAME)); +// metadata.addProperty("persistentId", createValue(context, persistentId)); +// String updateStrategy = element.getAttribute(UPDATE_STRATEGY_ATTRIBUTE); +// if (updateStrategy != null) { +// metadata.addProperty("updateStrategy", createValue(context, updateStrategy)); +// } +// if (element.hasAttribute(UPDATE_METHOD_ATTRIBUTE)) { +// metadata.addProperty("updateMethod", createValue(context, element.getAttribute(UPDATE_METHOD_ATTRIBUTE))); +// } else if ("component-managed".equals(updateStrategy)) { +// throw new ComponentDefinitionException(UPDATE_METHOD_ATTRIBUTE + " attribute must be set when " + UPDATE_STRATEGY_ATTRIBUTE + " is set to 'component-managed'"); +// } +// metadata.addProperty("beanName", createIdRef(context, component.getId())); +// context.getComponentDefinitionRegistry().registerComponentDefinition(metadata); +// return component; +// } + + /** + * Create a reference to the ConfigurationAdmin service if not already done + * and add it to the registry. + * + * @param context the parser context + * @return a metadata pointing to the config admin + */ + private Metadata createConfigAdminProxy(ParserContext context) { + MutableBeanMetadata bean = context.createMetadata(MutableBeanMetadata.class); + bean.setRuntimeClass(CmNamespaceHandler.class); + bean.setFactoryMethod("getConfigAdmin"); + bean.setActivation(MutableBeanMetadata.ACTIVATION_LAZY); + bean.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE); + return bean; + } + +// private void registerManagedObjectManager(ParserContext context, ComponentDefinitionRegistry registry) { +// if (registry.getComponentDefinition(MANAGED_OBJECT_MANAGER_NAME) == null) { +// MutableBeanMetadata beanMetadata = context.createMetadata(MutableBeanMetadata.class); +// beanMetadata.setScope(BeanMetadata.SCOPE_SINGLETON); +// beanMetadata.setId(MANAGED_OBJECT_MANAGER_NAME); +// beanMetadata.setRuntimeClass(ManagedObjectManager.class); +// registry.registerComponentDefinition(beanMetadata); +// } +// } + + private static ValueMetadata createValue(ParserContext context, String value) { + return createValue(context, value, null); + } + + private static ValueMetadata createValue(ParserContext context, String value, String type) { + MutableValueMetadata m = context.createMetadata(MutableValueMetadata.class); + m.setStringValue(value); + m.setType(type); + return m; + } + + private static RefMetadata createRef(ParserContext context, String value) { + MutableRefMetadata m = context.createMetadata(MutableRefMetadata.class); + m.setComponentId(value); + return m; + } + private static ReferenceMetadata createReference(ParserContext context, String interfaceName) { + MutableReferenceMetadata m = context.createMetadata(MutableReferenceMetadata.class); + m.setInterface(interfaceName); + return m; + } + + private static IdRefMetadata createIdRef(ParserContext context, String value) { + MutableIdRefMetadata m = context.createMetadata(MutableIdRefMetadata.class); + m.setComponentId(value); + return m; + } + + private static CollectionMetadata createList(ParserContext context, List list) { + MutableCollectionMetadata m = context.createMetadata(MutableCollectionMetadata.class); + m.setCollectionClass(List.class); + m.setValueType(String.class.getName()); + for (String v : list) { + m.addValue(createValue(context, v, String.class.getName())); + } + return m; + } + + private static String getTextValue(Element element) { + StringBuffer value = new StringBuffer(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node item = nl.item(i); + if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) { + value.append(item.getNodeValue()); + } + } + return value.toString(); + } + + private static boolean nodeNameEquals(Node node, String name) { + return (name.equals(node.getNodeName()) || name.equals(node.getLocalName())); + } + + public static boolean isBlueprintNamespace(String ns) { + return BLUEPRINT_NAMESPACE.equals(ns); + } + + public String getId(ParserContext context, Element element) { + if (element.hasAttribute(ID_ATTRIBUTE)) { + return element.getAttribute(ID_ATTRIBUTE); + } else { + return generateId(context); + } + } + + public void generateIdIfNeeded(ParserContext context, MutableComponentMetadata metadata) { + if (metadata.getId() == null) { + metadata.setId(generateId(context)); + } + } + + private String generateId(ParserContext context) { + String id; + do { + id = ".cm-" + ++idCounter; + } while (context.getComponentDefinitionRegistry().containsComponentDefinition(id)); + return id; + } + +// private Parser getParser(ParserContext ctx) { +// if (ctx instanceof ParserContextImpl) { +// return ((ParserContextImpl) ctx).getParser(); +// } +// throw new RuntimeException("Unable to get parser"); +// } + + public List parseInterfaceNames(Element element) { + List interfaceNames = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (nodeNameEquals(e, VALUE_ELEMENT)) { + String v = getTextValue(e).trim(); + if (interfaceNames.contains(v)) { + throw new ComponentDefinitionException("The element " + INTERFACES_ELEMENT + " should not contain the same interface twice"); + } + interfaceNames.add(getTextValue(e)); + } else { + throw new ComponentDefinitionException("Unsupported element " + e.getNodeName() + " inside an " + INTERFACES_ELEMENT + " element"); + } + } + } + return interfaceNames; + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlExpressionParser.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlExpressionParser.java new file mode 100644 index 00000000..76f7e53b --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlExpressionParser.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.blueprint.cm; + +import java.util.Map; + +import org.apache.commons.jexl2.JexlContext; +import org.apache.commons.jexl2.JexlEngine; +import org.apache.commons.jexl2.MapContext; +import org.apache.commons.jexl2.UnifiedJEXL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @version $Rev$ $Date$ + */ +public class JexlExpressionParser { + private static final Logger log = LoggerFactory.getLogger(JexlExpressionParser.class); + + private final JexlEngine engine; + private final UnifiedJEXL jexl; + protected final JexlContext context; + + public JexlExpressionParser(final Map vars) { + if (vars == null) { + throw new IllegalArgumentException("vars"); + } + engine = new JexlEngine(); + jexl = new UnifiedJEXL(engine); + context = new MapContext(vars); + + log.trace("Using variables: {}", vars); + } + +// private FlatResolver resolver = new FlatResolver(true); +// +// protected Expression createExpression(final String expression) throws Exception { +// // assert expression != null; +// +// Expression expr = engine.createExpression(expression); +// expr.addPreResolver(resolver); +// +// return expr; +// } + + public Object evaluate(final String expression) throws Exception { + if (expression == null) { + throw new IllegalArgumentException("expression"); + } + + log.trace("Evaluating expression: {}", expression); + return jexl.parse(expression).evaluate(context); +// Expression expr = createExpression(expression); +// Object obj = expr.evaluate(context); +// log.trace("Result: {}", obj); +// +// return obj; + } + +// public String parse(final String input) { +// if (input == null) { +// throw new IllegalArgumentException("input"); +// } +// +// log.trace("Parsing input: {}", input); +// +// StringBuilder buff = new StringBuilder(); +// +// int cur = 0; +// int prefixLoc; +// int suffixLoc; +// +// while (cur < input.length()) { +// prefixLoc = input.indexOf("${", cur); +// +// if (prefixLoc < 0) { +// break; +// } +// +// suffixLoc = findBlockEnd(prefixLoc + 2, input); +// if (suffixLoc < 0) { +// throw new RuntimeException("Missing '}': " + input); +// } +// +// String expr = input.substring(prefixLoc + 2, suffixLoc); +// buff.append(input.substring(cur, prefixLoc)); +// +// try { +// buff.append(evaluate(expr)); +// } +// catch (Exception e) { +// throw new RuntimeException("Failed to evaluate: " + expr, e); +// } +// +// cur = suffixLoc + 1; +// } +// +// buff.append(input.substring(cur)); +// +// log.trace("Parsed result: {}", buff); +// +// return buff.toString(); +// } + +// private int findBlockEnd(int pos, String input) { +// int nested = 0; +// while (pos < input.length()) { +// char ch = input.charAt(pos); +// if (ch == '{') { +// nested++; +// } else if (ch == '}') { +// if (nested == 0) { +// return pos; +// } else { +// nested--; +// } +// } +// pos++; +// } +// return -1; +// } + +// public String parse(final String input, final boolean trim) { +// String output = parse(input); +// if (trim && output != null) { +// output = output.trim(); +// } +// +// return output; +// } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlPropertyPlaceholder.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlPropertyPlaceholder.java new file mode 100644 index 00000000..19a41101 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/cm/JexlPropertyPlaceholder.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.blueprint.cm; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.apache.aries.blueprint.compendium.cm.CmPropertyPlaceholder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @version $Rev$ $Date$ + */ +public class JexlPropertyPlaceholder extends CmPropertyPlaceholder { + + private static final Logger LOGGER = LoggerFactory.getLogger(JexlPropertyPlaceholder.class); + + private transient JexlExpressionParser parser; + + @Override + protected String processString(String str) { + LOGGER.debug("Processing {} from configuration with pid {}", str, getPersistentId()); + JexlExpressionParser parser = getParser(); + try { + return parser.evaluate(str).toString(); + } catch (Exception e) { + LOGGER.info("Could not evaluate expressions {} for {}", str, getPersistentId()); + LOGGER.info("Exception:", e); + } + return str; + } + + protected synchronized JexlExpressionParser getParser() { + if (parser == null) { +// try { + parser = new JexlExpressionParser(toMap()); +// } catch (IOException e) { + // ignore +// } + } + return parser; + } + + private Map toMap() { + return new ConfigMap(); +// Map map = new HashMap(); +// if (config != null) { +// Dictionary properties = config.getProperties(); +// for (Enumeration e = properties.keys(); e.hasMoreElements(); ) { +// String key = e.nextElement(); +// Object value = properties.get(key); +// map.put(key, value); +// } +// } +// return map; + } + + private class ConfigMap implements Map { + + public int size() { + return 0; + } + + public boolean isEmpty() { + return false; + } + + public boolean containsKey(Object o) { + return getProperty((String) o) != null; + } + + public boolean containsValue(Object o) { + return false; + } + + public Object get(Object o) { + return getProperty((String) o); + } + + public Object put(String s, Object o) { + return null; + } + + public Object remove(Object o) { + return null; + } + + public void putAll(Map map) { + } + + public void clear() { + } + + public Set keySet() { + return null; + } + + public Collection values() { + return null; + } + + public Set> entrySet() { + return null; + } + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DateEditor.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DateEditor.java new file mode 100644 index 00000000..2f37c280 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DateEditor.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.xbean.blueprint.context.impl; + +import java.beans.PropertyEditorSupport; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; + +/** + * Editor for java.util.Date + */ +public class DateEditor extends PropertyEditorSupport { + + private DateFormat dateFormat = DateFormat.getInstance(); + + public void setAsText(String text) throws IllegalArgumentException { + try { + setValue(dateFormat.parse(text)); + } + catch (ParseException e) { + throw new IllegalArgumentException( + "Could not convert Date for " + text + ": " + e.getMessage()); + } + } + + public String getAsText() { + Date value = (Date) getValue(); + return (value != null ? dateFormat.format(value) : ""); + } + +} diff --git a/server/src/java/org/xbean/server/spring/configuration/DefaultProperty.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DefaultProperty.java similarity index 63% rename from server/src/java/org/xbean/server/spring/configuration/DefaultProperty.java rename to xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DefaultProperty.java index eb8807b1..90541dc2 100644 --- a/server/src/java/org/xbean/server/spring/configuration/DefaultProperty.java +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/DefaultProperty.java @@ -1,20 +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 * - * Copyright 2005 the original author or authors. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.xbean.server.spring.configuration; +package org.apache.xbean.blueprint.context.impl; /** * DefaultProperty contains the default value assigned to a property with a specific name and type. @@ -25,7 +25,7 @@ public class DefaultProperty { private String name; private Class type; - private Object value; + private String value; /** * Creates a new empty default property. This instance is unusable until the name, type and values are assigned. @@ -39,7 +39,7 @@ public DefaultProperty() { * @param type the type of the property * @param value the default value */ - public DefaultProperty(String name, Class type, Object value) { + public DefaultProperty(String name, Class type, String value) { this.name = name; this.type = type; this.value = value; @@ -81,7 +81,7 @@ public void setType(Class type) { * Gets the default value. * @return the default value */ - public Object getValue() { + public String getValue() { return value; } @@ -89,7 +89,7 @@ public Object getValue() { * Sets the default value. * @param value the default value */ - public void setValue(Object value) { + public void setValue(String value) { this.value = value; } diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/FileEditor.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/FileEditor.java new file mode 100644 index 00000000..4e96b048 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/FileEditor.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.xbean.blueprint.context.impl; + +import java.beans.PropertyEditorSupport; +import java.io.File; + +/** + * Editor for java.io.File + */ +public class FileEditor extends PropertyEditorSupport { + + public void setAsText(String text) throws IllegalArgumentException { + setValue(new File(text)); + } + + public String getAsText() { + File value = (File) getValue(); + return (value != null ? value.toString() : ""); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/MappingMetaData.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/MappingMetaData.java new file mode 100644 index 00000000..22e2b5d6 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/MappingMetaData.java @@ -0,0 +1,219 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context.impl; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * A helper class which understands how to map an XML namespaced element to + * Spring bean configurations + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class MappingMetaData { + private Properties properties; + private String packageName; + + /** + * Creates an empty MappingMetaData for the specified Java package. + * @param packageName the Java package to map + */ + public MappingMetaData(String packageName) { + this.packageName = packageName; + this.properties = new Properties(); + } + + /** + * Creates MappingMetaData using the specified properties which contan the package name. + * @param properties + */ + public MappingMetaData(Properties properties) { + this.properties = properties; + } + + /** + * Returns the Java class name for the given XML element name + */ + public String getClassName(String localName) { + String className = properties.getProperty(localName); + if (className == null && packageName != null) { + if (packageName.length() > 0) { + className = packageName + "." + localName; + } + else { + className = localName; + } + } + return className; + } + + /** + * Returns the property name for the given element and attribute name + * + * @param elementName the XML local name of the element + * @param attributeName the XML local name of the attribute + * @return the property name to use or null if the attribute is not a valid property + */ + public String getPropertyName(String elementName, String attributeName) { + return properties.getProperty(elementName + ".alias." + attributeName, attributeName); + } + + /** + * Returns a valid property name if the childElementName maps to a nested list property + * + * @param elementName the owner element + * @param childElementName is the child element name which maps to the nested list property + * @return the property name if available or null if it is not applicable + */ + public String getNestedListProperty(String elementName, String childElementName) { + return properties.getProperty(elementName + ".list." + childElementName); + } + + /** + * Returns a valid property name if the childElementName maps to a nested bean property + * + * @param elementName the owner element + * @param childElementName is the child element name which maps to the nested bean property + * @return the property name if available or null if it is not applicable + */ + public String getNestedProperty(String elementName, String childElementName) { + return properties.getProperty(elementName + ".alias." + childElementName); + } + + public boolean isDefaultConstructor(Constructor constructor) { + String property = properties.getProperty(constructorToPropertyName(constructor) + ".default"); + if (property != null) { + return Boolean.valueOf(property).booleanValue(); + } + return false; + } + + public boolean isDefaultFactoryMethod(Class beanClass, Method factoryMethod) { + String property = properties.getProperty(methodToPropertyName(beanClass, factoryMethod) + ".default"); + if (property != null) { + return Boolean.valueOf(property).booleanValue(); + } + return false; + } + + public String[] getParameterNames(Constructor constructor) { + String property = properties.getProperty(constructorToPropertyName(constructor) + ".parameterNames"); + if (property != null) { + ArrayList names = Collections.list(new StringTokenizer(property, ", ")); + return (String[]) names.toArray(new String[0]); + } + return null; + } + + public String[] getParameterNames(Class beanClass, Method factoryMethod) { + String property = properties.getProperty(methodToPropertyName(beanClass, factoryMethod) + ".parameterNames"); + if (property != null) { + ArrayList names = Collections.list(new StringTokenizer(property, ", ")); + return (String[]) names.toArray(new String[0]); + } + return null; + } + + public static String constructorToPropertyName(Constructor constructor) { + StringBuffer buf = new StringBuffer(); + buf.append(constructor.getName()).append("("); + Class[] parameterTypes = constructor.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + Class parameterType = parameterTypes[i]; + buf.append(parameterType.getName()); + if (i < parameterTypes.length - 1) { + buf.append(","); + } + } + buf.append(")"); + return buf.toString(); + } + + public static String methodToPropertyName(Class beanClass, Method method) { + StringBuffer buf = new StringBuffer(); + buf.append(beanClass.getName()).append("."); + buf.append(method.getName()).append("("); + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + Class parameterType = parameterTypes[i]; + buf.append(parameterType.getName()); + if (i < parameterTypes.length - 1) { + buf.append(","); + } + } + buf.append(")"); + return buf.toString(); + } + + public String getInitMethodName(String elementName) { + return properties.getProperty(elementName + ".initMethod"); + } + + public String getDestroyMethodName(String elementName) { + return properties.getProperty(elementName + ".destroyMethod"); + } + + public String getFactoryMethodName(String elementName) { + return properties.getProperty(elementName + ".factoryMethod"); + } + + public String getContentProperty(String elementName) { + return properties.getProperty(elementName + ".contentProperty"); + } + + public String getMapEntryName(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".map.entryName"); + } + + public String getMapKeyName(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".map.keyName"); + } + + public boolean isFlatMap(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".map.flat") != null; + } + + public String getMapDupsMode(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".map.dups"); + } + + public String getMapDefaultKey(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".map.defaultKey"); + } + + public String getFlatCollectionProperty(String elementName, String property) + { + return properties.getProperty(elementName + "." + property + ".flatCollection"); + } + + public boolean isFlatProperty(String elementName, String property) { + return properties.getProperty(elementName + "." + property + ".flat") != null; + } + + public String getPropertyEditor(String elementName, String property) + { + return properties.getProperty(elementName + "." + property + ".propertyEditor"); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgs.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgs.java new file mode 100644 index 00000000..93474e06 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgs.java @@ -0,0 +1,349 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context.impl; + +import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.mutable.MutableBeanMetadata; +import org.apache.aries.blueprint.mutable.MutableValueMetadata; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.reflect.BeanProperty; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * NamedConstructorArgs is a BeanFactoryPostProcessor that converts property declarations into indexed constructor args + * based on the the constructor parameter names annotation. This process first selects a constructor and then fills in + * the constructor arguments from the properties defined in the bean definition. If a property is not defined in the + * bean definition, first the defaultValues map is checked for a value and if a value is not present a Java default + * value is provided for the constructor argument (e.g. numbers are assigned 0 and objects are assigned null). + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class NamedConstructorArgs { + private Map defaultValues = new HashMap(); + + /** + * Gets the default values that are assigned to constructor arguments without a defined value. + * + * @return the default values that are assigned to constructor arguments without a defined value + */ + public List getDefaultValues() { + List values = new LinkedList(); + for (Map.Entry entry : defaultValues.entrySet()) { + PropertyKey key = entry.getKey(); + String value = entry.getValue(); + values.add(new DefaultProperty(key.name, key.type, value)); + } + return values; + } + + /** + * Sets the default values that are assigned to constructor arguments without a defined value. + * + * @param defaultValues the values that are assigned to constructor arguments without a defined value + */ + public void setDefaultValues(List defaultValues) { + this.defaultValues.clear(); + for (DefaultProperty defaultValue : defaultValues) { + addDefaultValue(defaultValue); + } + } + + /** + * Adds a default value for a property with the specified name and type. + * + * @param name the name of the property + * @param type the type of the property + * @param value the default value for a property with the specified name and type + */ + public void addDefaultValue(String name, Class type, String value) { + defaultValues.put(new PropertyKey(name, type), value); + } + + /** + * Adds a defautl value for a property. + * + * @param defaultProperty the default property information + */ + private void addDefaultValue(DefaultProperty defaultProperty) { + defaultValues.put(new PropertyKey(defaultProperty.getName(), defaultProperty.getType()), defaultProperty.getValue()); + } + + public void processParameters(MutableBeanMetadata beanMetadata, MappingMetaData metadata, ParserContext parserContext) { + + // if this bean already has constructor arguments defined, don't mess with them + if (beanMetadata.getArguments().size() > 0) { + return; + } + + // try to get a list of constructor arg names to use + ConstructionInfo constructionInfo = selectConstructionMethod(beanMetadata, metadata); + if (constructionInfo == null) { + return; + } + + // remove each named property and add an indexed constructor arg + List beanProperties = beanMetadata.getProperties(); + LinkedHashMap propMap = new LinkedHashMap(); + for (BeanProperty beanProperty : beanProperties) { + propMap.put(beanProperty.getName(), beanProperty); + } + String[] parameterNames = constructionInfo.parameterNames; + Class[] parameterTypes = constructionInfo.parameterTypes; + for (int i = 0; i < parameterNames.length; i++) { + String parameterName = parameterNames[i]; + Class parameterType = parameterTypes[i]; + + BeanProperty beanProperty = propMap.get(parameterName); + if (beanProperty != null) { + propMap.remove(parameterName); + beanMetadata.removeProperty(beanProperty); + beanMetadata.addArgument(beanProperty.getValue(), parameterType.getName(), i); + } else { + String defaultValue = defaultValues.get(new PropertyKey(parameterName, parameterType)); + if (defaultValue == null) { + defaultValue = DEFAULT_VALUE.get(parameterType); + } +// if (defaultValue instanceof FactoryBean) { +// try { +// defaultValue = ((FactoryBean)defaultValue).getObject(); +// } catch (Exception e) { +// throw new FatalBeanException("Unable to get object value from bean factory", e); +// } +// } + MutableValueMetadata valueMetadata = parserContext.createMetadata(MutableValueMetadata.class); + valueMetadata.setStringValue(defaultValue); + valueMetadata.setType(parameterType.getName()); + beanMetadata.addArgument(valueMetadata, parameterType.getName(), i); + } + } + + // todo set any usable default values on the bean definition + } + + private ConstructionInfo selectConstructionMethod(MutableBeanMetadata beanMetadata, MappingMetaData metadata) { + Class beanClass = beanMetadata.getRuntimeClass(); + + // get a set containing the names of the defined properties + Set definedProperties = new HashSet(); + List values = beanMetadata.getProperties(); + for (BeanProperty beanProperty : values) { + definedProperties.add(beanProperty.getName()); + } + + // first check for a factory method + if (beanMetadata.getFactoryMethod() != null) { + return selectFactory(beanClass, beanMetadata, metadata, definedProperties); + } else { + return selectConstructor(beanClass, metadata, definedProperties); + } + } + + private ConstructionInfo selectFactory(Class beanClass, MutableBeanMetadata beanMetadata, MappingMetaData metadata, Set definedProperties) { + String factoryMethodName = beanMetadata.getFactoryMethod(); + + // get the factory methods sorted by longest arg length first + Method[] methods = beanClass.getMethods(); + List factoryMethods = new ArrayList(methods.length); + for (Method method : methods) { + if (method.getName().equals(factoryMethodName)) { + factoryMethods.add(method); + } + } + + Collections.sort(factoryMethods, new MethodArgLengthComparator()); + + // if a factory method has been annotated as the default constructor we always use that constructor + for (Method factoryMethod : factoryMethods) { + if (metadata.isDefaultFactoryMethod(beanClass, factoryMethod)) { + return new ConstructionInfo(beanClass, factoryMethod, metadata); + } + } + + // try to find a constructor for which we have all of the properties defined + for (Method factoryMethod : factoryMethods) { + ConstructionInfo constructionInfo = new ConstructionInfo(beanClass, factoryMethod, metadata); + if (isUsableConstructor(constructionInfo, definedProperties)) { + return constructionInfo; + } + } + return null; + } + + private ConstructionInfo selectConstructor(Class beanClass, MappingMetaData metadata, Set definedProperties) { + // get the constructors sorted by longest arg length first + List constructors = new ArrayList(Arrays.asList(beanClass.getConstructors())); + Collections.sort(constructors, new ConstructorArgLengthComparator()); + + // if a constructor has been annotated as the default constructor we always use that constructor + for (Constructor constructor : constructors) { + if (metadata.isDefaultConstructor(constructor)) { + return new ConstructionInfo(constructor, metadata); + } + } + + // try to find a constructor for which we have all of the properties defined + for (Constructor constructor : constructors) { + ConstructionInfo constructionInfo = new ConstructionInfo(constructor, metadata); + if (isUsableConstructor(constructionInfo, definedProperties)) { + return constructionInfo; + } + } + return null; + } + + private boolean isUsableConstructor(ConstructionInfo constructionInfo, Set definedProperties) { + // if we don't have parameter names this is not the constructor we are looking for + String[] parameterNames = constructionInfo.parameterNames; + if (parameterNames == null) { + return false; + } + + Class[] parameterTypes = constructionInfo.parameterTypes; + for (int i = 0; i < parameterNames.length; i++) { + String parameterName = parameterNames[i]; + Class parameterType = parameterTypes[i]; + + // can we satify this property using a defined property or default property + if (!definedProperties.contains(parameterName) && !defaultValues.containsKey(new PropertyKey(parameterName, parameterType))) { + return false; + } + } + + return true; + } + + private class ConstructionInfo { + private final Class[] parameterTypes; + private final String[] parameterNames; + + public ConstructionInfo(Constructor constructor, MappingMetaData metadata) { + this.parameterTypes = constructor.getParameterTypes(); + String[] names = metadata.getParameterNames(constructor); + + // verify that we have enough parameter names + int expectedParameterCount = parameterTypes.length; + if (names != null && names.length != expectedParameterCount) { + throw new ComponentDefinitionException("Excpected " + expectedParameterCount + " parameter names for constructor but only got " + + names.length + ": " + constructor.toString()); + } + if (expectedParameterCount == 0) { + names = new String[0]; + } + + this.parameterNames = names; + } + + public ConstructionInfo(Class beanClass, Method factoryMethod, MappingMetaData metadata) { + this.parameterTypes = factoryMethod.getParameterTypes(); + + String[] names = metadata.getParameterNames(beanClass, factoryMethod); + + // verify that we have enough parameter names + int expectedParameterCount = parameterTypes.length; + if (names != null && names.length != expectedParameterCount) { + throw new ComponentDefinitionException("Excpected " + expectedParameterCount + " parameter names for factory method but only got " + + names.length + ": " + factoryMethod.toString()); + } + if (expectedParameterCount == 0) { + names = new String[0]; + } + + this.parameterNames = names; + } + } + + private static class MethodArgLengthComparator implements Comparator { + public int compare(Method o1, Method o2) { + return getArgLength(o2) - getArgLength(o1); + } + + private int getArgLength(Method object) { + return object.getParameterTypes().length; + } + } + + private static class ConstructorArgLengthComparator implements Comparator { + public int compare(Constructor o1, Constructor o2) { + return getArgLength(o2) - getArgLength(o1); + } + + private int getArgLength(Constructor object) { + return object.getParameterTypes().length; + } + } + + private static class PropertyKey { + private final String name; + private final Class type; + + public PropertyKey(String name, Class type) { + this.name = name; + this.type = type; + } + + public boolean equals(Object object) { + if (!(object instanceof PropertyKey)) { + return false; + } + + PropertyKey defaultProperty = (PropertyKey) object; + return name.equals(defaultProperty.name) && type.equals(type); + } + + public int hashCode() { + int result = 17; + result = 37 * result + name.hashCode(); + result = 37 * result + type.hashCode(); + return result; + } + + public String toString() { + return "[" + name + " " + type + "]"; + } + } + + private static final Map DEFAULT_VALUE; + + static { + Map temp = new HashMap(); + temp.put(Boolean.TYPE, Boolean.FALSE.toString()); + temp.put(Byte.TYPE, "0B"); + temp.put(Character.TYPE, "\\u000"); + temp.put(Short.TYPE, "0S"); + temp.put(Integer.TYPE, "0"); + temp.put(Long.TYPE, "0L"); + temp.put(Float.TYPE, "0F"); + temp.put(Double.TYPE, "0D"); + + DEFAULT_VALUE = Collections.unmodifiableMap(temp); + } +} diff --git a/spring/src/java/org/xbean/spring/context/impl/NamespaceHelper.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamespaceHelper.java similarity index 67% rename from spring/src/java/org/xbean/spring/context/impl/NamespaceHelper.java rename to xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamespaceHelper.java index 55c7efdd..d8a060f5 100644 --- a/spring/src/java/org/xbean/spring/context/impl/NamespaceHelper.java +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/NamespaceHelper.java @@ -1,29 +1,28 @@ /** - * - * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com - * - * 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 - * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - **/ -package org.xbean.spring.context.impl; + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context.impl; /** * A helper class for turning namespaces into META-INF/services URIs * - * @version $Revision: 1.1 $ + * @version $Revision$ */ public class NamespaceHelper { - public static final String META_INF_PREFIX = "META-INF/services/org/xbean/spring/"; + public static final String META_INF_PREFIX = "META-INF/services/org/apache/xbean/blueprint/"; /** * Converts the namespace and localName into a valid path name we can use on diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/ObjectNameEditor.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/ObjectNameEditor.java new file mode 100644 index 00000000..06dbade4 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/ObjectNameEditor.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.xbean.blueprint.context.impl; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import java.beans.PropertyEditorSupport; + +/** + * Editor for java.net.URI + */ +public class ObjectNameEditor extends PropertyEditorSupport { + + public void setAsText(String text) throws IllegalArgumentException { + try { + setValue(new ObjectName(text)); + } + catch (MalformedObjectNameException e) { + throw new IllegalArgumentException("Could not convert ObjectName for " + text + ": " + e.getMessage()); + } + } + + public String getAsText() { + ObjectName value = (ObjectName) getValue(); + return (value != null ? value.toString() : ""); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/PropertyEditorHelper.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/PropertyEditorHelper.java new file mode 100644 index 00000000..703a871b --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/PropertyEditorHelper.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.xbean.blueprint.context.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.beans.PropertyEditorManager; + +/** + * A helper method to register some custom editors + * + * @version $Revision$ + */ +public class PropertyEditorHelper { + private static final Log log = LogFactory.getLog(PropertyEditorHelper.class); + + public static void registerCustomEditors() { + registerEditor("java.io.File", "org.apache.xbean.blueprint.context.impl.FileEditor"); + registerEditor("java.net.URI", "org.apache.xbean.blueprint.context.impl.URIEditor"); + registerEditor("java.util.Date", "org.apache.xbean.blueprint.context.impl.DateEditor"); + registerEditor("javax.management.ObjectName", "org.apache.xbean.blueprint.context.impl.ObjectNameEditor"); + } + + protected static void registerEditor(String typeName, String editorName) { + Class type = loadClass(typeName); + Class editor = loadClass(editorName); + if (type != null && editor != null) { + PropertyEditorManager.registerEditor(type, editor); + } + } + + public static void unregisterCustomEditors() { + unregisterEditor("java.io.File"); + unregisterEditor("java.net.URI"); + unregisterEditor("java.util.Date"); + unregisterEditor("javax.management.ObjectName"); + } + + protected static void unregisterEditor(String typeName) { + Class type = loadClass(typeName); + if (type != null) { + PropertyEditorManager.registerEditor(type, null); + } + } + public static Class loadClass(String name) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + try { + return contextClassLoader.loadClass(name); + } + catch (ClassNotFoundException e) { + } + catch (NoClassDefFoundError e) { + } + } + try { + return PropertyEditorHelper.class.getClassLoader().loadClass(name); + } + catch (ClassNotFoundException e) { + log.debug("Could not find class: " + name + " on the classpath"); + return null; + } + catch (NoClassDefFoundError e) { + log.debug("Could not load class: " + name + " on the classpath. " + e.getMessage()); + return null; + } + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameHelper.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameHelper.java new file mode 100644 index 00000000..75cf841d --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameHelper.java @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context.impl; + +import java.beans.PropertyDescriptor; +import java.util.List; + +import javax.xml.namespace.QName; +import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.mutable.MutableBeanMetadata; +import org.apache.aries.blueprint.mutable.MutableCollectionMetadata; +import org.apache.aries.blueprint.mutable.MutableValueMetadata; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * + * @version $Revision: 1.1 $ + */ +public class QNameHelper { + private static final Log log = LogFactory.getLog(QNameHelper.class); + + public static Metadata createQNameMetadata(Element element, String qualifiedName, ParserContext parserContext) { + MutableBeanMetadata beanMetadata = parserContext.createMetadata(MutableBeanMetadata.class); + beanMetadata.setClassName(QName.class.getName()); + int index = qualifiedName.indexOf(':'); + if (index >= 0) { + String prefix = qualifiedName.substring(0, index); + String localName = qualifiedName.substring(index + 1); + String uri = recursiveGetAttributeValue(element, "xmlns:" + prefix); + beanMetadata.addArgument(valueMetadata(uri, parserContext), String.class.getName(), 0); + beanMetadata.addArgument(valueMetadata(localName, parserContext), String.class.getName(), 1); + beanMetadata.addArgument(valueMetadata(prefix, parserContext), String.class.getName(), 2); + } + else { + String uri = recursiveGetAttributeValue(element, "xmlns"); + if (uri != null) { + beanMetadata.addArgument(valueMetadata(uri, parserContext), String.class.getName(), 0); + beanMetadata.addArgument(valueMetadata(qualifiedName, parserContext), String.class.getName(), 1); + } else { + beanMetadata.addArgument(valueMetadata(qualifiedName, parserContext), String.class.getName(), 0); + } + } + return beanMetadata; + } + + private static Metadata valueMetadata(String stringValue, ParserContext parserContext) { + MutableValueMetadata value = parserContext.createMetadata(MutableValueMetadata.class); + value.setStringValue(stringValue); + return value; + } + + /** + * Recursive method to find a given attribute value in an element or its parents (towards root of dom tree) + * @param element element to start searching for attribute in + * @param attributeName name of desired attribute + * @return value of found attribute + */ + public static String recursiveGetAttributeValue(Element element, String attributeName) { + String answer = null; + try { + answer = element.getAttribute(attributeName); + } + catch (Exception e) { + if (log.isTraceEnabled()) { + log.trace("Caught exception looking up attribute: " + attributeName + " on element: " + element + ". Cause: " + e, e); + } + } + if (answer == null || answer.length() == 0) { + Node parentNode = element.getParentNode(); + if (parentNode instanceof Element) { + return recursiveGetAttributeValue((Element) parentNode, attributeName); + } + } + return answer; + } + + public static void coerceNamespaceAwarePropertyValues(MutableBeanMetadata bd, Element element, PropertyDescriptor descriptor, ParserContext parserContext) { + // When the property is an indexed property, the getPropertyType can return null. + if (descriptor.getPropertyType() == null) { + return; + } + if (descriptor.getPropertyType().isAssignableFrom(QName.class)) { + String name = descriptor.getName(); + BeanProperty propertyValue = XBeanNamespaceHandler.propertyByName(name, bd); + if (propertyValue != null) { + Metadata value = propertyValue.getValue(); + if (value instanceof ValueMetadata) { + bd.removeProperty(propertyValue); + Metadata valueMetadata = createQNameMetadata(element, ((ValueMetadata)value).getStringValue(), parserContext); + bd.addProperty(name, valueMetadata); + } + //else?? + } + } else if (descriptor.getPropertyType().isAssignableFrom(QName[].class)) { + String name = descriptor.getName(); + BeanProperty propertyValue = XBeanNamespaceHandler.propertyByName(name, bd); + if (propertyValue != null) { + Object value = propertyValue.getValue(); + if (value instanceof CollectionMetadata) { + List values = ((CollectionMetadata) value).getValues(); + MutableCollectionMetadata newValue = parserContext.createMetadata(MutableCollectionMetadata.class); + + for (Metadata v : values) { + if (v instanceof ValueMetadata) { + newValue.addValue(createQNameMetadata(element, ((ValueMetadata)v).getStringValue(), parserContext)); + } else { + newValue.addValue(v); + } + } + bd.removeProperty(propertyValue); + bd.addProperty(name, newValue); + } + } + } + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameNamespaceHandler.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameNamespaceHandler.java new file mode 100644 index 00000000..3ae65ba2 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/QNameNamespaceHandler.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.blueprint.context.impl; + +import java.net.URL; +import java.util.Collections; +import java.util.Set; + +import javax.xml.namespace.QName; +import org.apache.aries.blueprint.NamespaceHandler; +import org.apache.aries.blueprint.ParserContext; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * @version $Rev$ $Date$ + */ +public class QNameNamespaceHandler implements NamespaceHandler { + + private static final Set MANAGED_CLASS = Collections.singleton(QName.class); + private final QNameHelper qNameHelper = new QNameHelper(); + + public URL getSchemaLocation(String namespace) { + return null; + } + + public Set getManagedClasses() { + return MANAGED_CLASS; + } + + public Metadata parse(Element element, ParserContext context) { + if ("qname".equals(element.getLocalName())) { + return QNameHelper.createQNameMetadata(element, getElementText(element), context); + } + return null; + } + + public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) { + return component; + } + + private String getElementText(Element element) { + StringBuilder buffer = new StringBuilder(); + NodeList nodeList = element.getChildNodes(); + for (int i = 0, size = nodeList.getLength(); i < size; i++) { + Node node = nodeList.item(i); + if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) { + buffer.append(node.getNodeValue()); + } + } + return buffer.toString(); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/URIEditor.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/URIEditor.java new file mode 100644 index 00000000..d8aceaf4 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/URIEditor.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.xbean.blueprint.context.impl; + +import java.beans.PropertyEditorSupport; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Editor for java.net.URI + */ +public class URIEditor extends PropertyEditorSupport { + + public void setAsText(String text) throws IllegalArgumentException { + try { + setValue(new URI(text)); + } + catch (URISyntaxException e) { + throw new IllegalArgumentException( + "Could not convert URI for " + text + ": " + e.getMessage()); + } + } + + public String getAsText() { + URI value = (URI) getValue(); + return (value != null ? value.toString() : ""); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/XBeanNamespaceHandler.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/XBeanNamespaceHandler.java new file mode 100644 index 00000000..8c1b1bff --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/context/impl/XBeanNamespaceHandler.java @@ -0,0 +1,828 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.blueprint.context.impl; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.beans.PropertyEditor; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.apache.aries.blueprint.NamespaceHandler; +import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.mutable.MutableBeanMetadata; +import org.apache.aries.blueprint.mutable.MutableCollectionMetadata; +import org.apache.aries.blueprint.mutable.MutableMapMetadata; +import org.apache.aries.blueprint.mutable.MutableRefMetadata; +import org.apache.aries.blueprint.mutable.MutableValueMetadata; +import org.osgi.framework.Bundle; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.MapEntry; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.NonNullMetadata; +import org.osgi.service.blueprint.reflect.NullMetadata; +import org.osgi.service.blueprint.reflect.RefMetadata; +import org.osgi.service.blueprint.reflect.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * @version $Rev$ $Date$ + */ +public class XBeanNamespaceHandler implements NamespaceHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(XBeanNamespaceHandler.class); + + public static final String BLUEPRINT_NAMESPACE = "http://www.osgi.org/xmlns/blueprint/v1.0.0"; + private static final String BEAN_REFERENCE_PREFIX = "#"; + private static final String NULL_REFERENCE = "#null"; + + private final String namespace; + private final URL schemaLocation; + private final Set managedClasses; + private final MappingMetaData mappingMetaData; + private final Map managedClassesByName; + private final Map> propertyEditors; + private final NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(); + + public XBeanNamespaceHandler(String namespace, URL schemaLocation, Set managedClasses, Map> propertyEditors, Properties properties) { + this.namespace = namespace; + this.schemaLocation = schemaLocation; + this.managedClasses = managedClasses; + managedClassesByName = mapClasses(managedClasses); + this.propertyEditors = propertyEditors; + this.mappingMetaData = new MappingMetaData(properties); + } + + public XBeanNamespaceHandler(String namespace, String schemaLocation, Bundle bundle, String propertiesLocation) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { + URL propertiesUrl = bundle.getResource(propertiesLocation); + InputStream in = propertiesUrl.openStream(); + Properties properties = new Properties(); + try { + properties.load(in); + } finally { + in.close(); + } + this.namespace = namespace; + this.schemaLocation = bundle.getEntry(schemaLocation); + this.managedClasses = managedClassesFromProperties(bundle, properties); + managedClassesByName = mapClasses(managedClasses); + propertyEditors = propertyEditorsFromProperties(bundle, properties); + this.mappingMetaData = new MappingMetaData(properties); + } + + public XBeanNamespaceHandler(String namespace, String schemaLocation, String propertiesLocation) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { + ClassLoader cl = getClass().getClassLoader(); + URL propertiesUrl = cl.getResource(propertiesLocation); + if (propertiesUrl == null) { + throw new IOException("Could not locate properties at " + propertiesLocation); + } + InputStream in = propertiesUrl.openStream(); + Properties properties = new Properties(); + try { + properties.load(in); + } finally { + in.close(); + } + this.namespace = namespace; + this.schemaLocation = cl.getResource(schemaLocation); + this.managedClasses = managedClassesFromProperties(cl, properties); + managedClassesByName = mapClasses(managedClasses); + propertyEditors = propertyEditorsFromProperties(cl, properties); + this.mappingMetaData = new MappingMetaData(properties); + } + + private static Set managedClassesFromProperties(ClassLoader cl, Properties properties) { + Set managedClasses = new HashSet(); + Properties methods = new Properties(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (key.indexOf(".") < 0) { + String className = (String) entry.getValue(); + try { + Class beanClass = cl.loadClass(className); + managedClasses.add(beanClass); + findAnnotations(key, beanClass, methods); + } catch (NoClassDefFoundError e) { + LOGGER.warn("Could not load class: {} due to {}", className, e.getMessage()); + } catch (ClassNotFoundException e) { + LOGGER.warn("Could not load class: {}", className); + } + } + } + properties.putAll(methods); + return managedClasses; + } + + private static Set managedClassesFromProperties(Bundle bundle, Properties properties) { + Set managedClasses = new HashSet(); + Properties methods = new Properties(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (key.indexOf(".") < 0) { + String className = (String) entry.getValue(); + try { + Class beanClass = bundle.loadClass(className); + managedClasses.add(beanClass); + findAnnotations(key, beanClass, methods); + } catch (NoClassDefFoundError e) { + LOGGER.warn("Could not load class: {} due to {}", className, e.getMessage()); + } catch (ClassNotFoundException e) { + LOGGER.warn("Could not load class: {}", className); + } + } + } + properties.putAll(methods); + return managedClasses; + } + + private static void findAnnotations(String key, Class beanClass, Properties methods) { + for (Method m : beanClass.getMethods()) { + if (m.isAnnotationPresent(PostConstruct.class)) { + methods.put(key + ".initMethod", m.getName()); + } + if (m.isAnnotationPresent(PreDestroy.class)) { + methods.put(key + ".destroyMethod", m.getName()); + } + } + } + + private Map> propertyEditorsFromProperties(Bundle bundle, Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + Map> propertyEditors = new HashMap>(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (key.endsWith(".propertyEditor")) { + String className = (String) entry.getValue(); + Class clazz = bundle.loadClass(className).asSubclass(PropertyEditor.class); + propertyEditors.put(className, clazz); + } + } + return propertyEditors; + } + + private Map> propertyEditorsFromProperties(ClassLoader classLoader, Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + Map> propertyEditors = new HashMap>(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (key.endsWith(".propertyEditor")) { + String className = (String) entry.getValue(); + Class clazz = classLoader.loadClass(className).asSubclass(PropertyEditor.class); + propertyEditors.put(className, clazz); + } + } + return propertyEditors; + } + + private Map mapClasses(Set managedClasses) { + Map map = new HashMap(); + for (Class clazz : managedClasses) { + map.put(clazz.getName(), clazz); + } + return map; + } + + public URL getSchemaLocation(String s) { + if (namespace.equals(s)) { + return schemaLocation; + } + return null; + } + + public Set getManagedClasses() { + return managedClasses; + } + + public Metadata parse(Element element, ParserContext parserContext) { + String beanTypeName = element.getLocalName(); + String className = mappingMetaData.getClassName(beanTypeName); + if (className == null) { + throw new ComponentDefinitionException(beanTypeName + " not known to xbean namespace handler for " + namespace); + } + return parseInternal(element, parserContext, beanTypeName, className); + } + + private Metadata parseInternal(Element element, ParserContext parserContext, String beanTypeName, String className) { + MutableBeanMetadata beanMetaData = parserContext.createMetadata(MutableBeanMetadata.class); + beanMetaData.setClassName(className); + beanMetaData.setScope(BeanMetadata.SCOPE_SINGLETON); + beanMetaData.setActivation(BeanMetadata.ACTIVATION_EAGER); + beanMetaData.setRuntimeClass(managedClassesByName.get(className)); + if (beanMetaData.getRuntimeClass() == null) { + throw new ComponentDefinitionException("Unknown bean class: " + className); + } + + if (element.hasAttributeNS(BLUEPRINT_NAMESPACE, "id")) { + String id = element.getAttributeNS(BLUEPRINT_NAMESPACE, "id"); + beanMetaData.setId(id); + } else { + beanMetaData.setId(parserContext.generateId()); + } + + lifecycleMethods(beanTypeName, beanMetaData); + + attributeProperties(element, parserContext, beanTypeName, beanMetaData); + contentProperty(beanMetaData, element, parserContext); + nestedProperties(beanMetaData, element, beanTypeName, className, parserContext); + //QName resolution + coerceNamespaceAwarePropertyValues(beanMetaData, element, parserContext); + namedConstructorArgs.processParameters(beanMetaData, mappingMetaData, parserContext); + return beanMetaData; + } + + private void lifecycleMethods(String beanTypeName, MutableBeanMetadata beanMetaData) { + String initMethod = mappingMetaData.getInitMethodName(beanTypeName); + if (initMethod != null) { + beanMetaData.setInitMethod(initMethod); + } + String destroyMethod = mappingMetaData.getDestroyMethodName(beanTypeName); + if (destroyMethod != null) { + beanMetaData.setDestroyMethod(destroyMethod); + } + String factoryMethod = mappingMetaData.getFactoryMethodName(beanTypeName); + if (factoryMethod != null) { + beanMetaData.setFactoryMethod(factoryMethod); + } + } + + private void attributeProperties(Element element, ParserContext parserContext, String beanTypeName, MutableBeanMetadata beanMetaData) { + NamedNodeMap attrs = element.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Attr attr = (Attr) attrs.item(i); + if (namespace.equals(attr.getNamespaceURI()) || attr.getNamespaceURI() == null) { + String attrName = attr.getLocalName(); + String value = attr.getValue().trim(); + String propertyName = mappingMetaData.getPropertyName(beanTypeName, attrName); + String propertyEditor = mappingMetaData.getPropertyEditor(beanTypeName, attrName); + addProperty(propertyName, value, propertyEditor, beanMetaData, parserContext); + } + } + } + + private void contentProperty(MutableBeanMetadata definition, Element element, ParserContext parserContext) { + String name = mappingMetaData.getContentProperty(element.getLocalName()); + String propertyEditor = mappingMetaData.getPropertyEditor(element.getLocalName(), name); + if (name != null) { + String value = getElementText(element).trim(); + addProperty(name, value, propertyEditor, definition, parserContext); + } else { + ByteArrayInputStream in = new ByteArrayInputStream(getElementText(element).getBytes()); + Properties properties = new Properties(); + try { + properties.load(in); + } + catch (IOException e) { + return; + } + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + addProperty(key, value, propertyEditor, definition, parserContext); + } + } + } + + private void addProperty(String name, String value, String propertyEditor, MutableBeanMetadata definition, ParserContext parserContext) { + Metadata m = getValue(value, propertyEditor, parserContext); + definition.addProperty(name, m); + } + + private void nestedProperties(MutableBeanMetadata beanMetadata, Element element, String beanTypeName, String className, ParserContext parserContext) { + NodeList nodes = element.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node instanceof Element) { + Element child = (Element) node; + String childName = child.getLocalName(); + String namespace = child.getNamespaceURI(); + if (!this.namespace.equals(namespace)) { + BeanProperty prop = parserContext.parseElement(BeanProperty.class, beanMetadata, child); + beanMetadata.addProperty(prop); + continue; + } + Metadata childMetadata = null; + PropertyDescriptor pd = getPropertyDescriptor(mappingMetaData.getClassName(beanTypeName), childName); + Class propertyType = pd == null ? null : pd.getPropertyType(); + String propertyName = mappingMetaData.getNestedListProperty(beanTypeName, childName); + //explicit list + if (propertyName != null || isCollectionType(propertyType)) { + propertyName = propertyName == null ? childName : propertyName; + childMetadata = parserContext.parseElement(CollectionMetadata.class, beanMetadata, child); + } else if ((propertyName = mappingMetaData.getFlatCollectionProperty(beanTypeName, childName)) != null) { + //flat collection + Metadata elementMetadata = parse(child, parserContext); + BeanProperty list = propertyByName(propertyName, beanMetadata); + MutableCollectionMetadata listMeta; + if (list == null) { + listMeta = parserContext.createMetadata(MutableCollectionMetadata.class); + childMetadata = listMeta; + } else { + listMeta = (MutableCollectionMetadata) list.getValue(); + } + listMeta.addValue(elementMetadata); + } else if ((propertyName = mappingMetaData.getNestedProperty(beanTypeName, childName)) != null) { + // lets find the first child bean that parses fine + childMetadata = parseChildExtensionBean(child, beanMetadata, parserContext); + + } else if (mappingMetaData.isFlatProperty(beanTypeName, childName)) { + propertyName = childName; + String flatClassName = getPropertyDescriptor(mappingMetaData.getClassName(beanTypeName), childName).getPropertyType().getName(); + childMetadata = parseInternal(child, parserContext, childName, flatClassName); + } else { + childMetadata = tryParseNestedPropertyViaIntrospection(beanMetadata, className, child, parserContext); + propertyName = childName; + } + if (childMetadata == null) { + String text = getElementText(child); + if (text != null) { + MutableValueMetadata m = parserContext.createMetadata(MutableValueMetadata.class); + m.setStringValue(text.trim()); + childMetadata = m; + } + + +// propertyName = mappingMetaData.getPropertyName(beanTypeName, childName); +// NodeList childNodes = child.getChildNodes(); +// StringBuilder buf = new StringBuilder(); +// for (int j = 0; j < childNodes.getLength(); j++) { +// Node childNode = childNodes.item(j); +// if (childNode instanceof Element) { +// Element childElement = (Element) childNode; +// if (namespace.equals(childElement.getNamespaceURI())) { +// childMetadata = parse(childElement, parserContext); +// } else { +// try { +// childMetadata = parserContext.parseElement(BeanMetadata.class, beanMetaData, childElement); +// } catch (Exception e) { +// childMetadata = parserContext.parseElement(ValueMetadata.class, beanMetaData, childElement); +// } +// } +// +// break; +// } else if (childNode instanceof Text) { +// String value = childNode.getNodeValue(); +// buf.append(value); +// } +// } +// if (childMetadata == null) { +// MutableValueMetadata m = parserContext.createMetadata(MutableValueMetadata.class); +// m.setStringValue(buf.toString().trim()); +// childMetadata = m; +// } + } + if (childMetadata != null) { + beanMetadata.addProperty(propertyName, childMetadata); + } + } + } + } + + private Metadata parseChildExtensionBean(Element child, MutableBeanMetadata beanMetadata, ParserContext parserContext) { + NodeList nl = child.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element childElement = (Element) node; + String uri = childElement.getNamespaceURI(); + String localName = childElement.getLocalName(); + Metadata value = parserContext.parseElement(Metadata.class, beanMetadata, childElement); + if (value != null) { + return value; + } + //TODO ARIES-111 +// if (uri == null || +// uri.equals(BLUEPRINT_NAMESPACE)) { +// if ("bean".equals(localName)) { +// return parserContext.parseElement(BeanMetadata.class, beanMetadata, childElement); +// } else { +// return parserContext.parseElement(ValueMetadata.class, beanMetadata, childElement); +// } +// } else { +// Metadata value = parse(childElement, parserContext); +// if (value != null) { +// return value; +// } +// } + } + } + return null; + } + + private Metadata tryParseNestedPropertyViaIntrospection(MutableBeanMetadata beanMetadata, String className, Element element, ParserContext parserContext) { + String localName = element.getLocalName(); + PropertyDescriptor descriptor = getPropertyDescriptor(className, localName); + if (descriptor != null) { + return parseNestedPropertyViaIntrospection(beanMetadata, element, descriptor.getName(), descriptor.getPropertyType(), parserContext); + } else { + return parseNestedPropertyViaIntrospection(beanMetadata, element, localName, Object.class, parserContext); + } + } + + private Metadata parseNestedPropertyViaIntrospection(MutableBeanMetadata beanMetadata, Element element, String propertyName, Class propertyType, ParserContext parserContext) { + if (isMap(propertyType)) { + return parseCustomMapElement(beanMetadata, element, propertyName, parserContext); + } else if (isCollection(propertyType)) { + return parserContext.parseElement(CollectionMetadata.class, beanMetadata, element); + } else { + return parseChildExtensionBean(element, beanMetadata, parserContext); + } + } + + private boolean isMap(Class type) { + return Map.class.isAssignableFrom(type); + } + + /** + * Returns true if the given type is a collection type or an array + */ + private boolean isCollection(Class type) { + return type.isArray() || Collection.class.isAssignableFrom(type); + } + + protected String getLocalName(Element element) { + String localName = element.getLocalName(); + if (localName == null) { + localName = element.getNodeName(); + } + return localName; + } + + protected Metadata parseCustomMapElement(MutableBeanMetadata beanMetadata, Element element, String name, ParserContext parserContext) { + MutableMapMetadata map = parserContext.createMetadata(MutableMapMetadata.class); + + Element parent = (Element) element.getParentNode(); + String entryName = mappingMetaData.getMapEntryName(getLocalName(parent), name); + String keyName = mappingMetaData.getMapKeyName(getLocalName(parent), name); + String dups = mappingMetaData.getMapDupsMode(getLocalName(parent), name); + boolean flat = mappingMetaData.isFlatMap(getLocalName(parent), name); + String defaultKey = mappingMetaData.getMapDefaultKey(getLocalName(parent), name); + + if (entryName == null) entryName = "property"; + if (keyName == null) keyName = "key"; + if (dups == null) dups = "replace"; + + // TODO : support further customizations + //String valueName = "value"; + //boolean keyIsAttr = true; + //boolean valueIsAttr = false; + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element childElement = (Element) node; + + String localName = childElement.getLocalName(); + String uri = childElement.getNamespaceURI(); + if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) { + continue; + } + + // we could use namespaced attributes to differentiate real spring + // attributes from namespace-specific attributes + if (!flat && !isEmpty(uri) && localName.equals(entryName)) { + String key = childElement.getAttributeNS(uri, keyName); + if (key == null || key.length() == 0) { + key = defaultKey; + } + if (key == null) { + throw new RuntimeException("No key defined for map " + entryName); + } + + NonNullMetadata keyValue = (NonNullMetadata) getValue(key, mappingMetaData.getPropertyEditor(localName, key), parserContext); + + Element valueElement = getFirstChildElement(childElement); + Metadata value; + if (valueElement != null) { + value = parserContext.parseElement(Metadata.class, beanMetadata, valueElement); +// String valueElUri = valueElement.getNamespaceURI(); +// String valueElLocalName = valueElement.getLocalName(); +// if (valueElUri == null || +// valueElUri.equals(BLUEPRINT_NAMESPACE)) { +// if ("bean".equals(valueElLocalName)) { +// value = parserContext.parseElement(BeanMetadata.class, beanMetadata, valueElement); +// } else { +// value = parserContext.parseElement(BeanProperty.class, beanMetadata, valueElement).getValue(); +// } +// } else { +// value = parserContext.parseElement(ValueMetadata.class, beanMetadata, valueElement); +// } + } else { + value = getValue(getElementText(childElement), mappingMetaData.getPropertyEditor(localName, key), parserContext); + } + + addValueToMap(map, keyValue, value, dups, parserContext); + } else if (flat && !isEmpty(uri)) { + String key = childElement.getAttributeNS(uri, keyName); + if (key == null || key.length() == 0) { + key = defaultKey; + } + if (key == null) { + throw new RuntimeException("No key defined for map entry " + entryName); + } + NonNullMetadata keyValue = (NonNullMetadata) getValue(key, mappingMetaData.getPropertyEditor(localName, key), parserContext); + childElement = cloneElement(childElement); + childElement.removeAttributeNS(uri, keyName); + Metadata bdh = parse(childElement, parserContext); + addValueToMap(map, keyValue, bdh, dups, parserContext); + } + } + } + return map; + } + + /** + * Creates a clone of the element and its attribute (though not its content) + */ + protected Element cloneElement(Element element) { + Element answer = element.getOwnerDocument().createElementNS(element.getNamespaceURI(), element.getNodeName()); + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0, size = attributes.getLength(); i < size; i++) { + Attr attribute = (Attr) attributes.item(i); + String uri = attribute.getNamespaceURI(); + answer.setAttributeNS(uri, attribute.getName(), attribute.getNodeValue()); + } + return answer; + } + + + protected void addValueToMap(MutableMapMetadata map, NonNullMetadata keyValue, Metadata value, String dups, ParserContext parserContext) { + if (hasKey(map, keyValue)) { + if ("discard".equalsIgnoreCase(dups)) { + // Do nothing + } else if ("replace".equalsIgnoreCase(dups)) { + map.addEntry(keyValue, value); + } else if ("allow".equalsIgnoreCase(dups)) { + MutableCollectionMetadata l = parserContext.createMetadata(MutableCollectionMetadata.class); + l.addValue(get(map, keyValue)); + l.addValue(value); + map.addEntry(keyValue, l); + } else if ("always".equalsIgnoreCase(dups)) { + MutableCollectionMetadata l = (MutableCollectionMetadata) get(map, keyValue); + l.addValue(value); + } + } else { + if ("always".equalsIgnoreCase(dups)) { + MutableCollectionMetadata l = (MutableCollectionMetadata) get(map, keyValue); + if (l == null) { + l = parserContext.createMetadata(MutableCollectionMetadata.class); + map.addEntry(keyValue, l); + } + l.addValue(value); + } else { + map.addEntry(keyValue, value); + } + } + } + + private Metadata get(MutableMapMetadata map, NonNullMetadata keyValue) { + for (MapEntry entry : map.getEntries()) { + if (equals(entry.getKey(), keyValue)) { + return entry.getValue(); + } + } + return null; + } + + private boolean equals(NonNullMetadata key1, NonNullMetadata key2) { + if (key1 == key2) return true; + if (key1.getClass() != key2.getClass()) return false; + if (key1 instanceof RefMetadata) return ((RefMetadata) key1).getComponentId().equals(((RefMetadata) key2).getComponentId()); + if (key1 instanceof ReferenceMetadata) { + if (((ReferenceMetadata) key1).getTimeout() != ((ReferenceMetadata) key2).getTimeout()) return false; + } + if (key1 instanceof ServiceReferenceMetadata) { + ServiceReferenceMetadata sr1 = (ServiceReferenceMetadata) key1; + ServiceReferenceMetadata sr2 = (ServiceReferenceMetadata) key2; + return sr1.getAvailability() == sr2.getAvailability() + && sr1.getInterface().equals(sr2.getInterface()) + && sr1.getComponentName().equals(sr2.getComponentName()) + && sr1.getFilter().equals(sr2.getFilter()) + && sr1.getReferenceListeners().equals(sr2.getReferenceListeners()) + + && sr1.getId().equals(sr2.getId()) + && sr1.getActivation() == sr2.getActivation() + && sr1.getDependsOn().equals(sr2.getDependsOn()); + } + if (key1 instanceof ValueMetadata) { + ValueMetadata v1 = (ValueMetadata) key1; + ValueMetadata v2 = (ValueMetadata) key2; + if (v1.getStringValue() != null ? v1.getStringValue().equals(v2.getStringValue()) : v2.getStringValue() == null + && v1.getType() != null ? v1.getType().equals(v2.getType()) : v2.getType() == null) { + return true; + } + } + return false; + } + + private boolean hasKey(MutableMapMetadata map, NonNullMetadata keyValue) { + return get(map, keyValue) != null; + } + + protected boolean isEmpty(String uri) { + return uri == null || uri.length() == 0; + } + + protected Metadata getValue(String value, String propertyEditorName, ParserContext parserContext) { + if (value == null) return null; + + // + // If value is #null then we are explicitly setting the value null instead of an empty string + // + if (NULL_REFERENCE.equals(value)) { + return parserContext.createMetadata(NullMetadata.class); + } + + // + // If value starts with # then we have a ref + // + if (value.startsWith(BEAN_REFERENCE_PREFIX)) { + // strip off the # + value = value.substring(BEAN_REFERENCE_PREFIX.length()); + + // if the new value starts with a #, then we had an excaped value (e.g. ##value) + if (!value.startsWith(BEAN_REFERENCE_PREFIX)) { + MutableRefMetadata ref = parserContext.createMetadata(MutableRefMetadata.class); + ref.setComponentId(value); + return ref; + } + } + +// if( propertyEditor!=null ) { +// PropertyEditor p = createPropertyEditor(propertyEditor); +// +// RootBeanDefinition def = new RootBeanDefinition(); +// def.setBeanClass(PropertyEditorFactory.class); +// def.getPropertyValues().addPropertyValue("propertyEditor", p); +// def.getPropertyValues().addPropertyValue("value", value); +// +// return def; +// } + + // + // Neither null nor a reference + // + MutableValueMetadata metadata = parserContext.createMetadata(MutableValueMetadata.class); + if (propertyEditorName != null) { + PropertyEditor propertyEditor; + try { + propertyEditor = propertyEditors.get(propertyEditorName).newInstance(); + } catch (InstantiationException e) { + throw new ComponentDefinitionException("Could not create a " + propertyEditorName + " to convert value " + value + " for namespace " + namespace); + } catch (IllegalAccessException e) { + throw new ComponentDefinitionException("Could not create a " + propertyEditorName + " to convert value " + value + " for namespace " + namespace); + } + propertyEditor.setAsText(value); + value = propertyEditor.getAsText(); + } + metadata.setStringValue(value); + return metadata; + } + + protected Element getFirstChildElement(Element element) { + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + return (Element) node; + } + } + return null; + } + + + private boolean isCollectionType(Class propertyType) { + if (propertyType == null) { + return false; + } + if (Collection.class.isAssignableFrom(propertyType)) { + return true; + } + if (propertyType.isArray()) { + return true; + } + return false; + } + + public static BeanProperty propertyByName(String name, BeanMetadata meta) { + for (BeanProperty prop : meta.getProperties()) { + if (name.equals(prop.getName())) { + return prop; + } + } + return null; + } + + public ComponentMetadata decorate(Node node, ComponentMetadata componentMetadata, ParserContext parserContext) { + return componentMetadata; + } + + private void coerceNamespaceAwarePropertyValues(MutableBeanMetadata bd, Element element, ParserContext parserContext) { + // lets check for any QName types + BeanInfo beanInfo = getBeanInfo(getClass(bd.getClassName())); + if (beanInfo != null) { + PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor descriptor : descriptors) { + QNameHelper.coerceNamespaceAwarePropertyValues(bd, element, descriptor, parserContext); + } + } + } + + private PropertyDescriptor getPropertyDescriptor(String className, String localName) { + Class clazz = getClass(className); + return getPropertyDescriptor(clazz, localName); + } + + private PropertyDescriptor getPropertyDescriptor(Class clazz, String localName) { + BeanInfo beanInfo = getBeanInfo(clazz); + if (beanInfo != null) { + PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); + for (int i = 0; i < descriptors.length; i++) { + PropertyDescriptor descriptor = descriptors[i]; + String name = descriptor.getName(); + if (name.equals(localName)) { + return descriptor; + } + } + } + return null; + } + + private Class getClass(String className) throws ComponentDefinitionException { + if (className == null) { + return null; + } + + Class type = managedClassesByName.get(className); + if (type == null) { + throw new ComponentDefinitionException("Unknown type: " + className); + } + return type; + } + + private BeanInfo getBeanInfo(Class type) { + if (type == null) { + return null; + } + try { + return Introspector.getBeanInfo(type); + } + catch (IntrospectionException e) { + throw new ComponentDefinitionException("Failed to introspect type: " + type.getName() + ". Reason: " + e, e); + } + } + + private String getElementText(Element element) { + StringBuilder buffer = new StringBuilder(); + NodeList nodeList = element.getChildNodes(); + for (int i = 0, size = nodeList.getLength(); i < size; i++) { + Node node = nodeList.item(i); + if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) { + buffer.append(node.getNodeValue()); + } + } + return buffer.toString(); + } + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/AttributeMapping.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/AttributeMapping.java new file mode 100644 index 00000000..cc369dd6 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/AttributeMapping.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class AttributeMapping implements Comparable { + private final String attributeName; + private final String propertyName; + private final String description; + private final Type type; + private final String value; + private final boolean fixed; + private final boolean required; + private final String propertyEditor; + + public AttributeMapping(String attributeName, String propertyName, String description, Type type, String value, boolean fixed, boolean required, String propertyEditor) { + this.propertyEditor = propertyEditor; + if (attributeName == null) throw new NullPointerException("attributeName"); + if (propertyName == null) throw new NullPointerException("propertyName"); + if (type == null) throw new NullPointerException("type"); + this.attributeName = attributeName; + this.propertyName = propertyName; + if (description == null) description = ""; + this.description = description; + this.type = type; + this.value = value; + this.fixed = fixed; + this.required = required; + } + + public String getAttributeName() { + return attributeName; + } + + public String getPropertyName() { + return propertyName; + } + + public String getDescription() { + return description; + } + + public Type getType() { + return type; + } + + public String getValue() { + return value; + } + + public boolean isFixed() { + return fixed; + } + + public boolean isRequired() { + return required; + } + + public int hashCode() { + return attributeName.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof AttributeMapping) { + return attributeName.equals(((AttributeMapping) obj).attributeName); + } + return false; + } + + public int compareTo(Object obj) { + return attributeName.compareTo(((AttributeMapping) obj).attributeName); + } + + public String getPropertyEditor() { + return propertyEditor; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/DocumentationGenerator.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/DocumentationGenerator.java new file mode 100644 index 00000000..84e981b1 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/DocumentationGenerator.java @@ -0,0 +1,201 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class DocumentationGenerator implements GeneratorPlugin { + private final File destFile; + private LogFacade log; + + public DocumentationGenerator(File destFile) { + this.destFile = destFile; + } + + public void generate(NamespaceMapping namespaceMapping) throws IOException { + String namespace = namespaceMapping.getNamespace(); + + // TODO can only handle 1 schema document so far... + File file = new File(destFile.getParentFile(), destFile.getName() + ".html"); + file.getParentFile().mkdirs(); + log.log("Generating HTML documentation file: " + file + " for namespace: " + namespace); + PrintWriter out = new PrintWriter(new FileWriter(file)); + try { + generateDocumentation(out, namespaceMapping); + } finally { + out.close(); + } + } + + private void generateDocumentation(PrintWriter out, NamespaceMapping namespaceMapping) { + String namespace = namespaceMapping.getNamespace(); + + out.println(""); + out.println(""); + out.println(""); + out.println("Schema for namespace: " + namespace + ""); + out.println(""); + out.println(""); + out.println(""); + out.println(""); + out.println(); + out.println(""); + out.println(); + + generateRootElement(out, namespaceMapping); + + generateElementsSummary(out, namespaceMapping); + out.println(); + out.println(); + + generateElementsDetail(out, namespaceMapping); + + out.println(); + out.println(""); + out.println(""); + } + + private void generateRootElement(PrintWriter out, NamespaceMapping namespaceMapping) { + ElementMapping rootElement = namespaceMapping.getRootElement(); + if (rootElement != null) { + out.println("

    Root Element

    "); + out.println(""); + out.println(" "); + generateElementSummary(out, rootElement); + out.println("
    ElementDescriptionClass
    "); + out.println(); + } + } + + private void generateElementsSummary(PrintWriter out, NamespaceMapping namespaceMapping) { + out.println("

    Element Summary

    "); + out.println(""); + out.println(" "); + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + generateElementSummary(out, element); + } + out.println("
    ElementDescriptionClass
    "); + } + + private void generateElementSummary(PrintWriter out, ElementMapping element) { + out.println(" " + + "" + element.getElementName() + "" + + "" + element.getDescription() + "" + + "" + element.getClassName() + ""); + } + + private void generateElementsDetail(PrintWriter out, NamespaceMapping namespaceMapping) { + out.println("

    Element Detail

    "); + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + generateHtmlElementDetail(out, namespaceMapping, element); + } + } + + private void generateHtmlElementDetail(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) { + out.println("

    Element: " + element.getElementName() + "

    "); + + boolean hasAttributes = false; + boolean hasElements = false; + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext() && (!hasAttributes || !hasElements);) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + Type type = attributeMapping.getType(); + if (namespaceMapping.isSimpleType(type)) { + hasAttributes = true; + } else { + hasElements = true; + } + } + + if (hasAttributes) { + out.println(""); + out.println(" "); + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + Type type = attributeMapping.getPropertyEditor() != null ? Type.newSimpleType(String.class.getName()) : attributeMapping.getType(); + if (namespaceMapping.isSimpleType(type)) { + out.println(" "); + } + + } + out.println("
    AttributeTypeDescription
    " + attributeMapping.getAttributeName() + "" + Utils.getXsdType(type) + + "" + attributeMapping.getDescription() + "
    "); + } + + if (hasElements) { + out.println(""); + out.println(" "); + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + Type type = attributeMapping.getType(); + if (!namespaceMapping.isSimpleType(type)) { + out.print(" "); + } + } + out.println("
    ElementTypeDescription
    " + attributeMapping.getAttributeName() + ""); + printComplexPropertyTypeDocumentation(out, namespaceMapping, type); + out.println("" + attributeMapping.getDescription() + "
    "); + } + } + + private void printComplexPropertyTypeDocumentation(PrintWriter out, NamespaceMapping namespaceMapping, Type type) { + if (type.isCollection()) { + out.print("("); + } + + List types; + if (type.isCollection()) { + types = Utils.findImplementationsOf(namespaceMapping, type.getNestedType()); + } else { + types = Utils.findImplementationsOf(namespaceMapping, type); + } + + for (Iterator iterator = types.iterator(); iterator.hasNext();) { + ElementMapping element = (ElementMapping) iterator.next(); + out.print("" + element.getElementName() + ""); + if (iterator.hasNext()) { + out.print(" | "); + } + } + if (types.size() == 0) { + out.print("<spring:bean/>"); + } + + if (type.isCollection()) { + out.print(")*"); + } + } + + public LogFacade getLog() { + return log; + } + + public void setLog(LogFacade log) { + this.log = log; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ElementMapping.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ElementMapping.java new file mode 100644 index 00000000..55f1114c --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ElementMapping.java @@ -0,0 +1,173 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +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.TreeSet; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class ElementMapping implements Comparable { + private final String namespace; + private final String elementName; + private final String className; + private final String description; + private final boolean rootElement; + private final String initMethod; + private final String destroyMethod; + private final String factoryMethod; + private final String contentProperty; + private final Set attributes; + private final Map attributesByName; + private final List constructors; + private final List flatProperties; + private final Map maps; + private final Map flatCollections; + private final List superClasses; + private final HashSet interfaces; + + public ElementMapping(String namespace, String elementName, String className, String description, + boolean rootElement, String initMethod, String destroyMethod, String factoryMethod, + String contentProperty, Set attributes, List constructors, List flatProperties, Map maps, + Map flatCollections, List superClasses, HashSet interfaces) { + this.superClasses = superClasses; + this.interfaces = interfaces; + if (namespace == null) throw new NullPointerException("namespace"); + if (elementName == null) throw new NullPointerException("elementName"); + if (className == null) throw new NullPointerException("className"); + if (attributes == null) throw new NullPointerException("attributes"); + if (constructors == null) throw new NullPointerException("constructors"); + + this.namespace = namespace; + this.elementName = elementName; + this.className = className; + this.description = description; + this.rootElement = rootElement; + this.initMethod = initMethod; + this.destroyMethod = destroyMethod; + this.factoryMethod = factoryMethod; + this.contentProperty = contentProperty; + this.constructors = constructors; + this.attributes = Collections.unmodifiableSet(new TreeSet(attributes)); + this.maps = Collections.unmodifiableMap(maps); + this.flatProperties = Collections.unmodifiableList(flatProperties); + this.flatCollections = Collections.unmodifiableMap(flatCollections); + + Map attributesByName = new HashMap(); + for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { + AttributeMapping attribute = (AttributeMapping) iterator.next(); + attributesByName.put(attribute.getAttributeName(), attribute); + } + this.attributesByName = Collections.unmodifiableMap(attributesByName); + } + + public String getNamespace() { + return namespace; + } + + public String getElementName() { + return elementName; + } + + public String getClassName() { + return className; + } + + public String getDescription() { + return description; + } + + public boolean isRootElement() { + return rootElement; + } + + public String getInitMethod() { + return initMethod; + } + + public String getDestroyMethod() { + return destroyMethod; + } + + public String getFactoryMethod() { + return factoryMethod; + } + + public String getContentProperty() { + return contentProperty; + } + + public Set getAttributes() { + return attributes; + } + + public AttributeMapping getAttribute(String attributeName) { + return (AttributeMapping) attributesByName.get(attributeName); + } + + public Map getMapMappings() { + return maps; + } + + public MapMapping getMapMapping(String name) { + return (MapMapping) maps.get(name); + } + + public Map getFlatCollections() { + return flatCollections; + } + + public List getFlatProperties() { + return flatProperties; + } + + public List getConstructors() { + return constructors; + } + + public int hashCode() { + return elementName.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof ElementMapping) { + return elementName.equals(((ElementMapping) obj).elementName); + } + return false; + } + + public int compareTo(Object obj) { + return elementName.compareTo(((ElementMapping) obj).elementName); + } + + public HashSet getInterfaces() { + return interfaces; + } + + public List getSuperClasses() { + return superClasses; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/GeneratorPlugin.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/GeneratorPlugin.java new file mode 100644 index 00000000..f79d8bd2 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/GeneratorPlugin.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.xbean.blueprint.generator; + +import java.io.IOException; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public interface GeneratorPlugin { + void generate(NamespaceMapping namespaceMapping) throws IOException; + + LogFacade getLog(); + void setLog(LogFacade log); + +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/InvalidModelException.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/InvalidModelException.java new file mode 100644 index 00000000..0c8cf068 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/InvalidModelException.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.xbean.blueprint.generator; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class InvalidModelException extends RuntimeException { + public InvalidModelException(String message) { + super(message); + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/LogFacade.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/LogFacade.java new file mode 100644 index 00000000..f8c10095 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/LogFacade.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.xbean.blueprint.generator; + +public interface LogFacade { + + void log(String message); + + void log(String message, int level); +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MapMapping.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MapMapping.java new file mode 100644 index 00000000..7afad440 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MapMapping.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.xbean.blueprint.generator; + +public class MapMapping { + private String entryName; + private String keyName; + private boolean flat; + private String dupsMode; + private String defaultKey; + + public MapMapping(String entryName, + String keyName, + boolean flat, + String dupsMode, + String defaultKey) { + this.entryName = entryName; + this.keyName = keyName; + this.flat = flat; + this.dupsMode = dupsMode; + this.defaultKey = defaultKey; + } + + public String getEntryName() { + return entryName; + } + + public String getKeyName() { + return keyName; + } + + public boolean isFlat() { + return flat; + } + + public String getDupsMode() { + return dupsMode; + } + + public String getDefaultKey() { + return defaultKey; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingGeneratorTask.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingGeneratorTask.java new file mode 100644 index 00000000..db3e835e --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingGeneratorTask.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.xbean.blueprint.generator; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.util.Iterator; +import java.util.Set; +import java.util.List; +import java.util.LinkedList; +import java.util.Arrays; +import java.util.Collections; +import java.util.StringTokenizer; +import java.beans.PropertyEditorManager; + +/** + * An Ant task for executing generating mapping metadata. + * + * @version $Revision$ + */ +public class MappingGeneratorTask extends MatchingTask implements LogFacade { + private String namespace; + private Path srcDir; + private String excludedClasses = null; + private File destFile = new File("target/classes/schema.xsd"); + private String metaInfDir = "target/classes/"; + private String propertyEditorPaths = "org.apache.xbean.blueprint.context.impl"; + + public File getDestFile() { + return destFile; + } + + public void setDestFile(File destFile) { + this.destFile = destFile; + } + + public String getMetaInfDir() { + return metaInfDir; + } + + public void setMetaInfDir(String metaInfDir) { + this.metaInfDir = metaInfDir; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public Path getSrcDir() { + return srcDir; + } + + public void setSrcDir(Path srcDir) { + this.srcDir = srcDir; + } + + public String getPropertyEditorPaths() { + return propertyEditorPaths; + } + + public void setPropertyEditorPaths(String propertyEditorPaths) { + this.propertyEditorPaths = propertyEditorPaths; + } + + public void execute() throws BuildException { + if (namespace == null) { + throw new BuildException("'namespace' must be specified"); + } + if (srcDir == null) { + throw new BuildException("'srcDir' must be specified"); + } + if (destFile == null) { + throw new BuildException("'destFile' must be specified"); + } + + if (propertyEditorPaths != null) { + List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath())); + StringTokenizer paths = new StringTokenizer(propertyEditorPaths, " ,"); + editorSearchPath.addAll(Collections.list(paths)); + PropertyEditorManager.setEditorSearchPath((String[]) editorSearchPath.toArray(new String[editorSearchPath.size()])); + } + + ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + try { + String[] excludedClasses = null; + if (this.excludedClasses != null) { + excludedClasses = this.excludedClasses.split(" *, *"); + } + MappingLoader mappingLoader = new QdoxMappingLoader(namespace, getFiles(srcDir), excludedClasses); + + GeneratorPlugin[] plugins = new GeneratorPlugin[]{ + new XmlMetadataGenerator(metaInfDir, destFile), + new DocumentationGenerator(destFile), + new XsdGenerator(destFile) + }; + + // load the mappings + Set namespaces = mappingLoader.loadNamespaces(); + if (namespaces.isEmpty()) { + System.out.println("Warning: no namespaces found!"); + } + + // generate the files + for (Iterator iterator = namespaces.iterator(); iterator.hasNext();) { + NamespaceMapping namespaceMapping = (NamespaceMapping) iterator.next(); + for (int i = 0; i < plugins.length; i++) { + GeneratorPlugin plugin = plugins[i]; + plugin.setLog(this); + plugin.generate(namespaceMapping); + } + } + + log("...done."); + } catch (Exception e) { + throw new BuildException(e); + } finally { + Thread.currentThread().setContextClassLoader(oldCL); + } + } + + private File[] getFiles(Path path) { + if (path == null) { + return null; + } + String[] paths = path.list(); + File[] files = new File[paths.length]; + for (int i = 0; i < files.length; i++) { + files[i] = new File(paths[i]).getAbsoluteFile(); + } + return files; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingLoader.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingLoader.java new file mode 100644 index 00000000..1188165c --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/MappingLoader.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.xbean.blueprint.generator; + +import java.io.IOException; +import java.util.Set; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public interface MappingLoader { + Set loadNamespaces() throws IOException; +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/NamespaceMapping.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/NamespaceMapping.java new file mode 100644 index 00000000..139b12d9 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/NamespaceMapping.java @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class NamespaceMapping implements Comparable { + private final String namespace; + private final Set elements; + private final Map elementsByName; + private final ElementMapping rootElement; + + public NamespaceMapping(String namespace, Set elements, ElementMapping rootElement) { + this.namespace = namespace; + this.elements = Collections.unmodifiableSet(new TreeSet(elements)); + this.rootElement = rootElement; + + Map elementsByName = new HashMap(); + for (Iterator iterator = elements.iterator(); iterator.hasNext();) { + ElementMapping elementMapping = (ElementMapping) iterator.next(); + elementsByName.put(elementMapping.getElementName(), elementMapping); + } + this.elementsByName = Collections.unmodifiableMap(elementsByName); + } + + public String getNamespace() { + return namespace; + } + + public Set getElements() { + return elements; + } + + public ElementMapping getElement(String elementName) { + return (ElementMapping) elementsByName.get(elementName); + } + + public ElementMapping getRootElement() { + return rootElement; + } + + public int hashCode() { + return namespace.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof NamespaceMapping) { + return namespace.equals(((NamespaceMapping) obj).namespace); + } + return false; + } + + public int compareTo(Object obj) { + return namespace.compareTo(((NamespaceMapping) obj).namespace); + } + + private final HashMap checkedTypes = new HashMap(); + + public boolean isSimpleType(Type type) { + Boolean b = (Boolean) checkedTypes.get(type); + if (b == null){ + b = Utils.isSimpleType(type)? Boolean.TRUE: Boolean.FALSE; + checkedTypes.put(type, b); + } + return b.booleanValue(); + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ParameterMapping.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ParameterMapping.java new file mode 100644 index 00000000..20183953 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/ParameterMapping.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.xbean.blueprint.generator; + +/** + * @version $Rev$ $Date$ + */ +public class ParameterMapping { + private final String name; + private final Type type; + + public ParameterMapping(String name, Type type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/QdoxMappingLoader.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/QdoxMappingLoader.java new file mode 100644 index 00000000..7fcb9600 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/QdoxMappingLoader.java @@ -0,0 +1,567 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import com.thoughtworks.qdox.JavaDocBuilder; +import com.thoughtworks.qdox.model.BeanProperty; +import com.thoughtworks.qdox.model.DocletTag; +import com.thoughtworks.qdox.model.JavaClass; +import com.thoughtworks.qdox.model.JavaMethod; +import com.thoughtworks.qdox.model.JavaParameter; +import com.thoughtworks.qdox.model.JavaSource; +import com.thoughtworks.qdox.model.Type; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class QdoxMappingLoader implements MappingLoader { + public static final String XBEAN_ANNOTATION = "org.apache.xbean.XBean"; + public static final String PROPERTY_ANNOTATION = "org.apache.xbean.Property"; + public static final String INIT_METHOD_ANNOTATION = "org.apache.xbean.InitMethod"; + public static final String DESTROY_METHOD_ANNOTATION = "org.apache.xbean.DestroyMethod"; + public static final String FACTORY_METHOD_ANNOTATION = "org.apache.xbean.FactoryMethod"; + public static final String MAP_ANNOTATION = "org.apache.xbean.Map"; + public static final String FLAT_PROPERTY_ANNOTATION = "org.apache.xbean.Flat"; + public static final String FLAT_COLLECTION_ANNOTATION = "org.apache.xbean.FlatCollection"; + public static final String ELEMENT_ANNOTATION = "org.apache.xbean.Element"; + + private static final Log log = LogFactory.getLog(QdoxMappingLoader.class); + private final String defaultNamespace; + private final File[] srcDirs; + private final String[] excludedClasses; + private Type collectionType; + + public QdoxMappingLoader(String defaultNamespace, File[] srcDirs, String[] excludedClasses) { + this.defaultNamespace = defaultNamespace; + this.srcDirs = srcDirs; + this.excludedClasses = excludedClasses; + } + + public String getDefaultNamespace() { + return defaultNamespace; + } + + public File[] getSrcDirs() { + return srcDirs; + } + + public Set loadNamespaces() throws IOException { + JavaDocBuilder builder = new JavaDocBuilder(); + + log.debug("Source directories: "); + + for (File sourceDirectory : srcDirs) { + if (!sourceDirectory.isDirectory() && !sourceDirectory.toString().endsWith(".jar")) { + log.warn("Specified source directory isn't a directory or a jar file: '" + sourceDirectory.getAbsolutePath() + "'."); + } + log.debug(" - " + sourceDirectory.getAbsolutePath()); + + getSourceFiles(sourceDirectory, excludedClasses, builder); + } + + collectionType = builder.getClassByName("java.util.Collection").asType(); + return loadNamespaces(builder); + } + + private Set loadNamespaces(JavaDocBuilder builder) { + // load all of the elements + List elements = loadElements(builder); + + // index the elements by namespace and find the root element of each namespace + Map> elementsByNamespace = new HashMap>(); + Map namespaceRoots = new HashMap(); + for (ElementMapping element : elements) { + String namespace = element.getNamespace(); + Set namespaceElements = elementsByNamespace.get(namespace); + if (namespaceElements == null) { + namespaceElements = new HashSet(); + elementsByNamespace.put(namespace, namespaceElements); + } + namespaceElements.add(element); + if (element.isRootElement()) { + if (namespaceRoots.containsKey(namespace)) { + log.info("Multiple root elements found for namespace " + namespace); + } + namespaceRoots.put(namespace, element); + } + } + + // build the NamespaceMapping objects + Set namespaces = new TreeSet(); + for (Map.Entry> entry : elementsByNamespace.entrySet()) { + String namespace = entry.getKey(); + Set namespaceElements = entry.getValue(); + ElementMapping rootElement = namespaceRoots.get(namespace); + NamespaceMapping namespaceMapping = new NamespaceMapping(namespace, namespaceElements, rootElement); + namespaces.add(namespaceMapping); + } + return Collections.unmodifiableSet(namespaces); + } + + private List loadElements(JavaDocBuilder builder) { + JavaSource[] javaSources = builder.getSources(); + List elements = new ArrayList(); + for (JavaSource javaSource : javaSources) { + if (javaSource.getClasses().length == 0) { + log.info("No Java Classes defined in: " + javaSource.getURL()); + } else { + JavaClass[] classes = javaSource.getClasses(); + for (JavaClass javaClass : classes) { + ElementMapping element = loadElement(builder, javaClass); + if (element != null && !javaClass.isAbstract()) { + elements.add(element); + } else { + log.debug("No XML annotation found for type: " + javaClass.getFullyQualifiedName()); + } + } + } + } + return elements; + } + + private ElementMapping loadElement(JavaDocBuilder builder, JavaClass javaClass) { + DocletTag xbeanTag = javaClass.getTagByName(XBEAN_ANNOTATION); + if (xbeanTag == null) { + return null; + } + + String element = getElementName(javaClass, xbeanTag); + String description = getProperty(xbeanTag, "description"); + if (description == null) { + description = javaClass.getComment(); + + } + String namespace = getProperty(xbeanTag, "namespace", defaultNamespace); + boolean root = getBooleanProperty(xbeanTag, "rootElement"); + String contentProperty = getProperty(xbeanTag, "contentProperty"); + String factoryClass = getProperty(xbeanTag, "factoryClass"); + + Map mapsByPropertyName = new HashMap(); + List flatProperties = new ArrayList(); + Map flatCollections = new HashMap(); + Set attributes = new HashSet(); + Map attributesByPropertyName = new HashMap(); + + for (JavaClass jClass = javaClass; jClass != null; jClass = jClass.getSuperJavaClass()) { + BeanProperty[] beanProperties = jClass.getBeanProperties(); + for (BeanProperty beanProperty : beanProperties) { + // we only care about properties with a setter + if (beanProperty.getMutator() != null) { + AttributeMapping attributeMapping = loadAttribute(beanProperty, ""); + if (attributeMapping != null) { + attributes.add(attributeMapping); + attributesByPropertyName.put(attributeMapping.getPropertyName(), attributeMapping); + } + JavaMethod acc = beanProperty.getAccessor(); + if (acc != null) { + DocletTag mapTag = acc.getTagByName(MAP_ANNOTATION); + if (mapTag != null) { + MapMapping mm = new MapMapping( + mapTag.getNamedParameter("entryName"), + mapTag.getNamedParameter("keyName"), + Boolean.valueOf(mapTag.getNamedParameter("flat")), + mapTag.getNamedParameter("dups"), + mapTag.getNamedParameter("defaultKey")); + mapsByPropertyName.put(beanProperty.getName(), mm); + } + + DocletTag flatColTag = acc.getTagByName(FLAT_COLLECTION_ANNOTATION); + if (flatColTag != null) { + String childName = flatColTag.getNamedParameter("childElement"); + if (childName == null) + throw new InvalidModelException("Flat collections must specify the childElement attribute."); + flatCollections.put(beanProperty.getName(), childName); + } + + DocletTag flatPropTag = acc.getTagByName(FLAT_PROPERTY_ANNOTATION); + if (flatPropTag != null) { + flatProperties.add(beanProperty.getName()); + } + } + } + } + } + + String initMethod = null; + String destroyMethod = null; + String factoryMethod = null; + for (JavaClass jClass = javaClass; jClass != null; jClass = jClass.getSuperJavaClass()) { + JavaMethod[] methods = javaClass.getMethods(); + for (JavaMethod method : methods) { + if (method.isPublic() && !method.isConstructor()) { + if (initMethod == null && method.getTagByName(INIT_METHOD_ANNOTATION) != null) { + initMethod = method.getName(); + } + if (destroyMethod == null && method.getTagByName(DESTROY_METHOD_ANNOTATION) != null) { + destroyMethod = method.getName(); + } + if (factoryMethod == null && method.getTagByName(FACTORY_METHOD_ANNOTATION) != null) { + factoryMethod = method.getName(); + } + + } + } + } + + List> constructorArgs = new ArrayList>(); + JavaMethod[] methods = javaClass.getMethods(); + for (JavaMethod method : methods) { + JavaParameter[] parameters = method.getParameters(); + if (isValidConstructor(factoryMethod, method, parameters)) { + List args = new ArrayList(parameters.length); + for (JavaParameter parameter : parameters) { + AttributeMapping attributeMapping = attributesByPropertyName.get(parameter.getName()); + if (attributeMapping == null) { + attributeMapping = loadParameter(parameter); + + attributes.add(attributeMapping); + attributesByPropertyName.put(attributeMapping.getPropertyName(), attributeMapping); + } + args.add(new ParameterMapping(attributeMapping.getPropertyName(), toMappingType(parameter.getType(), null))); + } + constructorArgs.add(Collections.unmodifiableList(args)); + } + } + + HashSet interfaces = new HashSet(); + interfaces.addAll(getFullyQualifiedNames(javaClass.getImplementedInterfaces())); + + JavaClass actualClass = javaClass; + if (factoryClass != null) { + JavaClass clazz = builder.getClassByName(factoryClass); + if (clazz != null) { + log.info("Detected factory: using " + factoryClass + " instead of " + javaClass.getFullyQualifiedName()); + actualClass = clazz; + } else { + log.info("Could not load class built by factory: " + factoryClass); + } + } + + ArrayList superClasses = new ArrayList(); + JavaClass p = actualClass; + if (actualClass != javaClass) { + superClasses.add(actualClass.getFullyQualifiedName()); + } + while (true) { + JavaClass s = p.getSuperJavaClass(); + if (s == null || s.equals(p) || "java.lang.Object".equals(s.getFullyQualifiedName())) { + break; + } + p = s; + superClasses.add(p.getFullyQualifiedName()); + interfaces.addAll(getFullyQualifiedNames(p.getImplementedInterfaces())); + } + + return new ElementMapping(namespace, + element, + javaClass.getFullyQualifiedName(), + description, + root, + initMethod, + destroyMethod, + factoryMethod, + contentProperty, + attributes, + constructorArgs, + flatProperties, + mapsByPropertyName, + flatCollections, + superClasses, + interfaces); + } + + private List getFullyQualifiedNames(JavaClass[] implementedInterfaces) { + ArrayList l = new ArrayList(); + for (JavaClass implementedInterface : implementedInterfaces) { + l.add(implementedInterface.getFullyQualifiedName()); + } + return l; + } + + private String getElementName(JavaClass javaClass, DocletTag tag) { + String elementName = getProperty(tag, "element"); + if (elementName == null) { + String className = javaClass.getFullyQualifiedName(); + int index = className.lastIndexOf("."); + if (index > 0) { + className = className.substring(index + 1); + } + // strip off "Bean" from a spring factory bean + if (className.endsWith("FactoryBean")) { + className = className.substring(0, className.length() - 4); + } + elementName = Utils.decapitalise(className); + } + return elementName; + } + + private AttributeMapping loadAttribute(BeanProperty beanProperty, String defaultDescription) { + DocletTag propertyTag = getPropertyTag(beanProperty); + + if (getBooleanProperty(propertyTag, "hidden")) { + return null; + } + + String attribute = getProperty(propertyTag, "alias", beanProperty.getName()); + String attributeDescription = getAttributeDescription(beanProperty, propertyTag, defaultDescription); + String defaultValue = getProperty(propertyTag, "default"); + boolean fixed = getBooleanProperty(propertyTag, "fixed"); + boolean required = getBooleanProperty(propertyTag, "required"); + String nestedType = getProperty(propertyTag, "nestedType"); + String propertyEditor = getProperty(propertyTag, "propertyEditor"); + + return new AttributeMapping(attribute, + beanProperty.getName(), + attributeDescription, + toMappingType(beanProperty.getType(), nestedType), + defaultValue, + fixed, + required, + propertyEditor); + } + + private static DocletTag getPropertyTag(BeanProperty beanProperty) { + JavaMethod accessor = beanProperty.getAccessor(); + if (accessor != null) { + DocletTag propertyTag = accessor.getTagByName(PROPERTY_ANNOTATION); + if (propertyTag != null) { + return propertyTag; + } + } + JavaMethod mutator = beanProperty.getMutator(); + if (mutator != null) { + DocletTag propertyTag = mutator.getTagByName(PROPERTY_ANNOTATION); + if (propertyTag != null) { + return propertyTag; + } + } + return null; + } + + private String getAttributeDescription(BeanProperty beanProperty, DocletTag propertyTag, String defaultDescription) { + String description = getProperty(propertyTag, "description"); + if (description != null && description.trim().length() > 0) { + return description.trim(); + } + + JavaMethod accessor = beanProperty.getAccessor(); + if (accessor != null) { + description = accessor.getComment(); + if (description != null && description.trim().length() > 0) { + return description.trim(); + } + } + + JavaMethod mutator = beanProperty.getMutator(); + if (mutator != null) { + description = mutator.getComment(); + if (description != null && description.trim().length() > 0) { + return description.trim(); + } + } + return defaultDescription; + } + + private AttributeMapping loadParameter(JavaParameter parameter) { + String parameterName = parameter.getName(); + String parameterDescription = getParameterDescription(parameter); + + // first attempt to load the attribute from the java beans accessor methods + JavaClass javaClass = parameter.getParentMethod().getParentClass(); + BeanProperty beanProperty = javaClass.getBeanProperty(parameterName); + if (beanProperty != null) { + AttributeMapping attributeMapping = loadAttribute(beanProperty, parameterDescription); + // if the attribute mapping is null, the property was tagged as hidden and this is an error + if (attributeMapping == null) { + throw new InvalidModelException("Hidden property usage: " + + "The construction method " + toMethodLocator(parameter.getParentMethod()) + + " can not use a hidded property " + parameterName); + } + return attributeMapping; + } + + // create an attribute solely based on the parameter information + return new AttributeMapping(parameterName, + parameterName, + parameterDescription, + toMappingType(parameter.getType(), null), + null, + false, + false, + null); + } + + private String getParameterDescription(JavaParameter parameter) { + String parameterName = parameter.getName(); + DocletTag[] tags = parameter.getParentMethod().getTagsByName("param"); + for (DocletTag tag : tags) { + if (tag.getParameters()[0].equals(parameterName)) { + String parameterDescription = tag.getValue().trim(); + if (parameterDescription.startsWith(parameterName)) { + parameterDescription = parameterDescription.substring(parameterName.length()).trim(); + } + return parameterDescription; + } + } + return null; + } + + private boolean isValidConstructor(String factoryMethod, JavaMethod method, JavaParameter[] parameters) { + if (!method.isPublic() || parameters.length == 0) { + return false; + } + + if (factoryMethod == null) { + return method.isConstructor(); + } else { + return method.getName().equals(factoryMethod); + } + } + + private static String getProperty(DocletTag propertyTag, String propertyName) { + return getProperty(propertyTag, propertyName, null); + } + + private static String getProperty(DocletTag propertyTag, String propertyName, String defaultValue) { + String value = null; + if (propertyTag != null) { + value = propertyTag.getNamedParameter(propertyName); + } + if (value == null) { + return defaultValue; + } + return value; + } + + private boolean getBooleanProperty(DocletTag propertyTag, String propertyName) { + return toBoolean(getProperty(propertyTag, propertyName)); + } + + private static boolean toBoolean(String value) { + if (value != null) { + return Boolean.valueOf(value); + } + return false; + } + + private org.apache.xbean.blueprint.generator.Type toMappingType(Type type, String nestedType) { + try { + if (type.isArray()) { + return org.apache.xbean.blueprint.generator.Type.newArrayType(type.getValue(), type.getDimensions()); + } else if (type.isA(collectionType)) { + if (nestedType == null) nestedType = "java.lang.Object"; + return org.apache.xbean.blueprint.generator.Type.newCollectionType(type.getValue(), + org.apache.xbean.blueprint.generator.Type.newSimpleType(nestedType)); + } + } catch (Throwable t) { + log.debug("Could not load type mapping", t); + } + return org.apache.xbean.blueprint.generator.Type.newSimpleType(type.getValue()); + } + + private static String toMethodLocator(JavaMethod method) { + StringBuffer buf = new StringBuffer(); + buf.append(method.getParentClass().getFullyQualifiedName()); + if (!method.isConstructor()) { + buf.append(".").append(method.getName()); + } + buf.append("("); + JavaParameter[] parameters = method.getParameters(); + for (int i = 0; i < parameters.length; i++) { + JavaParameter parameter = parameters[i]; + if (i > 0) { + buf.append(", "); + } + buf.append(parameter.getName()); + } + buf.append(") : ").append(method.getLineNumber()); + return buf.toString(); + } + + private static void getSourceFiles(File base, String[] excludedClasses, JavaDocBuilder builder) throws IOException { + if (base.isDirectory()) { + listAllFileNames(base, "", excludedClasses, builder); + } else { + listAllJarEntries(base, excludedClasses, builder); + } + } + + private static void listAllFileNames(File base, String prefix, String[] excludedClasses, JavaDocBuilder builder) throws IOException { + if (!base.canRead() || !base.isDirectory()) { + throw new IllegalArgumentException(base.getAbsolutePath()); + } + File[] hits = base.listFiles(); + for (File hit : hits) { + String name = prefix.equals("") ? hit.getName() : prefix + "/" + hit.getName(); + if (hit.canRead() && !isExcluded(name, excludedClasses)) { + if (hit.isDirectory()) { + listAllFileNames(hit, name, excludedClasses, builder); + } else if (name.endsWith(".java")) { + builder.addSource(hit); + } + } + } + } + + private static void listAllJarEntries(File base, String[] excludedClasses, JavaDocBuilder builder) throws IOException { + JarFile jarFile = new JarFile(base); + for (Enumeration entries = jarFile.entries(); entries.hasMoreElements(); ) { + JarEntry entry = (JarEntry) entries.nextElement(); + String name = entry.getName(); + if (name.endsWith(".java") && !isExcluded(name, excludedClasses) && !name.endsWith("/package-info.java")) { + builder.addSource(new URL("jar:" + base.toURL().toString() + "!/" + name)); + } + } + } + + private static boolean isExcluded(String sourceName, String[] excludedClasses) { + if (excludedClasses == null) { + return false; + } + + String className = sourceName; + if (sourceName.endsWith(".java")) { + className = className.substring(0, className.length() - ".java".length()); + } + className = className.replace('/', '.'); + for (String excludedClass : excludedClasses) { + if (className.equals(excludedClass)) { + return true; + } + } + return false; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/SchemaGenerator.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/SchemaGenerator.java new file mode 100644 index 00000000..a1e109bf --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/SchemaGenerator.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Set; + +/** + * @version $Revision$ + */ +public class SchemaGenerator { + private final MappingLoader mappingLoader; + private final GeneratorPlugin[] plugins; + private final LogFacade log; + + public SchemaGenerator(LogFacade log, MappingLoader mappingLoader, GeneratorPlugin[] plugins) { + this.log = log; + this.mappingLoader = mappingLoader; + this.plugins = plugins; + } + + public void generate() throws IOException { + Set namespaces = mappingLoader.loadNamespaces(); + if (namespaces.isEmpty()) { + log.log("Warning: no namespaces found!"); + } + + for (Iterator iterator = namespaces.iterator(); iterator.hasNext();) { + NamespaceMapping namespaceMapping = (NamespaceMapping) iterator.next(); + for (int i = 0; i < plugins.length; i++) { + GeneratorPlugin plugin = plugins[i]; + plugin.generate(namespaceMapping); + } + } + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Type.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Type.java new file mode 100644 index 00000000..48eaa4f8 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Type.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.xbean.blueprint.generator; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class Type { + private final String name; + private final Type nestedType; + private final boolean primitive; + + public static Type newSimpleType(String name) { + if (name == null) throw new NullPointerException("type"); + if (name.indexOf("[") >= 0 || name.indexOf("]") >= 0) { + throw new IllegalArgumentException("Name can not contain '[' or ']' " + name); + } + return new Type(name, null); + } + + public static Type newArrayType(String type, int dimensions) { + if (type == null) throw new NullPointerException("type"); + if (dimensions < 1) throw new IllegalArgumentException("dimensions must be at least one"); + StringBuffer buf = new StringBuffer(type.length() + (dimensions * 2)); + buf.append(type); + for (int i = 0; i < dimensions; i ++) { + buf.append("[]"); + } + return new Type(buf.toString(), newSimpleType(type)); + } + + public static Type newCollectionType(String collectionType, Type elementType) { + if (collectionType == null) throw new NullPointerException("collectionType"); + if (elementType == null) throw new NullPointerException("elementType"); + return new Type(collectionType, elementType); + } + + private Type(String name, Type nestedType) { + this.name = name; + this.nestedType = nestedType; + primitive = (nestedType == null) && primitives.contains(name); + } + + public String getName() { + return name; + } + + public Type getNestedType() { + return nestedType; + } + + public boolean isCollection() { + return nestedType != null; + } + + public boolean isPrimitive() { + return primitive; + } + + public int hashCode() { + return super.hashCode(); + } + + public boolean equals(Object obj) { + return super.equals(obj); + } + + private static final Set primitives; + + static { + Set set = new HashSet(); + set.add("boolean"); + set.add("byte"); + set.add("char"); + set.add("short"); + set.add("int"); + set.add("long"); + set.add("float"); + set.add("double"); + primitives = Collections.unmodifiableSet(set); + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Utils.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Utils.java new file mode 100644 index 00000000..9aa66f04 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/Utils.java @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public final class Utils { + public static final String XBEAN_ANNOTATION = "org.apache.xbean.XBean"; + public static final String PROPERTY_ANNOTATION = "org.apache.xbean.Property"; + + private Utils() { + } + + public static String decapitalise(String value) { + if (value == null || value.length() == 0) { + return value; + } + return value.substring(0, 1).toLowerCase() + value.substring(1); + } + + public static boolean isSimpleType(Type type) { + if (type.isPrimitive()) { + return true; + } + if (type.isCollection()) { + return false; + } + + String name = type.getName(); + if (name.equals("java.lang.Class") || + name.equals("javax.xml.namespace.QName")) { + return true; + } + return hasPropertyEditor(name); + } + + private static boolean hasPropertyEditor(String type) { + Class theClass; + try { + theClass = loadClass(type); + // lets see if we can find a property editor for this type + PropertyEditor editor = PropertyEditorManager.findEditor(theClass); + return editor != null; + } catch (Throwable e) { + System.out.println("Warning, could not load class: " + type + ": " + e); + return false; + } + } + + /** + * Attempts to load the class on the current thread context class loader or + * the class loader which loaded us + */ + private static Class loadClass(String name) throws ClassNotFoundException { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + try { + return contextClassLoader.loadClass(name); + } catch (ClassNotFoundException e) { + } + } + return Utils.class.getClassLoader().loadClass(name); + } + + public static String getXsdType(Type type) { + String name = type.getName(); + String xsdType = (String) XSD_TYPES.get(name); + if (xsdType == null) { + xsdType = "xs:string"; + } + return xsdType; + } + + public static final Map XSD_TYPES; + + static { + // TODO check these XSD types are right... + Map map = new HashMap(); + map.put(String.class.getName(), "xs:string"); + map.put(Boolean.class.getName(), "xs:boolean"); + map.put(boolean.class.getName(), "xs:boolean"); + map.put(Byte.class.getName(), "xs:byte"); + map.put(byte.class.getName(), "xs:byte"); + map.put(Short.class.getName(), "xs:short"); + map.put(short.class.getName(), "xs:short"); + map.put(Integer.class.getName(), "xs:integer"); + map.put(int.class.getName(), "xs:integer"); + map.put(Long.class.getName(), "xs:long"); + map.put(long.class.getName(), "xs:long"); + map.put(Float.class.getName(), "xs:float"); + map.put(float.class.getName(), "xs:float"); + map.put(Double.class.getName(), "xs:double"); + map.put(double.class.getName(), "xs:double"); + map.put(java.util.Date.class.getName(), "xs:date"); + map.put(java.sql.Date.class.getName(), "xs:date"); + map.put("javax.xml.namespace.QName", "xs:QName"); + XSD_TYPES = Collections.unmodifiableMap(map); + } + + public static List findImplementationsOf(NamespaceMapping namespaceMapping, Type type) { + List elements = new ArrayList(); + String nestedTypeName = type.getName(); + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + if (element.getClassName().equals(nestedTypeName) || + element.getInterfaces().contains(nestedTypeName) || + element.getSuperClasses().contains(nestedTypeName)) + { + elements.add(element); + } + } + return elements; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/WikiDocumentationGenerator.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/WikiDocumentationGenerator.java new file mode 100644 index 00000000..d403894b --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/WikiDocumentationGenerator.java @@ -0,0 +1,212 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Hiram Chirino + * @version $Id$ + * @since 1.0 + */ +public class WikiDocumentationGenerator implements GeneratorPlugin { + private final File destFile; + private LogFacade log; + + public WikiDocumentationGenerator(File destFile) { + this.destFile = destFile; + } + + public void generate(NamespaceMapping namespaceMapping) throws IOException { + String namespace = namespaceMapping.getNamespace(); + File file = new File(destFile.getParentFile(), destFile.getName() + ".wiki"); + log.log("Generating WIKI documentation file: " + file + " for namespace: " + namespace); + PrintWriter out = new PrintWriter(new FileWriter(file)); + try { + generateDocumentation(out, namespaceMapping); + } finally { + out.close(); + } + } + + private void generateDocumentation(PrintWriter out, NamespaceMapping namespaceMapping) { + HashMap refercencedTypes = new HashMap(); + + // Build of map of types that are referenced by element types. + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attribute = (AttributeMapping) iterator.next(); + Type type = getNestedType( attribute.getType() ); + + if( namespaceMapping.isSimpleType( type) ) + continue; + + if( !refercencedTypes.containsKey(type.getName()) ) + refercencedTypes.put(type.getName(), new ArrayList()); + } + } + + // Add all the elements that implement those types. + for (Iterator iter = refercencedTypes.entrySet().iterator(); iter.hasNext();) { + + Map.Entry entry = (Map.Entry) iter.next(); + String type = (String) entry.getKey(); + ArrayList implementations = (ArrayList) entry.getValue(); + + for (Iterator iterator = namespaceMapping.getElements().iterator(); iterator.hasNext();) { + ElementMapping element = (ElementMapping) iterator.next(); + + // Check to see if the class is matches + boolean matched=false; + if (type.equals(element.getClassName())) { + implementations.add(element); + matched=true; + } + + // Perhaps a super class matches. + if(!matched) { + for (Iterator j = element.getSuperClasses().iterator(); j.hasNext();) { + String t = (String) j.next(); + if( type.equals(t) ) { + implementations.add(element); + matched=true; + break; + } + } + } + + // Or it might be an interface. + if(!matched) { + for (Iterator j = element.getInterfaces().iterator(); j.hasNext();) { + String t = (String) j.next(); + if( type.equals(t) ) { + implementations.add(element); + matched=true; + break; + } + } + } + } + } + + // Remove any entries that did not have associated elements + for (Iterator iter = refercencedTypes.values().iterator(); iter.hasNext();) { + ArrayList implementations = (ArrayList) iter.next(); + if( implementations.isEmpty() ) + iter.remove(); + } + + generateElementsByType(out, namespaceMapping, refercencedTypes); + generateElementsDetail(out, namespaceMapping, refercencedTypes); + generateElementsIndex(out, namespaceMapping, refercencedTypes); + } + + private Type getNestedType(Type type) { + if( type.isCollection() ) { + return getNestedType(type.getNestedType()); + } else { + return type; + } + } + + private void generateElementsByType(PrintWriter out, NamespaceMapping namespaceMapping, HashMap refercencedTypes) { + out.println("h3. Elements By Type"); + for (Iterator iter = refercencedTypes.entrySet().iterator(); iter.hasNext();) { + Entry entry = (Entry) iter.next(); + String className = (String) entry.getKey(); + Collection elements = (Collection) entry.getValue(); + + out.println("{anchor:"+className+"-types}"); + out.println("h4. The _["+className+"|#"+className+"-types]_ Type Implementations"); + + for (Iterator iterator = elements.iterator(); iterator.hasNext();) { + ElementMapping element = (ElementMapping) iterator.next(); + out.println(" | _[<"+element.getElementName() +">|#"+element.getElementName() +"-element]_ | {html}"+element.getDescription()+"{html} |"); + } + out.println(); + } + out.println(); + } + + private void generateElementsIndex(PrintWriter out, NamespaceMapping namespaceMapping, HashMap refercencedTypes) { + + out.println("h3. Element Index"); + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + out.println(" | _[<"+element.getElementName() +">|#"+element.getElementName() +"-element]_ | {html}"+element.getDescription()+"{html} |"); + } + out.println(); + } + + private void generateElementsDetail(PrintWriter out, NamespaceMapping namespaceMapping, HashMap refercencedTypes) { + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + generateElementDetail(out, namespaceMapping, element, refercencedTypes); + } + } + + private void generateElementDetail(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element, HashMap refercencedTypes) { + + out.println("{anchor:" + element.getElementName() + "-element}"); + out.println("h3. The _[<" + element.getElementName() + ">|#" + element.getElementName() + "-element]_ Element"); + + out.println(" {html}"+element.getDescription()+"{html}"); + + if( element.getAttributes().size() > 0 ) { + out.println("h4. Properties"); + out.println(" || Property Name || Type || Description ||"); + + for ( Iterator iterator = element.getAttributes().iterator(); iterator.hasNext(); ) { + AttributeMapping attribute = (AttributeMapping) iterator.next(); + Type type = attribute.getPropertyEditor() != null ? Type.newSimpleType(String.class.getName()): attribute.getType(); + out.println(" | " + attribute.getAttributeName() + " | "+getTypeLink(type, refercencedTypes)+" | {html}"+attribute.getDescription()+"{html} |"); + } + } + out.println(); + } + + private String getTypeLink(Type type, HashMap refercencedTypes) { + if (type.isCollection()) { + return "(" + getTypeLink(type.getNestedType(), refercencedTypes) + ")\\*"; + } else { + if( refercencedTypes.containsKey(type.getName()) ) { + return "_["+type.getName()+"|#"+type.getName()+"-types]_"; + } else { + return "_"+type.getName()+"_"; + } + } + } + + public LogFacade getLog() { + return log; + } + + public void setLog(LogFacade log) { + this.log = log; + } +} + diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlMetadataGenerator.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlMetadataGenerator.java new file mode 100644 index 00000000..b9c1f3c8 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlMetadataGenerator.java @@ -0,0 +1,274 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Properties; + +import org.apache.xbean.blueprint.context.impl.NamespaceHelper; + + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class XmlMetadataGenerator implements GeneratorPlugin { + private final String metaInfDir; + private LogFacade log; + private final File schema; + + public static final String NAMESPACE_HANDLER = "org.apache.xbean.blueprint.context.v2.XBeanNamespaceHandler"; + private final boolean generateSpringSchemasFile; + private final boolean generateSpringHandlersFile; + + public XmlMetadataGenerator(String metaInfDir, File schema) { + this(metaInfDir, schema, true, true); + } + + public XmlMetadataGenerator(String metaInfDir, File schema, boolean generateSpringSchemasFile, boolean generateSpringHandlersFile) { + this.metaInfDir = metaInfDir; + this.schema = schema; + this.generateSpringSchemasFile = generateSpringSchemasFile; + this.generateSpringHandlersFile = generateSpringHandlersFile; + } + + public void generate(NamespaceMapping namespaceMapping) throws IOException { + String namespace = namespaceMapping.getNamespace(); + if (namespace == null) { + return; + } + + File file = new File(metaInfDir, NamespaceHelper.createDiscoveryPathName(namespace)); + file.getParentFile().mkdirs(); + log.log("Generating META-INF properties file: " + file + " for namespace: " + namespace); + PrintWriter out = new PrintWriter(new FileWriter(file)); + try { + generatePropertiesFile(out, namespaceMapping.getElements()); + } finally { + out.close(); + } + + if( generateSpringHandlersFile ) { + + // Generate spring 2.0 mapping + file = new File(metaInfDir, "META-INF/spring.handlers"); + + Properties properties = new Properties(); + if (!file.exists()) { + log.log("Generating Spring 2.0 handler mapping: " + file + " for namespace: " + namespace); + } else { + log.log("Updating Spring 2.0 handler mapping: " + file + " for namespace: " + namespace); + + // read in current file + InputStream in = new FileInputStream(file); + try { + properties.load(in); + } catch (IOException e) { + in.close(); + } + } + + // add property + properties.put(namespace, NAMESPACE_HANDLER); + + // write properties + OutputStream fout = new FileOutputStream(file); + try { + properties.store(fout, "Generated by xbean-spring"); + } finally { + fout.close(); + } + } + + if (schema != null && generateSpringSchemasFile ) { + String cp = new File(metaInfDir).toURI().relativize(schema.toURI()).toString(); + file = new File(metaInfDir, "META-INF/spring.schemas"); + + Properties properties = new Properties(); + if (!file.exists()) { + log.log("Generating Spring 2.0 schema mapping: " + file + " for namespace: " + namespace); + } else { + log.log("Updating Spring 2.0 schema mapping: " + file + " for namespace: " + namespace); + + // read in current file + InputStream in = new FileInputStream(file); + try { + properties.load(in); + } catch (IOException e) { + in.close(); + } + } + + // add property + String uri = namespace; + if (!uri.endsWith("/")) { + uri += "/"; + } + properties.put(uri + cp, cp); + + // write properties + OutputStream fout = new FileOutputStream(file); + try { + properties.store(fout, "Generated by xbean-spring"); + } finally { + fout.close(); + } + } + } + + private void generatePropertiesFile(PrintWriter out, Set elements) { + out.println("# NOTE: this file is autogenerated by Apache XBean"); + out.println(); + out.println("# beans"); + + for (Iterator iter = elements.iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + out.println(element.getElementName() + " = " + element.getClassName()); + + generatePropertiesFileContent(out, element); + generatePropertiesFilePropertyAliases(out, element); + generatePropertiesFileConstructors(out, element); + out.println(); + } + } + + private void generatePropertiesFileContent(PrintWriter out, ElementMapping element) { + String contentProperty = element.getContentProperty(); + if (contentProperty != null) { + out.println(element.getElementName() + ".contentProperty = " + contentProperty); + } + String initMethod = element.getInitMethod(); + if (initMethod != null) { + out.println(element.getElementName() + ".initMethod = " + initMethod); + } + + String destroyMethod = element.getDestroyMethod(); + if (destroyMethod != null) { + out.println(element.getElementName() + ".destroyMethod = " + destroyMethod); + } + + String factoryMethod = element.getFactoryMethod(); + if (factoryMethod != null) { + out.println(element.getElementName() + ".factoryMethod = " + factoryMethod); + } + + for (Iterator iter = element.getAttributes().iterator(); iter.hasNext();) { + AttributeMapping attribute = (AttributeMapping) iter.next(); + if( attribute.getPropertyEditor() !=null ) { + out.println(element.getElementName() + "."+attribute.getPropertyName()+ ".propertyEditor = " + attribute.getPropertyEditor()); + } + } + + List flatProperties = element.getFlatProperties(); + for (Iterator itr = flatProperties.iterator(); itr.hasNext();) { + out.println(element.getElementName() + "." + itr.next() + ".flat"); + } + + Map maps = element.getMapMappings(); + for (Iterator itr = maps.entrySet().iterator(); itr.hasNext();) { + Map.Entry entry = (Map.Entry) itr.next(); + MapMapping mm = (MapMapping) entry.getValue(); + if (mm.getEntryName() != null) { + out.println(element.getElementName() + "." + entry.getKey() + ".map.entryName = " + mm.getEntryName()); + } + if (mm.getKeyName() != null) { + out.println(element.getElementName() + "." + entry.getKey() + ".map.keyName = " + mm.getKeyName()); + } + if (mm.isFlat()) { + out.println(element.getElementName() + "." + entry.getKey() + ".map.flat = " + Boolean.toString(mm.isFlat())); + } + if (mm.getDupsMode() != null) { + out.println(element.getElementName() + "." + entry.getKey() + ".map.dups = " + mm.getDupsMode()); + } + if (mm.getDefaultKey() != null) { + out.println(element.getElementName() + "." + entry.getKey() + ".map.defaultKey = " + mm.getDefaultKey()); + } + } + + Map flatCollections = element.getFlatCollections(); + for (Iterator itr = flatCollections.entrySet().iterator(); itr.hasNext();) { + Map.Entry entry = (Map.Entry) itr.next(); + String child = (String) entry.getValue(); + out.println(element.getElementName() + "." + child + ".flatCollection = " + entry.getKey()); + } + } + + private void generatePropertiesFileConstructors(PrintWriter out, ElementMapping element) { + List constructors = element.getConstructors(); + for (Iterator iterator = constructors.iterator(); iterator.hasNext();) { + List args = (List) iterator.next(); + generatePropertiesFileConstructor(out, element, args); + } + } + + private void generatePropertiesFileConstructor(PrintWriter out, ElementMapping element, List args) { + out.print(element.getClassName()); + if (element.getFactoryMethod() != null) { + out.print("." + element.getFactoryMethod()); + } + out.print("("); + for (Iterator iterator = args.iterator(); iterator.hasNext();) { + ParameterMapping parameterMapping = (ParameterMapping) iterator.next(); + out.print(parameterMapping.getType().getName()); + if (iterator.hasNext()) { + out.print(","); + } + } + out.print(").parameterNames ="); + for (Iterator iterator = args.iterator(); iterator.hasNext();) { + ParameterMapping parameterMapping = (ParameterMapping) iterator.next(); + out.print(" "); + out.print(parameterMapping.getName()); + } + out.println(); + } + + private void generatePropertiesFilePropertyAliases(PrintWriter out, ElementMapping element) { + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + String propertyName = attributeMapping.getPropertyName(); + String attributeName = attributeMapping.getAttributeName(); + if (!propertyName.equals(attributeName)) { + if (List.class.getName().equals(attributeMapping.getType().getName())) { + out.println(element.getElementName() + ".list." + attributeName + " = " + propertyName); + } else { + out.println(element.getElementName() + ".alias." + attributeName + " = " + propertyName); + } + } + } + } + + public LogFacade getLog() { + return log; + } + + public void setLog(LogFacade log) { + this.log = log; + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlWriter.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlWriter.java new file mode 100644 index 00000000..ab904ba8 --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XmlWriter.java @@ -0,0 +1,215 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.PrintWriter; +import java.io.Writer; +import java.util.LinkedList; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class XmlWriter { + private PrintWriter writer; + + private LinkedList elementStack = new LinkedList(); + + private boolean tagInProgress; + + private int depth; + + private String lineIndenter; + + private String encoding; + + private String docType; + + private boolean readyForNewLine; + + private boolean tagIsEmpty; + + public XmlWriter(PrintWriter writer, String lineIndenter) { + this(writer, lineIndenter, null, null); + } + + public XmlWriter(Writer writer, String lineIndenter) { + this(new PrintWriter(writer), lineIndenter); + } + + public XmlWriter(PrintWriter writer) { + this(writer, null, null); + } + + public XmlWriter(Writer writer) { + this(new PrintWriter(writer)); + } + + public XmlWriter(PrintWriter writer, String lineIndenter, String encoding, String doctype) { + this.writer = writer; + + this.lineIndenter = lineIndenter; + + this.encoding = encoding; + + this.docType = doctype; + + if (docType != null || encoding != null) { + writeDocumentHeaders(); + } + } + + public XmlWriter(Writer writer, String lineIndenter, String encoding, String doctype) { + this(new PrintWriter(writer), lineIndenter, encoding, doctype); + } + + public XmlWriter(PrintWriter writer, String encoding, String doctype) { + this(writer, " ", encoding, doctype); + } + + public XmlWriter(Writer writer, String encoding, String doctype) { + this(new PrintWriter(writer), encoding, doctype); + } + + public void startElement(String name) { + tagIsEmpty = false; + + finishTag(); + + write("<"); + + write(name); + + elementStack.addLast(name); + + tagInProgress = true; + + depth++; + + readyForNewLine = true; + + tagIsEmpty = true; + } + + public void writeText(String text) { + writeText(text, true); + } + + public void writeMarkup(String text) { + writeText(text, false); + } + + private void writeText(String text, boolean escapeHtml) { + readyForNewLine = false; + + tagIsEmpty = false; + + finishTag(); + + if (escapeHtml) { + text = text.replaceAll("&", "&"); + + text = text.replaceAll("<", "<"); + + text = text.replaceAll(">", ">"); + } + + write(text); + } + + public void addAttribute(String key, String value) { + write(" "); + + write(key); + + write("=\""); + + write(value); + + write("\""); + } + + public void endElement() { + depth--; + + if (tagIsEmpty) { + write("/"); + + readyForNewLine = false; + + finishTag(); + + elementStack.removeLast(); + } else { + finishTag(); + + write(""); + } + + readyForNewLine = true; + } + + private void write(String str) { + writer.write(str); + } + + private void finishTag() { + if (tagInProgress) { + write(">"); + } + + tagInProgress = false; + + if (readyForNewLine) { + endOfLine(); + } + readyForNewLine = false; + + tagIsEmpty = false; + } + + protected void endOfLine() { + write("\n"); + + for (int i = 0; i < depth; i++) { + write(lineIndenter); + } + } + + private void writeDocumentHeaders() { + write(""); + + endOfLine(); + + if (docType != null) { + write(""); + + endOfLine(); + } + } +} diff --git a/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XsdGenerator.java b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XsdGenerator.java new file mode 100644 index 00000000..29cc4dbb --- /dev/null +++ b/xbean-blueprint/src/main/java/org/apache/xbean/blueprint/generator/XsdGenerator.java @@ -0,0 +1,219 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class XsdGenerator implements GeneratorPlugin { + private final File destFile; + private LogFacade log; + + public XsdGenerator(File destFile) { + this.destFile = destFile; + } + + public void generate(NamespaceMapping namespaceMapping) throws IOException { + // TODO can only handle 1 schema document so far... + File file = destFile; + file.getParentFile().mkdirs(); + log.log("Generating XSD file: " + file + " for namespace: " + namespaceMapping.getNamespace()); + PrintWriter out = new PrintWriter(new FileWriter(file)); + try { + generateSchema(out, namespaceMapping); + } finally { + out.close(); + } + } + + public void generateSchema(PrintWriter out, NamespaceMapping namespaceMapping) { + out.println(""); + out.println(""); + out.println(); + out.println(""); + + for (Iterator iter = namespaceMapping.getElements().iterator(); iter.hasNext();) { + ElementMapping element = (ElementMapping) iter.next(); + generateElementMapping(out, namespaceMapping, element); + } + + out.println(); + out.println(""); + } + + private void generateElementMapping(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) { + out.println(); + out.println(" "); + + String localName = element.getElementName(); + + out.println(" "); + + if (!isEmptyString(element.getDescription())) { + out.println(" "); + out.println(" "); + out.println(" "); + } + + out.println(" "); + + int complexCount = 0; + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + if (!namespaceMapping.isSimpleType(attributeMapping.getType())) { + complexCount++; + } + } + if (complexCount > 0) { + out.println(" "); + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + if (!namespaceMapping.isSimpleType(attributeMapping.getType())) { + generateElementMappingComplexProperty(out, namespaceMapping, attributeMapping); + } + } + out.println(" "); + out.println(" "); + } + + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + if (namespaceMapping.isSimpleType(attributeMapping.getType())) { + generateElementMappingSimpleProperty(out, attributeMapping); + } else if (!attributeMapping.getType().isCollection()) { + generateElementMappingComplexPropertyAsRef(out, attributeMapping); + } + } + generateIDAttributeMapping(out, namespaceMapping, element); + + out.println(" "); + out.println(" "); + out.println(" "); + out.println(); + } + + private boolean isEmptyString(String str) { + if (str == null) { + return true; + } + for (int i = 0; i < str.length(); i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + private void generateIDAttributeMapping(PrintWriter out, NamespaceMapping namespaceMapping, ElementMapping element) { + for (Iterator iterator = element.getAttributes().iterator(); iterator.hasNext();) { + AttributeMapping attributeMapping = (AttributeMapping) iterator.next(); + if ("id".equals(attributeMapping.getAttributeName())) { + return; + } + } + out.println(" "); + } + + private void generateElementMappingSimpleProperty(PrintWriter out, AttributeMapping attributeMapping) { + // types with property editors need to be xs:string in the schema to validate + String type = attributeMapping.getPropertyEditor() != null ? + Utils.getXsdType(Type.newSimpleType(String.class.getName())) : Utils.getXsdType(attributeMapping.getType()); + if (!isEmptyString(attributeMapping.getDescription())) { + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + } else { + out.println(" "); + } + } + + private void generateElementMappingComplexPropertyAsRef(PrintWriter out, AttributeMapping attributeMapping) { + if (!isEmptyString(attributeMapping.getDescription())) { + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + } else { + out.println(" "); + } + } + + private void generateElementMappingComplexProperty(PrintWriter out, NamespaceMapping namespaceMapping, AttributeMapping attributeMapping) { + Type type = attributeMapping.getType(); + List types; + if (type.isCollection()) { + types = Utils.findImplementationsOf(namespaceMapping, type.getNestedType()); + } else { + types = Utils.findImplementationsOf(namespaceMapping, type); + } + String maxOccurs = type.isCollection() || "java.util.Map".equals(type.getName()) ? "unbounded" : "1"; + + out.println(" "); + if (!isEmptyString(attributeMapping.getDescription())) { + out.println(" "); + out.println(" "); + out.println(" "); + } + out.println(" "); + if (types.isEmpty()) { + // We don't know the type because it's generic collection. Allow folks to insert objets from any namespace + out.println(" "); + } else { + out.println(" "); + for (Iterator iterator = types.iterator(); iterator.hasNext();) { + ElementMapping element = (ElementMapping) iterator.next(); + out.println(" "); + } + out.println(" "); + out.println(" "); + } + out.println(" "); + out.println(" "); + } + + public LogFacade getLog() { + return log; + } + + public void setLog(LogFacade log) { + this.log = log; + } + +} diff --git a/xbean-blueprint/src/main/resources/OSGI-INF/blueprint/xbean-cm.xml b/xbean-blueprint/src/main/resources/OSGI-INF/blueprint/xbean-cm.xml new file mode 100644 index 00000000..787344d8 --- /dev/null +++ b/xbean-blueprint/src/main/resources/OSGI-INF/blueprint/xbean-cm.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/main/resources/org/apache/xbean/blueprint/cm/xbean-cm.xsd b/xbean-blueprint/src/main/resources/org/apache/xbean/blueprint/cm/xbean-cm.xsd new file mode 100644 index 00000000..b501bb92 --- /dev/null +++ b/xbean-blueprint/src/main/resources/org/apache/xbean/blueprint/cm/xbean-cm.xsd @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/site/site.xml b/xbean-blueprint/src/site/site.xml new file mode 100644 index 00000000..e00978f7 --- /dev/null +++ b/xbean-blueprint/src/site/site.xml @@ -0,0 +1,37 @@ + + + + + + + + + + ${parentProject} + + ${modules} + + ${reports} + + + + + + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadAttributeTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadAttributeTest.java new file mode 100644 index 00000000..ab4f5dd2 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadAttributeTest.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.xbean.blueprint.context; + +import junit.framework.TestCase; + +/** + * @author Guillaume Nodet + * @version $Id$ + * @since 2.3 + */ +public class BadAttributeTest extends TestCase { + + public void testBadNs() throws Exception { + try { + BlueprintTestSupport.parse("org/apache/xbean/blueprint/context/bad-attribute.xml"); + fail("This should have thrown an exception"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadElementTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadElementTest.java new file mode 100644 index 00000000..5db605f6 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadElementTest.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.xbean.blueprint.context; + +import junit.framework.TestCase; + +/** + * @author Guillaume Nodet + * @version $Id$ + * @since 2.3 + */ +public class BadElementTest extends TestCase { + + public void testBadNs() throws Exception { + try { + BlueprintTestSupport.parse("org/apache/xbean/blueprint/context/bad-element.xml"); + fail("This should have thrown an exception"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadNamespaceTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadNamespaceTest.java new file mode 100644 index 00000000..e2942dce --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BadNamespaceTest.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.xbean.blueprint.context; + +import junit.framework.TestCase; + +/** + * @author Guillaume Nodet + * @version $Id$ + * @since 2.3 + */ +public class BadNamespaceTest extends TestCase { + + public void testBadNs() throws Exception { + try { + BlueprintTestSupport.parse("org/apache/xbean/blueprint/context/bad-namespace.xml"); + fail("This should have thrown an exception"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerNullTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerNullTest.java new file mode 100644 index 00000000..eddc7d22 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerNullTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.BeerService; +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.NullMetadata; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 2.6 + */ +public class BeerNullTest extends BlueprintTestSupport { + + public void testBeer() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("beerService"); + + checkPropertyValue("name", "Stella", meta); + checkPropertyValue("id", "123", meta); + //no property set since this is the default +// checkPropertyValue("source", "tap", meta); + + BeanMetadataImpl meta2 = (BeanMetadataImpl) reg.getComponentDefinition("beerService2"); + + checkPropertyValue("name", "Blue Moon", meta2); + checkPropertyValue("id", "123", meta2); + assertTrue(propertyByName("source", meta2).getValue() instanceof NullMetadata); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/beer-xbean-null.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingBlueprintTest.java new file mode 100644 index 00000000..e647f154 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingBlueprintTest.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author James Strachan + * @version $Id$ + * @since 1.0 + */ +public class BeerUsingBlueprintTest extends BlueprintTestSupport { + + public void testBeer() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("beerService"); + checkPropertyValue("name", "Stella", meta); + checkPropertyValue("id", "123", meta); + assertEquals("id", "beerService", meta.getId()); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/beer-normal.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanNSTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanNSTest.java new file mode 100644 index 00000000..d149457c --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanNSTest.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.xbean.blueprint.context; + + +/** + * @author Guillaume Nodet + * @version $Id$ + * @since 2.2 + */ +public class BeerUsingXBeanNSTest extends BeerUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/beer-xbean-ns.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanSystemPropTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanSystemPropTest.java new file mode 100644 index 00000000..b436691a --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanSystemPropTest.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +/** + * @author Hiram Chirino + * @version $Id$ + * @since 2.0 + */ +public class BeerUsingXBeanSystemPropTest extends BeerUsingBlueprintTest { + + @Override + public void testBeer() throws Exception { + //disabled + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/beer-xbean-system-prop.xml"; + } + + protected void setUp() throws Exception { + System.setProperty("beerType", "Stella"); + super.setUp(); + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanTest.java new file mode 100644 index 00000000..3cbe6d8f --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BeerUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class BeerUsingXBeanTest extends BeerUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/beer-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BlueprintTestSupport.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BlueprintTestSupport.java new file mode 100644 index 00000000..4bba08f1 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/BlueprintTestSupport.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.xbean.blueprint.context; + +import java.beans.PropertyEditor; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import javax.xml.namespace.QName; +import javax.xml.validation.Schema; + +import junit.framework.TestCase; +import org.apache.aries.blueprint.NamespaceHandler; +import org.apache.aries.blueprint.parser.ComponentDefinitionRegistryImpl; +import org.apache.aries.blueprint.parser.NamespaceHandlerSet; +import org.apache.aries.blueprint.parser.Parser; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xbean.blueprint.context.impl.QNameNamespaceHandler; +import org.apache.xbean.blueprint.context.impl.XBeanNamespaceHandler; +import org.apache.xbean.blueprint.example.MilliLittersPropertyEditor; +import org.osgi.service.blueprint.reflect.BeanArgument; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.xml.sax.SAXException; + +/** + * A useful base class for testing spring based utilities. + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public abstract class BlueprintTestSupport extends TestCase { + protected Log log = LogFactory.getLog(getClass()); + private static final URI NAMESPACE_URI = URI.create("http://xbean.apache.org/schemas/pizza"); + private static final URI QNAME_URI = URI.create("http://xbean.apache.org/schemas/javax.xml.namespace.QName"); + + protected ComponentDefinitionRegistryImpl reg; + + protected void setUp() throws Exception { + reg = parse(getPlan()); + } + + protected static ComponentDefinitionRegistryImpl parse(String plan) throws Exception { + String schema = "META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/pizza"; + return parse(plan, schema); + } + + protected static ComponentDefinitionRegistryImpl parse(String plan, String schema) throws Exception { + Properties properties = new Properties(); + URL propUrl = BlueprintTestSupport.class.getClassLoader().getResource(schema); + InputStream in = propUrl.openStream(); + try { + properties.load(in); + } finally { + in.close(); + } + + Set classes = new HashSet(); + ClassLoader cl = BlueprintTestSupport.class.getClassLoader(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (!key.contains(".")) { + String className = (String) entry.getValue(); + Class clazz = cl.loadClass(className); + classes.add(clazz); + } + } + classes.add(QName.class); + + Map> propertyEditors = new HashMap>(); + propertyEditors.put(MilliLittersPropertyEditor.class.getName(), MilliLittersPropertyEditor.class); + final NamespaceHandler xbeanHandler = new XBeanNamespaceHandler(NAMESPACE_URI.toString(), BlueprintTestSupport.class.getClassLoader().getResource("restaurant.xsd"), classes, propertyEditors, properties); + final NamespaceHandler qnameHandler = new QNameNamespaceHandler(); + NamespaceHandlerSet handlers = new NamespaceHandlerSet() { + public Set getNamespaces() { + return new HashSet(Arrays.asList(NAMESPACE_URI, QNAME_URI)); + } + + public NamespaceHandler getNamespaceHandler(URI namespace) { + if (NAMESPACE_URI.equals(namespace)) { + return xbeanHandler; + } else if (QNAME_URI.equals(namespace)){ + return qnameHandler; + } + return null; + } + + public void removeListener(Listener listener) { + } + + public Schema getSchema() throws SAXException, IOException { + return null; + } + + public boolean isComplete() { + return false; + } + + public void addListener(Listener listener) { + } + + public void destroy() { + } + }; + return parse(plan, handlers); + } + + // from aries blueprint core AbstractBlueprintTest + protected static ComponentDefinitionRegistryImpl parse(String plan, NamespaceHandlerSet handlers) throws Exception { + ComponentDefinitionRegistryImpl registry = new ComponentDefinitionRegistryImpl(); + Parser parser = new Parser(); + parser.parse(Collections.singletonList(BlueprintTestSupport.class.getClassLoader().getResource(plan))); + parser.populate(handlers, registry); + return registry; + } + + protected abstract String getPlan(); + + protected static void checkPropertyValue(String name, Object expectedValued, BeanMetadataImpl meta) { + BeanProperty prop = propertyByName(name, meta); + assertEquals(expectedValued, ((ValueMetadata) prop.getValue()).getStringValue()); + } + + protected static BeanProperty propertyByName(String name, BeanMetadataImpl meta) { + List props = meta.getProperties(); + for (BeanProperty prop : props) { + if (name.equals(prop.getName())) { + return prop; + } + } + throw new RuntimeException("No such property: " + name + " in metadata: " + meta); + + } + + protected static void checkArgumentValue(int index, String expectedValued, BeanMetadataImpl meta, boolean allowNesting) { + List props = meta.getArguments(); + Metadata metadata = props.get(index).getValue(); + if (allowNesting && metadata instanceof BeanMetadata) { + metadata = ((BeanMetadata) metadata).getArguments().get(0).getValue(); + } + assertEquals(expectedValued, ((ValueMetadata) metadata).getStringValue()); + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/ComponentTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/ComponentTest.java new file mode 100644 index 00000000..14c5f6e2 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/ComponentTest.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.xbean.blueprint.context; + +import junit.framework.TestCase; + +import org.apache.xbean.blueprint.example.InnerBean; +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.CollectionMetadata; + +public class ComponentTest extends TestCase { + + protected void setUp() throws Exception { + InnerBean.INSTANCE = null; + } + + public void test1() throws Exception { + test("org/apache/xbean/blueprint/context/component-blueprint.xml"); + } + + public void test2() throws Exception { + test("org/apache/xbean/blueprint/context/component-xbean.xml"); + } + + protected void test(String file) throws Exception { + ComponentDefinitionRegistry f = BlueprintTestSupport.parse(file); + BeanMetadataImpl meta = (BeanMetadataImpl) f.getComponentDefinition("container"); + assertNotNull(meta); + CollectionMetadata list = (CollectionMetadata) BlueprintTestSupport.propertyByName("beans", meta).getValue(); + assertEquals(1, list.getValues().size()); + assertEquals(InnerBean.class.getName(), ((BeanMetadata)list.getValues().get(0)).getClassName()); + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingBlueprintTest.java new file mode 100644 index 00000000..398e7a77 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingBlueprintTest.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +import java.util.List; +import java.util.Map; + +import org.apache.xbean.blueprint.example.FavoriteService; +import org.apache.xbean.blueprint.example.GinService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.MapEntry; +import org.osgi.service.blueprint.reflect.MapMetadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; + +/** + * @author James Strachan + * @version $Id$ + * @since 1.0 + */ +public class FavoriteUsingBlueprintTest extends BlueprintTestSupport { + + public void testFavs() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("favoriteService"); + + assertEquals(1, meta.getProperties().size()); + BeanProperty prop = propertyByName("favorites", meta); + MapMetadata favorites = (MapMetadata) prop.getValue(); + assertEquals(3, favorites.getEntries().size()); + MapEntry me = favorites.getEntries().get(0); + assertEquals("Dan", ((ValueMetadata) me.getKey()).getStringValue()); + assertEquals("Grey Goose", ((ValueMetadata) me.getValue()).getStringValue()); + + me = favorites.getEntries().get(1); + assertEquals("IndecisiveDan", ((ValueMetadata) me.getKey()).getStringValue()); + + CollectionMetadata cm = (CollectionMetadata) me.getValue(); + assertEquals(2, cm.getValues().size()); + assertEquals("Malbec", ((ValueMetadata) cm.getValues().get(0)).getStringValue()); + assertEquals("0", ((ValueMetadata) ((BeanMetadata)cm.getValues().get(1)).getArguments().get(0).getValue()).getStringValue()); + + + me = favorites.getEntries().get(2); + assertEquals("WithInnerBean", ((ValueMetadata) me.getKey()).getStringValue()); + BeanMetadata bm = (BeanMetadata) me.getValue(); + assertEquals(GinService.class.getName(), bm.getClassName()); + assertEquals("Bombay Sapphire", ((ValueMetadata)bm.getProperties().get(0).getValue()).getStringValue()); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/favorite-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanMixedTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanMixedTest.java new file mode 100644 index 00000000..7ea898a3 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanMixedTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class FavoriteUsingXBeanMixedTest extends FavoriteUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/favorite-xbean-mixed.xml"; + } + +} \ No newline at end of file diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanTest.java new file mode 100644 index 00000000..244c3055 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FavoriteUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class FavoriteUsingXBeanTest extends FavoriteUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/favorite-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FlatMapTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FlatMapTest.java new file mode 100644 index 00000000..36fe8ba7 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/FlatMapTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.KegService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.MapEntry; +import org.osgi.service.blueprint.reflect.MapMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; + +/** + * @author gnodet + */ +public class FlatMapTest extends BlueprintTestSupport { + + public void testFlatMap() { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("flat-map"); + MapMetadata c = (MapMetadata) propertyByName("services", meta).getValue(); + assertEquals(3, c.getEntries().size()); + MapEntry me = c.getEntries().get(0); + assertEquals("key1", ((ValueMetadata) me.getKey()).getStringValue()); + CollectionMetadata l = (CollectionMetadata) me.getValue(); + assertEquals(2, l.getValues().size()); + checkEntry(l.getValues().get(0), "1000"); + checkEntry(l.getValues().get(1), "8750"); + + me = c.getEntries().get(1); + assertEquals("key2", ((ValueMetadata) me.getKey()).getStringValue()); + l = (CollectionMetadata) me.getValue(); + assertEquals(1, l.getValues().size()); + checkEntry(l.getValues().get(0), "20000"); + + me = c.getEntries().get(2); + assertEquals("others", ((ValueMetadata) me.getKey()).getStringValue()); + l = (CollectionMetadata) me.getValue(); + assertEquals(1, l.getValues().size()); + checkEntry(l.getValues().get(0), "0"); + } + + private void checkEntry(Metadata me, String value) { + BeanMetadataImpl beanMetadata = (BeanMetadataImpl) me; + assertEquals(KegService.class.getName(), beanMetadata.getClassName()); + assertEquals(1, beanMetadata.getProperties().size()); + assertEquals(value, ((ValueMetadata) beanMetadata.getProperties().get(0).getValue()).getStringValue()); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/flatmap-xbean.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/GinUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/GinUsingXBeanTest.java new file mode 100644 index 00000000..dc00113a --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/GinUsingXBeanTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.GinService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author James Strachan + * @version $Id$ + * @since 1.0 + */ +public class GinUsingXBeanTest extends BlueprintTestSupport { + + public void testWine() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("ginService"); + checkPropertyValue("name", "Bombay Sapphire", meta); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/gin.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanAndPropertiesTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanAndPropertiesTest.java new file mode 100644 index 00000000..c8af01d5 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanAndPropertiesTest.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +/** + * Used to verify that per Property Editors work correctly with + * a PropertyPlaceholderConfigurer used to configure the values. + * + * @author chirino + * @version $Id$ + * @since 2.2 + */ +public class KegXBeanAndPropertiesTest extends KegXBeanTest { + @Override + public void testBeer() throws Exception { + //disabled test + } + + @Override + protected void setUp() throws Exception { + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/keg-xbean-properties.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanTest.java new file mode 100644 index 00000000..c4732236 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/KegXBeanTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.KegService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * Used to verify that per propety Property Editors work correctly. + * + * @author chirino + * @version $Id$ + * @since 2.2 + */ +public class KegXBeanTest extends BlueprintTestSupport { + + public void testBeer() throws Exception { + //TODO blueprint value conversion using units?? + BeanMetadataImpl ml1000 = (BeanMetadataImpl) reg.getComponentDefinition("ml1000"); + BeanMetadataImpl empty = (BeanMetadataImpl) reg.getComponentDefinition("empty"); + BeanMetadataImpl pints5 = (BeanMetadataImpl) reg.getComponentDefinition("pints5"); + BeanMetadataImpl liter20 = (BeanMetadataImpl) reg.getComponentDefinition("liter20"); + + checkPropertyValue("remaining", "1000", ml1000); + checkPropertyValue("remaining", "0", empty); + checkPropertyValue("remaining", "8750", pints5); + checkPropertyValue("remaining", "20000", liter20); +// checkPropertyValue("remaining", "1000", ml1000); +// checkPropertyValue("remaining", "0", empty); +// checkPropertyValue("remaining", "8750", pints5); +// checkPropertyValue("remaining", "20000", liter20); + + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/keg-xbean.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingBlueprintTest.java new file mode 100644 index 00000000..f36f16f9 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingBlueprintTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.PizzaService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class PizzaUsingBlueprintTest extends BlueprintTestSupport { + + public void testPizza() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("pizzaService"); +// pizza.makePizza(); + + checkPropertyValue("topping", "Salami", meta); + checkPropertyValue("cheese", "Edam", meta); + checkPropertyValue("size", "17", meta); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/pizza-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanTest.java new file mode 100644 index 00000000..29c478b0 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanTest.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.xbean.blueprint.context; + + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class PizzaUsingXBeanTest extends PizzaUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/pizza-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWinBeanRefTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWinBeanRefTest.java new file mode 100644 index 00000000..febc3752 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWinBeanRefTest.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.xbean.blueprint.context; + +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.aries.blueprint.reflect.RefMetadataImpl; +import org.osgi.service.blueprint.reflect.BeanProperty; + +/** + * @version $Revision$ + */ +public class PizzaUsingXBeanWinBeanRefTest extends PizzaUsingBlueprintTest { + + public void testPizza() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("pizzaService"); + + BeanProperty p = propertyByName("topping", meta); + assertTrue(p.getValue() instanceof RefMetadataImpl); + assertEquals("topping", ((RefMetadataImpl)p.getValue()).getComponentId()); + checkPropertyValue("cheese", "#Edam", meta); + checkPropertyValue("size", "17", meta); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/pizza-xbean-bean-ref.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithJavaNamespaceTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithJavaNamespaceTest.java new file mode 100644 index 00000000..21a8594c --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithJavaNamespaceTest.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.xbean.blueprint.context; + + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class PizzaUsingXBeanWithJavaNamespaceTest extends PizzaUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/pizza-xbean-java.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithPropertiesTextNodeTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithPropertiesTextNodeTest.java new file mode 100644 index 00000000..400f366a --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/PizzaUsingXBeanWithPropertiesTextNodeTest.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.xbean.blueprint.context; + +/** + * + * @version $Revision$ + */ +public class PizzaUsingXBeanWithPropertiesTextNodeTest extends PizzaUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/pizza-xbean-properties.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingBlueprintTest.java new file mode 100644 index 00000000..0bd8d41f --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingBlueprintTest.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +import java.util.List; + +import javax.xml.namespace.QName; + +import org.apache.xbean.blueprint.example.QNameService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.aries.blueprint.reflect.CollectionMetadataImpl; +import org.osgi.service.blueprint.reflect.Metadata; + +public class QNameUsingBlueprintTest extends BlueprintTestSupport { + + public void testQName() throws Exception { + BeanMetadataImpl svc = (BeanMetadataImpl) reg.getComponentDefinition("qnameService"); + + List services = ((CollectionMetadataImpl)propertyByName("services", svc).getValue()).getValues(); + assertNotNull(services); + assertEquals(2, services.size()); + checkQName("urn:foo", "test", services.get(0)); + checkQName("urn:foo", "bar", services.get(1)); + + List list = ((CollectionMetadataImpl)propertyByName("list", svc).getValue()).getValues(); + assertNotNull(list); + assertEquals(1, list.size()); + checkQName("urn:foo", "list", list.get(0)); + } + + protected void checkQName(String namespace, String local, Metadata meta) { + checkArgumentValue(0, namespace, (BeanMetadataImpl) meta, false); + checkArgumentValue(1, local, (BeanMetadataImpl) meta, false); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/qname-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingXBeanTest.java new file mode 100644 index 00000000..b0447765 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/QNameUsingXBeanTest.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +public class QNameUsingXBeanTest extends QNameUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/qname-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingBlueprintTest.java new file mode 100644 index 00000000..743deea5 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingBlueprintTest.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +import java.util.List; + +import org.apache.xbean.blueprint.example.Recipe; +import org.apache.xbean.blueprint.example.RecipeService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.aries.blueprint.reflect.CollectionMetadataImpl; +import org.osgi.service.blueprint.reflect.Metadata; + +public class RecipeUsingBlueprintTest extends BlueprintTestSupport { + + public void testRecipes() throws Exception { + BeanMetadataImpl svc = (BeanMetadataImpl) reg.getComponentDefinition("recipeService"); + + List list = ((CollectionMetadataImpl)propertyByName("recipes", svc).getValue()).getValues(); + assertNotNull(list); + assertEquals(2, list.size()); + BeanMetadataImpl r = (BeanMetadataImpl) list.get(0); + checkPropertyValue("ingredients", "Food", r); + checkPropertyValue("instructions", "Mash together", r); + + r = (BeanMetadataImpl) list.get(1); + checkPropertyValue("ingredients", "Food", r); + checkPropertyValue("instructions", "Mash together", r); + + BeanMetadataImpl topRecipe = (BeanMetadataImpl) propertyByName("topRecipe", svc).getValue(); + assertNotNull(topRecipe); + checkPropertyValue("ingredients", "Food", topRecipe); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/recipe-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanMixedTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanMixedTest.java new file mode 100644 index 00000000..6265586f --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanMixedTest.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +public class RecipeUsingXBeanMixedTest extends RecipeUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/recipe-xbean-mixed.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanTest.java new file mode 100644 index 00000000..e5b0fff5 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RecipeUsingXBeanTest.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.context; + +public class RecipeUsingXBeanTest extends RecipeUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/recipe-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingBlueprintTest.java new file mode 100644 index 00000000..fd6c193a --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingBlueprintTest.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.xbean.blueprint.context; + +import java.util.List; + +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.aries.blueprint.reflect.ValueMetadataImpl; +import org.apache.aries.blueprint.reflect.CollectionMetadataImpl; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingBlueprintTest extends BlueprintTestSupport { + + public void testPizza() throws Exception { + BeanMetadataImpl restaurant = (BeanMetadataImpl) reg.getComponentDefinition("restaurant"); + + BeanProperty prop = propertyByName("serviceName", restaurant); + + BeanMetadataImpl qname = (BeanMetadataImpl) prop.getValue(); + assertEquals(3, qname.getArguments().size()); + assertEquals("http://acme.com", ((ValueMetadataImpl) qname.getArguments().get(0).getValue()).getStringValue()); + assertEquals("xyz", ((ValueMetadataImpl) qname.getArguments().get(1).getValue()).getStringValue()); + assertEquals("foo", ((ValueMetadataImpl) qname.getArguments().get(2).getValue()).getStringValue()); + + // dinners (1-many using list) + BeanProperty dinnerProp = propertyByName("dinnerMenu", restaurant); + List dinners = ((CollectionMetadata) dinnerProp.getValue()).getValues(); + assertNotNull("dinners is null!", dinners); + assertEquals("dinners size: " + dinners, 2, dinners.size()); + + BeanMetadataImpl pizza = (BeanMetadataImpl) dinners.get(0); + checkPropertyValue("topping", "Ham", pizza); + checkPropertyValue("cheese", "Mozzarella", pizza); + //TODO blueprint int value + checkPropertyValue("size", "15", pizza); + + pizza = (BeanMetadataImpl) dinners.get(1); + checkPropertyValue("topping", "Eggs", pizza); + checkPropertyValue("cheese", "Mozzarella", pizza); + //TODO blueprint int value + checkPropertyValue("size", "16", pizza); + + // dinners (1-many using Set) + BeanProperty snackProp = propertyByName("snackMenu", restaurant); + List snacks = ((CollectionMetadata) snackProp.getValue()).getValues(); + assertNotNull("dinners is null!", snacks); + assertEquals("dinners size: " + snacks, 2, snacks.size()); + for (Metadata snackMeta : snacks) { + BeanMetadataImpl snack = (BeanMetadataImpl) snackMeta; + BeanProperty toppingProp = propertyByName("topping", snack); + String topping = ((ValueMetadataImpl) toppingProp.getValue()).getStringValue(); + if ("Tofu".equals(topping)) { + checkPropertyValue("cheese", "Parmesan", snack); + checkPropertyValue("size", "6", snack); + } else if ("Prosciutto".equals(topping)) { + checkPropertyValue("cheese", "Blue", snack); + checkPropertyValue("size", "8", snack); + } else { + fail("wrong topping: " + snackMeta); + } + } + // lunches (1-many using array) + CollectionMetadataImpl lunches = (CollectionMetadataImpl) propertyByName("lunchMenu", restaurant).getValue(); + assertNotNull("lunches is null!", lunches); + assertEquals("lunches size: " + lunches, 1, lunches.getValues().size()); + + pizza = (BeanMetadataImpl) lunches.getValues().get(0); + checkPropertyValue("topping", "Chicken", pizza); + checkPropertyValue("cheese", "Brie", pizza); + checkPropertyValue("size", "17", pizza); + + // favourite (1-1) + BeanProperty favourite = propertyByName("favourite", restaurant); + pizza = (BeanMetadataImpl) favourite.getValue(); + assertNotNull("Pizza is null!", pizza); +// pizza.makePizza(); +// + checkPropertyValue("topping", "Salami", pizza); + checkPropertyValue("cheese", "Edam", pizza); + checkPropertyValue("size", "17", pizza); + + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingResourceXmlApplicationContextTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingResourceXmlApplicationContextTest.java new file mode 100644 index 00000000..b3f8d06a --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingResourceXmlApplicationContextTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingResourceXmlApplicationContextTest extends RestaurantUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-xbean.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingSpringExtendedTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingSpringExtendedTest.java new file mode 100644 index 00000000..31899f54 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingSpringExtendedTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingSpringExtendedTest extends RestaurantUsingBlueprintTest { + + @Override + public void testPizza() throws Exception { + //this test does not use any xbean, not sure how to make the QNameNamespaceHandler find attributes after the fact. + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-spring-extended.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanAsRootTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanAsRootTest.java new file mode 100644 index 00000000..5faaa85b --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanAsRootTest.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.xbean.blueprint.context; + + +public class RestaurantUsingXBeanAsRootTest extends RestaurantUsingBlueprintTest { + + //TODO blueprint disabled. First check is for blueprint namespace. + @Override + protected void setUp() throws Exception { + } + + @Override + public void testPizza() throws Exception { + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-xbean-root.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanMixedTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanMixedTest.java new file mode 100644 index 00000000..82d6f75e --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanMixedTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingXBeanMixedTest extends RestaurantUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-xbean-mixed.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanTest.java new file mode 100644 index 00000000..0fcbd909 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanTest.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.xbean.blueprint.context; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xbean.blueprint.example.RestaurantService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; + +import javax.xml.namespace.QName; + +import java.net.URI; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingXBeanTest extends RestaurantUsingBlueprintTest { + private static final Log log = LogFactory.getLog(RestaurantUsingXBeanTest.class); + + public void testPizza() throws Exception { + super.testPizza(); + + BeanMetadataImpl restaurant = (BeanMetadataImpl) reg.getComponentDefinition("restaurant"); + + ValueMetadata uri = (ValueMetadata) propertyByName("uri", restaurant).getValue(); + assertNotNull("URI is null", uri); + assertEquals("URI", "http://cheese.com", uri.getStringValue()); + + log.info("Successfully converted the property to a URI: " + uri); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanWithSimplerConfigTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanWithSimplerConfigTest.java new file mode 100644 index 00000000..c9b4419b --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/RestaurantUsingXBeanWithSimplerConfigTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantUsingXBeanWithSimplerConfigTest extends RestaurantUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/restaurant-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingBlueprintTest.java new file mode 100644 index 00000000..0b977585 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingBlueprintTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.SaladService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class SaladUsingBlueprintTest extends BlueprintTestSupport { + public void testSalad() throws Exception { + BeanMetadataImpl salad = (BeanMetadataImpl) reg.getComponentDefinition("saladService"); + + checkArgumentValue(0, "Cesar", salad, false); + checkArgumentValue(1, "Small", salad, false); + checkArgumentValue(2, "true", salad, false); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/salad-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingXBeanTest.java new file mode 100644 index 00000000..89dc1c45 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SaladUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class SaladUsingXBeanTest extends SaladUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/salad-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressBlueprintTest.java new file mode 100644 index 00000000..f3db6c97 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressBlueprintTest.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.xbean.blueprint.context; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Arrays; +import java.util.List; + +import org.apache.xbean.blueprint.example.SocketService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.osgi.service.blueprint.reflect.CollectionMetadata; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class SocketAddressBlueprintTest extends BlueprintTestSupport { + + public void testSocketService() throws Exception { + BeanMetadataImpl socketService = (BeanMetadataImpl) reg.getComponentDefinition("socketService"); + +// System.out.println(); +// System.out.println("==========================="); +// System.out.println(socketService.getAddresses()); +// System.out.println("==========================="); +// System.out.println(); + +// List expected = Arrays.asList(new InetSocketAddress("localhost", 42), new InetSocketAddress("localhost", 42)); + + assertEquals(1, socketService.getProperties().size()); + CollectionMetadata collection = (CollectionMetadata) socketService.getProperties().get(0).getValue(); + assertEquals(2, collection.getValues().size()); + + } + + public void testSocketAddress() throws Exception { + BeanMetadataImpl socketAddress = (BeanMetadataImpl) reg.getComponentDefinition("socketAddress"); + +// System.out.println(); +// System.out.println("==========================="); +// System.out.println(socketAddress); +// System.out.println("==========================="); +// System.out.println(); + + assertNotNull(socketAddress); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/socket-address-normal.xml"; + } +} \ No newline at end of file diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressXBeanTest.java new file mode 100644 index 00000000..b2a76781 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SocketAddressXBeanTest.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.xbean.blueprint.context; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class SocketAddressXBeanTest extends SocketAddressBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/socket-address.xml"; + } + +} \ No newline at end of file diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingBlueprintTest.java new file mode 100644 index 00000000..5c6f4c99 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingBlueprintTest.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.xbean.blueprint.context; + +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class SoupUsingBlueprintTest extends BlueprintTestSupport { + private static final long time = System.currentTimeMillis(); + + public void testSoup() throws Exception { + BeanMetadataImpl soup = (BeanMetadataImpl) reg.getComponentDefinition("soupService"); + BeanMetadataImpl nestedBean = (BeanMetadataImpl) reg.getComponentDefinition("nestedBean"); + BeanMetadataImpl nestedValue = (BeanMetadataImpl) reg.getComponentDefinition("nestedValue"); + + asssertValidSoup(soup); + asssertValidSoup(nestedBean); + asssertValidSoup(nestedValue); + +// reg.; +// assertFalse(soup.exists()); +// assertFalse(nestedBean.exists()); +// assertFalse(nestedValue.exists()); + } + + private void asssertValidSoup(BeanMetadataImpl soup) { + checkArgumentValue(0, "French Onion", soup, true); +// assertTrue(soup.getCreateTime() >= time); + assertNotNull(soup.getInitMethod()); + + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/soup-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingXBeanTest.java new file mode 100644 index 00000000..2e0bb43b --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SoupUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class SoupUsingXBeanTest extends SoupUsingBlueprintTest { + + @Override + protected void setUp() throws Exception { + reg = parse(getPlan(), "META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/soup"); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/soup-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SpringExtensionTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SpringExtensionTest.java new file mode 100644 index 00000000..669c9275 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/SpringExtensionTest.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.xbean.blueprint.context; + +/** + * @author gnodet + * @since 2.7 + */ +public class SpringExtensionTest extends BlueprintTestSupport { + + @Override + protected void setUp() throws Exception { + //disabled + } + + public void test() { + + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/spring-extension.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingBlueprintTest.java new file mode 100644 index 00000000..e1de9170 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingBlueprintTest.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.xbean.blueprint.context; + +import org.apache.xbean.blueprint.example.VodkaService; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author Dan Diephouse + * @version $Id: VodkaUsingSpringTest.java 434369 2006-08-24 10:24:21Z gnodet $ + * @since 1.0 + */ +public class VodkaUsingBlueprintTest extends BlueprintTestSupport { + + public void testWine() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("vodkaService"); + + checkPropertyValue("name", "Grey Goose", meta); + checkPropertyValue("id", "vodkaService", meta); + + // Test more complex classes + checkPropertyValue("vodkaClass", VodkaService.class.getName(), meta); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/vodka-normal.xml"; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingXBeanTest.java new file mode 100644 index 00000000..31bf49ff --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/VodkaUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id: VodkaUsingXBeanTest.java 434369 2006-08-24 10:24:21Z gnodet $ + * @since 2.0 + */ +public class VodkaUsingXBeanTest extends VodkaUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/vodka-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingBlueprintTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingBlueprintTest.java new file mode 100644 index 00000000..938e1e92 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingBlueprintTest.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.xbean.blueprint.context; + +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; + +/** + * @author James Strachan + * @version $Id$ + * @since 1.0 + */ +public class WineUsingBlueprintTest extends BlueprintTestSupport { + + public void testWine() throws Exception { + BeanMetadataImpl meta = (BeanMetadataImpl) reg.getComponentDefinition("wineService"); + + checkArgumentValue(1, "Amarone", meta, false); + checkArgumentValue(0, "wineService", meta, false); + } + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/wine-normal.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingXBeanTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingXBeanTest.java new file mode 100644 index 00000000..818db6fb --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/WineUsingXBeanTest.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.xbean.blueprint.context; + +/** + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class WineUsingXBeanTest extends WineUsingBlueprintTest { + + protected String getPlan() { + return "org/apache/xbean/blueprint/context/wine-xbean.xml"; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/JexlTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/JexlTest.java new file mode 100644 index 00000000..340796f6 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/JexlTest.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.xbean.blueprint.context.impl; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; +import org.apache.xbean.blueprint.cm.JexlExpressionParser; + +/** + * @version $Rev$ $Date$ + */ +public class JexlTest extends TestCase { + + public void testJexl() throws Exception { + Map vars = new HashMap (); + vars.put("foo", 1); + vars.put("bar", 2); + JexlExpressionParser parser = new JexlExpressionParser(vars); + assertEquals(3, parser.evaluate("${foo + bar}")); + + vars.put("ServerHostname", "localhost"); + vars.put("ActiveMQPort", 63636); + vars.put("PortOffset", 10); + assertEquals("tcp://localhost:63646", parser.evaluate("tcp://${ServerHostname}:${ActiveMQPort + PortOffset}")); + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgsTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgsTest.java new file mode 100644 index 00000000..054ffaf7 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/context/impl/NamedConstructorArgsTest.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.xbean.blueprint.context.impl; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Properties; + +import junit.framework.TestCase; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class NamedConstructorArgsTest extends TestCase { + private Properties properties = new Properties(); + + public void testPropertyParsing() { + assertEquals("bar", properties.getProperty("foo")); + assertEquals("blah", properties.getProperty("foo,chese")); + assertEquals("StringBuffer", properties.getProperty("java.lang.String(java.lang.StringBuffer)")); + assertEquals("char[]", properties.getProperty("java.lang.String([C)")); + assertEquals("byte[],int,int", properties.getProperty("java.lang.String([B,int,int)")); + assertEquals("URL[],ClassLoader", properties.getProperty("java.net.URLClassLoader([Ljava.net.URL;,java.lang.ClassLoader)")); + } + + public void testMappingMetaData() throws Exception { + MappingMetaData mappingMetaData = new MappingMetaData(properties); + Constructor constructor = URLClassLoader.class.getConstructor(new Class[] { URL[].class, ClassLoader.class}); + assertTrue(mappingMetaData.isDefaultConstructor(constructor)); + assertEquals(Arrays.asList(new String[] { "urls", "parent" }), + Arrays.asList(mappingMetaData.getParameterNames(constructor))); + + constructor = String.class.getConstructor(new Class[] { byte[].class, int.class, int.class}); + assertFalse(mappingMetaData.isDefaultConstructor(constructor)); + assertEquals(Arrays.asList(new String[] { "bytes", "offset", "length" }), + Arrays.asList(mappingMetaData.getParameterNames(constructor))); + } + + protected void setUp() throws Exception { + StringBuffer buf = new StringBuffer(); + buf.append("# test properties\n"); + buf.append("foo=bar\n"); + buf.append("foo,chese=blah\n"); + Constructor constructor = String.class.getConstructor(new Class[] { StringBuffer.class}); + buf.append(MappingMetaData.constructorToPropertyName(constructor) + "=StringBuffer\n"); + constructor = String.class.getConstructor(new Class[] { char[].class}); + buf.append(MappingMetaData.constructorToPropertyName(constructor) + "=char[]\n"); + constructor = String.class.getConstructor(new Class[] { byte[].class, int.class, int.class}); + buf.append(MappingMetaData.constructorToPropertyName(constructor) + "=byte[],int,int\n"); + constructor = URLClassLoader.class.getConstructor(new Class[] { URL[].class, ClassLoader.class}); + buf.append(MappingMetaData.constructorToPropertyName(constructor) + "=URL[],ClassLoader\n"); + + properties.load(new ByteArrayInputStream(buf.toString().getBytes())); + + constructor = URLClassLoader.class.getConstructor(new Class[] { URL[].class, ClassLoader.class}); + properties.put(MappingMetaData.constructorToPropertyName(constructor) + ".default", "true"); + properties.put(MappingMetaData.constructorToPropertyName(constructor) + ".parameterNames", "urls,parent"); + constructor = String.class.getConstructor(new Class[] { byte[].class, int.class, int.class}); + properties.put(MappingMetaData.constructorToPropertyName(constructor) + ".default", "false"); + properties.put(MappingMetaData.constructorToPropertyName(constructor) + ".parameterNames", "bytes,offset,length"); + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/BeerService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/BeerService.java new file mode 100644 index 00000000..df96625f --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/BeerService.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.xbean.blueprint.example; + + +/** + * @org.apache.xbean.XBean element="beer" description="Mmmmm beer" + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ + +// START SNIPPET: bean +public class BeerService { + private String id; + private String name; + private String source = "tap"; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/ContainerBean.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/ContainerBean.java new file mode 100644 index 00000000..b11878d2 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/ContainerBean.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.xbean.blueprint.example; + +/** + * @org.apache.xbean.XBean element="container" + * + */ +public class ContainerBean { + + InnerBean[] beans; + + /** + * @return the beans + */ + public InnerBean[] getBeans() { + return beans; + } + + /** + * @param beans the beans to set + */ + public void setBeans(InnerBean[] beans) { + this.beans = beans; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/DummyBean.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/DummyBean.java new file mode 100644 index 00000000..9c1ac2f1 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/DummyBean.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.xbean.blueprint.example; + +/** + * @org.apache.xbean.XBean element="dummy" + * @author gnodet + * @since 2.7 + */ +public class DummyBean { + + private Object inner; + + /** + * @return the inner + */ + public Object getInner() { + return inner; + } + + /** + * @param inner the inner to set + */ + public void setInner(Object inner) { + this.inner = inner; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FavoriteService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FavoriteService.java new file mode 100644 index 00000000..da8145b9 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FavoriteService.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.xbean.blueprint.example; + +import java.util.Map; + + +/** + * @org.apache.xbean.XBean element="favorite" + * + * @author Dan Diephouse + * @version $Id$ + */ + +// START SNIPPET: bean +public class FavoriteService { + private Map favorites; + + /** + * @org.apache.xbean.Map entryName="favorite-item" keyName="person" + * @return + */ + public Map getFavorites() { + return favorites; + } + + public void setFavorites(Map favorites) { + this.favorites = favorites; + } +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FlatMapService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FlatMapService.java new file mode 100644 index 00000000..044ceb4c --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/FlatMapService.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.xbean.blueprint.example; + +import java.util.Map; + +/** + * @org.apache.xbean.XBean element="flat-map" + * @author gnodet + */ +public class FlatMapService { + + private Map services; + + /** + * @org.apache.xbean.Map flat="true" dups="always" keyName="id" defaultKey="others" + */ + public Map getServices() { + return services; + } + + public void setServices(Map services) { + this.services = services; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/GinService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/GinService.java new file mode 100644 index 00000000..a1d64f49 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/GinService.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.xbean.blueprint.example; + +/** + * Gin is made from the distillation of white grain spirit and juniper berries. + * @org.apache.xbean.XBean element="gin" contentProperty="name" + * + * @version $Revision$ + */ +public class GinService { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/InnerBean.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/InnerBean.java new file mode 100644 index 00000000..157f4fad --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/InnerBean.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.xbean.blueprint.example; + +/** + * @org.apache.xbean.XBean element="inner" + * + */ +public class InnerBean { + + public static InnerBean INSTANCE = null; + + public InnerBean() { + Thread.dumpStack(); + if (INSTANCE == null) { + INSTANCE = this; + } else { + throw new IllegalStateException("Already instanciated"); + } + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/KegService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/KegService.java new file mode 100644 index 00000000..b3399fc5 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/KegService.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.xbean.blueprint.example; + +//START SNIPPET: java +/** + * @org.apache.xbean.XBean element="keg" + * + * Used to verify that property PropertyEditors work correctly. + * + * @author chirino + */ +public class KegService { + + private long remaining; + + /** + * Gets the amount of beer remaining in the keg (in ml) + * + * @param remaining + */ + public long getRemaining() { + return remaining; + } + + /** + * Sets the amount of beer remaining in the keg (in ml) + * + * @org.apache.xbean.Property propertyEditor="org.apache.xbean.blueprint.example.MilliLittersPropertyEditor" + * @param remaining + */ + public void setRemaining(long remaining) { + this.remaining = remaining; + } + + public long dispense( long amount ) { + this.remaining -= amount; + return this.remaining; + } + +} +// END SNIPPET: java + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/MilliLittersPropertyEditor.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/MilliLittersPropertyEditor.java new file mode 100644 index 00000000..7ae9f5ea --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/MilliLittersPropertyEditor.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.xbean.blueprint.example; + +import java.beans.PropertyEditorSupport; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +//START SNIPPET: java +/** + * + * Used to verify that per property PropertyEditors work correctly. + * + * @author chirino + */ +public class MilliLittersPropertyEditor extends PropertyEditorSupport { + + public void setAsText(String text) throws IllegalArgumentException { + + Pattern p = Pattern.compile("^(\\d+)\\s*(l(iter)?)?$", Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(text); + if( m.matches() ) { + setValue(new Long(Long.parseLong(m.group(1))*1000)); + return; + } + + p = Pattern.compile("^(\\d+)\\s*(ml)?$", Pattern.CASE_INSENSITIVE); + m = p.matcher(text); + if( m.matches() ) { + setValue(new Long(Long.parseLong(m.group(1)))); + return; + } + + p = Pattern.compile("^(\\d+)\\s*pints?$", Pattern.CASE_INSENSITIVE); + m = p.matcher(text); + if( m.matches() ) { + long pints = Long.parseLong(m.group(1)); + setValue(new Long( (long)(pints * 1750) )); + return; + } + + throw new IllegalArgumentException("Could convert not to long (in ml) for "+ text); + } + + public String getAsText() { + Long value = (Long) getValue(); + return (value != null ? value.toString() : ""); + } +} +//END SNIPPET: java diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/PizzaService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/PizzaService.java new file mode 100644 index 00000000..aea23f40 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/PizzaService.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.example; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @org.apache.xbean.XBean element="pizza" + * description="This is a tasty Pizza" + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ + +// START SNIPPET: bean +public class PizzaService { + + private static final Log log = LogFactory.getLog(PizzaService.class); + + private String topping; + private String cheese; + private int size; + private double price; + + public void makePizza() { + log.info("Making a pizza with topping: " + topping + " cheese: " + cheese + " with size: " + size); + } + + public String getCheese() { + return cheese; + } + + public void setCheese(String cheese) { + this.cheese = cheese; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + /** + * @org.apache.xbean.Property alias="myTopping" + */ + public String getTopping() { + return topping; + } + + public void setTopping(String topping) { + this.topping = topping; + } + +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/QNameService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/QNameService.java new file mode 100644 index 00000000..b279f6a9 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/QNameService.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.xbean.blueprint.example; + +import java.util.List; + +import javax.xml.namespace.QName; + +/** + * @org.apache.xbean.XBean element="qname-service" + * @author gnodet + */ +public class QNameService { + + private QName[] services; + private List list; + + public QName[] getServices() { + return services; + } + + public void setServices(QName[] services) { + this.services = services; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/Recipe.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/Recipe.java new file mode 100644 index 00000000..a3e2117b --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/Recipe.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.xbean.blueprint.example; + +/** + * @org.apache.xbean.XBean element="recipe" + * @author Dan Diephouse + */ +public class Recipe +{ + private String ingredients; + private String instructions; + + public String getInstructions() + { + return instructions; + } + + public void setInstructions(String instructions) + { + this.instructions = instructions; + } + + public String getIngredients() + { + return ingredients; + } + + public void setIngredients(String ingredients) + { + this.ingredients = ingredients; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RecipeService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RecipeService.java new file mode 100644 index 00000000..b27a2e9c --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RecipeService.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.xbean.blueprint.example; + +import java.util.List; +/** + * @org.apache.xbean.XBean element="recipe-service" + * @author Dan Diephouse + */ +public class RecipeService +{ + private List recipes; + private Recipe topRecipe; + + /** + * @org.apache.xbean.FlatCollection childElement="recipe" + * @return + */ + public List getRecipes() + { + return recipes; + } + + public void setRecipes(List recipes) + { + this.recipes = recipes; + } + + /** + * @org.apache.xbean.Flat + * @return + */ + public Recipe getTopRecipe() { + return topRecipe; + } + + public void setTopRecipe(Recipe topRecipe) { + this.topRecipe = topRecipe; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RestaurantService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RestaurantService.java new file mode 100644 index 00000000..7d3b88e5 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/RestaurantService.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.example; + +import javax.xml.namespace.QName; + +import java.net.URI; +import java.util.List; +import java.util.Set; + +/** + * An owner POJO used for testing out nested properties + * + * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/pizza" element="restaurant" + * description="A Restaurant thingy" + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ +public class RestaurantService { + + private PizzaService favourite; + private List dinnerMenu; + private PizzaService[] lunchMenu; + private Set snackMenu; + private QName serviceName; + private URI uri; + + /** + * @org.apache.xbean.Property nestedType="org.apache.xbean.blueprint.example.PizzaService" + */ + public List getDinnerMenu() { + return dinnerMenu; + } + + public void setDinnerMenu(List dinnerMenu) { + this.dinnerMenu = dinnerMenu; + } + + public PizzaService[] getLunchMenu() { + return lunchMenu; + } + + public void setLunchMenu(PizzaService[] lunchMenu) { + this.lunchMenu = lunchMenu; + } + + public Set getSnackMenu() { + return snackMenu; + } + + public void setSnackMenu(Set snackMenu) { + this.snackMenu = snackMenu; + } + + public PizzaService getFavourite() { + return favourite; + } + + public void setFavourite(PizzaService favourite) { + this.favourite = favourite; + } + + public QName getServiceName() { + return serviceName; + } + + public void setServiceName(QName name) { + this.serviceName = name; + } + + public URI getUri() { + return uri; + } + + public void setUri(URI uri) { + this.uri = uri; + } + +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SaladService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SaladService.java new file mode 100644 index 00000000..aeab5b25 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SaladService.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.example; + +/** + * Basic salad. + * @org.apache.xbean.XBean + * + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ + +// START SNIPPET: bean +public class SaladService { + private final String dressing; + private final String size; + private final boolean crouton; + + public SaladService(String dressing, String size, boolean crouton) { + this.dressing = dressing; + this.size = size; + this.crouton = crouton; + } + + /** + * Dressing What type of dressing do you want? + */ + public String getDressing() { + return dressing; + } + + /** + * What size do you want? + */ + public String getSize() { + return size; + } + + /** + * Do you want crutons on that? + * @org.apache.xbean.Property alias="addCroutons" + */ + public boolean isCrouton() { + return crouton; + } +} +// END SNIPPET: bean diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketAddressFactory.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketAddressFactory.java new file mode 100644 index 00000000..a962bfba --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketAddressFactory.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.xbean.blueprint.example; + +import java.net.SocketAddress; +import java.net.InetSocketAddress; + +/** + * @org.apache.xbean.XBean element="socketAddress" contentProperty="value" + */ +public class SocketAddressFactory { + /** + * @org.apache.xbean.FactoryMethod + */ + public static SocketAddress create(String value) { + return new InetSocketAddress(value, 42); + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketService.java new file mode 100644 index 00000000..c36ba9ba --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SocketService.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.xbean.blueprint.example; + +import java.net.SocketAddress; +import java.util.List; + +/** + * @org.apache.xbean.XBean element="socketService" + */ +public class SocketService { + private List addresses; + + public List getAddresses() { + return addresses; + } + + public void setAddresses(List addresses) { + this.addresses = addresses; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SoupService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SoupService.java new file mode 100644 index 00000000..89c03874 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/SoupService.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.xbean.blueprint.example; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @org.apache.xbean.XBean element="soup" + * description="This is a tasty soup" + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ + +// START SNIPPET: bean +public class SoupService { + private static final Log log = LogFactory.getLog(SoupService.class); + + /** + * @org.apache.xbean.FactoryMethod + */ + public static SoupService newSoup(String type) { + return new SoupService(type, System.currentTimeMillis()); + } + + private final String type; + private final long createTime; + private boolean exists = false; + + private SoupService(String type, long createTime) { + this.type = type; + this.createTime = createTime; + } + + /** + * @org.apache.xbean.InitMethod + */ + public void make() { + log.info("Making " + type + "soup"); + exists = true; + } + + /** + * @org.apache.xbean.DestroyMethod + */ + public void eat() { + log.info("Mummmm " + type + "soup is yummie!"); + exists = false; + } + + public boolean exists() { + return exists; + } + + /** + * What type of soup would you like? + */ + public String getSoupType() { + return type; + } + + public long getCreateTime() { + return createTime; + } +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/VodkaService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/VodkaService.java new file mode 100644 index 00000000..7a0a666e --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/VodkaService.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.xbean.blueprint.example; + +/** + * It comes from a potatoe, it must be good. + * + * @org.apache.xbean.XBean element="vodka" + * + * @author Dan Diephouse + * @version $Id: VodkaService.java 434369 2006-08-24 10:24:21Z gnodet $ + * @since 2.0 + */ + +// START SNIPPET: bean +public class VodkaService { + private String id; + private String name; + private Class vodkaClass; + + public VodkaService() { + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public Class getVodkaClass() { + return vodkaClass; + } + + public void setVodkaClass(Class vodkaClass) { + this.vodkaClass = vodkaClass; + } +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/WineService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/WineService.java new file mode 100644 index 00000000..11c2c1e9 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/example/WineService.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.xbean.blueprint.example; + +/** + * A drop of nice + * + * @org.apache.xbean.XBean element="wine" + * + * @author James Strachan + * @version $Id$ + * @since 2.0 + */ + +// START SNIPPET: bean +public class WineService { + private String id; + private String name; + + public WineService(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } +} +// END SNIPPET: bean + diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/CheeseService.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/CheeseService.java new file mode 100644 index 00000000..1028b089 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/CheeseService.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.xbean.blueprint.generator; + +/** + * Cheezy goodness + * + * @org.apache.xbean.XBean element="cheese" + * + * @author Dain Sundstrom + * @version $Id$ + */ +public class CheeseService { + private String id; + private String name; + + private long volume; + + public CheeseService(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public long getVolumeWithPropertyEditor() { + return volume; + } + + /** + * @org.apache.xbean.Property propertyEditor="org.apache.xbean.blueprint.example.MilliLittersPropertyEditor" + */ + public void setVolumeWithPropertyEditor(long volume) { + this.volume = volume; + } +} diff --git a/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/ModelTest.java b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/ModelTest.java new file mode 100644 index 00000000..4fbb6543 --- /dev/null +++ b/xbean-blueprint/src/test/java/org/apache/xbean/blueprint/generator/ModelTest.java @@ -0,0 +1,212 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.blueprint.generator; + +import junit.framework.TestCase; +import org.apache.xbean.blueprint.example.BeerService; +import org.xml.sax.*; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.beans.PropertyEditorManager; +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Dain Sundstrom + * @version $Id$ + * @since 1.0 + */ +public class ModelTest extends TestCase { + private static final String DEFAULT_NAMESPACE = "http://xbean.apache.org/test"; + + public void testQdox() throws Exception{ + String basedir = System.getProperties().getProperty("basedir", "."); + QdoxMappingLoader mappingLoader = new QdoxMappingLoader(DEFAULT_NAMESPACE, new File[] { new File(basedir, "/src/test/java")}, null); + NamespaceMapping defaultNamespace = getDefaultNamespace(mappingLoader); + assertNotNull(defaultNamespace); + ElementMapping element = defaultNamespace.getElement("pizza"); + assertNotNull(element); + AttributeMapping attribute = element.getAttribute("myTopping"); + assertNotNull(attribute); + assertEquals("topping", attribute.getPropertyName()); + + ElementMapping beer = defaultNamespace.getElement("beer"); + assertNotNull(beer); + AttributeMapping beerId = beer.getAttribute("id"); + assertNotNull(beerId); + AttributeMapping beerName = beer.getAttribute("name"); + assertNotNull(beerName); + + ElementMapping recipeService = defaultNamespace.getElement("recipe-service"); + assertNotNull(recipeService); + + Map flatCollections = recipeService.getFlatCollections(); + assertNotNull(flatCollections); + assertEquals(1, flatCollections.size()); + + ElementMapping cheese = defaultNamespace.getElement("cheese"); + assertNotNull(cheese); + AttributeMapping volume = cheese.getAttribute("volumeWithPropertyEditor"); + assertNotNull(volume); + assertNotNull(volume.getPropertyEditor()); + assertEquals(volume.getType().getName(), "long"); + + // validate xsd has string for attribute VolumeWithPropertyEditor + final AtomicBoolean gotExpected = new AtomicBoolean(false); + XsdGenerator generator = new XsdGenerator(null); + generator.generateSchema(new PrintWriter("dummy") { + @Override + public void println(String x) { + if (x.indexOf("volumeWithPropertyEditor") != -1) { + if (x.indexOf("xs:string") != -1) { + gotExpected.set(true); + } + } + } + }, defaultNamespace); + + assertTrue("xsd with string got genereated", gotExpected.get()); + } + + public void testQdoxExcludeClass() throws Exception{ + String basedir = System.getProperties().getProperty("basedir", "."); + QdoxMappingLoader mappingLoader = new QdoxMappingLoader(DEFAULT_NAMESPACE, + new File[] { new File(basedir, "/src/test/java")}, + new String[] { BeerService.class.getName() } ); + + NamespaceMapping defaultNamespace = getDefaultNamespace(mappingLoader); + assertNotNull(defaultNamespace); + + ElementMapping element = defaultNamespace.getElement("pizza"); + assertNotNull(element); + ElementMapping beer = defaultNamespace.getElement("beer"); + assertNull(beer); + } + + public void testQdoxExcludePackage() throws Exception{ + String basedir = System.getProperties().getProperty("basedir", "."); + QdoxMappingLoader mappingLoader = new QdoxMappingLoader(DEFAULT_NAMESPACE, + new File[] { new File(basedir, "/src/test/java")}, + new String[] { "org.apache.xbean.blueprint.example" } ); + + NamespaceMapping defaultNamespace = getDefaultNamespace(mappingLoader); + assertNotNull(defaultNamespace); + + ElementMapping element = defaultNamespace.getElement("pizza"); + assertNull(element); + ElementMapping beer = defaultNamespace.getElement("beer"); + assertNull(beer); + ElementMapping cheese = defaultNamespace.getElement("cheese"); + assertNotNull(cheese); + } + + private NamespaceMapping getDefaultNamespace(QdoxMappingLoader mappingLoader) throws IOException { + Set namespaces = mappingLoader.loadNamespaces(); + assertFalse(namespaces.isEmpty()); + + NamespaceMapping defaultNamespace = null; + for (Iterator iterator = namespaces.iterator(); iterator.hasNext();) { + NamespaceMapping namespaceMapping = (NamespaceMapping) iterator.next(); + if (namespaceMapping.getNamespace().equals(DEFAULT_NAMESPACE)) { + defaultNamespace = namespaceMapping; + break; + } + } + return defaultNamespace; + } + + public void testPropertyEditor() { + List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath())); + editorSearchPath.add("org.apache.xbean.blueprint.context.impl"); + PropertyEditorManager.setEditorSearchPath((String[]) editorSearchPath.toArray(new String[editorSearchPath.size()])); + assertTrue(Utils.isSimpleType(Type.newSimpleType("java.net.URI"))); + } + + public void xtestXSDValidation() throws Exception{ + + InputStream xmlFile = ModelTest.class.getResourceAsStream("model-test-xsd-validation.xml"); + File xsd = generateXSD(); + validate(xmlFile, xsd); + + } + + private File generateXSD() throws IOException { + String basedir = System.getProperties().getProperty("basedir", "."); + final File targetXSD = new File(basedir, "target/test-data/model-test.xsd"); + targetXSD.getParentFile().mkdirs(); + QdoxMappingLoader mappingLoader = new QdoxMappingLoader(DEFAULT_NAMESPACE, new File[] { new File(basedir, "/src/test/java")}, null); + NamespaceMapping namespaceMapping = getDefaultNamespace(mappingLoader); + XsdGenerator generator = new XsdGenerator(targetXSD); + generator.setLog(new LogFacade() { + public void log(String message) { + } + public void log(String message, int level) { + } + }); + generator.generate(namespaceMapping); + return targetXSD; + } + + private void validate(InputStream xml, final File xsd) throws ParserConfigurationException, SAXException, IOException { + assertNotNull(xml); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(true); + factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); + + final AtomicReference error = new AtomicReference(); + + DocumentBuilder builder = factory.newDocumentBuilder(); + + builder.setErrorHandler(new ErrorHandler() { + public void warning(SAXParseException exception) throws SAXException { + error.set(exception); + } + + public void error(SAXParseException exception) throws SAXException { + error.set(exception); + } + + public void fatalError(SAXParseException exception) throws SAXException { + error.set(exception); + } + }); + //TODO blueprint what ?? + builder.setEntityResolver(new EntityResolver() { + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { +// PluggableSchemaResolver springResolver = new PluggableSchemaResolver(getClass().getClassLoader()); + InputSource source = null;//springResolver.resolveEntity(publicId, systemId); + if (source == null && "http://xbean.apache.org/test.xsd".equals(systemId)) { + source = new InputSource(new FileInputStream(xsd)); + source.setPublicId(publicId); + source.setSystemId(systemId); + } + + return source; + } + }); + builder.parse(xml); + if (error.get() != null) { + error.get().printStackTrace(); + fail("Validation failed: " + error.get().getMessage()); + } + } +} diff --git a/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/component b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/component new file mode 100644 index 00000000..2eb43b1c --- /dev/null +++ b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/component @@ -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. +# +# +container=org.apache.xbean.spring.example.ContainerBean +inner=org.apache.xbean.spring.example.InnerBean diff --git a/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/pizza-simple b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/pizza-simple new file mode 100644 index 00000000..fa6a237b --- /dev/null +++ b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/pizza-simple @@ -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. +# +# +# START SNIPPET: config + +# the default package that POJOs are in +package = org.apache.xbean.spring.example + +# Mapping of XML Element localNames to classes +pizza = org.apache.xbean.spring.example.PizzaService +restaurant = org.apache.xbean.spring.example.RestaurantService + +# END SNIPPET: config diff --git a/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/restaurant b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/restaurant new file mode 100644 index 00000000..3e76e823 --- /dev/null +++ b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/restaurant @@ -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. +# +# +# START SNIPPET: config + +# the default package that POJOs are in +package = org.apache.xbean.spring.example + +# Mapping of XML Element localNames to classes +pizza = org.apache.xbean.spring.example.PizzaService +restaurant = org.apache.xbean.spring.example.RestaurantService + +# Mapping of XML Attributes to property names +pizza.alias.myTopping = topping + +# Mapping of nested bean properties +restaurant.dinnerMenu.list = dinnerMenu +restaurant.favourite = favourite + +# END SNIPPET: config diff --git a/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/salad b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/salad new file mode 100644 index 00000000..2faca0d6 --- /dev/null +++ b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/salad @@ -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. +# +# +# START SNIPPET: config + +# the default package that POJOs are in +package = org.apache.xbean.spring.example + +# Mapping of XML Element localNames to classes +salad = org.apache.xbean.spring.example.SaladService + +# Mapping of XML Attributes to property names +salad.alias.addCroutons = crouton + +# Mapping of constructor argument names +org.apache.xbean.spring.example.SaladService(java.lang.String,java.lang.String,boolean).parameterNames=dressing size crouton + +# END SNIPPET: config diff --git a/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/soup b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/soup new file mode 100644 index 00000000..76be7a61 --- /dev/null +++ b/xbean-blueprint/src/test/resources/META-INF/services/org/apache/xbean/blueprint/http/xbean.apache.org/schemas/soup @@ -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. +# +# +# START SNIPPET: config + +# the default package that POJOs are in +#TODO blueprint support this?? +#package = org.apache.xbean.blueprint.example + +# Mapping of XML Element localNames to classes +soup = org.apache.xbean.blueprint.example.SoupService + +# Mapping of life-cycle methods +soup.initMethod = make +soup.destroyMethod = eat +soup.factoryMethod = newSoup + +# Mapping of constructor argument names +org.apache.xbean.blueprint.example.SoupService.newSoup(java.lang.String).parameterNames = type + +# END SNIPPET: config diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-attribute.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-attribute.xml new file mode 100644 index 00000000..c511bb1c --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-attribute.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-element.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-element.xml new file mode 100644 index 00000000..b3777454 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-element.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-namespace.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-namespace.xml new file mode 100644 index 00000000..f7ad4341 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/bad-namespace.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-normal.xml new file mode 100644 index 00000000..b553ca36 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-normal.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-ns.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-ns.xml new file mode 100644 index 00000000..f32e32a6 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-ns.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-null.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-null.xml new file mode 100644 index 00000000..10058931 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-null.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-system-prop.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-system-prop.xml new file mode 100644 index 00000000..ec3f3a68 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean-system-prop.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean.xml new file mode 100644 index 00000000..afbeae08 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/beer-xbean.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-blueprint.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-blueprint.xml new file mode 100644 index 00000000..1ab94fef --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-blueprint.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-xbean.xml new file mode 100644 index 00000000..e70d1f9d --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/component-xbean.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-normal.xml new file mode 100644 index 00000000..274389a2 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-normal.xml @@ -0,0 +1,45 @@ + + + + + + + + + Grey Goose + + + + Malbec + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean-mixed.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean-mixed.xml new file mode 100644 index 00000000..e028a89b --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean-mixed.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + Malbec + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean.xml new file mode 100644 index 00000000..503a4233 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/favorite-xbean.xml @@ -0,0 +1,42 @@ + + + + + + + + Grey Goose + + + Malbec + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/flatmap-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/flatmap-xbean.xml new file mode 100644 index 00000000..61055203 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/flatmap-xbean.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/gin.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/gin.xml new file mode 100644 index 00000000..d8a00ee8 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/gin.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean-properties.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean-properties.xml new file mode 100644 index 00000000..0830a6b1 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean-properties.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean.xml new file mode 100644 index 00000000..8403dee2 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/keg-xbean.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-normal.xml new file mode 100644 index 00000000..16811b79 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-normal.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-bean-ref.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-bean-ref.xml new file mode 100644 index 00000000..5ed35608 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-bean-ref.xml @@ -0,0 +1,34 @@ + + + + + + + + + Salami + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-java.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-java.xml new file mode 100644 index 00000000..248b7c94 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-java.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-properties.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-properties.xml new file mode 100644 index 00000000..fc24ca4c --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean-properties.xml @@ -0,0 +1,34 @@ + + + + + + + cheese Edam + + + Salami + + size 17 + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean.xml new file mode 100644 index 00000000..30d91804 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/pizza-xbean.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-normal.xml new file mode 100644 index 00000000..f0d5899c --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-normal.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-xbean.xml new file mode 100644 index 00000000..6e22caf9 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/qname-xbean.xml @@ -0,0 +1,35 @@ + + + + + + + foo:test + foo:bar + + + foo:list + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-normal.xml new file mode 100644 index 00000000..76127ec7 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-normal.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean-mixed.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean-mixed.xml new file mode 100644 index 00000000..0acd1553 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean-mixed.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + Mash together + Food + + + Mash together + Food + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean.xml new file mode 100644 index 00000000..ea1eb878 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/recipe-xbean.xml @@ -0,0 +1,38 @@ + + + + + + + Mash together + Food + + + Mash together + Food + + + Mash together + Food + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-normal.xml new file mode 100644 index 00000000..820c7acd --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-normal.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-spring-extended.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-spring-extended.xml new file mode 100644 index 00000000..de547e3c --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-spring-extended.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-mixed.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-mixed.xml new file mode 100644 index 00000000..4ab4a6bf --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-mixed.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-root.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-root.xml new file mode 100644 index 00000000..5d271915 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-root.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-simple.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-simple.xml new file mode 100644 index 00000000..aff7e3da --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean-simple.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean.xml new file mode 100644 index 00000000..21af66b3 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/restaurant-xbean.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-normal.xml new file mode 100644 index 00000000..d27fcd93 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-normal.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-xbean.xml new file mode 100644 index 00000000..f3a47d03 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/salad-xbean.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address-normal.xml new file mode 100644 index 00000000..49869635 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address-normal.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address.xml new file mode 100644 index 00000000..91d43567 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/socket-address.xml @@ -0,0 +1,32 @@ + + + + + localhost + + + + localhost + localhost + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-normal.xml new file mode 100644 index 00000000..999b19f6 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-normal.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-xbean.xml new file mode 100644 index 00000000..4dd7de12 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/soup-xbean.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + French Onion + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/spring-extension.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/spring-extension.xml new file mode 100644 index 00000000..a3d27b0c --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/spring-extension.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-normal.xml new file mode 100644 index 00000000..947b0fad --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-normal.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-xbean.xml new file mode 100644 index 00000000..172c0be1 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/vodka-xbean.xml @@ -0,0 +1,33 @@ + + + + + + + vodkaService + + Grey Goose + + org.apache.xbean.blueprint.example.VodkaService + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-normal.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-normal.xml new file mode 100644 index 00000000..9acdbd24 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-normal.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-xbean.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-xbean.xml new file mode 100644 index 00000000..c4c49da5 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/context/wine-xbean.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/generator/model-test-xsd-validation.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/generator/model-test-xsd-validation.xml new file mode 100644 index 00000000..902088a0 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/generator/model-test-xsd-validation.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/jndi.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/jndi.xml new file mode 100644 index 00000000..be8d5861 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/jndi.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/spring.xml b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/spring.xml new file mode 100644 index 00000000..32adb1a8 --- /dev/null +++ b/xbean-blueprint/src/test/resources/org/apache/xbean/blueprint/jndi/spring.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xbean-bundleutils/pom.xml b/xbean-bundleutils/pom.xml new file mode 100644 index 00000000..2ee5ff75 --- /dev/null +++ b/xbean-bundleutils/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + org.apache.xbean + xbean + 3.12 + + xbean-bundleutils + bundle + Apache XBean OSGI Bundle Utilities + + + + org.slf4j + slf4j-api + + + org.osgi + org.osgi.core + 4.3.0 + provided + + + org.eclipse + osgi + 3.6.0.v20100517 + provided + + + + + + + + smx.svn + Servicemix svn Repository + default + http://svn.apache.org/repos/asf/servicemix/m2-repo/ + + true + + + false + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.eclipse.*;resolution:=optional, + org.osgi.framework.wiring;resolution:=optional, + * + + + + + + + + diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassFinder.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassFinder.java new file mode 100644 index 00000000..8a4ac48e --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassFinder.java @@ -0,0 +1,442 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.xbean.osgi.bundle.util.BundleDescription.ExportPackage; +import org.apache.xbean.osgi.bundle.util.BundleDescription.HeaderEntry; +import org.apache.xbean.osgi.bundle.util.BundleDescription.RequireBundle; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.service.packageadmin.RequiredBundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Finds all available classes to a bundle by scanning Bundle-ClassPath, + * Import-Package, and Require-Bundle headers of the given bundle and its fragments. + * DynamicImport-Package header is not considered during scanning. + * + * @version $Rev$ $Date$ + */ +public class BundleClassFinder { + + private static final Logger logger = LoggerFactory.getLogger(BundleClassFinder.class); + + public static final ClassDiscoveryFilter FULL_CLASS_DISCOVERY_FILTER = new DummyDiscoveryFilter(); + + public static final ClassDiscoveryFilter IMPORTED_PACKAGE_EXCLUSIVE_FILTER = new NonImportedPackageDiscoveryFilter(); + + protected static final String EXT = ".class"; + + protected static final String PATTERN = "*.class"; + + protected Bundle bundle; + + protected PackageAdmin packageAdmin; + + private Map> classMap; + + protected ClassDiscoveryFilter discoveryFilter; + + public BundleClassFinder(PackageAdmin packageAdmin, Bundle bundle) { + this(packageAdmin, bundle, FULL_CLASS_DISCOVERY_FILTER); + } + + public BundleClassFinder(PackageAdmin packageAdmin, Bundle bundle, ClassDiscoveryFilter discoveryFilter) { + this.packageAdmin = packageAdmin; + this.bundle = BundleUtils.unwrapBundle(bundle); + this.discoveryFilter = discoveryFilter; + } + + public List> loadClasses(Set classes) { + List> loadedClasses = new ArrayList>(classes.size()); + for (String clazz : classes) { + try { + loadedClasses.add(bundle.loadClass(clazz)); + } catch (Exception ignore) { + // ignore + } + } + return loadedClasses; + } + + /** + * Finds all available classes to the bundle. Some of the classes in the returned set + * might not be loadable. + * + * @return classes visible to the bundle. Not all classes returned might be loadable. + */ + public Set find() { + Set classes = new LinkedHashSet(); + classMap = new HashMap>(); + if (discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.IMPORT_PACKAGES)) { + scanImportPackages(classes, bundle, bundle); + } + if (discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.REQUIRED_BUNDLES)) { + scanRequireBundles(classes, bundle); + } + if (discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.BUNDLE_CLASSPATH)) { + scanBundleClassPath(classes, bundle); + } + if (discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.FRAGMENT_BUNDLES)) { + Bundle[] fragments = packageAdmin.getFragments(bundle); + if (fragments != null) { + for (Bundle fragment : fragments) { + scanImportPackages(classes, bundle, fragment); + scanRequireBundles(classes, fragment); + scanBundleClassPath(classes, fragment); + } + } + } + classMap.clear(); + return classes; + } + + protected boolean isClassAcceptable(String name, InputStream in) throws IOException { + return true; + } + + protected boolean isClassAcceptable(URL url) { + return true; + } + + protected BundleClassFinder createSubBundleClassFinder(PackageAdmin packageAdmin, Bundle bundle, ClassDiscoveryFilter classDiscoveryFilter) { + return new BundleClassFinder(packageAdmin, bundle, classDiscoveryFilter); + } + + protected String toJavaStyleClassName(String name) { + if (name.endsWith(EXT)) { + name = name.substring(0, name.length() - EXT.length()); + } + name = name.replace('/', '.'); + return name; + } + + /** + * Get the normal Java style package name from the parameter className. + * If the className is ended with .class extension, e.g. /org/apache/geronimo/TestCass.class or org.apache.geronimo.TestClass.class, + * then org/apache/geronimo is returned + * If the className is not ended with .class extension, e.g. /org/apache/geronimo/TestCass or org.apache.geronimo.TestClass, + * then org/apache/geronimo is returned + * @return Normal Java style package name, should be like org.apache.geronimo + */ + protected String toJavaStylePackageName(String className) { + if (className.endsWith(EXT)) { + className = className.substring(0, className.length() - EXT.length()); + } + className = className.replace('/', '.'); + int iLastDotIndex = className.lastIndexOf('.'); + if (iLastDotIndex != -1) { + return className.substring(0, iLastDotIndex); + } else { + return ""; + } + } + + private Set findAllClasses(Bundle bundle, ClassDiscoveryFilter userClassDiscoveryFilter, Set exportedPackageNames) { + Set allClasses = classMap.get(bundle); + if (allClasses == null) { + BundleClassFinder finder = createSubBundleClassFinder(packageAdmin, bundle, new ImportExclusivePackageDiscoveryFilterAdapter(userClassDiscoveryFilter, exportedPackageNames)); + allClasses = finder.find(); + classMap.put(bundle, allClasses); + } + return allClasses; + } + + private Set findAllClasses(Bundle bundle, String packageName) { + Set allClasses = classMap.get(bundle); + if (allClasses == null) { + BundleClassFinder finder = createSubBundleClassFinder(packageAdmin, bundle, new ImportExclusivePackageDiscoveryFilter(packageName)); + allClasses = finder.find(); + classMap.put(bundle, allClasses); + } + return allClasses; + } + + private void scanImportPackages(Collection classes, Bundle host, Bundle fragment) { + BundleDescription description = new BundleDescription(fragment.getHeaders()); + List imports = description.getExternalImports(); + for (BundleDescription.ImportPackage packageImport : imports) { + String packageName = packageImport.getName(); + if (discoveryFilter.packageDiscoveryRequired(packageName)) { + ExportedPackage[] exports = packageAdmin.getExportedPackages(packageName); + Bundle wiredBundle = isWired(host, exports); + if (wiredBundle != null) { + Set allClasses = findAllClasses(wiredBundle, packageName); + classes.addAll(allClasses); + } + } + } + } + + private void scanRequireBundles(Collection classes, Bundle bundle) { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + List requiredBundleList = description.getRequireBundle(); + for (RequireBundle requiredBundle : requiredBundleList) { + RequiredBundle[] requiredBundles = packageAdmin.getRequiredBundles(requiredBundle.getName()); + Bundle wiredBundle = isWired(bundle, requiredBundles); + if (wiredBundle != null) { + BundleDescription wiredBundleDescription = new BundleDescription(wiredBundle.getHeaders()); + List exportPackages = wiredBundleDescription.getExportPackage(); + Set exportedPackageNames = new HashSet(); + for (ExportPackage exportPackage : exportPackages) { + exportedPackageNames.add(exportPackage.getName()); + } + Set allClasses = findAllClasses(wiredBundle, discoveryFilter, exportedPackageNames); + classes.addAll(allClasses); + } + } + } + + private void scanBundleClassPath(Collection resources, Bundle bundle) { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + List paths = description.getBundleClassPath(); + if (paths.isEmpty()) { + scanDirectory(resources, bundle, "/"); + } else { + for (HeaderEntry path : paths) { + String name = path.getName(); + if (name.equals(".") || name.equals("/")) { + // scan root + scanDirectory(resources, bundle, "/"); + } else if (name.endsWith(".jar") || name.endsWith(".zip")) { + // scan embedded jar/zip + scanZip(resources, bundle, name); + } else { + // assume it's a directory + scanDirectory(resources, bundle, "/" + name); + } + } + } + } + + private void scanDirectory(Collection classes, Bundle bundle, String basePath) { + basePath = addSlash(basePath); + if (!discoveryFilter.directoryDiscoveryRequired(basePath)) { + return; + } + Enumeration e = bundle.findEntries(basePath, PATTERN, true); + if (e != null) { + while (e.hasMoreElements()) { + URL u = e.nextElement(); + String entryName = u.getPath().substring(basePath.length()); + if (discoveryFilter.packageDiscoveryRequired(toJavaStylePackageName(entryName))) { + if (isClassAcceptable(u)) { + classes.add(toJavaStyleClassName(entryName)); + } + } + } + } + } + + private void scanZip(Collection classes, Bundle bundle, String zipName) { + if (!discoveryFilter.jarFileDiscoveryRequired(zipName)) { + return; + } + URL zipEntry = bundle.getEntry(zipName); + if (zipEntry == null) { + return; + } + ZipInputStream in = null; + try { + in = new ZipInputStream(zipEntry.openStream()); + ZipEntry entry; + while ((entry = in.getNextEntry()) != null) { + String name = entry.getName(); + if (name.endsWith(EXT) && discoveryFilter.packageDiscoveryRequired(toJavaStylePackageName(name))) { + if (isClassAcceptable(name, in)) { + classes.add(toJavaStyleClassName(name)); + } + } + } + } catch (IOException ignore) { + logger.warn("Fail to check zip file " + zipName, ignore); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + protected String addSlash(String name) { + if (!name.endsWith("/")) { + name = name + "/"; + } + return name; + } + + protected Bundle isWired(Bundle bundle, ExportedPackage[] exports) { + if (exports != null) { + for (ExportedPackage exportedPackage : exports) { + Bundle[] importingBundles = exportedPackage.getImportingBundles(); + if (importingBundles != null) { + for (Bundle importingBundle : importingBundles) { + if (importingBundle == bundle) { + return exportedPackage.getExportingBundle(); + } + } + } + } + } + return null; + } + + protected Bundle isWired(Bundle bundle, RequiredBundle[] requiredBundles) { + if (requiredBundles != null) { + for (RequiredBundle requiredBundle : requiredBundles) { + Bundle[] requiringBundles = requiredBundle.getRequiringBundles(); + if (requiringBundles != null) { + for (Bundle requiringBundle : requiringBundles) { + if (requiringBundle == bundle) { + return requiredBundle.getBundle(); + } + } + } + } + } + return null; + } + + public static class DummyDiscoveryFilter implements ClassDiscoveryFilter { + + + public boolean directoryDiscoveryRequired(String url) { + return true; + } + + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange) { + return true; + } + + + public boolean jarFileDiscoveryRequired(String url) { + return true; + } + + + public boolean packageDiscoveryRequired(String packageName) { + return true; + } + } + + public static class NonImportedPackageDiscoveryFilter implements ClassDiscoveryFilter { + + + public boolean directoryDiscoveryRequired(String url) { + return true; + } + + + public boolean jarFileDiscoveryRequired(String url) { + return true; + } + + + public boolean packageDiscoveryRequired(String packageName) { + return true; + } + + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange) { + return !discoveryRange.equals(DiscoveryRange.IMPORT_PACKAGES); + } + } + + private static class ImportExclusivePackageDiscoveryFilter implements ClassDiscoveryFilter { + + private String expectedPckageName; + + public ImportExclusivePackageDiscoveryFilter(String expectedPckageName) { + this.expectedPckageName = expectedPckageName; + } + + + public boolean directoryDiscoveryRequired(String url) { + return true; + } + + + public boolean jarFileDiscoveryRequired(String url) { + return true; + } + + + public boolean packageDiscoveryRequired(String packageName) { + return expectedPckageName.equals(packageName); + } + + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange) { + return !discoveryRange.equals(DiscoveryRange.IMPORT_PACKAGES); + } + } + + private static class ImportExclusivePackageDiscoveryFilterAdapter implements ClassDiscoveryFilter { + + private Set acceptedPackageNames; + + private ClassDiscoveryFilter classDiscoveryFilter; + + public ImportExclusivePackageDiscoveryFilterAdapter(ClassDiscoveryFilter classDiscoveryFilter, Set acceptedPackageNames) { + this.classDiscoveryFilter = classDiscoveryFilter; + this.acceptedPackageNames = acceptedPackageNames; + } + + + public boolean directoryDiscoveryRequired(String url) { + return true; + } + + + public boolean jarFileDiscoveryRequired(String url) { + return true; + } + + + public boolean packageDiscoveryRequired(String packageName) { + return acceptedPackageNames.contains(packageName) && classDiscoveryFilter.packageDiscoveryRequired(packageName); + } + + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange) { + return !discoveryRange.equals(DiscoveryRange.IMPORT_PACKAGES); + } + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassLoader.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassLoader.java new file mode 100644 index 00000000..ffab5c1d --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleClassLoader.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleReference; + +/** + * ClassLoader for a {@link Bundle}. + *
    + * In OSGi, resource lookup on resources in the META-INF directory using {@link Bundle#getResource(String)} or + * {@link Bundle#getResources(String)} does not return the resources found in the wired bundles of the bundle + * (wired via Import-Package or DynamicImport-Package). This class loader implementation provides + * {@link #getResource(String) and {@link #getResources(String)} methods that do delegate such resource lookups to + * the wired bundles. + *
    + * The URLs returned by {@link Bundle#getResource(String)} or {@link Bundle#getResources(String)} methods are + * OSGi framework specific "bundle" URLs. This sometimes can cause problems with 3rd party libraries + * which do not understand how to interpret the "bundle" URLs. This ClassLoader implementation, if enabled, + * can return jar URLs for resources found in embedded jars in the bundle. If a resource is found within a + * directory in the bundle the URL returned for that resource is unconverted. + * + * @version $Rev$ $Date$ + */ +public class BundleClassLoader extends ClassLoader implements DelegatingBundleReference { + + protected final Bundle bundle; + protected final BundleResourceHelper resourceHelper; + + public BundleClassLoader(Bundle bundle) { + this(bundle, + BundleResourceHelper.getSearchWiredBundles(true), + BundleResourceHelper.getConvertResourceUrls(false)); + } + + public BundleClassLoader(Bundle bundle, boolean searchWiredBundles) { + this(bundle, + searchWiredBundles, + BundleResourceHelper.getConvertResourceUrls(false)); + } + + public BundleClassLoader(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) { + this.bundle = bundle; + this.resourceHelper = new BundleResourceHelper(bundle, searchWiredBundles, convertResourceUrls); + } + + @Override + public String toString() { + return "[BundleClassLoader] " + bundle; + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class clazz = bundle.loadClass(name); + if (resolve) { + resolveClass(clazz); + } + return clazz; + } + + @Override + public URL getResource(String name) { + return resourceHelper.getResource(name); + } + + @Override + public Enumeration getResources(String name) throws IOException { + return resourceHelper.getResources(name); + } + + @Override + public Enumeration findResources (String name) throws IOException { + return this.getResources(name); + } + + public void setSearchWiredBundles(boolean search) { + resourceHelper.setSearchWiredBundles(search); + } + + public boolean getSearchWiredBundles() { + return resourceHelper.getSearchWiredBundles(); + } + + public void setConvertResourceUrls(boolean convert) { + resourceHelper.setConvertResourceUrls(convert); + } + + public boolean getConvertResourceUrls() { + return resourceHelper.getConvertResourceUrls(); + } + + /** + * Return the bundle associated with this classloader. + * + * In most cases the bundle associated with the classloader is a regular framework bundle. + * However, in some cases the bundle associated with the classloader is a {@link DelegatingBundle}. + * In such cases, the unwrap parameter controls whether this function returns the + * {@link DelegatingBundle} instance or the main application bundle backing with the {@link DelegatingBundle}. + * + * @param unwrap If true and if the bundle associated with this classloader is a {@link DelegatingBundle}, + * this function will return the main application bundle backing with the {@link DelegatingBundle}. + * Otherwise, the bundle associated with this classloader is returned as is. + * @return The bundle associated with this classloader. + */ + public Bundle getBundle(boolean unwrap) { + if (unwrap && bundle instanceof DelegatingBundle) { + return ((DelegatingBundle) bundle).getMainBundle(); + } + return bundle; + } + + /** + * Return the bundle associated with this classloader. + * + * This method calls {@link #getBundle(boolean) getBundle(true)} and therefore always returns a regular + * framework bundle. + *

    + * Note: Some libraries use {@link BundleReference#getBundle()} to obtain a bundle for the given + * classloader and expect the returned bundle instance to be work with any OSGi API. Some of these API might + * not work if {@link DelegatingBundle} is returned. That is why this function will always return + * a regular framework bundle. See {@link #getBundle(boolean)} for more information. + * + * @return The bundle associated with this classloader. + */ + public Bundle getBundle() { + return getBundle(true); + } + + @Override + public int hashCode() { + return bundle.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || !other.getClass().equals(getClass())) { + return false; + } + BundleClassLoader otherBundleClassLoader = (BundleClassLoader) other; + return this.bundle == otherBundleClassLoader.bundle; + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleDescription.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleDescription.java new file mode 100644 index 00000000..b2a6518b --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleDescription.java @@ -0,0 +1,330 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xbean.osgi.bundle.util; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +/** + * @version $Rev$, $Date$ + */ +public class BundleDescription { + + private Map headers; + + public BundleDescription(Manifest manifest) { + this.headers = manifestToMap(manifest); + } + + public BundleDescription(Dictionary dictionary) { + this.headers = new DictionaryMap(dictionary); + } + + public BundleDescription(Map headers) { + this.headers = headers; + } + + /** + * Returns a list of packages that are listed in Import-Package header. + */ + public List getImportPackage() { + String headerValue = (String) headers.get(Constants.IMPORT_PACKAGE); + List imports = new ArrayList(); + List elements = HeaderParser.parseHeader(headerValue); + for (HeaderElement element : elements) { + ImportPackage p = new ImportPackage(element.getName(), element.getAttributes(), element.getDirectives()); + imports.add(p); + } + return imports; + } + + /** + * Returns a list of packages that are listed in Export-Package header. + */ + public List getExportPackage() { + String headerValue = (String) headers.get(Constants.EXPORT_PACKAGE); + List exports = new ArrayList(); + List elements = HeaderParser.parseHeader(headerValue); + for (HeaderElement element : elements) { + ExportPackage p = new ExportPackage(element.getName(), element.getAttributes(), element.getDirectives()); + exports.add(p); + } + return exports; + } + + /** + * Returns a list of packages that are listed in Import-Package header + * and are not listed in Export-Package header. + */ + public List getExternalImports() { + List imports = getImportPackage(); + List exports = getExportPackage(); + List realImports = new ArrayList(); + for (ImportPackage p : imports) { + if (!isExported(exports, p)) { + realImports.add(p); + } + } + return realImports; + } + + private static boolean isExported(List exports, ImportPackage p) { + for (ExportPackage export : exports) { + if (export.getName().equals(p.getName())) { + return true; + } + } + return false; + } + + /** + * Returns a list of bundle names that are listed in Require-Bundle header. + */ + public List getRequireBundle() { + String headerValue = (String) headers.get(Constants.REQUIRE_BUNDLE); + List requireBundles = new ArrayList(); + List elements = HeaderParser.parseHeader(headerValue); + for (HeaderElement element : elements) { + RequireBundle p = new RequireBundle(element.getName(), element.getAttributes(), element.getDirectives()); + requireBundles.add(p); + } + return requireBundles; + } + + /** + * Returns Fragment-Host header. + */ + public FragmentHost getFragmentHost() { + String headerValue = (String) headers.get(Constants.FRAGMENT_HOST); + List elements = HeaderParser.parseHeader(headerValue); + if (elements.size() == 1) { + HeaderElement element = elements.get(0); + return new FragmentHost(element.getName(), element.getAttributes(), element.getDirectives()); + } + return null; + } + + /** + * Returns a list of packages that are listed in DynamicImport-Package header. + */ + public List getDynamicImportPackage() { + String headerValue = (String) headers.get(Constants.DYNAMICIMPORT_PACKAGE); + return parseStandardHeader(headerValue); + } + + /** + * Returns a list of paths that are listed in Bundle-ClassPath header. + */ + public List getBundleClassPath() { + String headerValue = (String) headers.get(Constants.BUNDLE_CLASSPATH); + return parseStandardHeader(headerValue); + } + + public SymbolicName getSymbolicName() { + String headerValue = (String) headers.get(Constants.BUNDLE_SYMBOLICNAME); + List elements = HeaderParser.parseHeader(headerValue); + if (elements.size() == 1) { + HeaderElement element = elements.get(0); + return new SymbolicName(element.getName(), element.getAttributes(), element.getDirectives()); + } + return null; + } + + public Version getVersion() { + String headerValue = (String) headers.get(Constants.BUNDLE_VERSION); + return getVersionRange(headerValue).getLow(); + } + + public Map getHeaders() { + return headers; + } + + private List parseStandardHeader(String headerValue) { + List imports = new ArrayList(); + List elements = HeaderParser.parseHeader(headerValue); + for (HeaderElement element : elements) { + HeaderEntry p = new HeaderEntry(element.getName(), element.getAttributes(), element.getDirectives()); + imports.add(p); + } + return imports; + } + + private static Map manifestToMap(Manifest manifest) { + Attributes attributes = manifest.getMainAttributes(); + Map headers = new HashMap(); + for (Map.Entry entry : attributes.entrySet()) { + String key = entry.getKey().toString(); + String value = entry.getValue().toString(); + headers.put(key, value); + } + return headers; + } + + private static VersionRange getVersionRange(String version) { + if (version == null) { + version = "0.0.0"; + } + return VersionRange.parse(version); + } + + public static class HeaderEntry { + + private String name; + private Map attributes; + private Map directives; + + public HeaderEntry(String name, + Map attributes, + Map directives) { + this.name = name; + this.attributes = attributes; + this.directives = directives; + } + + public String getName() { + return name; + } + + public Map getAttributes() { + return attributes; + } + + public Map getDirectives() { + return directives; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Name: ").append(name); + builder.append(", Attributes: ").append(attributes); + builder.append(", Directives: ").append(directives); + return builder.toString(); + } + + } + + public static class ExportPackage extends HeaderEntry { + + private Version version; + + public ExportPackage(String name, + Map attributes, + Map directives) { + super(name, attributes, directives); + version = BundleDescription.getVersionRange(attributes.get(Constants.VERSION_ATTRIBUTE)).getLow(); + } + + public Version getVersion() { + return version; + } + } + + public static class ImportPackage extends HeaderEntry { + + private boolean optional; + private VersionRange versionRange; + + public ImportPackage(String name, + Map attributes, + Map directives) { + super(name, attributes, directives); + + String resolution = directives.get(Constants.RESOLUTION_DIRECTIVE); + optional = Constants.RESOLUTION_OPTIONAL.equals(resolution); + + versionRange = BundleDescription.getVersionRange(attributes.get(Constants.VERSION_ATTRIBUTE)); + } + + public boolean isOptional() { + return optional; + } + + public boolean isMandatory() { + return !optional; + } + + public VersionRange getVersionRange() { + return versionRange; + } + } + + public static class SymbolicName extends HeaderEntry { + + public SymbolicName(String name, + Map attributes, + Map directives) { + super(name, attributes, directives); + } + + } + + public static class RequireBundle extends HeaderEntry { + + private boolean optional; + private VersionRange versionRange; + + public RequireBundle(String name, + Map attributes, + Map directives) { + super(name, attributes, directives); + + String resolution = directives.get(Constants.RESOLUTION_DIRECTIVE); + optional = Constants.RESOLUTION_OPTIONAL.equals(resolution); + + versionRange = BundleDescription.getVersionRange(attributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE)); + } + + public boolean isOptional() { + return optional; + } + + public boolean isMandatory() { + return !optional; + } + + public VersionRange getVersionRange() { + return versionRange; + } + } + + public static class FragmentHost extends HeaderEntry { + + private VersionRange versionRange; + + public FragmentHost(String name, + Map attributes, + Map directives) { + super(name, attributes, directives); + versionRange = BundleDescription.getVersionRange(attributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE)); + } + + public VersionRange getVersionRange() { + return versionRange; + } + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceClassLoader.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceClassLoader.java new file mode 100644 index 00000000..c4d06aff --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceClassLoader.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.xbean.osgi.bundle.util; + +import org.osgi.framework.Bundle; + +/** + * ClassLoader for a {@link Bundle}. + *
    + * This ClassLoader implementation extends the {@link BundleClassLoader} and returns resources embedded + * in jar files in a bundle with jar URLs. + * + * @version $Rev$ $Date$ + */ +public class BundleResourceClassLoader extends BundleClassLoader { + + public BundleResourceClassLoader(Bundle bundle) { + super(bundle, + BundleResourceHelper.getSearchWiredBundles(true), + BundleResourceHelper.getConvertResourceUrls(true)); + } + + @Override + public String toString() { + return "[BundleResourceClassLoader] " + bundle; + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceFinder.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceFinder.java new file mode 100644 index 00000000..0b111fb5 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceFinder.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.xbean.osgi.bundle.util.BundleDescription.HeaderEntry; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.PackageAdmin; + +/** + * Finds all available resources to a bundle by scanning Bundle-ClassPath header + * of the given bundle and its fragments. + * DynamicImport-Package header is not considered during scanning. + * + * @version $Rev$ $Date$ + */ +public class BundleResourceFinder { + + public static final ResourceDiscoveryFilter FULL_DISCOVERY_FILTER = new DummyDiscoveryFilter(); + + private final Bundle bundle; + private final PackageAdmin packageAdmin; + private final String prefix; + private final String suffix; + private final String osgiSuffix; + private final boolean extendedMatching; + private ResourceDiscoveryFilter discoveryFilter; + + public BundleResourceFinder(PackageAdmin packageAdmin, Bundle bundle, String prefix, String suffix) { + this(packageAdmin, bundle, prefix, suffix, FULL_DISCOVERY_FILTER); + } + + /** + * Set up a BundleResourceFinder + * The suffix may contain a path fragment, unlike the bundle.findEntries method. + * + * @param packageAdmin package admin for finding fragments + * @param bundle bundle to search + * @param prefix search only paths and zip files starting with this prefix + * @param suffix return only entries ending in this suffix. + * @param discoveryFilter filter for matching directories and zip files. + */ + public BundleResourceFinder(PackageAdmin packageAdmin, Bundle bundle, String prefix, String suffix, ResourceDiscoveryFilter discoveryFilter) { + this.packageAdmin = packageAdmin; + this.bundle = BundleUtils.unwrapBundle(bundle); + this.prefix = addSlash(prefix.trim()); + this.suffix = suffix.trim(); + int pos = this.suffix.lastIndexOf("/"); + if (pos > -1) { + osgiSuffix = this.suffix.substring(pos + 1, this.suffix.length()); + extendedMatching = true; + } else { + osgiSuffix = "*" + this.suffix; + extendedMatching = false; + } + this.discoveryFilter = discoveryFilter; + } + + public void find(ResourceFinderCallback callback) throws Exception { + if (discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.BUNDLE_CLASSPATH)) { + if (!scanBundleClassPath(callback, bundle)) { + return; + } + } + if (packageAdmin != null && discoveryFilter.rangeDiscoveryRequired(DiscoveryRange.FRAGMENT_BUNDLES)) { + Bundle[] fragments = packageAdmin.getFragments(bundle); + if (fragments != null) { + for (Bundle fragment : fragments) { + if (!scanBundleClassPath(callback, fragment)) { + return; + } + } + } + } + } + + public Set find() { + Set resources = new LinkedHashSet(); + try { + find(new DefaultResourceFinderCallback(resources)); + } catch (Exception e) { + // this should not happen + throw new RuntimeException("Resource discovery failed", e); + } + return resources; + } + + private boolean scanBundleClassPath(ResourceFinderCallback callback, Bundle bundle) throws Exception { + BundleDescription desc = new BundleDescription(bundle.getHeaders()); + List paths = desc.getBundleClassPath(); + boolean continueScanning = true; + if (paths.isEmpty()) { + continueScanning = scanDirectory(callback, bundle, prefix); + } else { + for (HeaderEntry path : paths) { + String name = path.getName(); + if (name.equals(".") || name.equals("/")) { + // scan root + continueScanning = scanDirectory(callback, bundle, prefix); + } else if (name.endsWith(".jar") || name.endsWith(".zip")) { + // scan embedded jar/zip + continueScanning = scanZip(callback, bundle, name); + } else { + // assume it's a directory + continueScanning = scanDirectory(callback, bundle, prefix.startsWith("/") ? name + prefix : name + "/" + prefix); + } + if (!continueScanning) { + break; + } + } + } + return continueScanning; + } + + private boolean scanDirectory(ResourceFinderCallback callback, Bundle bundle, String basePath) throws Exception { + if (!discoveryFilter.directoryDiscoveryRequired(basePath)) { + return true; + } + Enumeration e = bundle.findEntries(basePath, osgiSuffix, true); + if (e != null) { + while (e.hasMoreElements()) { + URL url = (URL) e.nextElement(); + if (!extendedMatching || suffixMatches(url.getPath())) { + if (!callback.foundInDirectory(bundle, basePath, url)) { + return false; + } + } + } + } + return true; + } + + private boolean scanZip(ResourceFinderCallback callback, Bundle bundle, String zipName) throws Exception { + if (!discoveryFilter.zipFileDiscoveryRequired(zipName)) { + return true; + } + URL zipEntry = bundle.getEntry(zipName); + if (zipEntry == null) { + return true; + } + try { + ZipInputStream in = new ZipInputStream(zipEntry.openStream()); + ZipEntry entry; + while ((entry = in.getNextEntry()) != null) { + String name = entry.getName(); + if (prefixMatches(name) && suffixMatches(name)) { + if (!callback.foundInJar(bundle, zipName, entry, new ZipEntryInputStream(in))) { + return false; + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return true; + } + + private static class ZipEntryInputStream extends FilterInputStream { + public ZipEntryInputStream(ZipInputStream in) { + super(in); + } + public void close() throws IOException { + // not really necessary + // ((ZipInputStream) in).closeEntry(); + } + } + + private boolean prefixMatches(String name) { + if (prefix.length() == 0 || prefix.equals(".") || prefix.equals("/")) { + return true; + } else if (prefix.startsWith("/")) { + return name.startsWith(prefix, 1); + } else { + return name.startsWith(prefix); + } + } + + private boolean suffixMatches(String name) { + return (suffix.length() == 0) ? true : name.endsWith(suffix); + } + + private static String addSlash(String name) { + if (name == null ) return ""; + name = name.trim(); + if (name.length() != 0 && !name.endsWith("/")) { + name = name + "/"; + } + return name; + } + + public interface ResourceFinderCallback { + /** + * Resource found in a directory in a bundle. + * + * @return true to continue scanning, false to abort scanning. + */ + boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception; + + /** + * Resource found in a jar file in a bundle. + * + * @return true to continue scanning, false to abort scanning. + */ + boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream in) throws Exception; + } + + public static class DefaultResourceFinderCallback implements ResourceFinderCallback { + + private Set resources; + + public DefaultResourceFinderCallback() { + this(new LinkedHashSet()); + } + + public DefaultResourceFinderCallback(Set resources) { + this.resources = resources; + } + + public Set getResources() { + return resources; + } + + public boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception { + resources.add(url); + return true; + } + + public boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream in) throws Exception { + URL jarURL = bundle.getEntry(jarName); + URL url = new URL("jar:" + jarURL.toString() + "!/" + entry.getName()); + resources.add(url); + return true; + } + + } + + public static class DummyDiscoveryFilter implements ResourceDiscoveryFilter { + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange) { + return true; + } + + public boolean directoryDiscoveryRequired(String url) { + return true; + } + + public boolean zipFileDiscoveryRequired(String url) { + return true; + } + + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceHelper.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceHelper.java new file mode 100644 index 00000000..45b7215d --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleResourceHelper.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.zip.ZipEntry; + +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; + +/** + * Helper for finding resources in a {@link Bundle}. + *
    + * In OSGi, resource lookup on resources in the META-INF directory using {@link Bundle#getResource(String)} or + * {@link Bundle#getResources(String)} does not return the resources found in the wired bundles of the bundle + * (wired via Import-Package or DynamicImport-Package). This class loader implementation provides + * {@link #getResource(String) and {@link #getResources(String)} methods that do delegate META-INF resource lookups + * to the wired bundles. + *
    + * The URLs returned by {@link Bundle#getResource(String)} or {@link Bundle#getResources(String)} methods are + * OSGi framework specific "bundle" URLs. If enabled, this helper can convert the framework specific URLs into + * regular jar URLs. + * + * @version $Rev$ $Date$ + */ +public class BundleResourceHelper { + + public static final String SEARCH_WIRED_BUNDLES = BundleResourceHelper.class.getName() + ".searchWiredBundles"; + public static final String CONVERT_RESOURCE_URLS = BundleResourceHelper.class.getName() + ".convertResourceUrls"; + + private final static String META_INF_1 = "META-INF/"; + private final static String META_INF_2 = "/META-INF/"; + + protected final Bundle bundle; + private LinkedHashSet wiredBundles = null; + protected boolean searchWiredBundles; + protected boolean convertResourceUrls; + + public BundleResourceHelper(Bundle bundle) { + this(bundle, + BundleResourceHelper.getSearchWiredBundles(false), + BundleResourceHelper.getConvertResourceUrls(false)); + } + + public BundleResourceHelper(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) { + this.bundle = bundle; + this.searchWiredBundles = searchWiredBundles; + this.convertResourceUrls = convertResourceUrls; + } + + public void setSearchWiredBundles(boolean search) { + searchWiredBundles = search; + } + + public boolean getSearchWiredBundles() { + return searchWiredBundles; + } + + public void setConvertResourceUrls(boolean convert) { + convertResourceUrls = convert; + } + + public boolean getConvertResourceUrls() { + return convertResourceUrls; + } + + public URL getResource(String name) { + if (convertResourceUrls) { + return convertedFindResource(name); + } else { + return findResource(name); + } + } + + public Enumeration getResources(String name) throws IOException { + if (convertResourceUrls) { + return convertedFindResources(name); + } else { + return findResources(name); + } + } + + protected URL convert(URL url) { + return url; + } + + private synchronized LinkedHashSet getWiredBundles() { + if (wiredBundles == null) { + wiredBundles = BundleUtils.getWiredBundles((bundle instanceof DelegatingBundle) ? ((DelegatingBundle) bundle).getMainBundle() : bundle); + } + return wiredBundles; + } + + private boolean isMetaInfResource(String name) { + return searchWiredBundles && name != null && (name.startsWith(META_INF_1) || name.startsWith(META_INF_2)); + } + + private List getList() { + if (convertResourceUrls) { + return new ArrayList() { + public boolean add(URL u) { + return super.add(convert(u)); + } + }; + } else { + return new ArrayList(); + } + } + + private void addToList(List list, Enumeration enumeration) { + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + list.add(enumeration.nextElement()); + } + } + } + + protected URL findResource(String name) { + URL resource = bundle.getResource(name); + if (resource == null && isMetaInfResource(name)) { + LinkedHashSet wiredBundles = getWiredBundles(); + Iterator iterator = wiredBundles.iterator(); + while (iterator.hasNext() && resource == null) { + resource = iterator.next().getResource(name); + } + } + if (resource != null && convertResourceUrls) { + resource = convert(resource); + } + return resource; + } + + protected Enumeration findResources(String name) throws IOException { + Enumeration e = (Enumeration) bundle.getResources(name); + if (isMetaInfResource(name)) { + List allResources = getList(); + addToList(allResources, e); + LinkedHashSet wiredBundles = getWiredBundles(); + for (Bundle wiredBundle : wiredBundles) { + Enumeration resources = wiredBundle.getResources(name); + addToList(allResources, resources); + } + return Collections.enumeration(allResources); + } else if (e == null) { + return Collections.enumeration(Collections.emptyList()); + } else if (convertResourceUrls) { + List allResources = getList(); + addToList(allResources, e); + return Collections.enumeration(allResources); + } else { + return e; + } + } + + /** + * Lookup resource and return converted URL (in a generic way). + * + * @param name + * @return + */ + protected URL convertedFindResource(String name) { + ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName()); + PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference); + try { + List resources = findResources(packageAdmin, bundle, name, false); + if (resources.isEmpty() && isMetaInfResource(name)) { + LinkedHashSet wiredBundles = getWiredBundles(); + Iterator iterator = wiredBundles.iterator(); + while (iterator.hasNext() && resources.isEmpty()) { + Bundle wiredBundle = iterator.next(); + resources = findResources(packageAdmin, wiredBundle, name, false); + } + } + return (resources.isEmpty()) ? null : resources.get(0); + } catch (Exception e) { + return null; + } finally { + bundle.getBundleContext().ungetService(reference); + } + } + + /** + * Lookup resources and return converted URLs (in a generic way). + * + * @param name + * @return + */ + protected Enumeration convertedFindResources(String name) throws IOException { + ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName()); + PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference); + try { + List resources = findResources(packageAdmin, bundle, name, true); + if (isMetaInfResource(name)) { + LinkedHashSet wiredBundles = getWiredBundles(); + for (Bundle wiredBundle : wiredBundles) { + resources.addAll(findResources(packageAdmin, wiredBundle, name, true)); + } + } + return Collections.enumeration(resources); + } catch (Exception e) { + throw (IOException) new IOException("Error discovering resources: " + e).initCause(e); + } finally { + bundle.getBundleContext().ungetService(reference); + } + } + + private static List findResources(PackageAdmin packageAdmin, + Bundle bundle, + String name, + final boolean continueScanning) throws Exception { + BundleResourceFinder finder = new BundleResourceFinder(packageAdmin, bundle, "", name); + final List resources = new ArrayList(); + finder.find(new BundleResourceFinder.ResourceFinderCallback() { + + public boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception { + resources.add(url); + return continueScanning; + } + + public boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream inputStream) throws Exception { + URL jarURL = BundleUtils.getEntry(bundle, jarName); + URL url = new URL("jar:" + jarURL.toString() + "!/" + entry.getName()); + resources.add(url); + return continueScanning; + } + }); + return resources; + } + + public static boolean getSearchWiredBundles(boolean defaultValue) { + String value = System.getProperty(SEARCH_WIRED_BUNDLES); + return (value == null) ? defaultValue : Boolean.parseBoolean(value); + } + + public static boolean getConvertResourceUrls(boolean defaultValue) { + String value = System.getProperty(CONVERT_RESOURCE_URLS); + return (value == null) ? defaultValue : Boolean.parseBoolean(value); + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleUtils.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleUtils.java new file mode 100644 index 00000000..8c871246 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/BundleUtils.java @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleReference; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.framework.wiring.BundleWire; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; + +/** + * @version $Rev$ $Date$ + */ +public class BundleUtils { + + private static final boolean isOSGi43 = isOSGi43(); + + private static boolean isOSGi43() { + try { + Class.forName("org.osgi.framework.wiring.BundleWiring"); + return true; + } catch (Throwable e) { + return false; + } + } + + public final static String REFERENCE_SCHEME = "reference:"; + + public final static String FILE_SCHEMA = "file:"; + + public final static String REFERENCE_FILE_SCHEMA = "reference:file:"; + + /** + * Based on the constant field values, if it is bigger than the RESOLVED status value, the bundle has been resolved by the framework + * @param bundle + * @return true if the bundle is resolved, or false if not. + */ + public static boolean isResolved(Bundle bundle) { + return bundle.getState() >= Bundle.RESOLVED; + } + + /** + * resolve method will try to load the Object.class, the behavior triggers a resolved request to the OSGI framework. + * @param bundle + */ + public static void resolve(Bundle bundle) { + if (isFragment(bundle)) { + return; + } + try { + bundle.loadClass(Object.class.getName()); + } catch (Exception e) { + } + } + + /** + * If the bundle fulfills the conditions below, it could be started + * a. Not in the UNINSTALLED status. + * b. Not in the STARTING status. + * c. Not a fragment bundle. + * @param bundle + * @return + */ + public static boolean canStart(Bundle bundle) { + return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STARTING) && (!isFragment(bundle)); + } + + /** + * If the bundle fulfills the conditions below, it could be stopped + * a. Not in the UNINSTALLED status. + * b. Not in the STOPPING status. + * c. Not a fragment bundle. + * @param bundle + * @return + */ + public static boolean canStop(Bundle bundle) { + return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STOPPING) && (!isFragment(bundle)); + } + + /** + * If the bundle fulfills the conditions below, it could be un-installed + * a. Not in the UNINSTALLED status. + * @param bundle + * @return + */ + public static boolean canUninstall(Bundle bundle) { + return bundle.getState() != Bundle.UNINSTALLED; + } + + public static boolean isFragment(Bundle bundle) { + Dictionary headers = bundle.getHeaders(); + return (headers != null && headers.get(Constants.FRAGMENT_HOST) != null); + } + + /** + * Returns bundle (if any) associated with current thread's context classloader. + * Invoking this method is equivalent to getBundle(Thread.currentThread().getContextClassLoader(), unwrap) + */ + public static Bundle getContextBundle(boolean unwrap) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + return classLoader == null ? null : getBundle(classLoader, unwrap); + } + + /** + * Returns bundle (if any) associated with the classloader. + * @param classLoader + * @param unwrap if true and if the bundle associated with the context classloader is a + * {@link DelegatingBundle}, this function will return the main application bundle + * backing the {@link DelegatingBundle}. Otherwise, the bundle associated with + * the context classloader is returned as is. See {@link BundleClassLoader#getBundle(boolean)} + * for more information. + * @return The bundle associated with the classloader. Might be null. + */ + public static Bundle getBundle(ClassLoader classLoader, boolean unwrap) { + if (classLoader instanceof DelegatingBundleReference) { + return ((DelegatingBundleReference) classLoader).getBundle(unwrap); + } else if (classLoader instanceof BundleReference) { + return ((BundleReference) classLoader).getBundle(); + } else { + return null; + } + } + + /** + * If the given bundle is a {@link DelegatingBundle} this function will return the main + * application bundle backing the {@link DelegatingBundle}. Otherwise, the bundle + * passed in is returned as is. + */ + public static Bundle unwrapBundle(Bundle bundle) { + if (bundle instanceof DelegatingBundle) { + return ((DelegatingBundle) bundle).getMainBundle(); + } + return bundle; + } + + /** + * Works like {@link Bundle#getEntryPaths(String)} but also returns paths + * in attached fragment bundles. + * + * @param bundle + * @param name + * @return + */ + public static Enumeration getEntryPaths(Bundle bundle, String name) { + Enumeration entries = bundle.findEntries(name, null, false); + if (entries == null) { + return null; + } + LinkedHashSet paths = new LinkedHashSet(); + while (entries.hasMoreElements()) { + URL url = entries.nextElement(); + String path = url.getPath(); + if (path.startsWith("/")) { + path = path.substring(1); + } + paths.add(path); + } + return Collections.enumeration(paths); + } + + /** + * 1, If the bundle was installed with reference directory mode + * return the file URL directly. + * 2, For traditional package bundle, Works like {@link Bundle#getEntry(String)} + * + * In addition to the searching abaove, it also checks attached fragment bundles for the given entry. + * + * @param bundle + * @param name + * @return + * @throws MalformedURLException + */ + public static URL getEntry(Bundle bundle, String name) throws MalformedURLException { + + if (name.endsWith("/")) { + name = name.substring(0, name.length() - 1); + } + + File bundleFile = toFile(bundle); + if (bundleFile != null && bundleFile.isDirectory()) { + File entryFile = new File(bundleFile, name); + if (entryFile.exists()) { + return entryFile.toURI().toURL(); + } + } + + if (name.equals("/")) { + return bundle.getEntry(name); + } + + String path; + String pattern; + int pos = name.lastIndexOf("/"); + if (pos == -1) { + path = "/"; + pattern = name; + } else if (pos == 0) { + path = "/"; + pattern = name.substring(1); + } else { + path = name.substring(0, pos); + pattern = name.substring(pos + 1); + } + Enumeration entries = bundle.findEntries(path, pattern, false); + if (entries != null && entries.hasMoreElements()) { + return entries.nextElement(); + } else { + return null; + } + } + + public static URL getNestedEntry(Bundle bundle, String jarEntryName, String subEntryName) throws MalformedURLException { + File bundleFile = toFile(bundle); + if (bundleFile != null && bundleFile.isDirectory()) { + File entryFile = new File(bundleFile, jarEntryName); + if (entryFile.exists()) { + if (entryFile.isFile()) { + return new URL("jar:" + entryFile.toURI().toURL() + "!/" + subEntryName); + } else { + return new File(entryFile, subEntryName).toURI().toURL(); + } + } + return null; + } + return new URL("jar:" + bundle.getEntry(jarEntryName).toString() + "!/" + subEntryName); + } + + + public static File toFile(Bundle bundle) { + return toFile(bundle.getLocation()); + } + + public static File toFile(URL url) { + return toFile(url.toExternalForm()); + } + + /** + * Translate the reference:file:// style URL to the underlying file instance + * @param url + * @return + */ + public static File toFile(String url) { + if (url !=null && url.startsWith(REFERENCE_FILE_SCHEMA)) { + File file = null; + try { + file = new File(new URL(url.substring(REFERENCE_SCHEME.length())).toURI()); + if (file.exists()) { + return file; + } + } catch (Exception e) { + // If url includes special chars: { } [ ] % < > # ^ ? + // URISyntaxException or MalformedURLException will be thrown, + // so try to use File(String) directly + file = new File(url.substring(REFERENCE_FILE_SCHEMA.length())); + if (file.exists()) { + return file; + } + } + } + return null; + } + + public static String toReferenceFileLocation(File file) throws IOException { + if (!file.exists()) { + throw new IOException("file not exist " + file.getAbsolutePath()); + } + return REFERENCE_SCHEME + file.toURI(); + } + + + public static LinkedHashSet getWiredBundles(Bundle bundle) { + if (isOSGi43) { + return getWiredBundles43(bundle); + } else { + return getWiredBundles42(bundle); + } + } + + private static LinkedHashSet getWiredBundles42(Bundle bundle) { + ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName()); + PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference); + try { + return getWiredBundles(packageAdmin, bundle); + } finally { + bundle.getBundleContext().ungetService(reference); + } + } + + public static LinkedHashSet getWiredBundles(PackageAdmin packageAdmin, Bundle bundle) { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + // handle static wire via Import-Package + List imports = description.getExternalImports(); + LinkedHashSet wiredBundles = new LinkedHashSet(); + for (BundleDescription.ImportPackage packageImport : imports) { + ExportedPackage[] exports = packageAdmin.getExportedPackages(packageImport.getName()); + Bundle wiredBundle = getWiredBundle(bundle, exports); + if (wiredBundle != null) { + wiredBundles.add(wiredBundle); + } + } + // handle dynamic wire via DynamicImport-Package + if (!description.getDynamicImportPackage().isEmpty()) { + for (Bundle b : bundle.getBundleContext().getBundles()) { + if (!wiredBundles.contains(b)) { + ExportedPackage[] exports = packageAdmin.getExportedPackages(b); + Bundle wiredBundle = getWiredBundle(bundle, exports); + if (wiredBundle != null) { + wiredBundles.add(wiredBundle); + } + } + } + } + return wiredBundles; + } + + static Bundle getWiredBundle(Bundle bundle, ExportedPackage[] exports) { + if (exports != null) { + for (ExportedPackage exportedPackage : exports) { + Bundle[] importingBundles = exportedPackage.getImportingBundles(); + if (importingBundles != null) { + for (Bundle importingBundle : importingBundles) { + if (importingBundle == bundle) { + return exportedPackage.getExportingBundle(); + } + } + } + } + } + return null; + } + + // OSGi 4.3 API + + private static LinkedHashSet getWiredBundles43(Bundle bundle) { + LinkedHashSet wiredBundles = new LinkedHashSet(); + BundleWiring wiring = bundle.adapt(BundleWiring.class); + if (wiring != null) { + List wires; + wires = wiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE); + for (BundleWire wire : wires) { + wiredBundles.add(wire.getProviderWiring().getBundle()); + } + wires = wiring.getRequiredWires(BundleRevision.BUNDLE_NAMESPACE); + for (BundleWire wire : wires) { + wiredBundles.add(wire.getProviderWiring().getBundle()); + } + } + return wiredBundles; + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ClassDiscoveryFilter.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ClassDiscoveryFilter.java new file mode 100644 index 00000000..33b1f3ef --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ClassDiscoveryFilter.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.xbean.osgi.bundle.util; + +/** + * @version $Rev$ $Date$ + */ +public interface ClassDiscoveryFilter { + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange); + + public boolean jarFileDiscoveryRequired(String url); + + public boolean directoryDiscoveryRequired(String url); + + public boolean packageDiscoveryRequired(String packageName); +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundle.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundle.java new file mode 100644 index 00000000..de233c79 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundle.java @@ -0,0 +1,438 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.framework.wiring.BundleWiring; + +/** + * Bundle that delegates ClassLoader operations to a collection of {@link Bundle} objects. + * + * @version $Rev$ $Date$ + */ +public class DelegatingBundle implements Bundle { + + private static final String PACKAGE_CACHE = DelegatingBundle.class.getName() + ".packageCache"; + private static final String RESOURCE_CACHE_SIZE = DelegatingBundle.class.getName() + ".resourceCacheSize"; + + private static final URL NOT_FOUND_RESOURCE; + + static { + try { + NOT_FOUND_RESOURCE = new URL("file://foo"); + } catch (MalformedURLException e) { + throw new Error(e); + } + } + + private CopyOnWriteArrayList bundles; + private Bundle bundle; + private BundleContext bundleContext; + + private final boolean hasDynamicImports; + private final Map resourceCache; + private final boolean packageCacheEnabled; + private Map packageCache; + + public DelegatingBundle(Collection bundles) { + if (bundles.isEmpty()) { + throw new IllegalArgumentException("At least one bundle is required"); + } + this.bundles = new CopyOnWriteArrayList(bundles); + Iterator iterator = bundles.iterator(); + // assume first Bundle is the main bundle + this.bundle = iterator.next(); + this.bundleContext = new DelegatingBundleContext(this, bundle.getBundleContext()); + this.hasDynamicImports = hasDynamicImports(iterator); + this.resourceCache = initResourceCache(); + this.packageCacheEnabled = initPackageCacheEnabled(); + } + + public DelegatingBundle(Bundle bundle) { + this(Collections.singletonList(bundle)); + } + + private static Map initResourceCache() { + String value = System.getProperty(RESOURCE_CACHE_SIZE, "250"); + int size = Integer.parseInt(value); + if (size > 0) { + return Collections.synchronizedMap(new Cache(size)); + } else { + return null; + } + } + + private static boolean initPackageCacheEnabled() { + String value = System.getProperty(PACKAGE_CACHE, "true"); + boolean enabled = Boolean.parseBoolean(value); + return enabled; + } + + /* + * Returns true if a single bundle has Dynamic-ImportPackage: *. False, otherwise. + */ + private boolean hasDynamicImports(Iterator iterator) { + while (iterator.hasNext()) { + Bundle delegate = iterator.next(); + if (hasWildcardDynamicImport(delegate)) { + return true; + } + } + return false; + } + + private synchronized Map getPackageBundleMap() { + if (packageCache == null) { + packageCache = buildPackageBundleMap(); + } + return packageCache; + } + + private synchronized void reset() { + resourceCache.clear(); + packageCache = null; + } + + private Map buildPackageBundleMap() { + Map map = new HashMap(); + Iterator iterator = bundles.iterator(); + // skip first bundle + iterator.next(); + // attempt to load the class from the remaining bundles + while (iterator.hasNext()) { + Bundle bundle = iterator.next(); + BundleWiring wiring = bundle.adapt(BundleWiring.class); + if (wiring != null) { + List capabilities = wiring.getCapabilities(BundleRevision.PACKAGE_NAMESPACE); + if (capabilities != null && !capabilities.isEmpty()) { + for (BundleCapability capability : capabilities) { + Map attributes = capability.getAttributes(); + if (attributes != null) { + String packageName = String.valueOf(attributes.get(BundleRevision.PACKAGE_NAMESPACE)); + if (!map.containsKey(packageName)) { + map.put(packageName, bundle); + } + } + } + } + } + } + return map; + } + + public Bundle getMainBundle() { + return bundle; + } + + public Class loadClass(String name) throws ClassNotFoundException { + try { + Class clazz = bundle.loadClass(name); + return clazz; + } catch (ClassNotFoundException cnfe) { + if (name.startsWith("java.")) { + throw cnfe; + } + + int index = name.lastIndexOf('.'); + if (index > 0 && bundles.size() > 1) { + String packageName = name.substring(0, index); + if (packageCacheEnabled) { + return findCachedClass(name, packageName, cnfe); + } else { + return findClass(name, packageName, cnfe); + } + } + + throw cnfe; + } + } + + private Class findCachedClass(String className, String packageName, ClassNotFoundException cnfe) throws ClassNotFoundException { + Map map = getPackageBundleMap(); + Bundle bundle = map.get(packageName); + if (bundle == null) { + // Work-around for Introspector always looking for classes in sun.beans.infos + if (packageName.equals("sun.beans.infos") && className.endsWith("BeanInfo")) { + throw cnfe; + } + return findClass(className, packageName, cnfe); + } else { + return bundle.loadClass(className); + } + } + + private Class findClass(String className, String packageName, ClassNotFoundException cnfe) throws ClassNotFoundException { + Iterator iterator = bundles.iterator(); + // skip first bundle + iterator.next(); + while (iterator.hasNext()) { + Bundle delegate = iterator.next(); + if (hasDynamicImports && hasWildcardDynamicImport(delegate)) { + // skip any bundles with Dynamic-ImportPackage: * to avoid unnecessary wires + continue; + } + try { + return delegate.loadClass(className); + } catch (ClassNotFoundException e) { + // ignore + } + } + throw cnfe; + } + + private static boolean hasWildcardDynamicImport(Bundle bundle) { + Dictionary headers = bundle.getHeaders(); + if (headers != null) { + String value = headers.get(Constants.DYNAMICIMPORT_PACKAGE); + if (value == null) { + return false; + } else { + return "*".equals(value.trim()); + } + } else { + return false; + } + } + + public void addBundle(Bundle b) { + bundles.add(b); + reset(); + } + + public void removeBundle(Bundle b) { + bundles.remove(b); + reset(); + } + + public URL getResource(String name) { + URL resource = null; + if (resourceCache == null) { + resource = findResource(name); + } else { + resource = findCachedResource(name); + } + return resource; + } + + private URL findCachedResource(String name) { + URL resource = bundle.getResource(name); + if (resource == null) { + resource = resourceCache.get(name); + if (resource == null) { + Iterator iterator = bundles.iterator(); + // skip first bundle + iterator.next(); + // look for resource in the remaining bundles + resource = findResource(name, iterator); + resourceCache.put(name, (resource == null) ? NOT_FOUND_RESOURCE : resource); + } else if (resource == NOT_FOUND_RESOURCE) { + resource = null; + } + } + return resource; + } + + private URL findResource(String name) { + Iterator iterator = bundles.iterator(); + return findResource(name, iterator); + } + + private URL findResource(String name, Iterator iterator) { + URL resource = null; + while (iterator.hasNext() && resource == null) { + Bundle delegate = iterator.next(); + resource = delegate.getResource(name); + } + return resource; + } + + public Enumeration getResources(String name) throws IOException { + ArrayList allResources = new ArrayList(); + for (Bundle bundle : bundles) { + Enumeration e = bundle.getResources(name); + addToList(allResources, e); + } + return Collections.enumeration(allResources); + } + + private static void addToList(List list, Enumeration enumeration) { + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + list.add(enumeration.nextElement()); + } + } + } + + public BundleContext getBundleContext() { + return bundleContext; + } + + public Enumeration findEntries(String arg0, String arg1, boolean arg2) { + return bundle.findEntries(arg0, arg1, arg2); + } + + public long getBundleId() { + return bundle.getBundleId(); + } + + public URL getEntry(String arg0) { + return bundle.getEntry(arg0); + } + + public Enumeration getEntryPaths(String arg0) { + return bundle.getEntryPaths(arg0); + } + + public Dictionary getHeaders() { + return bundle.getHeaders(); + } + + public Dictionary getHeaders(String arg0) { + return bundle.getHeaders(arg0); + } + + public long getLastModified() { + return bundle.getLastModified(); + } + + public String getLocation() { + return bundle.getLocation(); + } + + public ServiceReference[] getRegisteredServices() { + return bundle.getRegisteredServices(); + } + + public ServiceReference[] getServicesInUse() { + return bundle.getServicesInUse(); + } + + public Map getSignerCertificates(int arg0) { + return bundle.getSignerCertificates(arg0); + } + + public int getState() { + return bundle.getState(); + } + + public String getSymbolicName() { + return bundle.getSymbolicName(); + } + + public Version getVersion() { + return bundle.getVersion(); + } + + public boolean hasPermission(Object arg0) { + return bundle.hasPermission(arg0); + } + + public void start() throws BundleException { + bundle.start(); + } + + public void start(int arg0) throws BundleException { + bundle.start(arg0); + } + + public void stop() throws BundleException { + bundle.stop(); + } + + public void stop(int arg0) throws BundleException { + bundle.stop(arg0); + } + + public void uninstall() throws BundleException { + bundle.uninstall(); + } + + public void update() throws BundleException { + bundle.update(); + } + + public void update(InputStream arg0) throws BundleException { + bundle.update(arg0); + } + + public int compareTo(Bundle other) { + return bundle.compareTo(other); + } + + public A adapt(Class type) { + return bundle.adapt(type); + } + + public File getDataFile(String filename) { + return bundle.getDataFile(filename); + } + + public String toString() { + return "[DelegatingBundle: " + bundles + "]"; + } + + private static class Cache extends LinkedHashMap { + + private final int maxSize; + + public Cache(int maxSize) { + this(16, maxSize, 0.75f); + } + + public Cache(int initialSize, int maxSize, float loadFactor) { + super(initialSize, loadFactor, true); + this.maxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (size() > maxSize) { + return true; + } else { + return false; + } + } + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleContext.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleContext.java new file mode 100644 index 00000000..bbea5cd8 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleContext.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Dictionary; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkListener; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; + +/** + * BundleContext for DelegatingBundle. + * + * @version $Rev$ $Date$ + */ +public class DelegatingBundleContext implements BundleContext { + + private DelegatingBundle bundle; + private BundleContext bundleContext; + + public DelegatingBundleContext(DelegatingBundle bundle, BundleContext bundleContext) { + this.bundle = bundle; + this.bundleContext = bundleContext; + } + + public Bundle getBundle() { + return bundle; + } + + public void addBundleListener(BundleListener arg0) { + bundleContext.addBundleListener(arg0); + } + + public void addFrameworkListener(FrameworkListener arg0) { + bundleContext.addFrameworkListener(arg0); + } + + public void addServiceListener(ServiceListener arg0, String arg1) throws InvalidSyntaxException { + bundleContext.addServiceListener(arg0, arg1); + } + + public void addServiceListener(ServiceListener arg0) { + bundleContext.addServiceListener(arg0); + } + + public Filter createFilter(String arg0) throws InvalidSyntaxException { + return bundleContext.createFilter(arg0); + } + + public Bundle getBundle(long arg0) { + return bundleContext.getBundle(arg0); + } + + public Bundle[] getBundles() { + return bundleContext.getBundles(); + } + + public File getDataFile(String arg0) { + return bundleContext.getDataFile(arg0); + } + + public String getProperty(String arg0) { + return bundleContext.getProperty(arg0); + } + + public S getService(ServiceReference reference) { + return bundleContext.getService(reference); + } + + public ServiceReference getServiceReference(String clazz) { + return bundleContext.getServiceReference(clazz); + } + + public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException { + return bundleContext.getServiceReferences(clazz, filter); + } + + public ServiceReference getServiceReference(Class clazz) { + return bundleContext.getServiceReference(clazz); + } + + public Collection> getServiceReferences(Class clazz, String filter) throws InvalidSyntaxException { + return bundleContext.getServiceReferences(clazz, filter); + } + + public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException { + return bundleContext.getAllServiceReferences(clazz, filter); + } + + public Bundle installBundle(String arg0, InputStream arg1) throws BundleException { + return bundleContext.installBundle(arg0, arg1); + } + + public Bundle installBundle(String arg0) throws BundleException { + return bundleContext.installBundle(arg0); + } + + public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) { + return bundleContext.registerService(clazz, service, properties); + } + + public ServiceRegistration registerService(String[] classes, Object service, Dictionary properties) { + return bundleContext.registerService(classes, service, properties); + } + + public ServiceRegistration registerService(Class clazz, S service, Dictionary properties) { + return bundleContext.registerService(clazz, service, properties); + } + + public void removeBundleListener(BundleListener arg0) { + bundleContext.removeBundleListener(arg0); + } + + public void removeFrameworkListener(FrameworkListener arg0) { + bundleContext.removeFrameworkListener(arg0); + } + + public void removeServiceListener(ServiceListener arg0) { + bundleContext.removeServiceListener(arg0); + } + + public boolean ungetService(ServiceReference reference) { + return bundleContext.ungetService(reference); + } + + public Bundle getBundle(String location) { + return bundleContext.getBundle(location); + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleReference.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleReference.java new file mode 100644 index 00000000..0de203d2 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DelegatingBundleReference.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.xbean.osgi.bundle.util; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleReference; + +/** + * DelegatingBundleReference is based on {@link BundleReference} and provides + * access to {@link DelegatingBundle}. + */ +public interface DelegatingBundleReference extends BundleReference { + + /** + * Returns the bundle associated with this classloader. + * + * In most cases the bundle associated with the classloader is a regular framework bundle. + * However, in some cases the bundle associated with the classloader is a {@link DelegatingBundle}. + * In such cases, the unwrap parameter controls whether this function returns the + * {@link DelegatingBundle} instance or the main application bundle backing with the {@link DelegatingBundle}. + * + * @param unwrap If true and if the bundle associated with this classloader is a {@link DelegatingBundle}, + * this function will return the main application bundle backing with the {@link DelegatingBundle}. + * Otherwise, the bundle associated with this classloader is returned as is. + * @return The bundle associated with this classloader. + */ + public Bundle getBundle(boolean unwrap); + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DictionaryMap.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DictionaryMap.java new file mode 100644 index 00000000..9997e773 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DictionaryMap.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.xbean.osgi.bundle.util; + +import java.util.AbstractMap; +import java.util.Dictionary; +import java.util.Set; + +/** + * Simple wrapper that exposes {@link Dictionary} class as a {@link Map}. + * + * @version $Rev$, $Date$ + */ +public class DictionaryMap extends AbstractMap { + + private final Dictionary dictionary; + + public DictionaryMap(Dictionary dictionary) { + this.dictionary = dictionary; + } + + @Override + public Object get(Object key) { + return dictionary.get(key); + } + + @Override + public Object put(Object key, Object value) { + return dictionary.put(key, value); + } + + @Override + public Object remove(Object key) { + return dictionary.remove(key); + } + + @Override + public int size() { + return dictionary.size(); + } + + @Override + public boolean isEmpty() { + return dictionary.isEmpty(); + } + + @Override + public Set entrySet() { + // TODO: implement + throw new UnsupportedOperationException(); + } + + @Override + public Set keySet() { + // TODO: implement + throw new UnsupportedOperationException(); + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DiscoveryRange.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DiscoveryRange.java new file mode 100644 index 00000000..9b3b0019 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/DiscoveryRange.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.xbean.osgi.bundle.util; + +/** + * @version $Rev$ $Date$ + */ +public enum DiscoveryRange { + REQUIRED_BUNDLES, IMPORT_PACKAGES, BUNDLE_CLASSPATH, FRAGMENT_BUNDLES +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderBuilder.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderBuilder.java new file mode 100644 index 00000000..67d805f3 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderBuilder.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.xbean.osgi.bundle.util; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement; + +/** + * @version $Rev$ $Date$ + */ +public class HeaderBuilder { + + private static final Pattern EXTENDED_PATTERN = Pattern.compile("[\\w_\\-\\.]+"); + + public static String build(List headerElements) { + if (headerElements == null || headerElements.size() == 0) { + return ""; + } + StringBuilder header = new StringBuilder(); + for (HeaderElement headerElement : headerElements) { + String name = headerElement.getName(); + if (name == null || name.length() == 0) { + throw new IllegalArgumentException("Invalid header name for the header elment " + headerElement); + } + if (header.length() > 0) { + header.append(","); + } + header.append(name); + for (Map.Entry attribute : headerElement.getAttributes().entrySet()) { + header.append(";").append(attribute.getKey()).append("="); + if (EXTENDED_PATTERN.matcher(attribute.getValue()).matches()) { + header.append(attribute.getValue()); + } else { + header.append("\"").append(attribute.getValue()).append("\""); + } + } + for (Map.Entry directive : headerElement.getDirectives().entrySet()) { + header.append(";").append(directive.getKey()).append(":="); + if (EXTENDED_PATTERN.matcher(directive.getValue()).matches()) { + header.append(directive.getValue()); + } else { + header.append("\"").append(directive.getValue()).append("\""); + } + } + } + return header.toString(); + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderParser.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderParser.java new file mode 100644 index 00000000..138ef107 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/HeaderParser.java @@ -0,0 +1,211 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xbean.osgi.bundle.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class to parse standard OSGi headers. + * + * @version $Rev$, $Date$ + */ +public class HeaderParser { + + /** + * Parse a given OSGi header into a list of header elements. + * + * @param header the OSGi header to parse + * @return the list of header elements extracted from this header + */ + public static List parseHeader(String header) { + List elements = new ArrayList(); + if (header == null || header.trim().length() == 0) { + return elements; + } + List clauses = parseDelimitedString(header, ",", false); + for (String clause : clauses) { + String[] tokens = clause.split(";"); + if (tokens.length < 1) { + throw new IllegalArgumentException("Invalid header clause: " + clause); + } + HeaderElement elem = new HeaderElement(tokens[0].trim()); + elements.add(elem); + int beginIndex = elements.size() - 1; + for (int i = 1; i < tokens.length; i++) { + int pos = tokens[i].indexOf('='); + if (pos != -1) { + if (pos > 0 && tokens[i].charAt(pos - 1) == ':') { + String name = tokens[i].substring(0, pos - 1).trim(); + String value = tokens[i].substring(pos + 1).trim(); + elem.addDirective(name, value); + } else { + String name = tokens[i].substring(0, pos).trim(); + String value = tokens[i].substring(pos + 1).trim(); + elem.addAttribute(name, value); + } + } else { + elem = new HeaderElement(tokens[i].trim()); + elements.add(elem); + } + } + for (; beginIndex < elements.size() - 1; beginIndex++) { + HeaderElement headerElement = elements.get(beginIndex); + headerElement.getAttributes().putAll(elem.getAttributes()); + headerElement.getDirectives().putAll(elem.getDirectives()); + } + } + return elements; + } + + private static List parseDelimitedString(String value, String delim, boolean includeQuotes) { + if (value == null) { + value = ""; + } + + List list = new ArrayList(); + + int CHAR = 1; + int DELIMITER = 2; + int STARTQUOTE = 4; + int ENDQUOTE = 8; + + StringBuffer sb = new StringBuffer(); + + int expecting = (CHAR | DELIMITER | STARTQUOTE); + + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + + boolean isDelimiter = (delim.indexOf(c) >= 0); + boolean isQuote = (c == '"'); + + if (isDelimiter && ((expecting & DELIMITER) > 0)) { + list.add(sb.toString().trim()); + sb.delete(0, sb.length()); + expecting = (CHAR | DELIMITER | STARTQUOTE); + } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { + if (includeQuotes) { + sb.append(c); + } + expecting = CHAR | ENDQUOTE; + } else if (isQuote && ((expecting & ENDQUOTE) > 0)) { + if (includeQuotes) { + sb.append(c); + } + expecting = (CHAR | STARTQUOTE | DELIMITER); + } else if ((expecting & CHAR) > 0) { + sb.append(c); + } else { + throw new IllegalArgumentException("Invalid delimited string: " + value); + } + } + + if (sb.length() > 0) { + list.add(sb.toString().trim()); + } + + return list; + } + + public static class HeaderElement { + + private String path; + private Map attributes; + private Map directives; + + public HeaderElement(String path) { + this.path = path; + this.attributes = new HashMap(); + this.directives = new HashMap(); + } + + public String getName() { + return this.path; + } + + public Map getAttributes() { + return attributes; + } + + public String getAttribute(String name) { + return attributes.get(name); + } + + public void addAttribute(String name, String value) { + attributes.put(name, value); + } + + public Map getDirectives() { + return directives; + } + + public String getDirective(String name) { + return directives.get(name); + } + + public void addDirective(String name, String value) { + directives.put(name, value); + } + + @Override + public String toString() { + return "HeaderElement [path=" + path + ", attributes=" + attributes + ", directives=" + directives + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((attributes == null) ? 0 : attributes.hashCode()); + result = prime * result + ((directives == null) ? 0 : directives.hashCode()); + result = prime * result + ((path == null) ? 0 : path.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HeaderElement other = (HeaderElement) obj; + if (attributes == null) { + if (other.attributes != null) + return false; + } else if (!attributes.equals(other.attributes)) + return false; + if (directives == null) { + if (other.directives != null) + return false; + } else if (!directives.equals(other.directives)) + return false; + if (path == null) { + if (other.path != null) + return false; + } else if (!path.equals(other.path)) + return false; + return true; + } + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ResourceDiscoveryFilter.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ResourceDiscoveryFilter.java new file mode 100644 index 00000000..f09ec05a --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/ResourceDiscoveryFilter.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.xbean.osgi.bundle.util; + +/** + * @version $Rev$ $Date$ + */ +public interface ResourceDiscoveryFilter { + + public boolean rangeDiscoveryRequired(DiscoveryRange discoveryRange); + + public boolean zipFileDiscoveryRequired(String url); + + public boolean directoryDiscoveryRequired(String url); +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/VersionRange.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/VersionRange.java new file mode 100644 index 00000000..5f94f192 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/VersionRange.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.xbean.osgi.bundle.util; + +import org.osgi.framework.Version; + +public class VersionRange { + + private Version low = null; + private boolean isLowInclusive = false; + private Version high = null; + private boolean isHighInclusive = false; + + public static final VersionRange infiniteRange = new VersionRange(Version.emptyVersion, true, null, true); + + public VersionRange(Version low, boolean isLowInclusive, Version high, boolean isHighInclusive) { + this.low = low; + this.isLowInclusive = isLowInclusive; + this.high = high; + this.isHighInclusive = isHighInclusive; + } + + public Version getLow() { + return low; + } + + public boolean isLowInclusive() { + return isLowInclusive; + } + + public Version getHigh() { + return high; + } + + public boolean isHighInclusive() { + return isHighInclusive; + } + + public boolean isInRange(Version version) { + // We might not have an upper end to the range. + if (high == null) { + return (version.compareTo(low) >= 0); + } else if (isLowInclusive() && isHighInclusive()) { + return (version.compareTo(low) >= 0) && (version.compareTo(high) <= 0); + } else if (isHighInclusive()) { + return (version.compareTo(low) > 0) && (version.compareTo(high) <= 0); + } else if (isLowInclusive()) { + return (version.compareTo(low) >= 0) && (version.compareTo(high) < 0); + } + return (version.compareTo(low) > 0) && (version.compareTo(high) < 0); + } + + public static VersionRange parse(String range) { + // Check if the version is an interval. + if (range.indexOf(',') >= 0) { + String s = range.substring(1, range.length() - 1); + String vlo = s.substring(0, s.indexOf(',')).trim(); + String vhi = s.substring(s.indexOf(',') + 1, s.length()).trim(); + return new VersionRange ( + new Version(vlo), (range.charAt(0) == '['), + new Version(vhi), (range.charAt(range.length() - 1) == ']')); + } else { + return new VersionRange(new Version(range), true, null, false); + } + } + + public String toString() { + if (high != null) { + StringBuffer sb = new StringBuffer(); + sb.append(isLowInclusive ? '[' : '('); + sb.append(low.toString()); + sb.append(','); + sb.append(high.toString()); + sb.append(isHighInclusive ? ']' : ')'); + return sb.toString(); + } else { + return low.toString(); + } + } +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleClassLoader.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleClassLoader.java new file mode 100644 index 00000000..2a5a99d6 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleClassLoader.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.xbean.osgi.bundle.util.equinox; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; + +import org.apache.xbean.osgi.bundle.util.BundleResourceHelper; +import org.apache.xbean.osgi.bundle.util.DelegatingBundle; +import org.apache.xbean.osgi.bundle.util.DelegatingBundleReference; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleReference; + +/** + * ClassLoader for a {@link Bundle}. + *
    + * This ClassLoader implementation extends the {@link URLClassLoader} and converts resource "bundle" + * URLs (found in directories or embedded jar files) into regular jar URLs. + * This ClassLoader implementation will only work on Equinox framework. + * + * @version $Rev$ $Date$ + */ +public class EquinoxBundleClassLoader extends URLClassLoader implements DelegatingBundleReference { + + private final Bundle bundle; + private final BundleResourceHelper resourceHelper; + + public EquinoxBundleClassLoader(Bundle bundle) { + this(bundle, + BundleResourceHelper.getSearchWiredBundles(true), + BundleResourceHelper.getConvertResourceUrls(true)); + } + + public EquinoxBundleClassLoader(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) { + super(new URL[] {}); + this.bundle = bundle; + this.resourceHelper = new EquinoxBundleResourceHelper(bundle, searchWiredBundles, convertResourceUrls); + } + + @Override + public String toString() { + return "[EquinoxBundleClassLoader] " + bundle; + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class clazz = bundle.loadClass(name); + if (resolve) { + resolveClass(clazz); + } + return clazz; + } + + @Override + public URL getResource(String name) { + return resourceHelper.getResource(name); + } + + @Override + public Enumeration findResources(String name) throws IOException { + return resourceHelper.getResources(name); + } + + public void setSearchWiredBundles(boolean search) { + resourceHelper.setSearchWiredBundles(search); + } + + public boolean getSearchWiredBundles() { + return resourceHelper.getSearchWiredBundles(); + } + + public void setConvertResourceUrls(boolean convert) { + resourceHelper.setConvertResourceUrls(convert); + } + + public boolean getConvertResourceUrls() { + return resourceHelper.getConvertResourceUrls(); + } + + /** + * Return the bundle associated with this classloader. + * + * In most cases the bundle associated with the classloader is a regular framework bundle. + * However, in some cases the bundle associated with the classloader is a {@link DelegatingBundle}. + * In such cases, the unwrap parameter controls whether this function returns the + * {@link DelegatingBundle} instance or the main application bundle backing with the {@link DelegatingBundle}. + * + * @param unwrap If true and if the bundle associated with this classloader is a {@link DelegatingBundle}, + * this function will return the main application bundle backing with the {@link DelegatingBundle}. + * Otherwise, the bundle associated with this classloader is returned as is. + * @return The bundle associated with this classloader. + */ + public Bundle getBundle(boolean unwrap) { + if (unwrap && bundle instanceof DelegatingBundle) { + return ((DelegatingBundle) bundle).getMainBundle(); + } + return bundle; + } + + /** + * Return the bundle associated with this classloader. + * + * This method calls {@link #getBundle(boolean) getBundle(true)} and therefore always returns a regular + * framework bundle. + *

    + * Note: Some libraries use {@link BundleReference#getBundle()} to obtain a bundle for the given + * classloader and expect the returned bundle instance to be work with any OSGi API. Some of these API might + * not work if {@link DelegatingBundle} is returned. That is why this function will always return + * a regular framework bundle. See {@link #getBundle(boolean)} for more information. + * + * @return The bundle associated with this classloader. + */ + public Bundle getBundle() { + return getBundle(true); + } + + @Override + public int hashCode() { + return bundle.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || !other.getClass().equals(getClass())) { + return false; + } + EquinoxBundleClassLoader otherBundleClassLoader = (EquinoxBundleClassLoader) other; + return this.bundle == otherBundleClassLoader.bundle; + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleResourceHelper.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleResourceHelper.java new file mode 100644 index 00000000..b7947bf3 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/equinox/EquinoxBundleResourceHelper.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.xbean.osgi.bundle.util.equinox; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.apache.xbean.osgi.bundle.util.BundleResourceHelper; +import org.eclipse.osgi.service.urlconversion.URLConverter; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +/** + * Equinox implementation of BundleResourceHelper. + *
    + * This implementation of the {@link BundleResourceHelper} converts resource URLs + * returned by {@link #getResource(String)} and {@link #getResources(String)} into jar URLs + * using Equinox's URLConverter service. + * + * @version $Rev$ $Date$ + */ +public class EquinoxBundleResourceHelper extends BundleResourceHelper { + + private URLConverter converter; + + public EquinoxBundleResourceHelper(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) { + super(bundle, searchWiredBundles, convertResourceUrls); + init(); + } + + private void init() { + BundleContext context = bundle.getBundleContext(); + if (context != null) { + ServiceReference urlReference = context.getServiceReference(URLConverter.class.getName()); + if (urlReference != null) { + converter = (URLConverter) context.getService(urlReference); + } + } + } + + @Override + public URL getResource(String name) { + if (convertResourceUrls) { + return (converter == null) ? convertedFindResource(name) : findResource(name); + } else { + return findResource(name); + } + } + + @Override + public Enumeration getResources(String name) throws IOException { + if (convertResourceUrls) { + return (converter == null) ? convertedFindResources(name) : findResources(name); + } else { + return findResources(name); + } + } + + @Override + protected URL convert(URL url) { + try { + URL convertedURL = converter.resolve(url); + return convertedURL; + } catch (IOException e) { + e.printStackTrace(); + return url; + } + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarEntry.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarEntry.java new file mode 100644 index 00000000..9fefdb37 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarEntry.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.xbean.osgi.bundle.util.jar; + +import java.io.IOException; +import java.net.URL; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +/** + * @version $Rev$ $Date$ + */ +public class BundleJarEntry extends JarEntry { + private final URL entryURL; + private final Manifest manifest; + + public BundleJarEntry(String name, URL entryURL, Manifest manifest) { + super(removeSlash(name)); + this.entryURL = entryURL; + this.manifest = manifest; + } + + private static String removeSlash(String name) { + if (name.startsWith("/")) { + name = name.substring(1); + } + return name; + } + + public URL getEntryURL() { + return entryURL; + } + + public Attributes getAttributes() throws IOException { + if (manifest == null) { + return null; + } + return manifest.getAttributes(getName()); + } + + public Certificate[] getCertificates() { + return null; + } + + public void setTime(long time) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Can not change the time of unpacked jar entry"); + } + + public long getTime() { + return -1; + } + + public void setSize(long size) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Can not change the size of unpacked jar entry"); + } + + public long getSize() { + return -1; + } + + public long getCompressedSize() { + return getSize(); + } + + public void setCompressedSize(long compressedSize) { + throw new UnsupportedOperationException("Can not change the compressed size of unpacked jar entry"); + } + + public long getCrc() { + return super.getCrc(); + } + + public void setCrc(long crc) { + throw new UnsupportedOperationException("Can not change the crc of unpacked jar entry"); + } + + public int getMethod() { + return ZipEntry.STORED; + } + + public void setMethod(int method) { + throw new UnsupportedOperationException("Can not change the method of unpacked jar entry"); + } + + public byte[] getExtra() { + return null; + } + + public void setExtra(byte[] extra) { + throw new UnsupportedOperationException("Can not change the extra data of unpacked jar entry"); + } + + public String getComment() { + return null; + } + + public void setComment(String comment) { + throw new UnsupportedOperationException("Can not change the comment of unpacked jar entry"); + } + + public boolean isDirectory() { + return entryURL.getPath().endsWith("/"); + } + +} diff --git a/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarFile.java b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarFile.java new file mode 100644 index 00000000..c515c047 --- /dev/null +++ b/xbean-bundleutils/src/main/java/org/apache/xbean/osgi/bundle/util/jar/BundleJarFile.java @@ -0,0 +1,196 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.osgi.bundle.util.jar; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import org.osgi.framework.Bundle; + +/** + * @version $Rev$ $Date$ + */ +public class BundleJarFile extends JarFile { + + private static final File DUMMY_JAR_FILE; + + static { + try { + DUMMY_JAR_FILE = createTempFile(); + new JarOutputStream(new FileOutputStream(BundleJarFile.DUMMY_JAR_FILE), new Manifest()).close(); + } catch (IOException e) { + throw new ExceptionInInitializerError(e); + } + } + private final Bundle bundle; + private boolean manifestLoaded = false; + private Manifest manifest; + + public BundleJarFile(Bundle bundle) throws IOException { + super(DUMMY_JAR_FILE); + this.bundle = bundle; + } + + public Bundle getBundle() { + return bundle; + } + + public Manifest getManifest() throws IOException { + if (!manifestLoaded) { + URL manifestURL = bundle.getEntry("META-INF/MANIFEST.MF"); + if (manifestURL != null) { + InputStream in = null; + try { + in = manifestURL.openStream(); + manifest = new Manifest(in); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // ignore + } + } + } + } + manifestLoaded = true; + } + return manifest; + } + + public BundleJarEntry getBundleJarEntry(String name) { + URL url = bundle.getEntry(name); + if (url == null) { + return null; + } + return new BundleJarEntry(name, url, getManifestSafe()); + } + + public JarEntry getJarEntry(String name) { + return getBundleJarEntry(name); + } + + public ZipEntry getEntry(String name) { + return getBundleJarEntry(name); + } + + public Enumeration entries() { + Manifest manifest = getManifestSafe(); + Enumeration e = bundle.findEntries("/", "*", true); + LinkedList entries = new LinkedList(); + while (e.hasMoreElements()) { + URL entryURL = (URL) e.nextElement(); + entries.add(new BundleJarEntry(entryURL.getPath(), entryURL, manifest)); + } + return Collections.enumeration(entries); + } + + public InputStream getInputStream(ZipEntry zipEntry) throws IOException { + BundleJarEntry entry; + if (zipEntry instanceof BundleJarEntry) { + entry = (BundleJarEntry) zipEntry; + } else { + entry = getBundleJarEntry(zipEntry.getName()); + } + + if (entry == null) { + throw new IOException("Entry not found: name=" + zipEntry.getName()); + } else if (entry.isDirectory()) { + return new EmptyInputStream(); + } else { + return entry.getEntryURL().openStream(); + } + } + + public String getName() { + return bundle.getSymbolicName(); + } + + public int size() { + return -1; + } + + public void close() throws IOException { + } + + private Manifest getManifestSafe() { + Manifest manifest = null; + try { + manifest = getManifest(); + } catch (IOException e) { + // ignore + } + return manifest; + } + + // be careful to clean up the temp file... we tell the vm to delete this on exit + // but VMs can't be trusted to acutally delete the file + private static File createTempFile() throws IOException { + File tempFile = File.createTempFile("geronimo-fileutils", ".tmpfile"); + tempFile.deleteOnExit(); + return tempFile; + } + + + private static final class EmptyInputStream extends InputStream { + + public int read() { + return -1; + } + + public int read(byte b[]) { + return -1; + } + + public int read(byte b[], int off, int len) { + return -1; + } + + public long skip(long n) { + return 0; + } + + public int available() { + return 0; + } + + public void close() { + } + + public synchronized void mark(int readlimit) { + } + + public synchronized void reset() { + } + + public boolean markSupported() { + return false; + } + } + +} diff --git a/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/BundleDescriptionTest.java b/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/BundleDescriptionTest.java new file mode 100644 index 00000000..421e32a3 --- /dev/null +++ b/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/BundleDescriptionTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xbean.osgi.bundle.util; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +import junit.framework.TestCase; + +public class BundleDescriptionTest extends TestCase { + + public void testSimple() throws Exception { + Map headers = new HashMap(); + headers.put(Constants.IMPORT_PACKAGE, + "com.thoughtworks.xstream;version=\"1.3\",com.thoughtworks.xstream.converters,org.apache.geronimo.kernel.proxy"); + headers.put(Constants.EXPORT_PACKAGE, + "org.apache.geronimo.kernel.rmi;uses:=\"javax.rmi.ssl,org.apache.geronimo.gbean,org.slf4j\",org.apache.geronimo.kernel.proxy"); + + BundleDescription desc = new BundleDescription(headers); + + List imports = desc.getImportPackage(); + assertEquals(3, imports.size()); + assertEquals("com.thoughtworks.xstream", imports.get(0).getName()); + assertEquals("1.3", imports.get(0).getAttributes().get("version")); + assertEquals(Version.parseVersion("1.3"), imports.get(0).getVersionRange().getLow()); + assertEquals("com.thoughtworks.xstream.converters", imports.get(1).getName()); + assertEquals("org.apache.geronimo.kernel.proxy", imports.get(2).getName()); + + List exports = desc.getExportPackage(); + assertEquals(2, exports.size()); + assertEquals("org.apache.geronimo.kernel.rmi", exports.get(0).getName()); + assertEquals("javax.rmi.ssl,org.apache.geronimo.gbean,org.slf4j", exports.get(0).getDirectives().get("uses")); + assertEquals("org.apache.geronimo.kernel.proxy", exports.get(1).getName()); + + List externalImports = desc.getExternalImports(); + assertEquals(2, externalImports.size()); + assertEquals("com.thoughtworks.xstream", externalImports.get(0).getName()); + assertEquals("1.3", externalImports.get(0).getAttributes().get("version")); + assertEquals("com.thoughtworks.xstream.converters", externalImports.get(1).getName()); + } + + public void testSymbolicName() throws Exception { + Map headers = new HashMap(); + + BundleDescription desc = new BundleDescription(headers); + + // test simple + headers.put(Constants.BUNDLE_SYMBOLICNAME, "foo1"); + assertEquals("foo1", desc.getSymbolicName().getName()); + + // test with a directive + headers.put(Constants.BUNDLE_SYMBOLICNAME, "foo2; singleton:=true"); + assertEquals("foo2", desc.getSymbolicName().getName()); + assertEquals("true", desc.getSymbolicName().getDirectives().get("singleton")); + } +} + diff --git a/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/HeaderParserTest.java b/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/HeaderParserTest.java new file mode 100644 index 00000000..aae26128 --- /dev/null +++ b/xbean-bundleutils/src/test/java/org/apache/xbean/osgi/bundle/util/HeaderParserTest.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.xbean.osgi.bundle.util; + +import java.util.List; + +import org.apache.xbean.osgi.bundle.util.HeaderParser; +import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement; + +import junit.framework.TestCase; + +public class HeaderParserTest extends TestCase { + + public void testSimple() throws Exception { + List paths = HeaderParser.parseHeader("/foo.xml, /foo/bar.xml"); + assertEquals(2, paths.size()); + assertEquals("/foo.xml", paths.get(0).getName()); + assertEquals(0, paths.get(0).getAttributes().size()); + assertEquals(0, paths.get(0).getDirectives().size()); + assertEquals("/foo/bar.xml", paths.get(1).getName()); + assertEquals(0, paths.get(1).getAttributes().size()); + assertEquals(0, paths.get(1).getDirectives().size()); + + assertEquals(paths, HeaderParser.parseHeader(HeaderBuilder.build(paths))); + } + + public void testComplex() throws Exception { + String header = "OSGI-INF/blueprint/comp1_named.xml;ignored-directive:=true,OSGI-INF/blueprint/comp2_named.xml;some-other-attribute=1"; + List paths = HeaderParser.parseHeader(header); + assertEquals(2, paths.size()); + assertEquals("OSGI-INF/blueprint/comp1_named.xml", paths.get(0).getName()); + assertEquals(0, paths.get(0).getAttributes().size()); + assertEquals(1, paths.get(0).getDirectives().size()); + assertEquals("true", paths.get(0).getDirective("ignored-directive")); + assertEquals("OSGI-INF/blueprint/comp2_named.xml", paths.get(1).getName()); + assertEquals(1, paths.get(1).getAttributes().size()); + assertEquals("1", paths.get(1).getAttribute("some-other-attribute")); + assertEquals(0, paths.get(1).getDirectives().size()); + assertEquals(paths, HeaderParser.parseHeader(HeaderBuilder.build(paths))); + } + + public void testPaths() throws Exception { + String header = "OSGI-INF/blueprint/comp1_named.xml;ignored-directive:=true,OSGI-INF/blueprint/comp2_named.xml;foo.xml;a=b;1:=2;c:=d;4=5"; + List paths = HeaderParser.parseHeader(header); + assertEquals(3, paths.size()); + assertEquals("OSGI-INF/blueprint/comp1_named.xml", paths.get(0).getName()); + assertEquals(0, paths.get(0).getAttributes().size()); + assertEquals(1, paths.get(0).getDirectives().size()); + assertEquals("true", paths.get(0).getDirective("ignored-directive")); + assertEquals("OSGI-INF/blueprint/comp2_named.xml", paths.get(1).getName()); + assertEquals(2, paths.get(1).getAttributes().size()); + assertEquals(2, paths.get(1).getDirectives().size()); + assertEquals("foo.xml", paths.get(2).getName()); + assertEquals(2, paths.get(2).getAttributes().size()); + assertEquals("b", paths.get(2).getAttribute("a")); + assertEquals("5", paths.get(2).getAttribute("4")); + assertEquals(2, paths.get(2).getDirectives().size()); + assertEquals("d", paths.get(2).getDirective("c")); + assertEquals("2", paths.get(2).getDirective("1")); + assertEquals(paths, HeaderParser.parseHeader(HeaderBuilder.build(paths))); + } + + public void testExportPackages() throws Exception { + String header = "org.apache.geronimo.kernel.rmi;uses:=\"javax.rmi.ssl,org.apache.geronimo.gbean,org.slf4j\",org.apache.geronimo.kernel.proxy"; + List paths = HeaderParser.parseHeader(header); + assertEquals(2, paths.size()); + + assertEquals("org.apache.geronimo.kernel.rmi", paths.get(0).getName()); + assertEquals(0, paths.get(0).getAttributes().size()); + assertEquals(1, paths.get(0).getDirectives().size()); + assertEquals("javax.rmi.ssl,org.apache.geronimo.gbean,org.slf4j", paths.get(0).getDirective("uses")); + + assertEquals("org.apache.geronimo.kernel.proxy", paths.get(1).getName()); + assertEquals(0, paths.get(1).getAttributes().size()); + assertEquals(0, paths.get(1).getDirectives().size()); + assertEquals(paths, HeaderParser.parseHeader(HeaderBuilder.build(paths))); + } + + public void testImportPackages() throws Exception { + String header = "com.thoughtworks.xstream;version=\"1.3\",com.thoughtworks.xstream.converters"; + List paths = HeaderParser.parseHeader(header); + assertEquals(2, paths.size()); + + assertEquals("com.thoughtworks.xstream", paths.get(0).getName()); + assertEquals(1, paths.get(0).getAttributes().size()); + assertEquals("1.3", paths.get(0).getAttribute("version")); + assertEquals(0, paths.get(0).getDirectives().size()); + + assertEquals("com.thoughtworks.xstream.converters", paths.get(1).getName()); + assertEquals(0, paths.get(1).getAttributes().size()); + assertEquals(0, paths.get(1).getDirectives().size()); + + assertEquals(paths, HeaderParser.parseHeader(HeaderBuilder.build(paths))); + } + + public void testMultipleImportPackages() throws Exception { + String header = "javax.transaction; javax.transaction.xa; version=1.1; partial=true; mandatory:=partial"; + List paths = HeaderParser.parseHeader(header); + assertEquals(2, paths.size()); + + assertEquals("javax.transaction", paths.get(0).getName()); + assertEquals(2, paths.get(0).getAttributes().size()); + assertEquals("1.1", paths.get(0).getAttribute("version")); + assertEquals(1, paths.get(0).getDirectives().size()); + assertEquals("partial", paths.get(0).getDirectives().get("mandatory")); + + assertEquals("javax.transaction.xa", paths.get(1).getName()); + assertEquals(2, paths.get(1).getAttributes().size()); + assertEquals("1.1", paths.get(1).getAttribute("version")); + assertEquals(1, paths.get(1).getDirectives().size()); + assertEquals("partial", paths.get(1).getDirectives().get("mandatory")); + } +} diff --git a/xbean-classloader/pom.xml b/xbean-classloader/pom.xml new file mode 100644 index 00000000..46642c3a --- /dev/null +++ b/xbean-classloader/pom.xml @@ -0,0 +1,79 @@ + + + + + + + 4.0.0 + + + xbean + org.apache.xbean + 3.12 + + + xbean-classloader + bundle + Apache XBean :: Classloader + xbean-classloader includes a flexibie multi-parent classloader + + + + org.apache.xbean + maven-xbean-plugin + + + + mapping + + + + + http://xbean.apache.org/schemas/classloader + + + + org.apache.felix + maven-bundle-plugin + + + + org.springframework.beans.factory*;resolution:=optional, + * + + + + + + + + + + cglib + cglib-nodep + test + + + org.springframework + spring-beans + provided + + + diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractResourceHandle.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractResourceHandle.java new file mode 100644 index 00000000..ce46e512 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractResourceHandle.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.xbean.classloader; + +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * @version $Rev$ $Date$ + */ +public abstract class AbstractResourceHandle implements ResourceHandle { + public byte[] getBytes() throws IOException { + InputStream in = getInputStream(); + try { + byte[] bytes = IoUtil.getBytes(in); + return bytes; + } finally { + IoUtil.close(in); + } + } + + public Manifest getManifest() throws IOException { + return null; + } + + public Certificate[] getCertificates() { + return null; + } + + public Attributes getAttributes() throws IOException { + Manifest m = getManifest(); + if (m == null) { + return null; + } + + String entry = getUrl().getFile(); + return m.getAttributes(entry); + } + + public void close() { + } + + public String toString() { + return "[" + getName() + ": " + getUrl() + "; code source: " + getCodeSourceUrl() + "]"; + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractUrlResourceLocation.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractUrlResourceLocation.java new file mode 100644 index 00000000..780d8ee2 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/AbstractUrlResourceLocation.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.net.URL; + +/** + * @version $Rev$ $Date$ + */ +public abstract class AbstractUrlResourceLocation implements ResourceLocation { + private final URL codeSource; + + public AbstractUrlResourceLocation(URL codeSource) { + this.codeSource = codeSource; + } + + public final URL getCodeSource() { + return codeSource; + } + + public void close() { + } + + public final boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractUrlResourceLocation that = (AbstractUrlResourceLocation) o; + return codeSource.equals(that.codeSource); + } + + public final int hashCode() { + return codeSource.hashCode(); + } + + public final String toString() { + return "[" + getClass().getName() + ": " + codeSource + "]"; + } +} diff --git a/server/src/java/org/xbean/server/classloader/ClassLoaderUtil.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java similarity index 71% rename from server/src/java/org/xbean/server/classloader/ClassLoaderUtil.java rename to xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java index 944a75f7..bea480a5 100644 --- a/server/src/java/org/xbean/server/classloader/ClassLoaderUtil.java +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java @@ -1,24 +1,25 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 the original author or authors. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.xbean.server.classloader; +package org.apache.xbean.classloader; import java.util.Map; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.beans.Introspector; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; @@ -36,7 +37,7 @@ private ClassLoaderUtil() { /** * Cleans well known class loader leaks in VMs and libraries. There is a lot of bad code out there and this method - * will clear up the know problems. This method should only be called when the class loader will no longer be used. + * will clear up the known problems. This method should only be called when the class loader will no longer be used. * It this method is called two often it can have a serious impact on preformance. * @param classLoader the class loader to destroy */ @@ -46,12 +47,13 @@ public static void destroy(ClassLoader classLoader) { clearSunSoftCache(ObjectOutputStream.class, "subclassAudits"); clearSunSoftCache(ObjectStreamClass.class, "localDescs"); clearSunSoftCache(ObjectStreamClass.class, "reflectors"); + Introspector.flushCaches(); } /** * Clears the caches maintained by the SunVM object stream implementation. This method uses reflection and * setAccessable to obtain access to the Sun cache. The cache is locked with a synchronize monitor and cleared. - * This method completely clears the class loader cache which will impact preformance of object serialization. + * This method completely clears the class loader cache which will impact performance of object serialization. * @param clazz the name of the class containing the cache field * @param fieldName the name of the cache field */ diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java new file mode 100644 index 00000000..50242f6e --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.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.xbean.classloader; + +/** + * DestroyableClassLoader is a mixin interface for a ClassLoader that add a destroy method to propertly cleanup a + * classloader then dereferenced by the server. + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public interface DestroyableClassLoader { + /** + * Destroys the clasloader releasing all resources. After this mehtod is called, the class loader will no longer + * load any classes or resources. + */ + void destroy(); +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceHandle.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceHandle.java new file mode 100644 index 00000000..6f0abfb3 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceHandle.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.xbean.classloader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.net.MalformedURLException; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * @version $Rev$ $Date$ + */ +public class DirectoryResourceHandle extends AbstractResourceHandle { + private final String name; + private final File file; + private final Manifest manifest; + private final URL url; + private final URL codeSource; + + public DirectoryResourceHandle(String name, File file, File codeSource, Manifest manifest) throws MalformedURLException { + this.name = name; + this.file = file; + this.codeSource = codeSource.toURI().toURL(); + this.manifest = manifest; + url = file.toURI().toURL(); + } + + public String getName() { + return name; + } + + public URL getUrl() { + return url; + } + + public URL getCodeSourceUrl() { + return codeSource; + } + + public boolean isDirectory() { + return file.isDirectory(); + } + + public InputStream getInputStream() throws IOException { + if (file.isDirectory()) { + return new IoUtil.EmptyInputStream(); + } + return new FileInputStream(file); + } + + public int getContentLength() { + if (file.isDirectory() || file.length() > Integer.MAX_VALUE) { + return -1; + } else { + return (int) file.length(); + } + } + + public Manifest getManifest() throws IOException { + return manifest; + } + + public Attributes getAttributes() throws IOException { + if (manifest == null) { + return null; + } + return manifest.getAttributes(getName()); + } + + /** + * Always return null. This could be implementd by verifing the signatures + * in the manifest file against the actual file, but we don't need this right now. + * @return null + */ + public Certificate[] getCertificates() { + return null; + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceLocation.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceLocation.java new file mode 100644 index 00000000..a6a218ed --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/DirectoryResourceLocation.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.xbean.classloader; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.jar.Manifest; + +/** + * @version $Rev$ $Date$ + */ +public class DirectoryResourceLocation extends AbstractUrlResourceLocation { + private final File baseDir; + private boolean manifestLoaded = false; + private Manifest manifest; + + public DirectoryResourceLocation(File baseDir) throws MalformedURLException { + super(baseDir.toURI().toURL()); + this.baseDir = baseDir; + } + + public ResourceHandle getResourceHandle(String resourceName) { + File file = new File(baseDir, resourceName); + if (!file.exists()) { + return null; + } + + try { + ResourceHandle resourceHandle = new DirectoryResourceHandle(resourceName, file, baseDir, getManifestSafe()); + return resourceHandle; + } catch (MalformedURLException e) { + return null; + } + } + + public Manifest getManifest() throws IOException { + if (!manifestLoaded) { + File manifestFile = new File(baseDir, "META-INF/MANIFEST.MF"); + + if (manifestFile.isFile() && manifestFile.canRead()) { + FileInputStream in = null; + try { + in = new FileInputStream(manifestFile); + manifest = new Manifest(in); + } finally { + IoUtil.close(in); + } + } + manifestLoaded = true; + } + return manifest; + } + + private Manifest getManifestSafe() { + Manifest manifest = null; + try { + manifest = getManifest(); + } catch (IOException e) { + // ignore + } + return manifest; + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/IoUtil.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/IoUtil.java new file mode 100644 index 00000000..dec3dee5 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/IoUtil.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.xbean.classloader; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.jar.JarFile; + +/** + * @version $Rev$ $Date$ + */ +public final class IoUtil { + private IoUtil() { + } + + public static byte[] getBytes(InputStream inputStream) throws IOException { + try { + byte[] buffer = new byte[4096]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { + out.write(buffer, 0, count); + } + byte[] bytes = out.toByteArray(); + return bytes; + } finally { + close(inputStream); + } + } + + public static void flush(OutputStream thing) { + if (thing != null) { + try { + thing.flush(); + } catch(Exception ignored) { + } + } + } + + public static void flush(Writer thing) { + if (thing != null) { + try { + thing.flush(); + } catch(Exception ignored) { + } + } + } + + public static void close(JarFile thing) { + if (thing != null) { + try { + thing.close(); + } catch(Exception ignored) { + } + } + } + + public static void close(InputStream thing) { + if (thing != null) { + try { + thing.close(); + } catch(Exception ignored) { + } + } + } + + public static void close(OutputStream thing) { + if (thing != null) { + try { + thing.close(); + } catch(Exception ignored) { + } + } + } + + public static void close(Reader thing) { + if (thing != null) { + try { + thing.close(); + } catch(Exception ignored) { + } + } + } + + public static void close(Writer thing) { + if (thing != null) { + try { + thing.close(); + } catch(Exception ignored) { + } + } + } + + public static final class EmptyInputStream extends InputStream { + public int read() { + return -1; + } + + public int read(byte b[]) { + return -1; + } + + public int read(byte b[], int off, int len) { + return -1; + } + + public long skip(long n) { + return 0; + } + + public int available() { + return 0; + } + + public void close() { + } + + public synchronized void mark(int readlimit) { + } + + public synchronized void reset() { + } + + public boolean markSupported() { + return false; + } + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java new file mode 100644 index 00000000..4af16343 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java @@ -0,0 +1,344 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.io.IOException; +import java.io.File; +import java.net.URL; +import java.net.URI; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.security.cert.Certificate; +import java.util.Collection; +import java.util.Enumeration; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * The JarFileClassLoader that loads classes and resources from a list of JarFiles. This method is simmilar to URLClassLoader + * except it properly closes JarFiles when the classloader is destroyed so that the file read lock will be released, and + * the jar file can be modified and deleted. + *

    + * Note: This implementation currently does not work reliably on windows, since the jar URL handler included with the Sun JavaVM + * holds a read lock on the JarFile, and this lock is not released when the jar url is dereferenced. To fix this a + * replacement for the jar url handler must be written. + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class JarFileClassLoader extends MultiParentClassLoader { + private static final URL[] EMPTY_URLS = new URL[0]; + + private final UrlResourceFinder resourceFinder = new UrlResourceFinder(); + private final AccessControlContext acc; + + /** + * Creates a JarFileClassLoader that is a child of the system class loader. + * @param name the name of this class loader + * @param urls a list of URLs from which classes and resources should be loaded + */ + public JarFileClassLoader(String name, URL[] urls) { + super(name, EMPTY_URLS); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + /** + * Creates a JarFileClassLoader that is a child of the specified class loader. + * @param name the name of this class loader + * @param urls a list of URLs from which classes and resources should be loaded + * @param parent the parent of this class loader + */ + public JarFileClassLoader(String name, URL[] urls, ClassLoader parent) { + super(name, EMPTY_URLS, parent); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + public JarFileClassLoader(String name, URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { + super(name, EMPTY_URLS, parent, inverseClassLoading, hiddenClasses, nonOverridableClasses); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + /** + * Creates a named class loader as a child of the specified parents. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + * @param parents the parents of this class loader + */ + public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents) { + super(name, EMPTY_URLS, parents); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) { + super(name, EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { + super(name, EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses); + this.acc = AccessController.getContext(); + addURLs(urls); + } + + /** + * {@inheritDoc} + */ + public URL[] getURLs() { + return resourceFinder.getUrls(); + } + + /** + * {@inheritDoc} + */ + public void addURL(final URL url) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + resourceFinder.addUrl(url); + return null; + } + }, acc); + } + + /** + * Adds an array of urls to the end of this class loader. + * @param urls the URLs to add + */ + protected void addURLs(final URL[] urls) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + resourceFinder.addUrls(urls); + return null; + } + }, acc); + } + + /** + * {@inheritDoc} + */ + public void destroy() { + resourceFinder.destroy(); + super.destroy(); + } + + /** + * {@inheritDoc} + */ + public URL findResource(final String resourceName) { + return (URL) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return resourceFinder.findResource(resourceName); + } + }, acc); + } + + /** + * {@inheritDoc} + */ + public Enumeration findResources(final String resourceName) throws IOException { + // todo this is not right + // first get the resources from the parent classloaders + Enumeration parentResources = super.findResources(resourceName); + + // get the classes from my urls + Enumeration myResources = (Enumeration) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return resourceFinder.findResources(resourceName); + } + }, acc); + + // join the two together + Enumeration resources = new UnionEnumeration(parentResources, myResources); + return resources; + } + + /** + * {@inheritDoc} + */ + protected String findLibrary(String libraryName) { + // if the libraryName is actually a directory it is invalid + int pathEnd = libraryName.lastIndexOf('/'); + if (pathEnd == libraryName.length() - 1) { + throw new IllegalArgumentException("libraryName ends with a '/' character: " + libraryName); + } + + // get the name if the library file + final String resourceName; + if (pathEnd < 0) { + resourceName = System.mapLibraryName(libraryName); + } else { + String path = libraryName.substring(0, pathEnd + 1); + String file = libraryName.substring(pathEnd + 1); + resourceName = path + System.mapLibraryName(file); + } + + // get a resource handle to the library + ResourceHandle resourceHandle = (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return resourceFinder.getResource(resourceName); + } + }, acc); + + if (resourceHandle == null) { + return null; + } + + // the library must be accessable on the file system + URL url = resourceHandle.getUrl(); + if (!"file".equals(url.getProtocol())) { + return null; + } + + String path = new File(URI.create(url.toString())).getPath(); + return path; + } + + /** + * {@inheritDoc} + */ + protected Class findClass(final String className) throws ClassNotFoundException { + try { + return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws ClassNotFoundException { + // first think check if we are allowed to define the package + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + String packageName; + int packageEnd = className.lastIndexOf('.'); + if (packageEnd >= 0) { + packageName = className.substring(0, packageEnd); + securityManager.checkPackageDefinition(packageName); + } + } + + + // convert the class name to a file name + String resourceName = className.replace('.', '/') + ".class"; + + // find the class file resource + ResourceHandle resourceHandle = resourceFinder.getResource(resourceName); + if (resourceHandle == null) { + throw new ClassNotFoundException(className); + } + + byte[] bytes; + Manifest manifest; + try { + // get the bytes from the class file + bytes = resourceHandle.getBytes(); + + // get the manifest for defining the packages + manifest = resourceHandle.getManifest(); + } catch (IOException e) { + throw new ClassNotFoundException(className, e); + } + + // get the certificates for the code source + Certificate[] certificates = resourceHandle.getCertificates(); + + // the code source url is used to define the package and as the security context for the class + URL codeSourceUrl = resourceHandle.getCodeSourceUrl(); + + // define the package (required for security) + definePackage(className, codeSourceUrl, manifest); + + // this is the security context of the class + CodeSource codeSource = new CodeSource(codeSourceUrl, certificates); + + // load the class into the vm + Class clazz = defineClass(className, bytes, 0, bytes.length, codeSource); + return clazz; + } + }, acc); + } catch (PrivilegedActionException e) { + throw (ClassNotFoundException) e.getException(); + } + } + + private void definePackage(String className, URL jarUrl, Manifest manifest) { + int packageEnd = className.lastIndexOf('.'); + if (packageEnd < 0) { + return; + } + + String packageName = className.substring(0, packageEnd); + String packagePath = packageName.replace('.', '/') + "/"; + + Attributes packageAttributes = null; + Attributes mainAttributes = null; + if (manifest != null) { + packageAttributes = manifest.getAttributes(packagePath); + mainAttributes = manifest.getMainAttributes(); + } + Package pkg = getPackage(packageName); + if (pkg != null) { + if (pkg.isSealed()) { + if (!pkg.isSealed(jarUrl)) { + throw new SecurityException("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl); + } + } else { + if (isSealed(packageAttributes, mainAttributes)) { + throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl); + } + } + } else { + String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes); + String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes); + String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes); + String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes); + String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes); + String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes); + + URL sealBase = null; + if (isSealed(packageAttributes, mainAttributes)) { + sealBase = jarUrl; + } + + definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); + } + } + + private String getAttribute(Attributes.Name name, Attributes packageAttributes, Attributes mainAttributes) { + if (packageAttributes != null) { + String value = packageAttributes.getValue(name); + if (value != null) { + return value; + } + } + if (mainAttributes != null) { + return mainAttributes.getValue(name); + } + return null; + } + + private boolean isSealed(Attributes packageAttributes, Attributes mainAttributes) { + String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes); + if (sealed == null) { + return false; + } + return "true".equalsIgnoreCase(sealed); + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlConnection.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlConnection.java new file mode 100644 index 00000000..32914b6c --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlConnection.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.xbean.classloader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.security.Permission; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * @version $Rev$ $Date$ + */ +public class JarFileUrlConnection extends JarURLConnection { + public static final URL DUMMY_JAR_URL; + static { + try { + DUMMY_JAR_URL = new URL("jar", "", -1, "file:dummy!/", new URLStreamHandler() { + protected URLConnection openConnection(URL u) { + throw new UnsupportedOperationException(); + } + }); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + private final URL url; + private final JarFile jarFile; + private final JarEntry jarEntry; + private final URL jarFileUrl; + + public JarFileUrlConnection(URL url, JarFile jarFile, JarEntry jarEntry) throws MalformedURLException { + super(DUMMY_JAR_URL); + + if (url == null) throw new NullPointerException("url is null"); + if (jarFile == null) throw new NullPointerException("jarFile is null"); + if (jarEntry == null) throw new NullPointerException("jarEntry is null"); + + this.url = url; + this.jarFile = jarFile; + this.jarEntry = jarEntry; + jarFileUrl = new File(jarFile.getName()).toURI().toURL(); + } + + public JarFile getJarFile() throws IOException { + return jarFile; + } + + public synchronized void connect() { + } + + public URL getJarFileURL() { + return jarFileUrl; + } + + public String getEntryName() { + return getJarEntry().getName(); + } + + public Manifest getManifest() throws IOException { + return jarFile.getManifest(); + } + + public JarEntry getJarEntry() { + return jarEntry; + } + + public Attributes getAttributes() throws IOException { + return getJarEntry().getAttributes(); + } + + public Attributes getMainAttributes() throws IOException { + return getManifest().getMainAttributes(); + } + + public Certificate[] getCertificates() throws IOException { + return getJarEntry().getCertificates(); + } + + public URL getURL() { + return url; + } + + public int getContentLength() { + long size = getJarEntry().getSize(); + if (size > Integer.MAX_VALUE) { + return -1; + } + return (int) size; + } + + public long getLastModified() { + return getJarEntry().getTime(); + } + + public synchronized InputStream getInputStream() throws IOException { + return jarFile.getInputStream(jarEntry); + } + + public Permission getPermission() throws IOException { + URL jarFileUrl = new File(jarFile.getName()).toURI().toURL(); + return jarFileUrl.openConnection().getPermission(); + } + + public String toString() { + return JarFileUrlConnection.class.getName() + ":" + url; + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlStreamHandler.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlStreamHandler.java new file mode 100644 index 00000000..8fdeec6e --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileUrlStreamHandler.java @@ -0,0 +1,106 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.io.File; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @version $Rev$ $Date$ + */ +public class JarFileUrlStreamHandler extends URLStreamHandler { + public static URL createUrl(JarFile jarFile, JarEntry jarEntry) throws MalformedURLException { + return createUrl(jarFile, jarEntry, new File(jarFile.getName()).toURI().toURL()); + } + + public static URL createUrl(JarFile jarFile, JarEntry jarEntry, URL codeSource) throws MalformedURLException { + JarFileUrlStreamHandler handler = new JarFileUrlStreamHandler(jarFile, jarEntry); + URL url = new URL("jar", "", -1, codeSource + "!/" + jarEntry.getName(), handler); + handler.setExpectedUrl(url); + return url; + } + + private URL expectedUrl; + private final JarFile jarFile; + private final JarEntry jarEntry; + + public JarFileUrlStreamHandler(JarFile jarFile, JarEntry jarEntry) { + if (jarFile == null) throw new NullPointerException("jarFile is null"); + if (jarEntry == null) throw new NullPointerException("jarEntry is null"); + + this.jarFile = jarFile; + this.jarEntry = jarEntry; + } + + public void setExpectedUrl(URL expectedUrl) { + if (expectedUrl == null) throw new NullPointerException("expectedUrl is null"); + this.expectedUrl = expectedUrl; + } + + public URLConnection openConnection(URL url) throws IOException { + if (expectedUrl == null) throw new IllegalStateException("expectedUrl was not set"); + + // the caller copied the URL reusing a stream handler from a previous call + if (!expectedUrl.equals(url)) { + // the new url is supposed to be within our context, so it must have a jar protocol + if (!url.getProtocol().equals("jar")) { + throw new IllegalArgumentException("Unsupported protocol " + url.getProtocol()); + } + + // split the path at "!/" into the file part and entry part + String path = url.getPath(); + String[] chunks = path.split("!/", 2); + + // if we only got only one chunk, it didn't contain the required "!/" delimiter + if (chunks.length == 1) { + throw new MalformedURLException("Url does not contain a '!' character: " + url); + } + + String file = chunks[0]; + String entryPath = chunks[1]; + + // this handler only supports jars on the local file system + if (!file.startsWith("file:")) { + // let the system handler deal with this + return new URL(url.toExternalForm()).openConnection(); + } + file = file.substring("file:".length()); + + // again the new url is supposed to be within our context so it must reference the same jar file + if (!jarFile.getName().equals(file)) { + // let the system handler deal with this + return new URL(url.toExternalForm()).openConnection(); + } + + // get the entry + JarEntry newEntry = jarFile.getJarEntry(entryPath); + if (newEntry == null) { + throw new FileNotFoundException("Entry not found: " + url); + } + return new JarFileUrlConnection(url, jarFile, newEntry); + } + + return new JarFileUrlConnection(url, jarFile, jarEntry); + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceHandle.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceHandle.java new file mode 100644 index 00000000..79564750 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceHandle.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.xbean.classloader; + +import java.util.jar.JarFile; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.net.URL; +import java.net.MalformedURLException; +import java.io.InputStream; +import java.io.IOException; +import java.security.cert.Certificate; + +/** + * @version $Rev$ $Date$ + */ +public class JarResourceHandle extends AbstractResourceHandle { + private final JarFile jarFile; + private final JarEntry jarEntry; + private final URL url; + private final URL codeSource; + + public JarResourceHandle(JarFile jarFile, JarEntry jarEntry, URL codeSource) throws MalformedURLException { + this.jarFile = jarFile; + this.jarEntry = jarEntry; + this.url = JarFileUrlStreamHandler.createUrl(jarFile, jarEntry, codeSource); + this.codeSource = codeSource; + } + + public String getName() { + return jarEntry.getName(); + } + + public URL getUrl() { + return url; + } + + public URL getCodeSourceUrl() { + return codeSource; + } + + public boolean isDirectory() { + return jarEntry.isDirectory(); + } + + public InputStream getInputStream() throws IOException { + return jarFile.getInputStream(jarEntry); + } + + public int getContentLength() { + return (int) jarEntry.getSize(); + } + + public Manifest getManifest() throws IOException { + return jarFile.getManifest(); + } + + public Attributes getAttributes() throws IOException { + return jarEntry.getAttributes(); + } + + public Certificate[] getCertificates() { + return jarEntry.getCertificates(); + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceLocation.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceLocation.java new file mode 100644 index 00000000..75c7307d --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarResourceLocation.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.xbean.classloader; + +import java.util.jar.Manifest; +import java.util.jar.JarFile; +import java.util.jar.JarEntry; +import java.net.URL; +import java.net.MalformedURLException; +import java.io.IOException; + +/** + * @version $Rev$ $Date$ + */ +public class JarResourceLocation extends AbstractUrlResourceLocation { + private final JarFile jarFile; + + public JarResourceLocation(URL codeSource, JarFile jarFile) { + super(codeSource); + this.jarFile = jarFile; + } + + public ResourceHandle getResourceHandle(String resourceName) { + JarEntry jarEntry = jarFile.getJarEntry(resourceName); + if (jarEntry != null) { + try { + URL url = new URL(getCodeSource(), resourceName); + return new JarResourceHandle(jarFile, jarEntry, getCodeSource()); + } catch (MalformedURLException e) { + } + } + return null; + } + + public Manifest getManifest() throws IOException { + return jarFile.getManifest(); + } + + public void close() { + IoUtil.close(jarFile); + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java new file mode 100644 index 00000000..55b24e68 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java @@ -0,0 +1,367 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.net.URL; +import java.net.URLStreamHandlerFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class + * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced + * with a operation that checks each parent in order. This getParent method of this class will always return null, + * which may be interperated by the calling code to mean that this class loader is a direct child of the system class + * loader. + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class MultiParentClassLoader extends NamedClassLoader { + private final ClassLoader[] parents; + private final boolean inverseClassLoading; + private final String[] hiddenClasses; + private final String[] nonOverridableClasses; + private final String[] hiddenResources; + private final String[] nonOverridableResources; + private final Map> cache = new ConcurrentHashMap>(); + + /** + * Creates a named class loader with no parents. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + */ + public MultiParentClassLoader(String name, URL[] urls) { + this(name, urls, ClassLoader.getSystemClassLoader()); + } + + /** + * Creates a named class loader as a child of the specified parent. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + * @param parent the parent of this class loader + */ + public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent) { + this(name, urls, new ClassLoader[] {parent}); + } + + /** + * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory + * for accessing the urls.. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + * @param parent the parent of this class loader + * @param factory the URLStreamHandlerFactory used to access the urls + */ + public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { + this(name, urls, new ClassLoader[] {parent}, factory); + } + + /** + * Creates a named class loader as a child of the specified parents. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + * @param parents the parents of this class loader + */ + public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents) { + this(name, urls, parents, false, new String[0], new String[0]); + } + + public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { + this(name, urls, new ClassLoader[]{parent}, inverseClassLoading, hiddenClasses, nonOverridableClasses); + } + + /** + * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory + * for accessing the urls.. + * @param name the name of this class loader + * @param urls the urls from which this class loader will classes and resources + * @param parents the parents of this class loader + * @param factory the URLStreamHandlerFactory used to access the urls + */ + public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) { + super(name, urls, null, factory); + this.parents = copyParents(parents); + this.inverseClassLoading = false; + this.hiddenClasses = new String[0]; + this.nonOverridableClasses = new String[0]; + this.hiddenResources = new String[0]; + this.nonOverridableResources = new String[0]; + } + + public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) { + this(name, urls, parents, inverseClassLoading, (String[]) hiddenClasses.toArray(new String[hiddenClasses.size()]), (String[]) nonOverridableClasses.toArray(new String[nonOverridableClasses.size()])); + } + + public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { + super(name, urls); + this.parents = copyParents(parents); + this.inverseClassLoading = inverseClassLoading; + this.hiddenClasses = hiddenClasses; + this.nonOverridableClasses = nonOverridableClasses; + hiddenResources = toResources(hiddenClasses); + nonOverridableResources = toResources(nonOverridableClasses); + } + + private static String[] toResources(String[] classes) { + String[] resources = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + String className = classes[i]; + resources[i] = className.replace('.', '/'); + } + return resources; + } + + private static ClassLoader[] copyParents(ClassLoader[] parents) { + ClassLoader[] newParentsArray = new ClassLoader[parents.length]; + for (int i = 0; i < parents.length; i++) { + ClassLoader parent = parents[i]; + if (parent == null) { + throw new NullPointerException("parent[" + i + "] is null"); + } + newParentsArray[i] = parent; + } + return newParentsArray; + } + + /** + * Gets the parents of this class loader. + * @return the parents of this class loader + */ + public ClassLoader[] getParents() { + return parents; + } + + /** + * {@inheritDoc} + */ + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class result = null; + + // + // check if the class is already in the local cache + // + SoftReference reference = cache.get(name); + if (reference != null) { + result = reference.get(); + } + if (result == null) { + result = doLoadClass(name, resolve); + cache.put(name, new SoftReference(result)); + } + + return result; + } + + private synchronized Class doLoadClass(String name, boolean resolve) throws ClassNotFoundException { + // + // Check if class is in the loaded classes cache + // + Class cachedClass = findLoadedClass(name); + if (cachedClass != null) { + return resolveClass(cachedClass, resolve); + } + + // + // if we are using inverse class loading, check local urls first + // + if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) { + try { + Class clazz = findClass(name); + return resolveClass(clazz, resolve); + } catch (ClassNotFoundException ignored) { + } + } + + // + // Check parent class loaders + // + if (!isHiddenClass(name)) { + for (int i = 0; i < parents.length; i++) { + ClassLoader parent = parents[i]; + try { + Class clazz = parent.loadClass(name); + return resolveClass(clazz, resolve); + } catch (ClassNotFoundException ignored) { + // this parent didn't have the class; try the next one + } + } + } + + // + // if we are not using inverse class loading, check local urls now + // + // don't worry about excluding non-overridable classes here... we + // have alredy checked he parent and the parent didn't have the + // class, so we can override now + if (!isDestroyed()) { + try { + Class clazz = findClass(name); + return resolveClass(clazz, resolve); + } catch (ClassNotFoundException ignored) { + } + } + + throw new ClassNotFoundException(name + " in classloader " + getName()); + } + + private boolean isNonOverridableClass(String name) { + for (int i = 0; i < nonOverridableClasses.length; i++) { + if (name.startsWith(nonOverridableClasses[i])) { + return true; + } + } + return false; + } + + private boolean isHiddenClass(String name) { + for (int i = 0; i < hiddenClasses.length; i++) { + if (name.startsWith(hiddenClasses[i])) { + return true; + } + } + return false; + } + + private Class resolveClass(Class clazz, boolean resolve) { + if (resolve) { + resolveClass(clazz); + } + return clazz; + } + + /** + * {@inheritDoc} + */ + public URL getResource(String name) { + if (isDestroyed()) { + return null; + } + + // + // if we are using inverse class loading, check local urls first + // + if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) { + URL url = findResource(name); + if (url != null) { + return url; + } + } + + // + // Check parent class loaders + // + if (!isHiddenResource(name)) { + for (int i = 0; i < parents.length; i++) { + ClassLoader parent = parents[i]; + URL url = parent.getResource(name); + if (url != null) { + return url; + } + } + } + + // + // if we are not using inverse class loading, check local urls now + // + // don't worry about excluding non-overridable resources here... we + // have alredy checked he parent and the parent didn't have the + // resource, so we can override now + if (!isDestroyed()) { + // parents didn't have the resource; attempt to load it from my urls + return findResource(name); + } + + return null; + } + + /** + * {@inheritDoc} + */ + public Enumeration findResources(String name) throws IOException { + if (isDestroyed()) { + return Collections.enumeration(Collections.EMPTY_SET); + } + + List resources = new ArrayList(); + + // + // if we are using inverse class loading, add the resources from local urls first + // + if (inverseClassLoading && !isDestroyed()) { + List myResources = Collections.list(super.findResources(name)); + resources.addAll(myResources); + } + + // + // Add parent resources + // + for (int i = 0; i < parents.length; i++) { + ClassLoader parent = parents[i]; + List parentResources = Collections.list(parent.getResources(name)); + resources.addAll(parentResources); + } + + // + // if we are not using inverse class loading, add the resources from local urls now + // + if (!inverseClassLoading && !isDestroyed()) { + List myResources = Collections.list(super.findResources(name)); + resources.addAll(myResources); + } + + return Collections.enumeration(resources); + } + + private boolean isNonOverridableResource(String name) { + for (int i = 0; i < nonOverridableResources.length; i++) { + if (name.startsWith(nonOverridableResources[i])) { + return true; + } + } + return false; + } + + private boolean isHiddenResource(String name) { + for (int i = 0; i < hiddenResources.length; i++) { + if (name.startsWith(hiddenResources[i])) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public String toString() { + return "[" + getClass().getName() + ":" + + " name=" + getName() + + " urls=" + Arrays.asList(getURLs()) + + " parents=" + Arrays.asList(parents) + + "]"; + } +} diff --git a/server/src/java/org/xbean/server/classloader/NamedClassLoader.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java similarity index 67% rename from server/src/java/org/xbean/server/classloader/NamedClassLoader.java rename to xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java index 696d9de8..c7c34891 100644 --- a/server/src/java/org/xbean/server/classloader/NamedClassLoader.java +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java @@ -1,20 +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 * - * Copyright 2005 the original author or authors. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.xbean.server.classloader; +package org.apache.xbean.classloader; import java.net.URL; import java.net.URLClassLoader; @@ -32,6 +32,7 @@ */ public class NamedClassLoader extends URLClassLoader implements DestroyableClassLoader { private final String name; + private volatile boolean destroyed = false; /** * Creates a named class loader with no parents. @@ -67,10 +68,22 @@ public NamedClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHa this.name = name; } + /** + * Check if this classloader has been destroyed + * @return + */ + public boolean isDestroyed() { + return destroyed; + } + /** * {@inheritDoc} */ public void destroy() { + synchronized(this) { + if (destroyed) return; + destroyed = true; + } ClassLoaderUtil.destroy(this); } diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceEnumeration.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceEnumeration.java new file mode 100644 index 00000000..551e6875 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceEnumeration.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.xbean.classloader; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Collection; +import java.util.NoSuchElementException; + +/** + * @version $Rev$ $Date$ + */ +public class ResourceEnumeration implements Enumeration { + private Iterator iterator; + private final String resourceName; + private Object next; + + public ResourceEnumeration(Collection resourceLocations, String resourceName) { + this.iterator = resourceLocations.iterator(); + this.resourceName = resourceName; + } + + public boolean hasMoreElements() { + fetchNext(); + return (next != null); + } + + public Object nextElement() { + fetchNext(); + + // save next into a local variable and clear the next field + Object next = this.next; + this.next = null; + + // if we didn't have a next throw an exception + if (next == null) { + throw new NoSuchElementException(); + } + return next; + } + + private void fetchNext() { + if (iterator == null) { + return; + } + if (next != null) { + return; + } + + try { + while (iterator.hasNext()) { + ResourceLocation resourceLocation = (ResourceLocation) iterator.next(); + ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName); + if (resourceHandle != null) { + next = resourceHandle.getUrl(); + return; + } + } + // no more elements + // clear the iterator so it can be GCed + iterator = null; + } catch (IllegalStateException e) { + // Jar file was closed... this means the resource finder was destroyed + // clear the iterator so it can be GCed + iterator = null; + throw e; + } + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceFinder.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceFinder.java new file mode 100644 index 00000000..823ba6e2 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceFinder.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.xbean.classloader; + +import java.net.URL; +import java.util.Enumeration; + +/** + * Abstraction of resource searching policy. Given resource name, the resource + * finder performs implementation-specific lookup, and, if it is able to locate + * the resource, returns the {@link AbstractResourceHandle handle(s)} or URL(s) of it. + * + * @version $Rev$ $Date$ + */ +public interface ResourceFinder { + /** + * Find the resource by name and return URL of it if found. + * + * @param name the resource name + * @return resource URL or null if resource was not found + */ + public URL findResource(String name); + + /** + * Find all resources with given name and return enumeration of their URLs. + * + * @param name the resource name + * @return enumeration of resource URLs (possibly empty). + */ + public Enumeration findResources(String name); + + /** + * Get the resource by name and, if found, open connection to it and return + * the {@link AbstractResourceHandle handle} of it. + * + * @param name the resource name + * @return resource handle or null if resource was not found + */ + public ResourceHandle getResource(String name); + +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceHandle.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceHandle.java new file mode 100644 index 00000000..68f7c9ea --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceHandle.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.xbean.classloader; + +import java.net.URL; +import java.io.InputStream; +import java.io.IOException; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.security.cert.Certificate; + +/** + * This is a handle (a connection) to some resource, which may + * be a class, native library, text file, image, etc. Handles are returned + * by a ResourceFinder. A resource handle allows easy access to the resource data + * (using methods {@link #getInputStream} or {@link #getBytes}) as well as + * access resource metadata, such as attributes, certificates, etc. + *

    + * As soon as the handle is no longer in use, it should be explicitly + * {@link #close}d, similarly to I/O streams. + * + * @version $Rev$ $Date$ + */ +public interface ResourceHandle { + /** + * Return the name of the resource. The name is a "/"-separated path + * name that identifies the resource. + */ + String getName(); + + /** + * Returns the URL of the resource. + */ + URL getUrl(); + + /** + * Does this resource refer to a directory. Directory resources are commly used + * as the basis for a URL in client application. A directory resource has 0 bytes for it's content. + */ + boolean isDirectory(); + + /** + * Returns the CodeSource URL for the class or resource. + */ + URL getCodeSourceUrl(); + + /** + * Returns and InputStream for reading this resource data. + */ + InputStream getInputStream() throws IOException; + + /** + * Returns the length of this resource data, or -1 if unknown. + */ + int getContentLength(); + + /** + * Returns this resource data as an array of bytes. + */ + byte[] getBytes() throws IOException; + + /** + * Returns the Manifest of the JAR file from which this resource + * was loaded, or null if none. + */ + Manifest getManifest() throws IOException; + + /** + * Return the Certificates of the resource, or null if none. + */ + Certificate[] getCertificates(); + + /** + * Return the Attributes of the resource, or null if none. + */ + Attributes getAttributes() throws IOException; + + /** + * Closes a connection to the resource indentified by this handle. Releases + * any I/O objects associated with the handle. + */ + void close(); +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceLocation.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceLocation.java new file mode 100644 index 00000000..41572145 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ResourceLocation.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.xbean.classloader; + +import java.util.jar.Manifest; +import java.io.IOException; +import java.net.URL; + +/** + * This is a location which is searched by + * @version $Rev$ $Date$ + */ +public interface ResourceLocation { + URL getCodeSource(); + ResourceHandle getResourceHandle(String resourceName); + Manifest getManifest() throws IOException; + void close(); +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java new file mode 100644 index 00000000..0bb4dc35 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.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.xbean.classloader; + +import org.springframework.beans.factory.FactoryBean; + +/** + * A factory bean to expose the current thread context class loader. + * + * * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/classloader" + * element="threadContextClassLoader" description="References the ClassLoader of the current thread context" + * @version $Revision$ + */ +public class ThreadContextClassLoaderFactoryBean implements FactoryBean { + + public Object getObject() throws Exception { + return Thread.currentThread().getContextClassLoader(); + } + + public Class getObjectType() { + return ClassLoader.class; + } + + public boolean isSingleton() { + return true; + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/UnionEnumeration.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/UnionEnumeration.java new file mode 100644 index 00000000..30aadcc3 --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/UnionEnumeration.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.xbean.classloader; + +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * @version $Rev$ $Date$ + */ +public final class UnionEnumeration implements Enumeration { + private final LinkedList enumerations = new LinkedList(); + + public UnionEnumeration(List enumerations) { + this.enumerations.addAll(enumerations); + } + + public UnionEnumeration(Enumeration first, Enumeration second) { + if (first == null) throw new NullPointerException("first is null"); + if (second == null) throw new NullPointerException("second is null"); + + enumerations.add(first); + enumerations.add(second); + } + + public boolean hasMoreElements() { + while (!enumerations.isEmpty()) { + Enumeration enumeration = (Enumeration) enumerations.getFirst(); + if (enumeration.hasMoreElements()) { + return true; + } + enumerations.removeFirst(); + } + return false; + } + + public Object nextElement() { + while (!enumerations.isEmpty()) { + Enumeration enumeration = (Enumeration) enumerations.getFirst(); + if (enumeration.hasMoreElements()) { + return enumeration.nextElement(); + } + enumerations.removeFirst(); + } + throw new NoSuchElementException(); + } +} diff --git a/xbean-classloader/src/main/java/org/apache/xbean/classloader/UrlResourceFinder.java b/xbean-classloader/src/main/java/org/apache/xbean/classloader/UrlResourceFinder.java new file mode 100644 index 00000000..2659289e --- /dev/null +++ b/xbean-classloader/src/main/java/org/apache/xbean/classloader/UrlResourceFinder.java @@ -0,0 +1,302 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * @version $Rev$ $Date$ + */ +public class UrlResourceFinder implements ResourceFinder { + private final Object lock = new Object(); + + private final LinkedHashSet urls = new LinkedHashSet(); + private final LinkedHashMap classPath = new LinkedHashMap(); + private final LinkedHashSet watchedFiles = new LinkedHashSet(); + + private boolean destroyed = false; + + public UrlResourceFinder() { + } + + public UrlResourceFinder(URL[] urls) { + addUrls(urls); + } + + public void destroy() { + synchronized (lock) { + if (destroyed) { + return; + } + destroyed = true; + urls.clear(); + for (Iterator iterator = classPath.values().iterator(); iterator.hasNext();) { + ResourceLocation resourceLocation = (ResourceLocation) iterator.next(); + resourceLocation.close(); + } + classPath.clear(); + } + } + + public ResourceHandle getResource(String resourceName) { + synchronized (lock) { + if (destroyed) { + return null; + } + for (Iterator iterator = getClassPath().entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + ResourceLocation resourceLocation = (ResourceLocation) entry.getValue(); + ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName); + if (resourceHandle != null && !resourceHandle.isDirectory()) { + return resourceHandle; + } + } + } + return null; + } + + public URL findResource(String resourceName) { + synchronized (lock) { + if (destroyed) { + return null; + } + for (Iterator iterator = getClassPath().entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + ResourceLocation resourceLocation = (ResourceLocation) entry.getValue(); + ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName); + if (resourceHandle != null) { + return resourceHandle.getUrl(); + } + } + } + return null; + } + + public Enumeration findResources(String resourceName) { + synchronized (lock) { + return new ResourceEnumeration(new ArrayList(getClassPath().values()), resourceName); + } + } + + public void addUrl(URL url) { + addUrls(Collections.singletonList(url)); + } + + public URL[] getUrls() { + synchronized (lock) { + return (URL[]) urls.toArray(new URL[urls.size()]); + } + } + + /** + * Adds an array of urls to the end of this class loader. + * @param urls the URLs to add + */ + protected void addUrls(URL[] urls) { + addUrls(Arrays.asList(urls)); + } + + /** + * Adds a list of urls to the end of this class loader. + * @param urls the URLs to add + */ + protected void addUrls(List urls) { + synchronized (lock) { + if (destroyed) { + throw new IllegalStateException("UrlResourceFinder has been destroyed"); + } + + boolean shouldRebuild = this.urls.addAll(urls); + if (shouldRebuild) { + rebuildClassPath(); + } + } + } + + private LinkedHashMap getClassPath() { + assert Thread.holdsLock(lock): "This method can only be called while holding the lock"; + + for (Iterator iterator = watchedFiles.iterator(); iterator.hasNext();) { + File file = (File) iterator.next(); + if (file.canRead()) { + rebuildClassPath(); + break; + } + } + + return classPath; + } + + /** + * Rebuilds the entire class path. This class is called when new URLs are added or one of the watched files + * becomes readable. This method will not open jar files again, but will add any new entries not alredy open + * to the class path. If any file based url is does not exist, we will watch for that file to appear. + */ + private void rebuildClassPath() { + assert Thread.holdsLock(lock): "This method can only be called while holding the lock"; + + // copy all of the existing locations into a temp map and clear the class path + Map existingJarFiles = new LinkedHashMap(classPath); + classPath.clear(); + + LinkedList locationStack = new LinkedList(urls); + try { + while (!locationStack.isEmpty()) { + URL url = (URL) locationStack.removeFirst(); + + // Skip any duplicate urls in the claspath + if (classPath.containsKey(url)) { + continue; + } + + // Check is this URL has already been opened + ResourceLocation resourceLocation = (ResourceLocation) existingJarFiles.remove(url); + + // If not opened, cache the url and wrap it with a resource location + if (resourceLocation == null) { + try { + File file = cacheUrl(url); + resourceLocation = createResourceLocation(url, file); + } catch (FileNotFoundException e) { + // if this is a file URL, the file doesn't exist yet... watch to see if it appears later + if ("file".equals(url.getProtocol())) { + File file = new File(url.getPath()); + watchedFiles.add(file); + continue; + + } + } catch (IOException ignored) { + // can't seem to open the file... this is most likely a bad jar file + // so don't keep a watch out for it because that would require lots of checking + // Dain: We may want to review this decision later + continue; + } + } + + // add the jar to our class path + classPath.put(resourceLocation.getCodeSource(), resourceLocation); + + // push the manifest classpath on the stack (make sure to maintain the order) + List manifestClassPath = getManifestClassPath(resourceLocation); + locationStack.addAll(0, manifestClassPath); + } + } catch (Error e) { + destroy(); + throw e; + } + + for (Iterator iterator = existingJarFiles.values().iterator(); iterator.hasNext();) { + ResourceLocation resourceLocation = (ResourceLocation) iterator.next(); + resourceLocation.close(); + } + } + + protected File cacheUrl(URL url) throws IOException { + if (!"file".equals(url.getProtocol())) { + // download the jar + throw new Error("Only local file jars are supported " + url); + } + + File file; + try { + file = new File(url.toURI()); + } catch (URISyntaxException e) { + file = new File(url.getPath()); + } + if (!file.exists()) { + throw new FileNotFoundException(file.getAbsolutePath()); + } + if (!file.canRead()) { + throw new IOException("File is not readable: " + file.getAbsolutePath()); + } + return file; + } + + protected ResourceLocation createResourceLocation(URL codeSource, File cacheFile) throws IOException { + if (!cacheFile.exists()) { + throw new FileNotFoundException(cacheFile.getAbsolutePath()); + } + if (!cacheFile.canRead()) { + throw new IOException("File is not readable: " + cacheFile.getAbsolutePath()); + } + + ResourceLocation resourceLocation = null; + if (cacheFile.isDirectory()) { + // DirectoryResourceLocation will only return "file" URLs within this directory + // do not user the DirectoryResourceLocation for non file based urls + resourceLocation = new DirectoryResourceLocation(cacheFile); + } else { + resourceLocation = new JarResourceLocation(codeSource, new JarFile(cacheFile)); + } + return resourceLocation; + } + + private List getManifestClassPath(ResourceLocation resourceLocation) { + try { + // get the manifest, if possible + Manifest manifest = resourceLocation.getManifest(); + if (manifest == null) { + // some locations don't have a manifest + return Collections.EMPTY_LIST; + } + + // get the class-path attribute, if possible + String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH); + if (manifestClassPath == null) { + return Collections.EMPTY_LIST; + } + + // build the urls... + // the class-path attribute is space delimited + URL codeSource = resourceLocation.getCodeSource(); + LinkedList classPathUrls = new LinkedList(); + for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) { + String entry = tokenizer.nextToken(); + try { + // the class path entry is relative to the resource location code source + URL entryUrl = new URL(codeSource, entry); + classPathUrls.addLast(entryUrl); + } catch (MalformedURLException ignored) { + // most likely a poorly named entry + } + } + return classPathUrls; + } catch (IOException ignored) { + // error opening the manifest + return Collections.EMPTY_LIST; + } + } +} diff --git a/xbean-classloader/src/site/site.xml b/xbean-classloader/src/site/site.xml new file mode 100644 index 00000000..e00978f7 --- /dev/null +++ b/xbean-classloader/src/site/site.xml @@ -0,0 +1,37 @@ + + + + + + + + + + ${parentProject} + + ${modules} + + ${reports} + + + + + + diff --git a/xbean-classloader/src/test-data/resourceFinderTest/jar1/resource b/xbean-classloader/src/test-data/resourceFinderTest/jar1/resource new file mode 100644 index 00000000..76d44d91 --- /dev/null +++ b/xbean-classloader/src/test-data/resourceFinderTest/jar1/resource @@ -0,0 +1 @@ +resource1 \ No newline at end of file diff --git a/xbean-classloader/src/test-data/resourceFinderTest/jar2/resource b/xbean-classloader/src/test-data/resourceFinderTest/jar2/resource new file mode 100644 index 00000000..61d09517 --- /dev/null +++ b/xbean-classloader/src/test-data/resourceFinderTest/jar2/resource @@ -0,0 +1 @@ +resource2 \ No newline at end of file diff --git a/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java b/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java new file mode 100644 index 00000000..85d04e2e --- /dev/null +++ b/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.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.xbean.classloader; + +import java.net.URL; + +/** + * Test the JarFileClassLoader. + * + * @author Dain Sundstrom + * @version $Id$ + * @since 2.0 + */ +public class JarFileClassLoaderTest extends MultiParentClassLoaderTest { + protected MultiParentClassLoader createClassLoader(String name, URL[] urls, ClassLoader[] parents) { + return new JarFileClassLoader(name, urls, parents); + } +} diff --git a/server/src/test/org/xbean/server/classloader/MultiParentClassLoaderTest.java b/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java similarity index 93% rename from server/src/test/org/xbean/server/classloader/MultiParentClassLoaderTest.java rename to xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java index 3d5bb897..876a6e44 100644 --- a/server/src/test/org/xbean/server/classloader/MultiParentClassLoaderTest.java +++ b/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java @@ -1,20 +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 * - * Copyright 2005 the original author or authors. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.xbean.server.classloader; +package org.apache.xbean.classloader; import java.io.File; import java.io.FileOutputStream; @@ -220,7 +220,7 @@ public void testGetResources() throws Exception { } /** - * Test getResources returns an empty enumeration when attempt is made to loade a non-existant resource. + * Test getResources returns an empty enumeration when attempt is made to loade a non-existant resource. * @throws Exception if a problem occurs */ public void testGetNonExistantResources() throws Exception { @@ -342,7 +342,6 @@ protected void tearDown() throws Exception { super.tearDown(); for (int i = 0; i < files.length; i++) { files[i].delete(); - assertFileNotExists(files[i]); } } diff --git a/xbean-classloader/src/test/java/org/apache/xbean/classloader/UrlResourceFinderTest.java b/xbean-classloader/src/test/java/org/apache/xbean/classloader/UrlResourceFinderTest.java new file mode 100644 index 00000000..131a80b9 --- /dev/null +++ b/xbean-classloader/src/test/java/org/apache/xbean/classloader/UrlResourceFinderTest.java @@ -0,0 +1,487 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.classloader; + +import java.net.URL; +import java.net.MalformedURLException; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.io.InputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; + +import junit.framework.TestCase; + +/** + * @version $Rev$ $Date$ + */ +public class UrlResourceFinderTest extends TestCase { + private File basedir = new File(System.getProperty("basedir")); + private File jarFile; + private Manifest manifest; + private Attributes resourceAttributes; + private File alternateJarFile; + private File testResource; + + /** + * There are 2 "jars" with a "resource" inside. Make sure the enumeration has exactly 2 elements and + * that hasMoreElements() doesn't advance the iterator. + * + * @throws Exception + */ + public void testResourceEnumeration() throws Exception { + URL jar1 = new File(basedir, "src/test-data/resourceFinderTest/jar1/").toURL(); + URL jar2 = new File(basedir, "src/test-data/resourceFinderTest/jar2/").toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar1, jar2}); + + Enumeration enumeration = resourceFinder.findResources("resource"); + + // resource1 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource1 = (URL) enumeration.nextElement(); + assertNotNull(resource1); + assertEquals("resource1", toString(resource1.openStream())); + + // resource2 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource2 = (URL) enumeration.nextElement(); + assertNotNull(resource2); + assertEquals("resource2", toString(resource2.openStream())); + assertFalse(enumeration.hasMoreElements()); + } + + public void testDirectoryResource() throws Exception { + URL jar = new File(basedir, "src/test-data/resourceFinderTest/jar1/").toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + + ResourceHandle resource = resourceFinder.getResource("resource"); + assertNotNull(resource); + + // handle.getBytes() + assertEquals("resource1", new String(resource.getBytes())); + + // handle.getInputStream() + assertEquals("resource1", toString(resource.getInputStream())); + + // handle.getUrl() + URL url = resource.getUrl(); + assertEquals("resource1", toString(url.openStream())); + + // copy the url and verify we can still get the data + URL copyUrl = new URL(url.toExternalForm()); + assertEquals("resource1", toString(copyUrl.openStream())); + + // resourceFinder.findResource + URL directUrl = resourceFinder.findResource("resource"); + assertEquals("resource1", toString(directUrl.openStream())); + assertEquals("resource1", toString(new URL(directUrl.toExternalForm()).openStream())); + + // handle.getContentLength() + assertEquals("resource1".length(), resource.getContentLength()); + + // handle.getName() + assertEquals("resource", resource.getName()); + + // handle.getAttributes() + assertNull(resource.getAttributes()); + + // handle.getManifest() + assertNull(resource.getManifest()); + } + + public void testJarResource() throws Exception { + URL jar = jarFile.toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + + ResourceHandle resource = resourceFinder.getResource("resource"); + assertNotNull(resource); + + // handle.getBytes() + assertEquals("resource3", new String(resource.getBytes())); + + // handle.getInputStream() + assertEquals("resource3", toString(resource.getInputStream())); + + // handle.getUrl() + URL url = resource.getUrl(); + assertEquals("resource3", toString(url.openStream())); + + // copy the url and verify we can still get the data + URL copyUrl = new URL(url.toExternalForm()); + assertEquals("resource3", toString(copyUrl.openStream())); + + // resourceFinder.findResource + URL directUrl = resourceFinder.findResource("resource"); + assertEquals("resource3", toString(directUrl.openStream())); + assertEquals("resource3", toString(new URL(directUrl.toExternalForm()).openStream())); + + // handle.getContentLength() + assertEquals("resource3".length(), resource.getContentLength()); + + // handle.getName() + assertEquals("resource", resource.getName()); + + // handle.getAttributes() + assertEquals(resourceAttributes, resource.getAttributes()); + + // handle.getManifest() + assertEquals(manifest, resource.getManifest()); + } + + public void testAddURL() throws Exception { + URL jar1 = new File(basedir, "src/test-data/resourceFinderTest/jar1/").toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar1}); + + Enumeration enumeration = resourceFinder.findResources("resource"); + + // resource1 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource1 = (URL) enumeration.nextElement(); + assertNotNull(resource1); + assertEquals("resource1", toString(resource1.openStream())); + assertFalse(enumeration.hasMoreElements()); + + // addUrl + URL jar2 = new File(basedir, "src/test-data/resourceFinderTest/jar2/").toURL(); + resourceFinder.addUrl(jar2); + + // getResource should find the first jar only + ResourceHandle resource = resourceFinder.getResource("resource"); + assertNotNull(resource); + assertEquals("resource1", new String(resource.getBytes())); + + // findResource should find the first jar only + resource1 = resourceFinder.findResource("resource"); + assertEquals("resource1", toString(resource1.openStream())); + + // findResouces should see both jars + enumeration = resourceFinder.findResources("resource"); + + // resource1 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + resource1 = (URL) enumeration.nextElement(); + assertNotNull(resource1); + assertEquals("resource1", toString(resource1.openStream())); + assertTrue(enumeration.hasMoreElements()); + + // resource2 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource2 = (URL) enumeration.nextElement(); + assertNotNull(resource2); + assertEquals("resource2", toString(resource2.openStream())); + assertFalse(enumeration.hasMoreElements()); + } + + public void testConcurrentAddURL() throws Exception { + URL jar1 = new File(basedir, "src/test-data/resourceFinderTest/jar1/").toURL(); + URL jar2 = new File(basedir, "src/test-data/resourceFinderTest/jar2/").toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar1, jar2}); + + Enumeration enumeration = resourceFinder.findResources("resource"); + + // resource1 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource1 = (URL) enumeration.nextElement(); + assertNotNull(resource1); + assertEquals("resource1", toString(resource1.openStream())); + assertTrue(enumeration.hasMoreElements()); + + // + // addURL + // + URL newJar = jarFile.toURL(); + resourceFinder.addUrl(newJar); + + // new resources should be available + // getResource should find the first jar only + ResourceHandle jar3Resouce = resourceFinder.getResource("jar3"); + assertNotNull(jar3Resouce); + assertEquals("jar3", new String(jar3Resouce.getBytes())); + + // findResource should find the first jar only + URL jar3Url = resourceFinder.findResource("jar3"); + assertEquals("jar3", toString(jar3Url.openStream())); + + // + // enumeration from above should still be valid, but only see the resources available at the time it was created + // + + // resource2 + assertTrue(enumeration.hasMoreElements()); + assertTrue(enumeration.hasMoreElements()); + URL resource2 = (URL) enumeration.nextElement(); + assertNotNull(resource2); + assertEquals("resource2", toString(resource2.openStream())); + assertFalse(enumeration.hasMoreElements()); + } + + public void testDirectoryDestroy() throws Exception { + URL jar = new File(basedir, "src/test-data/resourceFinderTest/jar1/").toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + assertDestroyed(resourceFinder, "resource1", null); + } + + public void testJarDestroy() throws Exception { + URL jar = jarFile.toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + assertDestroyed(resourceFinder, "resource3", manifest); + } + + public void testUrlCopy() throws Exception { + URL jar = jarFile.toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + + // get the resource + URL resource = resourceFinder.findResource("resource"); + assertNotNull(resource); + assertEquals("resource3", toString(resource.openStream())); + + // copy resource with string + URL stringCopy = new URL(resource.toExternalForm()); + assertEquals("resource3", toString(stringCopy.openStream())); + + // copy resource perserving the url handler + URL handlerCopy = new URL(resource, resource.toExternalForm()); + assertEquals("resource3", toString(handlerCopy.openStream())); + + // access the other resource using the original url as a starting point + URL other = new URL(resource, "jar3"); + assertEquals("jar3", toString(other.openStream())); + } + + public void testUrlAccess() throws Exception { + URL jar = jarFile.toURL(); + UrlResourceFinder resourceFinder = new UrlResourceFinder(new URL[]{jar}); + + // get geronimo url from the resource finder + URL geronimoUrl = resourceFinder.findResource("resource"); + assertNotNull(geronimoUrl); + assertEquals("resource3", toString(geronimoUrl.openStream())); + + // get a system url by copying the url by string + URL systemUrl = new URL(geronimoUrl.toExternalForm()); + assertEquals("resource3", toString(systemUrl.openStream())); + + // verify both can see the jar3 file withing the jar file + assertEquals("jar3", toString(new URL(systemUrl, "jar3").openStream())); + assertEquals("jar3", toString(new URL(geronimoUrl, "jar3").openStream())); + + // verify both can see the jar3 file withing the jar file using a full url spec + String mainEntry = "jar:" + jarFile.toURL().toExternalForm() + "!/jar3"; + assertEquals("jar3", toString(new URL(systemUrl, mainEntry).openStream())); + assertEquals("jar3", toString(new URL(geronimoUrl, mainEntry).openStream())); + + // verify both throw a FileNotFoundExcetion for an unknown file + try { + new URL(systemUrl, "unknown").openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + try { + new URL(geronimoUrl, "unknown").openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + + // verify both can see the alternate jar + String alternateEntry = "jar:" + alternateJarFile.toURL().toExternalForm() + "!/jar4"; + assertEquals("jar4", toString(new URL(systemUrl, alternateEntry).openStream())); + assertEquals("jar4", toString(new URL(geronimoUrl, alternateEntry).openStream())); + + // verify both throw a FileNotFoundExcetion for an unknown entry in the alternate file + String alternateUnknownEntry = "jar:" + alternateJarFile.toURL().toExternalForm() + "!/unknown"; + try { + new URL(systemUrl, alternateUnknownEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + try { + new URL(geronimoUrl, alternateUnknownEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + + // verify both work an excepton for a non-jar entry + assertEquals("testResource", toString(new URL(systemUrl, testResource.toURL().toExternalForm()).openStream())); + assertEquals("testResource", toString(new URL(geronimoUrl, testResource.toURL().toExternalForm()).openStream())); + + // verify both fail for a spec without a !/ + String badEntry = "jar:" + alternateJarFile.toURL().toExternalForm(); + try { + new URL(systemUrl, badEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (MalformedURLException expected) { + } + try { + new URL(geronimoUrl, badEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (MalformedURLException expected) { + } + + // verify both throw FileNotFoundException for a nested jar file + badEntry = "jar:" + alternateJarFile.toURL().toExternalForm() + "!/foo.jar!/bar"; + try { + new URL(systemUrl, badEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + try { + new URL(geronimoUrl, badEntry).openStream(); + fail("Expected a FileNotFoundException"); + } catch (FileNotFoundException expected) { + } + } + + public void assertDestroyed(UrlResourceFinder resourceFinder, String resourceValue, Manifest expectedManifest) throws Exception { + ResourceHandle resource = resourceFinder.getResource("resource"); + assertNotNull(resource); + assertEquals(resourceValue, new String(resource.getBytes())); + + // handle.getUrl() + URL url = resource.getUrl(); + assertEquals(resourceValue, toString(url.openStream())); + + // copy the url and verify we can still get the data + URL copyUrl = new URL(url.toExternalForm()); + assertEquals(resourceValue, toString(copyUrl.openStream())); + + // resourceFinder.findResource + URL directUrl = resourceFinder.findResource("resource"); + assertEquals(resourceValue, toString(directUrl.openStream())); + URL directUrlCopy = new URL(directUrl.toExternalForm()); + assertEquals(resourceValue, toString(directUrlCopy.openStream())); + + // destroy + resourceFinder.destroy(); + + // getResource always returns null + assertNull(resourceFinder.getResource("resource")); + + // findResource always returns null + assertNull(resourceFinder.findResource("resource")); + + // findResources always returns an empty enumeration + assertFalse(resourceFinder.findResources("resource").hasMoreElements()); + + // existing url may not work + try { + assertEquals(resourceValue, toString(url.openStream())); + } catch (IllegalStateException expected) { + } catch (IOException expected) { + } + try { + assertEquals(resourceValue, toString(directUrl.openStream())); + } catch (IllegalStateException expected) { + } catch (IOException expected) { + } + + // the copied urls will work since they are proviced by the vm + assertEquals(resourceValue, toString(copyUrl.openStream())); + assertEquals(resourceValue, toString(directUrlCopy.openStream())); + + // existing resource handle may not work since the location was closed + assertEquals("resource", resource.getName()); + try { + if (expectedManifest != null) { + assertEquals(expectedManifest.getAttributes("resource"), resource.getAttributes()); + } + } catch (IllegalStateException expected) { + } + try { + assertEquals(expectedManifest, resource.getManifest()); + } catch (IllegalStateException expected) { + } + try { + assertEquals(resourceValue, toString(resource.getUrl().openStream())); + } catch (IllegalStateException expected) { + } + try { + assertEquals(resourceValue, toString(resource.getInputStream())); + } catch (IllegalStateException expected) { + } catch (IOException expected) { + } + try { + assertEquals(resourceValue, new String(resource.getBytes())); + } catch (IllegalStateException expected) { + } catch (IOException expected) { + } + } + + protected void setUp() throws Exception { + super.setUp(); + + // + // Build a simple Jar file to test with + // + manifest = new Manifest(); + Attributes mainAttributes = manifest.getMainAttributes(); + mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + mainAttributes.putValue("food", "nacho"); + resourceAttributes = new Attributes(); + resourceAttributes.putValue("drink", "margarita"); + manifest.getEntries().put("resource", resourceAttributes); + + File targetDir = new File(basedir, "target"); + jarFile = new File(targetDir, "resourceFinderTest.jar"); + JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile), manifest); + jarOutputStream.putNextEntry(new ZipEntry("resource")); + jarOutputStream.write("resource3".getBytes()); + jarOutputStream.putNextEntry(new ZipEntry("jar3")); + jarOutputStream.write("jar3".getBytes()); + IoUtil.close(jarOutputStream); + + alternateJarFile = new File(targetDir, "alternate.jar"); + System.out.println(alternateJarFile.getAbsolutePath()); + jarOutputStream = new JarOutputStream(new FileOutputStream(alternateJarFile), manifest); + jarOutputStream.putNextEntry(new ZipEntry("resource")); + jarOutputStream.write("resource4".getBytes()); + jarOutputStream.putNextEntry(new ZipEntry("jar4")); + jarOutputStream.write("jar4".getBytes()); + IoUtil.close(jarOutputStream); + + testResource = new File(targetDir, "testResource"); + FileOutputStream fileOutputStream = new FileOutputStream(testResource); + fileOutputStream.write("testResource".getBytes()); + IoUtil.close(fileOutputStream); + } + + protected void tearDown() throws Exception { + jarFile.delete(); + super.tearDown(); + } + + private static String toString(InputStream in) throws IOException { + try { + byte[] bytes = IoUtil.getBytes(in); + String string = new String(bytes); + return string; + } finally { + IoUtil.close(in); + } + } +} diff --git a/xbean-classpath/pom.xml b/xbean-classpath/pom.xml new file mode 100644 index 00000000..7495cb81 --- /dev/null +++ b/xbean-classpath/pom.xml @@ -0,0 +1,36 @@ + + + + + + + 4.0.0 + + + xbean + org.apache.xbean + 3.12 + + + xbean-classpath + bundle + Apache XBean :: ClassPath + + diff --git a/classpath/src/java/org/xbean/classpath/ClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPath.java similarity index 51% rename from classpath/src/java/org/xbean/classpath/ClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPath.java index f359bcbe..38771f11 100644 --- a/classpath/src/java/org/xbean/classpath/ClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; import java.io.File; import java.net.URL; diff --git a/classpath/src/java/org/xbean/classpath/ClassPathFactory.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPathFactory.java similarity index 70% rename from classpath/src/java/org/xbean/classpath/ClassPathFactory.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPathFactory.java index c18a0d9f..459fd3c8 100644 --- a/classpath/src/java/org/xbean/classpath/ClassPathFactory.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ClassPathFactory.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; public class ClassPathFactory { public static ClassPath createClassPath(String name){ diff --git a/classpath/src/java/org/xbean/classpath/ContextClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ContextClassPath.java similarity index 70% rename from classpath/src/java/org/xbean/classpath/ContextClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/ContextClassPath.java index add08884..81cc22eb 100644 --- a/classpath/src/java/org/xbean/classpath/ContextClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/ContextClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; import java.io.File; import java.net.URLClassLoader; diff --git a/classpath/src/java/org/xbean/classpath/SunURLClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/SunURLClassPath.java similarity index 84% rename from classpath/src/java/org/xbean/classpath/SunURLClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/SunURLClassPath.java index 06c68179..48a08311 100644 --- a/classpath/src/java/org/xbean/classpath/SunURLClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/SunURLClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; import java.io.File; import java.net.URL; diff --git a/classpath/src/java/org/xbean/classpath/SystemClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/SystemClassPath.java similarity index 81% rename from classpath/src/java/org/xbean/classpath/SystemClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/SystemClassPath.java index a23129f8..e8ba28b5 100644 --- a/classpath/src/java/org/xbean/classpath/SystemClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/SystemClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; import java.net.URLClassLoader; import java.net.URL; diff --git a/classpath/src/java/org/xbean/classpath/TomcatClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatClassPath.java similarity index 91% rename from classpath/src/java/org/xbean/classpath/TomcatClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatClassPath.java index 8265bd50..f525bfd3 100644 --- a/classpath/src/java/org/xbean/classpath/TomcatClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; import java.io.File; import java.lang.reflect.Method; diff --git a/classpath/src/java/org/xbean/classpath/TomcatWebAppClassPath.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatWebAppClassPath.java similarity index 54% rename from classpath/src/java/org/xbean/classpath/TomcatWebAppClassPath.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatWebAppClassPath.java index f67304dc..32c66e84 100644 --- a/classpath/src/java/org/xbean/classpath/TomcatWebAppClassPath.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/TomcatWebAppClassPath.java @@ -1,12 +1,12 @@ /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright 2005 David Blevins - * - * 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.xbean.classpath; +package org.apache.xbean.classpath; public class TomcatWebAppClassPath extends TomcatClassPath { diff --git a/classpath/src/java/org/xbean/classpath/maven/MavenResolver.java b/xbean-classpath/src/main/java/org/apache/xbean/classpath/maven/MavenResolver.java similarity index 64% rename from classpath/src/java/org/xbean/classpath/maven/MavenResolver.java rename to xbean-classpath/src/main/java/org/apache/xbean/classpath/maven/MavenResolver.java index bf47e9c0..579b8679 100644 --- a/classpath/src/java/org/xbean/classpath/maven/MavenResolver.java +++ b/xbean-classpath/src/main/java/org/apache/xbean/classpath/maven/MavenResolver.java @@ -1,20 +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 * - * Copyright 2005 the original author or authors. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.xbean.classpath.maven; +package org.apache.xbean.classpath.maven; import java.net.URL; diff --git a/xbean-classpath/src/site/site.xml b/xbean-classpath/src/site/site.xml new file mode 100644 index 00000000..e00978f7 --- /dev/null +++ b/xbean-classpath/src/site/site.xml @@ -0,0 +1,37 @@ + + + + + + + + + + ${parentProject} + + ${modules} + + ${reports} + + + + + + diff --git a/xbean-classpath/src/test/java/org/apache/xbean/classpath/SystemClassPathTest.java b/xbean-classpath/src/test/java/org/apache/xbean/classpath/SystemClassPathTest.java new file mode 100644 index 00000000..cf3f7976 --- /dev/null +++ b/xbean-classpath/src/test/java/org/apache/xbean/classpath/SystemClassPathTest.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.xbean.classpath; + +/** + * @version $Revision$ $Date$ + */ + +import junit.framework.TestCase; + +public class SystemClassPathTest extends TestCase { + + public void testAddJarToPath() throws Exception { + SystemClassPath systemClassPath = new SystemClassPath(); + + ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + + try { + systemClassLoader.loadClass("groovy.lang.GroovyShell"); + fail("Class already exists"); + } catch (ClassNotFoundException e) { + // this should fail + } + + +// URL groovyJar = new URL("http://www.ibiblio.org/maven/groovy/jars/groovy-SNAPSHOT.jar"); +// systemClassPath.addJarToPath(groovyJar); +// +// try { +// systemClassLoader.loadClass("groovy.lang.GroovyShell"); +// } catch (ClassNotFoundException e) { +// // this should fail pass +// fail("Class already exists"); +// } + } +} diff --git a/xbean-finder-shaded/pom.xml b/xbean-finder-shaded/pom.xml new file mode 100755 index 00000000..ea6154f2 --- /dev/null +++ b/xbean-finder-shaded/pom.xml @@ -0,0 +1,125 @@ + + + + + + + 4.0.0 + + xbean + org.apache.xbean + 3.12 + + xbean-finder-shaded + jar + Apache XBean :: Finder shaded (repackaged) + + + org.apache.xbean.finder;version=${project.version} + org.apache.xbean.finder;version=${project.version},org.apache.xbean.asm;version=3.1,org.apache.xbean.asm.commons;version=3.1 + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + true + + + org.objectweb.asm + org.apache.xbean.asm + + + + + org.apache.xbean:xbean-finder + + + *:*:sources + org.apache.xbean:xbean-asm-shaded + junit:junit + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + + true + + !org.objectweb.asm.*,!org.apache.xbean.finder.*,org.apache.xbean.asm,*;resolution:=optional + + <_nouses>true + + + + + bundle-manifest + package + + bundle + + + + + + + + + org.apache.xbean + xbean-finder + ${project.version} + + + org.apache.xbean + xbean-asm-shaded + ${project.version} + + + diff --git a/xbean-finder/pom.xml b/xbean-finder/pom.xml new file mode 100644 index 00000000..d8fde53b --- /dev/null +++ b/xbean-finder/pom.xml @@ -0,0 +1,82 @@ + + + + + + + 4.0.0 + + xbean + org.apache.xbean + 3.12 + + xbean-finder + bundle + Apache XBean :: Classpath Resource Finder + XBean Finder helps to find annotations in classes + + + org.apache.xbean + xbean-bundleutils + + + org.slf4j + slf4j-api + + + asm + asm + 3.2 + provided + + + asm + asm-commons + 3.2 + provided + + + org.osgi + org.osgi.core + 4.2.0 + provided + + + + + + org.apache.felix + maven-bundle-plugin + 2.0.0 + true + + + org.apache.xbean.finder.*;version=${project.version} + + org.objectweb.asm;version=3.1, + org.objectweb.asm.commons;version=3.1, + * + + + + + + + diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/AbstractFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/AbstractFinder.java new file mode 100644 index 00000000..7154fd43 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/AbstractFinder.java @@ -0,0 +1,1139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.finder; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.xbean.finder.util.SingleLinkedList; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.EmptyVisitor; +import org.objectweb.asm.signature.SignatureVisitor; + +/** + * @version $Rev$ $Date$ + */ +public abstract class AbstractFinder implements IAnnotationFinder { + private final Map> annotated = new HashMap>(); + protected final Map classInfos = new HashMap(); + protected final Map originalInfos = new HashMap(); + private final List classesNotLoaded = new ArrayList(); + private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES; + + protected abstract URL getResource(String className); + + protected abstract Class loadClass(String fixedName) throws ClassNotFoundException; + + public List getAnnotatedClassNames() { + return new ArrayList(originalInfos.keySet()); + } + + /** + * The link() method must be called to successfully use the findSubclasses and findImplementations methods + * @return + * @throws IOException + */ + public AbstractFinder link() throws IOException { + // already linked? + if (originalInfos.size() > 0) return this; + + // keep track of what was originally from the archives + originalInfos.putAll(classInfos); + + for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) { + + linkParent(classInfo); + } + + for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) { + + linkInterfaces(classInfo); + } + + return this; + } + + private void linkParent(ClassInfo classInfo) throws IOException { + if (classInfo.superType == null) return; + if (classInfo.superType.equals("java.lang.Object")) return; + + ClassInfo parentInfo = classInfo.superclassInfo; + + if (parentInfo == null) { + + parentInfo = classInfos.get(classInfo.superType); + + if (parentInfo == null) { + + if (classInfo.clazz != null) { + readClassDef(((Class) classInfo.clazz).getSuperclass()); + } else { + readClassDef(classInfo.superType); + } + + parentInfo = classInfos.get(classInfo.superType); + + if (parentInfo == null) return; + + linkParent(parentInfo); + } + + classInfo.superclassInfo = parentInfo; + } + + if (!parentInfo.subclassInfos.contains(classInfo)) { + parentInfo.subclassInfos.add(classInfo); + } + } + + private void linkInterfaces(ClassInfo classInfo) throws IOException { + final List infos = new ArrayList(); + + if (classInfo.clazz != null){ + final Class[] interfaces = classInfo.clazz.getInterfaces(); + + for (Class clazz : interfaces) { + ClassInfo interfaceInfo = classInfos.get(clazz.getName()); + + if (interfaceInfo == null){ + readClassDef(clazz); + } + + interfaceInfo = classInfos.get(clazz.getName()); + + if (interfaceInfo != null) { + infos.add(interfaceInfo); + } + } + } else { + for (String className : classInfo.interfaces) { + ClassInfo interfaceInfo = classInfos.get(className); + + if (interfaceInfo == null){ + readClassDef(className); + } + + interfaceInfo = classInfos.get(className); + + if (interfaceInfo != null) { + infos.add(interfaceInfo); + } + } + } + + for (ClassInfo info : infos) { + linkInterfaces(info); + } + } + + public boolean isAnnotationPresent(Class annotation) { + List infos = annotated.get(annotation.getName()); + return infos != null && !infos.isEmpty(); + } + + /** + * Returns a list of classes that could not be loaded in last invoked findAnnotated* method. + *

    + * The list will only contain entries of classes whose byte code matched the requirements + * of last invoked find* method, but were unable to be loaded and included in the results. + *

    + * The list returned is unmodifiable. Once obtained, the returned list will be a live view of the + * results from the last findAnnotated* method call. + *

    + * This method is not thread safe. + * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call. + */ + public List getClassesNotLoaded() { + return Collections.unmodifiableList(classesNotLoaded); + } + + public List findAnnotatedPackages(Class annotation) { + classesNotLoaded.clear(); + List packages = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof PackageInfo) { + PackageInfo packageInfo = (PackageInfo) info; + try { + Package pkg = packageInfo.get(); + // double check via proper reflection + if (pkg.isAnnotationPresent(annotation)) { + packages.add(pkg); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(packageInfo.getName()); + } + } + } + return packages; + } + + public List> findAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof ClassInfo) { + ClassInfo classInfo = (ClassInfo) info; + try { + Class clazz = classInfo.get(); + // double check via proper reflection + if (clazz.isAnnotationPresent(annotation)) { + classes.add(clazz); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return classes; + } + + public List>> findMetaAnnotatedClasses(Class annotation) { + List> classes = findAnnotatedClasses(annotation); + List>> list = new ArrayList>>(); + for (final Class clazz : classes) { + list.add(new MetaAnnotatedClass(clazz)); + } + return list; + } + + /** + * Naive implementation - works extremelly slow O(n^3) + * + * @param annotation + * @return list of directly or indirectly (inherited) annotated classes + */ + public List> findInheritedAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + try { + if(info instanceof ClassInfo){ + classes.add(((ClassInfo) info).get()); + } + } catch (ClassNotFoundException cnfe) { + // TODO: ignored, but a log message would be appropriate + } + } + boolean annClassFound; + List tempClassInfos = new ArrayList(classInfos.values()); + do { + annClassFound = false; + for (int pos = 0; pos < tempClassInfos.size(); pos++) { + ClassInfo classInfo = tempClassInfos.get(pos); + try { + // check whether any superclass is annotated + String superType = classInfo.getSuperType(); + for (Class clazz : classes) { + if (superType.equals(clazz.getName())) { + classes.add(classInfo.get()); + tempClassInfos.remove(pos); + annClassFound = true; + break; + } + } + // check whether any interface is annotated + List interfces = classInfo.getInterfaces(); + for (String interfce: interfces) { + for (Class clazz : classes) { + if (interfce.replaceFirst("<.*>","").equals(clazz.getName())) { + classes.add(classInfo.get()); + tempClassInfos.remove(pos); + annClassFound = true; + break; + } + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } catch (NoClassDefFoundError e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } while (annClassFound); + return classes; + } + + public List findAnnotatedMethods(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List methods = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && !info.getName().equals("")) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(annotation)) { + methods.add(method); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return methods; + } + + public List> findMetaAnnotatedMethods(Class annotation) { + List methods = findAnnotatedMethods(annotation); + List> list = new ArrayList>(); + for (final Method method : methods) { + list.add(new MetaAnnotatedMethod(method)); + } + return list; + } + + public List findAnnotatedConstructors(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List constructors = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && info.getName().equals("")) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Constructor constructor : clazz.getConstructors()) { + if (constructor.isAnnotationPresent(annotation)) { + constructors.add(constructor); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return constructors; + } + + public List findAnnotatedFields(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List fields = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof FieldInfo) { + FieldInfo fieldInfo = (FieldInfo) info; + ClassInfo classInfo = fieldInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(annotation)) { + fields.add(field); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return fields; + } + + public List> findMetaAnnotatedFields(Class annotation) { + List fields = findAnnotatedFields(annotation); + List> list = new ArrayList>(); + for (final Field field : fields) { + list.add(new MetaAnnotatedField(field)); + } + + return list; + } + + public List> findClassesInPackage(String packageName, boolean recursive) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (recursive && classInfo.getPackageName().startsWith(packageName)){ + classes.add(classInfo.get()); + } else if (classInfo.getPackageName().equals(packageName)){ + classes.add(classInfo.get()); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List> findSubclasses(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + + classesNotLoaded.clear(); + + final ClassInfo classInfo = classInfos.get(clazz.getName()); + + List> found = new ArrayList>(); + + if (classInfo == null) return found; + + findSubclasses(classInfo, found, clazz); + + return found; + } + + private void findSubclasses(ClassInfo classInfo, List> found, Class clazz) { + + for (ClassInfo subclassInfo : classInfo.subclassInfos) { + + try { + found.add(subclassInfo.get().asSubclass(clazz)); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(subclassInfo.getName()); + } + + findSubclasses(subclassInfo, found, clazz); + } + } + + private List> _findSubclasses(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + + List> classes = new ArrayList>(); + + + for (ClassInfo classInfo : classInfos.values()) { + + try { + + if (clazz.getName().equals(classInfo.superType)) { + + if (clazz.isAssignableFrom(classInfo.get())) { + + classes.add(classInfo.get().asSubclass(clazz)); + + classes.addAll(_findSubclasses(classInfo.get().asSubclass(clazz))); + } + } + + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + + } + + return classes; + } + + public List> findImplementations(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface"); + classesNotLoaded.clear(); + + final String interfaceName = clazz.getName(); + + // Collect all interfaces extending the main interface (recursively) + // Collect all implementations of interfaces + // i.e. all *directly* implementing classes + List infos = collectImplementations(interfaceName); + + // Collect all subclasses of implementations + List> classes = new ArrayList>(); + for (ClassInfo info : infos) { + try { + final Class impl = (Class) info.get(); + + if (clazz.isAssignableFrom(impl)) { + classes.add(impl); + + // Optimization: Don't need to call this method if parent class was already searched + + + + classes.addAll(_findSubclasses(impl)); + } + + } catch (ClassNotFoundException e) { + classesNotLoaded.add(info.getName()); + } + } + return classes; + } + + private List collectImplementations(String interfaceName) { + final List infos = new ArrayList(); + + for (ClassInfo classInfo : classInfos.values()) { + + if (classInfo.interfaces.contains(interfaceName)) { + + infos.add(classInfo); + + try { + + final Class clazz = classInfo.get(); + + if (clazz.isInterface() && !clazz.isAnnotation()) { + + infos.addAll(collectImplementations(classInfo.name)); + + } + + } catch (ClassNotFoundException ignore) { + // we'll deal with this later + } + } + } + return infos; + } + + protected List getAnnotationInfos(String name) { + List infos = annotated.get(name); + if (infos == null) { + infos = new SingleLinkedList(); + annotated.put(name, infos); + } + return infos; + } + + protected void readClassDef(String className) { + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + try { + URL resource = getResource(className); + if (resource != null) { + InputStream in = resource.openStream(); + try { + readClassDef(in); + } finally { + in.close(); + } + } else { + classesNotLoaded.add(className + " (no resource found for class)"); + } + } catch (IOException e) { + classesNotLoaded.add(className + e.getMessage()); + } + + } + + protected void readClassDef(InputStream in) throws IOException { + readClassDef(in, null); + } + + protected void readClassDef(InputStream in, String path) throws IOException { + ClassReader classReader = new ClassReader(in); + classReader.accept(new InfoBuildingVisitor(path), ASM_FLAGS); + } + + protected void readClassDef(Class clazz) { + List infos = new ArrayList(); + + Package aPackage = clazz.getPackage(); + if (aPackage != null){ + final PackageInfo info = new PackageInfo(aPackage); + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = getAnnotationInfos(annotation.getName()); + if (!annotationInfos.contains(info)) { + annotationInfos.add(info); + } + } + } + + ClassInfo classInfo = new ClassInfo(clazz); + infos.add(classInfo); + classInfos.put(clazz.getName(), classInfo); + for (Method method : clazz.getDeclaredMethods()) { + infos.add(new MethodInfo(classInfo, method)); + } + + for (Constructor constructor : clazz.getConstructors()) { + infos.add(new MethodInfo(classInfo, constructor)); + } + + for (Field field : clazz.getDeclaredFields()) { + infos.add(new FieldInfo(classInfo, field)); + } + + for (Info info : infos) { + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = getAnnotationInfos(annotation.getName()); + annotationInfos.add(info); + } + } + } + + public class Annotatable { + private final List annotations = new ArrayList(); + + public Annotatable(AnnotatedElement element) { + for (Annotation annotation : getAnnotations(element)) { + annotations.add(new AnnotationInfo(annotation.annotationType().getName())); + } + } + + public Annotatable() { + } + + public List getAnnotations() { + return annotations; + } + + /** + * Utility method to get around some errors caused by + * interactions between the Equinox class loaders and + * the OpenJPA transformation process. There is a window + * where the OpenJPA transformation process can cause + * an annotation being processed to get defined in a + * classloader during the actual defineClass call for + * that very class (e.g., recursively). This results in + * a LinkageError exception. If we see one of these, + * retry the request. Since the annotation will be + * defined on the second pass, this should succeed. If + * we get a second exception, then it's likely some + * other problem. + * + * @param element The AnnotatedElement we need information for. + * + * @return An array of the Annotations defined on the element. + */ + private Annotation[] getAnnotations(AnnotatedElement element) { + try { + return element.getAnnotations(); + } catch (LinkageError e) { + return element.getAnnotations(); + } + } + + } + + public static interface Info { + String getName(); + + List getAnnotations(); + } + + public class PackageInfo extends Annotatable implements Info { + private final String name; + private final ClassInfo info; + private final Package pkg; + + public PackageInfo(Package pkg){ + super(pkg); + this.pkg = pkg; + this.name = pkg.getName(); + this.info = null; + } + + public PackageInfo(String name) { + info = new ClassInfo(name, null); + this.name = name; + this.pkg = null; + } + + public String getName() { + return name; + } + + public Package get() throws ClassNotFoundException { + return (pkg != null)?pkg:info.get().getPackage(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PackageInfo that = (PackageInfo) 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; + } + } + + public class ClassInfo extends Annotatable implements Info { + private String name; + private final List methods = new SingleLinkedList(); + private final List constructors = new SingleLinkedList(); + private String superType; + private ClassInfo superclassInfo; + private final List subclassInfos = new SingleLinkedList(); + private final List interfaces = new SingleLinkedList(); + private final List fields = new SingleLinkedList(); + //e.g. bundle class path prefix. + private String path; + private Class clazz; + + public ClassInfo(Class clazz) { + super(clazz); + this.clazz = clazz; + this.name = clazz.getName(); + Class superclass = clazz.getSuperclass(); + this.superType = superclass != null ? superclass.getName(): null; + for (Class intrface : clazz.getInterfaces()) { + this.interfaces.add(intrface.getName()); + } + } + + public ClassInfo(String name, String superType) { + this.name = name; + this.superType = superType; + } + + public String getPackageName(){ + return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "" ; + } + + public List getConstructors() { + return constructors; + } + + public List getInterfaces() { + return interfaces; + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + + public String getName() { + return name; + } + + public String getSuperType() { + return superType; + } + + public Class get() throws ClassNotFoundException { + if (clazz != null) return clazz; + try { + String fixedName = name.replaceFirst("<.*>", ""); + this.clazz = loadClass(fixedName); + return clazz; + } catch (ClassNotFoundException notFound) { + classesNotLoaded.add(name); + throw notFound; + } + } + + public String toString() { + return name; + } + + public String getPath() { + return path; + } + } + + public class MethodInfo extends Annotatable implements Info { + private final ClassInfo declaringClass; + private final String returnType; + private final String name; + private final List> parameterAnnotations = new ArrayList>(); + + public MethodInfo(ClassInfo info, Constructor constructor){ + super(constructor); + this.declaringClass = info; + this.name = ""; + this.returnType = Void.TYPE.getName(); + } + + public MethodInfo(ClassInfo info, Method method){ + super(method); + this.declaringClass = info; + this.name = method.getName(); + this.returnType = method.getReturnType().getName(); + } + + public MethodInfo(ClassInfo declarignClass, String name, String returnType) { + this.declaringClass = declarignClass; + this.name = name; + this.returnType = returnType; + } + + public List> getParameterAnnotations() { + return parameterAnnotations; + } + + public List getParameterAnnotations(int index) { + if (index >= parameterAnnotations.size()) { + for (int i = parameterAnnotations.size(); i <= index; i++) { + List annotationInfos = new ArrayList(); + parameterAnnotations.add(i, annotationInfos); + } + } + return parameterAnnotations.get(index); + } + + public String getName() { + return name; + } + + public ClassInfo getDeclaringClass() { + return declaringClass; + } + + public String getReturnType() { + return returnType; + } + + public String toString() { + return declaringClass + "@" + name; + } + } + + public class FieldInfo extends Annotatable implements Info { + private final String name; + private final String type; + private final ClassInfo declaringClass; + + public FieldInfo(ClassInfo info, Field field){ + super(field); + this.declaringClass = info; + this.name = field.getName(); + this.type = field.getType().getName(); + } + + public FieldInfo(ClassInfo declaringClass, String name, String type) { + this.declaringClass = declaringClass; + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public ClassInfo getDeclaringClass() { + return declaringClass; + } + + public String getType() { + return type; + } + + public String toString() { + return declaringClass + "#" + name; + } + } + + public class AnnotationInfo extends Annotatable implements Info { + private final String name; + + public AnnotationInfo(Annotation annotation){ + this(annotation.getClass().getName()); + } + + public AnnotationInfo(Class annotation) { + this.name = annotation.getName().intern(); + } + + public AnnotationInfo(String name) { + name = name.replaceAll("^L|;$", ""); + name = name.replace('/', '.'); + this.name = name.intern(); + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } + } + + public class InfoBuildingVisitor extends EmptyVisitor { + private Info info; + private String path; + + public InfoBuildingVisitor(String path) { + this.path = path; + } + + public InfoBuildingVisitor(Info info) { + this.info = info; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (name.endsWith("package-info")) { + info = new PackageInfo(javaName(name)); + } else { + ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName)); + classInfo.path = path; +// if (signature == null) { + for (String interfce : interfaces) { + classInfo.getInterfaces().add(javaName(interfce)); + } +// } else { +// // the class uses generics +// new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo)); +// } + info = classInfo; + classInfos.put(classInfo.getName(), classInfo); + } + } + + private String javaName(String name) { + return (name == null)? null:name.replace('/', '.'); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + info.getAnnotations().add(annotationInfo); + getAnnotationInfos(annotationInfo.getName()).add(info); + return new InfoBuildingVisitor(annotationInfo); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + ClassInfo classInfo = ((ClassInfo) info); + FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc); + classInfo.getFields().add(fieldInfo); + return new InfoBuildingVisitor(fieldInfo); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + ClassInfo classInfo = ((ClassInfo) info); + MethodInfo methodInfo = new MethodInfo(classInfo, name, desc); + classInfo.getMethods().add(methodInfo); + return new InfoBuildingVisitor(methodInfo); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) { + MethodInfo methodInfo = ((MethodInfo) info); + List annotationInfos = methodInfo.getParameterAnnotations(param); + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + annotationInfos.add(annotationInfo); + return new InfoBuildingVisitor(annotationInfo); + } + } + + public static class GenericAwareInfoBuildingVisitor implements SignatureVisitor { + + public enum TYPE { + CLASS + } + + public enum STATE { + BEGIN, END, SUPERCLASS, INTERFACE, FORMAL_TYPE_PARAM + } + + private Info info; + private GenericAwareInfoBuildingVisitor.TYPE type; + private GenericAwareInfoBuildingVisitor.STATE state; + + private static boolean debug = false; + + public GenericAwareInfoBuildingVisitor() { + } + + public GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE type, Info info) { + this.type = type; + this.info = info; + this.state = GenericAwareInfoBuildingVisitor.STATE.BEGIN; + } + + public void visitFormalTypeParameter(String s) { + if (debug) System.out.println(" s=" + s); + switch (state) { + case BEGIN: + ((ClassInfo) info).name += "<" + s; + } + state = GenericAwareInfoBuildingVisitor.STATE.FORMAL_TYPE_PARAM; + } + + public SignatureVisitor visitClassBound() { + if (debug) System.out.println(" visitClassBound()"); + return this; + } + + public SignatureVisitor visitInterfaceBound() { + if (debug) System.out.println(" visitInterfaceBound()"); + return this; + } + + public SignatureVisitor visitSuperclass() { + if (debug) System.out.println(" visitSuperclass()"); + state = GenericAwareInfoBuildingVisitor.STATE.SUPERCLASS; + return this; + } + + public SignatureVisitor visitInterface() { + if (debug) System.out.println(" visitInterface()"); + ((ClassInfo) info).getInterfaces().add(""); + state = GenericAwareInfoBuildingVisitor.STATE.INTERFACE; + return this; + } + + public SignatureVisitor visitParameterType() { + if (debug) System.out.println(" visitParameterType()"); + return this; + } + + public SignatureVisitor visitReturnType() { + if (debug) System.out.println(" visitReturnType()"); + return this; + } + + public SignatureVisitor visitExceptionType() { + if (debug) System.out.println(" visitExceptionType()"); + return this; + } + + public void visitBaseType(char c) { + if (debug) System.out.println(" visitBaseType(" + c + ")"); + } + + public void visitTypeVariable(String s) { + if (debug) System.out.println(" visitTypeVariable(" + s + ")"); + } + + public SignatureVisitor visitArrayType() { + if (debug) System.out.println(" visitArrayType()"); + return this; + } + + public void visitClassType(String s) { + if (debug) System.out.println(" visitClassType(" + s + ")"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + if (interfce.length() == 0) { + interfce = javaName(s); + } else { + interfce += javaName(s); + } + interfces.set(idx, interfce); + break; + case SUPERCLASS: + if (!s.equals("java/lang/Object")) { + ((ClassInfo) info).superType = javaName(s); + } + } + } + + public void visitInnerClassType(String s) { + if (debug) System.out.println(" visitInnerClassType(" + s + ")"); + } + + public void visitTypeArgument() { + if (debug) System.out.println(" visitTypeArgument()"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += "<"; + interfces.set(idx, interfce); + } + } + + public SignatureVisitor visitTypeArgument(char c) { + if (debug) System.out.println(" visitTypeArgument(" + c + ")"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += "<"; + interfces.set(idx, interfce); + } + return this; + } + + public void visitEnd() { + if (debug) System.out.println(" visitEnd()"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += ">"; + interfces.set(idx, interfce); + break; + case FORMAL_TYPE_PARAM: + String name = ((ClassInfo) info).name; + if (name.contains("<")) { + ((ClassInfo) info).name += ">"; + } + } + state = GenericAwareInfoBuildingVisitor.STATE.END; + } + + private String javaName(String name) { + return (name == null)? null:name.replace('/', '.'); + } + + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/Annotated.java b/xbean-finder/src/main/java/org/apache/xbean/finder/Annotated.java new file mode 100644 index 00000000..a182b678 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/Annotated.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.xbean.finder; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.GenericDeclaration; +import java.util.Collection; + +/** + * @version $Rev$ $Date$ + */ +public interface Annotated extends AnnotatedElement { + T get(); +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMember.java b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMember.java new file mode 100644 index 00000000..28a90ca3 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMember.java @@ -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. + */ +package org.apache.xbean.finder; + +import java.lang.reflect.Member; + +/** + * @version $Rev$ $Date$ + */ +public interface AnnotatedMember extends Annotated, Member { + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMethod.java b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMethod.java new file mode 100644 index 00000000..509688c8 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotatedMethod.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +/** + * @version $Rev$ $Date$ + */ +public interface AnnotatedMethod extends AnnotatedMember { + + public Annotation[][] getParameterAnnotations(); + + public Class[] getExceptionTypes(); + + public Class[] getParameterTypes(); + + public String toGenericString(); + + public Type[] getGenericExceptionTypes(); + + public Type[] getGenericParameterTypes(); + + public boolean isVarArgs(); + +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java new file mode 100644 index 00000000..f122b7a9 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java @@ -0,0 +1,1729 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.finder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +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 org.apache.xbean.finder.archive.Archive; +import org.apache.xbean.finder.util.Classes; +import org.apache.xbean.finder.util.SingleLinkedList; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.EmptyVisitor; +import org.objectweb.asm.signature.SignatureVisitor; + +/** + * ClassFinder searches the classpath of the specified classloader for + * packages, classes, constructors, methods, or fields with specific annotations. + *

    + * For security reasons ASM is used to find the annotations. Classes are not + * loaded unless they match the requirements of a called findAnnotated* method. + * Once loaded, these classes are cached. + * + * @version $Rev$ $Date$ + */ +public class AnnotationFinder implements IAnnotationFinder { + + private final Set> metaroots = new HashSet>(); + + private final Map> annotated = new HashMap>(); + + protected final Map classInfos = new HashMap(); + protected final Map originalInfos = new HashMap(); + private final List classesNotLoaded = new ArrayList(); + private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES; + private final Archive archive; + private final boolean checkRuntimeAnnotation; + + private AnnotationFinder(AnnotationFinder parent, Iterable classNames) { + this.archive = new SubArchive(classNames); + this.checkRuntimeAnnotation = parent.checkRuntimeAnnotation; + this.metaroots.addAll(parent.metaroots); + for (Class metaroot : metaroots) { + final ClassInfo info = parent.classInfos.get(metaroot.getName()); + if (info == null) continue; + readClassDef(info); + } + for (String name : classNames) { + final ClassInfo info = parent.classInfos.get(name); + if (info == null) continue; + readClassDef(info); + } + + resolveAnnotations(parent, new ArrayList()); + for (ClassInfo classInfo : classInfos.values()) { + if (isMetaRoot(classInfo)) { + try { + metaroots.add((Class) classInfo.get()); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + + for (Class metaroot : metaroots) { + List infoList = annotated.get(metaroot.getName()); + for (Info info : infoList) { + final String className = info.getName() + "$$"; + final ClassInfo i = parent.classInfos.get(className); + if (i == null) continue; + readClassDef(i); + } + } + } + + public AnnotationFinder(Archive archive, boolean checkRuntimeAnnotation) { + this.archive = archive; + this.checkRuntimeAnnotation = checkRuntimeAnnotation; + + for (Archive.Entry entry : archive) { + final String className = entry.getName(); + try { + readClassDef(entry.getBytecode()); + } catch (NoClassDefFoundError e) { + throw new NoClassDefFoundError("Could not fully load class: " + className + "\n due to:" + e.getMessage()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // keep track of what was originally from the archives + originalInfos.putAll(classInfos); + } + + public AnnotationFinder(Archive archive) { + this(archive, true); + } + + public boolean hasMetaAnnotations() { + return metaroots.size() > 0; + } + + private void readClassDef(ClassInfo info) { + classInfos.put(info.name, info); + index(info); + index(info.constructors); + index(info.methods); + index(info.fields); + } + + private void resolveAnnotations(AnnotationFinder parent, List scanned) { + // Get a list of the annotations that exist before we start + final List annotations = new ArrayList(annotated.keySet()); + + for (String annotation : annotations) { + if (scanned.contains(annotation)) continue; + final ClassInfo info = parent.classInfos.get(annotation); + if (info == null) continue; + readClassDef(info); + } + + // If the "annotated" list has grown, then we must scan those + if (annotated.keySet().size() != annotations.size()) { + resolveAnnotations(parent, annotations); + } + } + + + private void index(List infos) { + for (Info i : infos) { + index(i); + } + } + + private void index(Info i) { + for (AnnotationInfo annotationInfo : i.getAnnotations()) { + index(annotationInfo, i); + } + } + + public List getAnnotatedClassNames() { + return new ArrayList(originalInfos.keySet()); + } + + public Archive getArchive() { + return archive; + } + + /** + * The link() method must be called to successfully use the findSubclasses and findImplementations methods + * + * @return + * @throws java.io.IOException + */ + public AnnotationFinder link() { + + enableFindSubclasses(); + + enableFindImplementations(); + + enableMetaAnnotations(); + + return this; + } + + public AnnotationFinder enableMetaAnnotations() { + // diff new and old lists + resolveAnnotations(new ArrayList()); + + linkMetaAnnotations(); + + return this; + } + + public AnnotationFinder enableFindImplementations() { + for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) { + + linkInterfaces(classInfo); + + } + + return this; + } + + public AnnotationFinder enableFindSubclasses() { + for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) { + + linkParent(classInfo); + } + + return this; + } + + /** + * Used to support meta annotations + *

    + * Once the list of classes has been read from the Archive, we + * iterate over all the annotations that are used by those classes + * and recursively resolve any annotations those annotations use. + * + * @param scanned + * @throws ClassNotFoundException + * @throws IOException + */ + private void resolveAnnotations(List scanned) { + // Get a list of the annotations that exist before we start + final List annotations = new ArrayList(annotated.keySet()); + + for (String annotation : annotations) { + if (scanned.contains(annotation)) continue; + readClassDef(annotation); + } + + // If the "annotated" list has grown, then we must scan those + if (annotated.keySet().size() != annotations.size()) { + resolveAnnotations(annotations); + } + + +// for (ClassInfo classInfo : classInfos.values()) { +// for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) { +// for (AnnotationInfo info : annotationInfo.getAnnotations()) { +// final String annotation = info.getName(); +// +// if (hasName(annotation, "Metaroot") && !scanned.contains(annotation)) { +// readClassDef(annotation); +// } +// } +// } +// } + } + + private void linkMetaAnnotations() { + for (ClassInfo classInfo : classInfos.values()) { + if (isMetaRoot(classInfo)) { + try { + metaroots.add((Class) classInfo.get()); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + + for (Class metaroot : metaroots) { + List infoList = annotated.get(metaroot.getName()); + for (Info info : infoList) { + readClassDef(info.getName() + "$$"); + } + } + } + + private boolean isMetaRoot(ClassInfo classInfo) { + if (!classInfo.isAnnotation()) return false; + + if (classInfo.getName().equals("javax.annotation.Metatype")) return true; + if (isSelfAnnotated(classInfo, "Metatype")) return true; + if (isSelfAnnotated(classInfo, "Metaroot")) return false; + + for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) { + final ClassInfo annotation = classInfos.get(annotationInfo.getName()); + if (annotation == null) return false; + if (annotation.getName().equals("javax.annotation.Metaroot")) return true; + if (isSelfAnnotated(annotation, "Metaroot")) return true; + } + + return false; + } + + private boolean isSelfAnnotated(ClassInfo classInfo, String metatype) { + if (!classInfo.isAnnotation()) return false; + + final String name = classInfo.getName(); + if (!hasName(name, metatype)) return false; + + for (AnnotationInfo info : classInfo.getAnnotations()) { + if (info.getName().equals(name)) return true; + } + + return true; + } + + private boolean hasName(String className, String simpleName) { + return className.equals(simpleName) || className.endsWith("." + simpleName) || className.endsWith("$" + simpleName); + } + + private void linkParent(ClassInfo classInfo) { + if (classInfo.superType == null) return; + if (classInfo.superType.equals("java.lang.Object")) return; + + ClassInfo parentInfo = classInfo.superclassInfo; + + if (parentInfo == null) { + + parentInfo = classInfos.get(classInfo.superType); + + if (parentInfo == null) { + + if (classInfo.clazz != null) { + readClassDef(((Class) classInfo.clazz).getSuperclass()); + } else { + readClassDef(classInfo.superType); + } + + parentInfo = classInfos.get(classInfo.superType); + + if (parentInfo == null) return; + + linkParent(parentInfo); + } + + classInfo.superclassInfo = parentInfo; + } + + if (!parentInfo.subclassInfos.contains(classInfo)) { + parentInfo.subclassInfos.add(classInfo); + } + } + + private void linkInterfaces(ClassInfo classInfo) { + final List infos = new ArrayList(); + + if (classInfo.clazz != null) { + final Class[] interfaces = classInfo.clazz.getInterfaces(); + + for (Class clazz : interfaces) { + ClassInfo interfaceInfo = classInfos.get(clazz.getName()); + + if (interfaceInfo == null) { + readClassDef(clazz); + } + + interfaceInfo = classInfos.get(clazz.getName()); + + if (interfaceInfo != null) { + infos.add(interfaceInfo); + } + } + } else { + for (String className : classInfo.interfaces) { + ClassInfo interfaceInfo = classInfos.get(className); + + if (interfaceInfo == null) { + readClassDef(className); + } + + interfaceInfo = classInfos.get(className); + + if (interfaceInfo != null) { + infos.add(interfaceInfo); + } + } + } + + for (ClassInfo info : infos) { + linkInterfaces(info); + } + } + + public boolean isAnnotationPresent(Class annotation) { + List infos = annotated.get(annotation.getName()); + return infos != null && !infos.isEmpty(); + } + + /** + * Returns a list of classes that could not be loaded in last invoked findAnnotated* method. + *

    + * The list will only contain entries of classes whose byte code matched the requirements + * of last invoked find* method, but were unable to be loaded and included in the results. + *

    + * The list returned is unmodifiable. Once obtained, the returned list will be a live view of the + * results from the last findAnnotated* method call. + *

    + * This method is not thread safe. + * + * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call. + */ + public List getClassesNotLoaded() { + return Collections.unmodifiableList(classesNotLoaded); + } + + public List findAnnotatedPackages(Class annotation) { + classesNotLoaded.clear(); + List packages = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof PackageInfo) { + PackageInfo packageInfo = (PackageInfo) info; + try { + Package pkg = packageInfo.get(); + // double check via proper reflection + if (!checkRuntimeAnnotation || pkg.isAnnotationPresent(annotation)) { + packages.add(pkg); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(packageInfo.getName()); + } + } + } + return packages; + } + + public List> findAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof ClassInfo) { + ClassInfo classInfo = (ClassInfo) info; + try { + Class clazz = classInfo.get(); + // double check via proper reflection + if (!checkRuntimeAnnotation || clazz.isAnnotationPresent(annotation)) { + classes.add(clazz); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return classes; + } + + public List>> findMetaAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + Set> classes = findMetaAnnotatedClasses(annotation, new HashSet>()); + + List>> list = new ArrayList>>(); + + for (Class clazz : classes) { + if (Annotation.class.isAssignableFrom(clazz) && isMetaAnnotation((Class) clazz)) continue; + list.add(new MetaAnnotatedClass(clazz)); + } + + return list; + } + + private static boolean isMetaAnnotation(Class clazz) { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + if (isMetatypeAnnotation(annotation.annotationType())) return true; + } + + return false; + } + + private static boolean isMetatypeAnnotation(Class type) { + if (isSelfAnnotated(type, "Metatype")) return true; + + for (Annotation annotation : type.getAnnotations()) { + if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true; + } + + return false; + } + + private static boolean isSelfAnnotated(Class type, String name) { + return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type); + } + + private static boolean validTarget(Class type) { + final Target target = type.getAnnotation(Target.class); + + if (target == null) return false; + + final ElementType[] targets = target.value(); + + return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE; + } + + + private Set> findMetaAnnotatedClasses(Class annotation, Set> classes) { + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof ClassInfo) { + ClassInfo classInfo = (ClassInfo) info; + try { + Class clazz = classInfo.get(); + + if (classes.contains(clazz)) continue; + + // double check via proper reflection + if (!checkRuntimeAnnotation || clazz.isAnnotationPresent(annotation)) { + classes.add(clazz); + } + + String meta = info.getMetaAnnotationName(); + if (meta != null) { + classes.addAll(findMetaAnnotatedClasses((Class) clazz, classes)); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return classes; + } + + /** + * Naive implementation - works extremelly slow O(n^3) + * + * @param annotation + * @return list of directly or indirectly (inherited) annotated classes + */ + public List> findInheritedAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + try { + if (info instanceof ClassInfo) { + classes.add(((ClassInfo) info).get()); + } + } catch (ClassNotFoundException cnfe) { + // TODO: ignored, but a log message would be appropriate + } + } + boolean annClassFound; + List tempClassInfos = new ArrayList(classInfos.values()); + do { + annClassFound = false; + for (int pos = 0; pos < tempClassInfos.size(); pos++) { + ClassInfo classInfo = tempClassInfos.get(pos); + try { + // check whether any superclass is annotated + String superType = classInfo.getSuperType(); + for (Class clazz : classes) { + if (superType.equals(clazz.getName())) { + classes.add(classInfo.get()); + tempClassInfos.remove(pos); + annClassFound = true; + break; + } + } + // check whether any interface is annotated + List interfces = classInfo.getInterfaces(); + for (String interfce : interfces) { + for (Class clazz : classes) { + if (interfce.replaceFirst("<.*>", "").equals(clazz.getName())) { + classes.add(classInfo.get()); + tempClassInfos.remove(pos); + annClassFound = true; + break; + } + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } catch (NoClassDefFoundError e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } while (annClassFound); + return classes; + } + + public List findAnnotatedMethods(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List methods = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && !info.getName().equals("")) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Method method : clazz.getDeclaredMethods()) { + if (!checkRuntimeAnnotation || method.isAnnotationPresent(annotation)) { + methods.add(method); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return methods; + } + + public List> findMetaAnnotatedMethods(Class annotation) { + classesNotLoaded.clear(); + + Set methods = findMetaAnnotatedMethods(annotation, new HashSet(), new HashSet()); + + List> targets = new ArrayList>(); + + for (Method method : methods) { + targets.add(new MetaAnnotatedMethod(method)); + } + + return targets; + } + + private Set findMetaAnnotatedMethods(Class annotation, Set methods, Set seen) { + List infos = getAnnotationInfos(annotation.getName()); + + for (Info info : infos) { + + String meta = info.getMetaAnnotationName(); + if (meta != null) { + if (meta.equals(annotation.getName())) continue; + if (!seen.add(meta)) continue; + + + ClassInfo metaInfo = classInfos.get(meta); + + Class clazz; + try { + clazz = metaInfo.get(); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(metaInfo.getName()); + continue; + } + + findMetaAnnotatedMethods((Class) clazz, methods, seen); + + } else if (info instanceof MethodInfo && !((MethodInfo) info).isConstructor()) { + + MethodInfo methodInfo = (MethodInfo) info; + + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + try { + Class clazz = classInfo.get(); + for (Method method : clazz.getDeclaredMethods()) { + if (!checkRuntimeAnnotation || method.isAnnotationPresent(annotation)) { + methods.add(method); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + + return methods; + } + + public List> findMetaAnnotatedFields(Class annotation) { + classesNotLoaded.clear(); + + Set fields = findMetaAnnotatedFields(annotation, new HashSet(), new HashSet()); + + List> targets = new ArrayList>(); + + for (Field field : fields) { + targets.add(new MetaAnnotatedField(field)); + } + + return targets; + } + + private Set findMetaAnnotatedFields(Class annotation, Set fields, Set seen) { + List infos = getAnnotationInfos(annotation.getName()); + + for (Info info : infos) { + + String meta = info.getMetaAnnotationName(); + if (meta != null) { + if (meta.equals(annotation.getName())) continue; + if (!seen.add(meta)) continue; + + + ClassInfo metaInfo = classInfos.get(meta); + + Class clazz; + try { + clazz = metaInfo.get(); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(metaInfo.getName()); + continue; + } + + findMetaAnnotatedFields((Class) clazz, fields, seen); + + } else if (info instanceof FieldInfo) { + + FieldInfo fieldInfo = (FieldInfo) info; + + ClassInfo classInfo = fieldInfo.getDeclaringClass(); + + try { + Class clazz = classInfo.get(); + for (Field field : clazz.getDeclaredFields()) { + if (!checkRuntimeAnnotation || field.isAnnotationPresent(annotation)) { + fields.add(field); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + + return fields; + } + + public List findAnnotatedConstructors(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List constructors = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && info.getName().equals("")) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Constructor constructor : clazz.getConstructors()) { + if (!checkRuntimeAnnotation || constructor.isAnnotationPresent(annotation)) { + constructors.add(constructor); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return constructors; + } + + public List findAnnotatedFields(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List fields = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof FieldInfo) { + FieldInfo fieldInfo = (FieldInfo) info; + ClassInfo classInfo = fieldInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Field field : clazz.getDeclaredFields()) { + if (!checkRuntimeAnnotation || field.isAnnotationPresent(annotation)) { + fields.add(field); + } + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + } + return fields; + } + + public List> findClassesInPackage(String packageName, boolean recursive) { + classesNotLoaded.clear(); + List> classes = new ArrayList>(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (recursive && classInfo.getPackageName().startsWith(packageName)) { + classes.add(classInfo.get()); + } else if (classInfo.getPackageName().equals(packageName)) { + classes.add(classInfo.get()); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List> findSubclasses(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + + classesNotLoaded.clear(); + + final ClassInfo classInfo = classInfos.get(clazz.getName()); + + List> found = new ArrayList>(); + + if (classInfo == null) return found; + + findSubclasses(classInfo, found, clazz); + + return found; + } + + private void findSubclasses(ClassInfo classInfo, List> found, Class clazz) { + + for (ClassInfo subclassInfo : classInfo.subclassInfos) { + + try { + found.add(subclassInfo.get().asSubclass(clazz)); + } catch (ClassNotFoundException e) { + classesNotLoaded.add(subclassInfo.getName()); + } + + findSubclasses(subclassInfo, found, clazz); + } + } + + private List> _findSubclasses(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + + List> classes = new ArrayList>(); + + + for (ClassInfo classInfo : classInfos.values()) { + + try { + + if (clazz.getName().equals(classInfo.superType)) { + + if (clazz.isAssignableFrom(classInfo.get())) { + + classes.add(classInfo.get().asSubclass(clazz)); + + classes.addAll(_findSubclasses(classInfo.get().asSubclass(clazz))); + } + } + + } catch (ClassNotFoundException e) { + classesNotLoaded.add(classInfo.getName()); + } + + } + + return classes; + } + + public List> findImplementations(Class clazz) { + if (clazz == null) throw new NullPointerException("class cannot be null"); + if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface"); + classesNotLoaded.clear(); + + final String interfaceName = clazz.getName(); + + // Collect all interfaces extending the main interface (recursively) + // Collect all implementations of interfaces + // i.e. all *directly* implementing classes + List infos = collectImplementations(interfaceName); + + // Collect all subclasses of implementations + List> classes = new ArrayList>(); + for (ClassInfo info : infos) { + try { + final Class impl = (Class) info.get(); + + if (clazz.isAssignableFrom(impl)) { + classes.add(impl); + + // Optimization: Don't need to call this method if parent class was already searched + + + classes.addAll(_findSubclasses(impl)); + } + + } catch (ClassNotFoundException e) { + classesNotLoaded.add(info.getName()); + } + } + return classes; + } + + private List collectImplementations(String interfaceName) { + final List infos = new ArrayList(); + + for (ClassInfo classInfo : classInfos.values()) { + + if (classInfo.interfaces.contains(interfaceName)) { + + infos.add(classInfo); + + try { + + final Class clazz = classInfo.get(); + + if (clazz.isInterface() && !clazz.isAnnotation()) { + + infos.addAll(collectImplementations(classInfo.name)); + + } + + } catch (ClassNotFoundException ignore) { + // we'll deal with this later + } + } + } + return infos; + } + + protected List getAnnotationInfos(String name) { + final List infos = annotated.get(name); + if (infos != null) return infos; + return Collections.EMPTY_LIST; + } + + protected List initAnnotationInfos(String name) { + List infos = annotated.get(name); + if (infos == null) { + infos = new SingleLinkedList(); + annotated.put(name, infos); + } + return infos; + } + + protected void readClassDef(String className) { + if (classInfos.containsKey(className)) return; + try { + readClassDef(archive.getBytecode(className)); + } catch (Exception e) { + if (className.endsWith("$$")) return; + classesNotLoaded.add(className); + } + } + + protected void readClassDef(InputStream in) throws IOException { + try { + ClassReader classReader = new ClassReader(in); + classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS); + } finally { + in.close(); + } + } + + protected void readClassDef(Class clazz) { + List infos = new ArrayList(); + + Package aPackage = clazz.getPackage(); + if (aPackage != null) { + final PackageInfo info = new PackageInfo(aPackage); + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = initAnnotationInfos(annotation.getName()); + if (!annotationInfos.contains(info)) { + annotationInfos.add(info); + } + } + } + + ClassInfo classInfo = new ClassInfo(clazz); + infos.add(classInfo); + classInfos.put(clazz.getName(), classInfo); + for (Method method : clazz.getDeclaredMethods()) { + infos.add(new MethodInfo(classInfo, method)); + } + + for (Constructor constructor : clazz.getConstructors()) { + infos.add(new MethodInfo(classInfo, constructor)); + } + + for (Field field : clazz.getDeclaredFields()) { + infos.add(new FieldInfo(classInfo, field)); + } + + for (Info info : infos) { + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = initAnnotationInfos(annotation.getName()); + annotationInfos.add(info); + } + } + } + + public AnnotationFinder select(Class... clazz) { + String[] names = new String[clazz.length]; + int i = 0; + for (Class name : clazz) { + names[i++] = name.getName(); + } + + return new AnnotationFinder(this, Arrays.asList(names)); + } + + public AnnotationFinder select(String... clazz) { + return new AnnotationFinder(this, Arrays.asList(clazz)); + } + + public AnnotationFinder select(Iterable clazz) { + return new AnnotationFinder(this, clazz); + } + + public class SubArchive implements Archive { + private List classes = new ArrayList(); + + public SubArchive(String... classes) { + for (String name : classes) { + this.classes.add(new E(name)); + } + } + + public SubArchive(Iterable classes) { + for (String name : classes) { + this.classes.add(new E(name)); + } + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + return archive.getBytecode(className); + } + + public Class loadClass(String className) throws ClassNotFoundException { + return archive.loadClass(className); + } + + public Iterator iterator() { + return classes.iterator(); + } + + public class E implements Entry { + private final String name; + + public E(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public InputStream getBytecode() throws IOException { + return new ByteArrayInputStream(new byte[0]); + } + } + } + + public class Annotatable { + private final List annotations = new ArrayList(); + + public Annotatable(AnnotatedElement element) { + for (Annotation annotation : getAnnotations(element)) { + annotations.add(new AnnotationInfo(annotation.annotationType().getName())); + } + } + + public Annotatable() { + } + + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; + } + + public List getAnnotations() { + return annotations; + } + + public String getMetaAnnotationName() { + return null; + } + + /** + * Utility method to get around some errors caused by + * interactions between the Equinox class loaders and + * the OpenJPA transformation process. There is a window + * where the OpenJPA transformation process can cause + * an annotation being processed to get defined in a + * classloader during the actual defineClass call for + * that very class (e.g., recursively). This results in + * a LinkageError exception. If we see one of these, + * retry the request. Since the annotation will be + * defined on the second pass, this should succeed. If + * we get a second exception, then it's likely some + * other problem. + * + * @param element The AnnotatedElement we need information for. + * @return An array of the Annotations defined on the element. + */ + private Annotation[] getAnnotations(AnnotatedElement element) { + try { + return element.getAnnotations(); + } catch (LinkageError e) { + return element.getAnnotations(); + } + } + + } + + public static interface Info { + + String getMetaAnnotationName(); + + String getName(); + + List getAnnotations(); + + Annotation[] getDeclaredAnnotations(); + } + + public class PackageInfo extends Annotatable implements Info { + private final String name; + private final ClassInfo info; + private final Package pkg; + + public PackageInfo(Package pkg) { + super(pkg); + this.pkg = pkg; + this.name = pkg.getName(); + this.info = null; + } + + public PackageInfo(String name) { + info = new ClassInfo(name, null); + this.name = name; + this.pkg = null; + } + + public String getName() { + return name; + } + + public Package get() throws ClassNotFoundException { + return (pkg != null) ? pkg : info.get().getPackage(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PackageInfo that = (PackageInfo) 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; + } + } + + public class ClassInfo extends Annotatable implements Info { + private String name; + private final List methods = new SingleLinkedList(); + private final List constructors = new SingleLinkedList(); + private String superType; + private ClassInfo superclassInfo; + private final List subclassInfos = new SingleLinkedList(); + private final List interfaces = new SingleLinkedList(); + private final List fields = new SingleLinkedList(); + private Class clazz; + + + public ClassInfo(Class clazz) { + super(clazz); + this.clazz = clazz; + this.name = clazz.getName(); + Class superclass = clazz.getSuperclass(); + this.superType = superclass != null ? superclass.getName() : null; + for (Class intrface : clazz.getInterfaces()) { + this.interfaces.add(intrface.getName()); + } + } + + public ClassInfo(String name, String superType) { + this.name = name; + this.superType = superType; + } + + @Override + public String getMetaAnnotationName() { + for (AnnotationInfo info : getAnnotations()) { + for (Class metaroot : metaroots) { + if (info.getName().equals(metaroot.getName())) return name; + } + } + + if (name.endsWith("$$")) { + ClassInfo info = classInfos.get(name.substring(0, name.length() - 2)); + if (info != null) { + return info.getMetaAnnotationName(); + } + } + + return null; + } + + public String getPackageName() { + return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : ""; + } + + public List getConstructors() { + return constructors; + } + + public List getInterfaces() { + return interfaces; + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + + public String getName() { + return name; + } + + public String getSuperType() { + return superType; + } + + public boolean isAnnotation() { + return "java.lang.Object".equals(superType) && interfaces.size() == 1 && "java.lang.annotation.Annotation".equals(interfaces.get(0)); + } + + public Class get() throws ClassNotFoundException { + if (clazz != null) return clazz; + try { + String fixedName = name.replaceFirst("<.*>", ""); + this.clazz = archive.loadClass(fixedName); + return clazz; + } catch (ClassNotFoundException notFound) { + classesNotLoaded.add(name); + throw notFound; + } + } + + public String toString() { + return name; + } + } + + public class MethodInfo extends Annotatable implements Info { + private final ClassInfo declaringClass; + private final String descriptor; + private final String name; + private final List> parameterAnnotations = new ArrayList>(); + private Member method; + + public MethodInfo(ClassInfo info, Constructor constructor) { + super(constructor); + this.declaringClass = info; + this.name = ""; + this.descriptor = null; + } + + public MethodInfo(ClassInfo info, Method method) { + super(method); + this.declaringClass = info; + this.name = method.getName(); + this.descriptor = method.getReturnType().getName(); + this.method = method; + } + + public MethodInfo(ClassInfo declarignClass, String name, String descriptor) { + this.declaringClass = declarignClass; + this.name = name; + this.descriptor = descriptor; + } + + @Override + public String getMetaAnnotationName() { + return declaringClass.getMetaAnnotationName(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + super.getDeclaredAnnotations(); + try { + return ((AnnotatedElement) get()).getDeclaredAnnotations(); + } catch (ClassNotFoundException e) { + return super.getDeclaredAnnotations(); + } + } + + public boolean isConstructor() { + return getName().equals(""); + } + + public List> getParameterAnnotations() { + return parameterAnnotations; + } + + public List getParameterAnnotations(int index) { + if (index >= parameterAnnotations.size()) { + for (int i = parameterAnnotations.size(); i <= index; i++) { + List annotationInfos = new ArrayList(); + parameterAnnotations.add(i, annotationInfos); + } + } + return parameterAnnotations.get(index); + } + + public String getName() { + return name; + } + + public ClassInfo getDeclaringClass() { + return declaringClass; + } + + public String toString() { + return declaringClass + "@" + name; + } + + public Member get() throws ClassNotFoundException { + if (method == null) { + method = toMethod(); + } + + return method; + } + + private Method toMethod() throws ClassNotFoundException { + org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, descriptor); + + Class clazz = this.declaringClass.get(); + List parameterTypes = new ArrayList(); + + for (Type type : method.getArgumentTypes()) { + String paramType = type.getClassName(); + try { + parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader())); + } catch (ClassNotFoundException cnfe) { + throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe); + } + } + + Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]); + + IllegalStateException noSuchMethod = null; + while (clazz != null) { + try { + return clazz.getDeclaredMethod(name, parameters); + } catch (NoSuchMethodException e) { + if (noSuchMethod == null) { + noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + name, e); + } + clazz = clazz.getSuperclass(); + } + } + + throw noSuchMethod; + } + + } + + public class FieldInfo extends Annotatable implements Info { + private final String name; + private final String type; + private final ClassInfo declaringClass; + private Field field; + + public FieldInfo(ClassInfo info, Field field) { + super(field); + this.declaringClass = info; + this.name = field.getName(); + this.type = field.getType().getName(); + this.field = field; + } + + public FieldInfo(ClassInfo declaringClass, String name, String type) { + this.declaringClass = declaringClass; + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public ClassInfo getDeclaringClass() { + return declaringClass; + } + + public String getType() { + return type; + } + + public String toString() { + return declaringClass + "#" + name; + } + + @Override + public String getMetaAnnotationName() { + return declaringClass.getMetaAnnotationName(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + super.getDeclaredAnnotations(); + try { + return ((AnnotatedElement) get()).getDeclaredAnnotations(); + } catch (ClassNotFoundException e) { + return super.getDeclaredAnnotations(); + } + } + + public Member get() throws ClassNotFoundException { + if (field == null) { + field = toField(); + } + + return field; + } + + private Field toField() throws ClassNotFoundException { + + Class clazz = this.declaringClass.get(); + + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException e) { + throw new IllegalStateException(name, e); + } + + } + } + + public class AnnotationInfo extends Annotatable implements Info { + private final String name; + + public AnnotationInfo(Annotation annotation) { + this(annotation.getClass().getName()); + } + + public AnnotationInfo(Class annotation) { + this.name = annotation.getName().intern(); + } + + public AnnotationInfo(String name) { + name = Type.getType(name).getClassName(); + this.name = name.intern(); + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } + } + + private void index(AnnotationInfo annotationInfo, Info info) { + initAnnotationInfos(annotationInfo.getName()).add(info); + } + + public class InfoBuildingVisitor extends EmptyVisitor { + private Info info; + + public InfoBuildingVisitor() { + } + + public InfoBuildingVisitor(Info info) { + this.info = info; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (name.endsWith("package-info")) { + info = new PackageInfo(javaName(name)); + } else { + + ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName)); + +// if (signature == null) { + for (String interfce : interfaces) { + classInfo.getInterfaces().add(javaName(interfce)); + } +// } else { +// // the class uses generics +// new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo)); +// } + info = classInfo; + + classInfos.put(classInfo.getName(), classInfo); + } + } + + private String javaName(String name) { + return (name == null) ? null : name.replace('/', '.'); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + super.visitInnerClass(name, outerName, innerName, access); + } + + @Override + public void visitAttribute(Attribute attribute) { + super.visitAttribute(attribute); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + info.getAnnotations().add(annotationInfo); + index(annotationInfo, info); + return new InfoBuildingVisitor(annotationInfo); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + ClassInfo classInfo = ((ClassInfo) info); + FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc); + classInfo.getFields().add(fieldInfo); + return new InfoBuildingVisitor(fieldInfo); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + ClassInfo classInfo = ((ClassInfo) info); + MethodInfo methodInfo = new MethodInfo(classInfo, name, desc); + + classInfo.getMethods().add(methodInfo); + return new InfoBuildingVisitor(methodInfo); + } + + + @Override + public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) { + MethodInfo methodInfo = ((MethodInfo) info); + List annotationInfos = methodInfo.getParameterAnnotations(param); + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + annotationInfos.add(annotationInfo); + return new InfoBuildingVisitor(annotationInfo); + } + } + + public static class GenericAwareInfoBuildingVisitor implements SignatureVisitor { + + public enum TYPE { + CLASS + } + + public enum STATE { + BEGIN, END, SUPERCLASS, INTERFACE, FORMAL_TYPE_PARAM + } + + private Info info; + private GenericAwareInfoBuildingVisitor.TYPE type; + private GenericAwareInfoBuildingVisitor.STATE state; + + private static boolean debug = false; + + public GenericAwareInfoBuildingVisitor() { + } + + public GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE type, Info info) { + this.type = type; + this.info = info; + this.state = GenericAwareInfoBuildingVisitor.STATE.BEGIN; + } + + public void visitFormalTypeParameter(String s) { + if (debug) System.out.println(" s=" + s); + switch (state) { + case BEGIN: + ((ClassInfo) info).name += "<" + s; + } + state = GenericAwareInfoBuildingVisitor.STATE.FORMAL_TYPE_PARAM; + } + + public SignatureVisitor visitClassBound() { + if (debug) System.out.println(" visitClassBound()"); + return this; + } + + public SignatureVisitor visitInterfaceBound() { + if (debug) System.out.println(" visitInterfaceBound()"); + return this; + } + + public SignatureVisitor visitSuperclass() { + if (debug) System.out.println(" visitSuperclass()"); + state = GenericAwareInfoBuildingVisitor.STATE.SUPERCLASS; + return this; + } + + public SignatureVisitor visitInterface() { + if (debug) System.out.println(" visitInterface()"); + ((ClassInfo) info).getInterfaces().add(""); + state = GenericAwareInfoBuildingVisitor.STATE.INTERFACE; + return this; + } + + public SignatureVisitor visitParameterType() { + if (debug) System.out.println(" visitParameterType()"); + return this; + } + + public SignatureVisitor visitReturnType() { + if (debug) System.out.println(" visitReturnType()"); + return this; + } + + public SignatureVisitor visitExceptionType() { + if (debug) System.out.println(" visitExceptionType()"); + return this; + } + + public void visitBaseType(char c) { + if (debug) System.out.println(" visitBaseType(" + c + ")"); + } + + public void visitTypeVariable(String s) { + if (debug) System.out.println(" visitTypeVariable(" + s + ")"); + } + + public SignatureVisitor visitArrayType() { + if (debug) System.out.println(" visitArrayType()"); + return this; + } + + public void visitClassType(String s) { + if (debug) System.out.println(" visitClassType(" + s + ")"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + if (interfce.length() == 0) { + interfce = javaName(s); + } else { + interfce += javaName(s); + } + interfces.set(idx, interfce); + break; + case SUPERCLASS: + if (!s.equals("java/lang/Object")) { + ((ClassInfo) info).superType = javaName(s); + } + } + } + + public void visitInnerClassType(String s) { + if (debug) System.out.println(" visitInnerClassType(" + s + ")"); + } + + public void visitTypeArgument() { + if (debug) System.out.println(" visitTypeArgument()"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += "<"; + interfces.set(idx, interfce); + } + } + + public SignatureVisitor visitTypeArgument(char c) { + if (debug) System.out.println(" visitTypeArgument(" + c + ")"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += "<"; + interfces.set(idx, interfce); + } + return this; + } + + public void visitEnd() { + if (debug) System.out.println(" visitEnd()"); + switch (state) { + case INTERFACE: + List interfces = ((ClassInfo) info).getInterfaces(); + int idx = interfces.size() - 1; + String interfce = interfces.get(idx); + interfce += ">"; + interfces.set(idx, interfce); + break; + case FORMAL_TYPE_PARAM: + String name = ((ClassInfo) info).name; + if (name.contains("<")) { + ((ClassInfo) info).name += ">"; + } + } + state = GenericAwareInfoBuildingVisitor.STATE.END; + } + + private String javaName(String name) { + return (name == null) ? null : name.replace('/', '.'); + } + + } + +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAnnotationFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAnnotationFinder.java new file mode 100644 index 00000000..a4a2403f --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAnnotationFinder.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.finder; + +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; + +import org.apache.xbean.osgi.bundle.util.BundleResourceFinder; +import org.apache.xbean.osgi.bundle.util.BundleUtils; +import org.apache.xbean.osgi.bundle.util.ResourceDiscoveryFilter; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.PackageAdmin; + +/** + * @version $Rev$ $Date$ + */ +public class BundleAnnotationFinder extends AbstractFinder { + private final Bundle bundle; + private final Set paths; + + public BundleAnnotationFinder(PackageAdmin packageAdmin, Bundle bundle) throws Exception { + this(packageAdmin, bundle, BundleResourceFinder.FULL_DISCOVERY_FILTER); + } + + public BundleAnnotationFinder(PackageAdmin packageAdmin, Bundle bundle, ResourceDiscoveryFilter discoveryFilter) throws Exception { + this(packageAdmin, bundle, discoveryFilter, Collections.emptySet()); + } + + public BundleAnnotationFinder(PackageAdmin packageAdmin, Bundle bundle, ResourceDiscoveryFilter discoveryFilter, Set paths) throws Exception { + this.bundle = BundleUtils.unwrapBundle(bundle); + BundleResourceFinder bundleResourceFinder = new BundleResourceFinder(packageAdmin, this.bundle, "", ".class", discoveryFilter); + bundleResourceFinder.find(new AnnotationFindingCallback()); + this.paths = paths; + } + + @Override + protected URL getResource(String s) { + return bundle.getResource(s); + } + + @Override + protected Class loadClass(String s) throws ClassNotFoundException { + return bundle.loadClass(s); + } + + @Override + public List getAnnotatedClassNames() { + List classNames = new ArrayList(originalInfos.size()); + for (Map.Entry entry: originalInfos.entrySet()) { + if (paths.contains(entry.getValue().getPath())) { + classNames.add(entry.getKey()); + } + } + return classNames; + } + + private class AnnotationFindingCallback implements BundleResourceFinder.ResourceFinderCallback { + + public boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception { + InputStream in = url.openStream(); + try { + readClassDef(in, baseDir); + } finally { + in.close(); + } + return true; + } + + + public boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream in) throws Exception { + readClassDef(in, jarName); + return true; + } + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAssignableClassFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAssignableClassFinder.java new file mode 100644 index 00000000..aadcd193 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/BundleAssignableClassFinder.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.xbean.finder; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.xbean.osgi.bundle.util.BundleClassFinder; +import org.apache.xbean.osgi.bundle.util.BundleDescription; +import org.apache.xbean.osgi.bundle.util.ClassDiscoveryFilter; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @version $Rev$ $Date$ + */ +public class BundleAssignableClassFinder extends BundleClassFinder { + + private static final Logger logger = LoggerFactory.getLogger(BundleAssignableClassFinder.class); + + private Class[] clses; + + private Set targetClassNames = new HashSet(); + + private Set targetInterfaceNames = new HashSet(); + + private Set wiredImportedPackageNames = new HashSet(); + + /** + * Create a new BundleClassFinder, it will search all the classes based the rule defined by the parameters via ASM tool + * @param packageAdmin + * @param bundle + * @param clses + * @param discoveryFilter + */ + public BundleAssignableClassFinder(PackageAdmin packageAdmin, Bundle bundle, Class[] clses, ClassDiscoveryFilter discoveryFilter) { + super(packageAdmin, bundle, discoveryFilter); + if (clses == null || clses.length == 0) { + throw new IllegalArgumentException("At least one class or interface should be specified"); + } + this.clses = clses; + for (Class cls : clses) { + String asmStyleName = cls.getName().replace('.', '/'); + if (cls.isInterface()) { + targetInterfaceNames.add(asmStyleName); + } else { + targetClassNames.add(asmStyleName); + } + } + initialize(); + } + + public BundleAssignableClassFinder(PackageAdmin packageAdmin, Class[] clses, Bundle bundle) { + this(packageAdmin, bundle, clses, FULL_CLASS_DISCOVERY_FILTER); + } + + @Override + protected BundleClassFinder createSubBundleClassFinder(PackageAdmin packageAdmin, Bundle bundle, ClassDiscoveryFilter classDiscoveryFilter) { + return new BundleAssignableClassFinder(packageAdmin, bundle, clses, classDiscoveryFilter); + } + + @Override + protected boolean isClassAcceptable(String name, InputStream in) throws IOException { + ClassReader classReader = new ClassReader(in); + String className = classReader.getClassName(); + if ((classReader.getAccess() & Opcodes.ACC_INTERFACE) == 0) { + if (targetClassNames.contains(className)) { + return true; + } + } else { + if (targetInterfaceNames.contains(className)) { + return true; + } + } + String[] interfaceNames = classReader.getInterfaces(); + try { + for (String interfaceName : interfaceNames) { + if (wiredImportedPackageNames.contains(toASMStylePackageName(interfaceName))) { + return isClassAssignable(bundle.loadClass(toJavaStyleClassName(interfaceName))); + } else { + if (isInterfaceAssignable(interfaceName)) { + return true; + } + } + } + String superClassName = classReader.getSuperName(); + if (wiredImportedPackageNames.contains(toASMStylePackageName(superClassName))) { + return isClassAssignable(bundle.loadClass(toJavaStyleClassName(superClassName))); + } + return isSuperClassAssignable(superClassName); + } catch (ClassNotFoundException e) { + return false; + } + } + + @Override + protected boolean isClassAcceptable(URL url) { + InputStream in = null; + try { + in = url.openStream(); + return isClassAcceptable("", in); + } catch (IOException e) { + logger.warn("Unable to check the class of url " + url, e); + return false; + } finally { + if (in != null) + try { + in.close(); + } catch (Exception e) { + } + } + } + + private void initialize() { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + List imports = description.getExternalImports(); + for (BundleDescription.ImportPackage packageImport : imports) { + String packageName = packageImport.getName(); + ExportedPackage[] exports = packageAdmin.getExportedPackages(packageName); + Bundle wiredBundle = isWired(bundle, exports); + if (wiredBundle != null) { + wiredImportedPackageNames.add(packageName.replace('.', '/')); + break; + } + } + } + + private boolean isClassAssignable(Class cls) { + for (Class targetClass : clses) { + if (targetClass.isAssignableFrom(cls)) { + return true; + } + } + return false; + } + + /** + * + * @param interfaceName The interface name should be in the format of org/test/SimpleInterface + * @return return true if the method parameter interfaceName is assignable to any interface in the expected interfaces + */ + private boolean isInterfaceAssignable(String interfaceName) { + //Check each interface in interfaceNames set + if (targetInterfaceNames.contains(interfaceName)) { + return true; + } + //Check ancestor intefaces + URL url = bundle.getResource(interfaceName + ".class"); + if (url == null) { + //TODO what should we do if we do not find the interface ? + return false; + } + InputStream in = null; + try { + in = url.openStream(); + ClassReader classReader = new ClassReader(in); + String[] superInterfaceNames = classReader.getInterfaces(); + for (String superInterfaceName : superInterfaceNames) { + if (isInterfaceAssignable(superInterfaceName)) { + return true; + } + } + return false; + } catch (IOException e) { + logger.warn("Unable to check the interface " + interfaceName, e); + return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (Exception e) { + } + } + } + } + + /** + * + * @param superClassName The super class name should be in the format of org/test/SimpleClass + * @return return true if the method parameter superClassName is assignable to any interface in the expected interfaces or any class in the expected classes + */ + private boolean isSuperClassAssignable(String superClassName) { + if (targetClassNames.contains(superClassName)) { + return true; + } else if (superClassName.equals("java/lang/Object")) { + return false; + } + + //Check parent class + URL url = bundle.getResource(superClassName + ".class"); + if (url == null) { + //TODO what should we do if we do not find the super class ? + return false; + } + InputStream in = null; + try { + in = url.openStream(); + ClassReader classReader = new ClassReader(in); + + //Check interfaces + String[] superInterfaceNames = classReader.getInterfaces(); + for (String superInterfaceName : superInterfaceNames) { + if (isInterfaceAssignable(superInterfaceName)) { + return true; + } + } + + //Check className + return isSuperClassAssignable(classReader.getSuperName()); + } catch (IOException e) { + logger.warn("Unable to check the super class " + superClassName, e); + return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (Exception e) { + } + } + } + } + + /** + * Get the ASM style package name from the parameter className. + * If the className is ended with .class extension, e.g. /org/apache/geronimo/TestCass.class or org.apache.geronimo.TestClass.class, + * then org/apache/geronimo is returned + * If the className is not ended with .class extension, e.g. /org/apache/geronimo/TestCass or org.apache.geronimo.TestClass, + * then org/apache/geronimo is returned + * @param className + * @return ASM style package name, should be in the format of "org/apache/geronimo" + */ + protected String toASMStylePackageName(String className) { + if (className.endsWith(EXT)) { + className = className.substring(0, className.length() - EXT.length()); + } + className = className.replace('.', '/'); + int iLastDotIndex = className.lastIndexOf('/'); + if (iLastDotIndex != -1) { + return className.substring(0, iLastDotIndex); + } else { + return ""; + } + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/ClassFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/ClassFinder.java new file mode 100644 index 00000000..d45296a7 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/ClassFinder.java @@ -0,0 +1,224 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * ClassFinder searches the classpath of the specified classloader for + * packages, classes, constructors, methods, or fields with specific annotations. + * + * For security reasons ASM is used to find the annotations. Classes are not + * loaded unless they match the requirements of a called findAnnotated* method. + * Once loaded, these classes are cached. + * + * The getClassesNotLoaded() method can be used immediately after any find* + * method to get a list of classes which matched the find requirements (i.e. + * contained the annotation), but were unable to be loaded. + * + * @author David Blevins + * @version $Rev$ $Date$ + */ +public class ClassFinder extends AbstractFinder { + + private final ClassLoader classLoader; + + /** + * Creates a ClassFinder that will search the urls in the specified classloader + * excluding the urls in the classloader's parent. + * + * To include the parent classloader, use: + * + * new ClassFinder(classLoader, false); + * + * To exclude the parent's parent, use: + * + * new ClassFinder(classLoader, classLoader.getParent().getParent()); + * + * @param classLoader source of classes to scan + * @throws Exception if something goes wrong + */ + public ClassFinder(ClassLoader classLoader) throws Exception { + this(classLoader, true); + } + + /** + * Creates a ClassFinder that will search the urls in the specified classloader. + * + * @param classLoader source of classes to scan + * @param excludeParent Allegedly excludes classes from parent classloader, whatever that might mean + * @throws Exception if something goes wrong. + */ + public ClassFinder(ClassLoader classLoader, boolean excludeParent) throws Exception { + this(classLoader, getUrls(classLoader, excludeParent)); + } + + /** + * Creates a ClassFinder that will search the urls in the specified classloader excluding + * the urls in the 'exclude' classloader. + * + * @param classLoader source of classes to scan + * @param exclude source of classes to exclude from scanning + * @throws Exception if something goes wrong + */ + public ClassFinder(ClassLoader classLoader, ClassLoader exclude) throws Exception { + this(classLoader, getUrls(classLoader, exclude)); + } + + public ClassFinder(ClassLoader classLoader, URL url) { + this(classLoader, Arrays.asList(url)); + } + + public ClassFinder(ClassLoader classLoader, Collection urls) { + this.classLoader = classLoader; + + List classNames = new ArrayList(); + for (URL location : urls) { + try { + if (location.getProtocol().equals("jar")) { + classNames.addAll(jar(location)); + } else if (location.getProtocol().equals("file")) { + try { + // See if it's actually a jar + URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); + JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); + juc.getJarFile(); + classNames.addAll(jar(jarUrl)); + } catch (IOException e) { + classNames.addAll(file(location)); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + for (String className : classNames) { + readClassDef(className); + } + } + + public ClassFinder(Class... classes){ + this(Arrays.asList(classes)); + } + + public ClassFinder(List> classes){ + this.classLoader = null; + for (Class clazz : classes) { + try { + readClassDef(clazz); + } catch (NoClassDefFoundError e) { + throw new NoClassDefFoundError("Could not fully load class: " + clazz.getName() + "\n due to:" + e.getMessage() + "\n in classLoader: \n" + clazz.getClassLoader()); + } + } + } + + private static Collection getUrls(ClassLoader classLoader, boolean excludeParent) throws IOException { + return getUrls(classLoader, excludeParent? classLoader.getParent() : null); + } + + private static Collection getUrls(ClassLoader classLoader, ClassLoader excludeParent) throws IOException { + UrlSet urlSet = new UrlSet(classLoader); + if (excludeParent != null){ + urlSet = urlSet.exclude(excludeParent); + } + return urlSet.getUrls(); + } + + @Override + protected URL getResource(String className) { + return classLoader.getResource(className); + } + + @Override + protected Class loadClass(String fixedName) throws ClassNotFoundException { + return classLoader.loadClass(fixedName); + } + + + + private List file(URL location) { + List classNames = new ArrayList(); + File dir = new File(URLDecoder.decode(location.getPath())); + if (dir.getName().equals("META-INF")) { + dir = dir.getParentFile(); // Scrape "META-INF" off + } + if (dir.isDirectory()) { + scanDir(dir, classNames, ""); + } + return classNames; + } + + private void scanDir(File dir, List classNames, String packageName) { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + scanDir(file, classNames, packageName + file.getName() + "."); + } else if (file.getName().endsWith(".class")) { + String name = file.getName(); + name = name.replaceFirst(".class$", ""); + if (name.contains(".")) continue; + classNames.add(packageName + name); + } + } + } + + private List jar(URL location) throws IOException { + String jarPath = location.getFile(); + if (jarPath.indexOf("!") > -1){ + jarPath = jarPath.substring(0, jarPath.indexOf("!")); + } + URL url = new URL(jarPath); + InputStream in = url.openStream(); + try { + JarInputStream jarStream = new JarInputStream(in); + return jar(jarStream); + } finally { + in.close(); + } + } + + private List jar(JarInputStream jarStream) throws IOException { + List classNames = new ArrayList(); + + JarEntry entry; + while ((entry = jarStream.getNextJarEntry()) != null) { + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + String className = entry.getName(); + className = className.replaceFirst(".class$", ""); + if (className.contains(".")) continue; + className = className.replace('/', '.'); + classNames.add(className); + } + + return classNames; + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/IAnnotationFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/IAnnotationFinder.java new file mode 100644 index 00000000..a834a752 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/IAnnotationFinder.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +/** + * Temporary interface to bridge the gap between the two finder impls + * @version $Rev$ $Date$ + */ +public interface IAnnotationFinder { + boolean isAnnotationPresent(Class annotation); + + List getClassesNotLoaded(); + + List findAnnotatedPackages(Class annotation); + + List> findAnnotatedClasses(Class annotation); + + List> findInheritedAnnotatedClasses(Class annotation); + + List findAnnotatedMethods(Class annotation); + + List findAnnotatedConstructors(Class annotation); + + List findAnnotatedFields(Class annotation); + + List> findClassesInPackage(String packageName, boolean recursive); + + List> findSubclasses(Class clazz); + + List> findImplementations(Class clazz); + + List> findMetaAnnotatedMethods(Class annotation); + + List> findMetaAnnotatedFields(Class annotation); + + List>> findMetaAnnotatedClasses(Class annotation); + + List getAnnotatedClassNames(); +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotated.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotated.java new file mode 100644 index 00000000..056b75fd --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotated.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.xbean.finder; + +import java.util.Collection; + +/** + * @version $Rev$ $Date$ + */ +public interface MetaAnnotated extends Annotated { + + public Collection> getMetaAnnotations(); + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedClass.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedClass.java new file mode 100644 index 00000000..1ba1d818 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedClass.java @@ -0,0 +1,294 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.net.URL; +import java.security.ProtectionDomain; + +/** +* @version $Rev$ $Date$ +*/ +public class MetaAnnotatedClass extends MetaAnnotatedElement> { + + public MetaAnnotatedClass(Class clazz) { + super(clazz, unroll(clazz)); + } + + public MetaAnnotatedClass forName(String className) throws ClassNotFoundException { + return to(target.forName(className)); + } + + private MetaAnnotatedClass to(Class clazz) { + return new MetaAnnotatedClass(clazz); + } + + public MetaAnnotatedClass forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { + return to(target.forName(name, initialize, loader)); + } + + public T newInstance() throws InstantiationException, IllegalAccessException { + return target.newInstance(); + } + + public boolean isInstance(Object obj) { + return target.isInstance(obj); + } + + public boolean isAssignableFrom(Class cls) { + return target.isAssignableFrom(cls); + } + + public boolean isInterface() { + return target.isInterface(); + } + + public boolean isArray() { + return target.isArray(); + } + + public boolean isPrimitive() { + return target.isPrimitive(); + } + + public boolean isAnnotation() { + return target.isAnnotation(); + } + + public boolean isSynthetic() { + return target.isSynthetic(); + } + + public String getName() { + return target.getName(); + } + + public ClassLoader getClassLoader() { + return target.getClassLoader(); + } + + public TypeVariable>[] getTypeParameters() { + return target.getTypeParameters(); + } + + public MetaAnnotatedClass getSuperclass() { + return new MetaAnnotatedClass(target.getSuperclass()); + } + + public Type getGenericSuperclass() { + return target.getGenericSuperclass(); + } + + public Package getPackage() { + return target.getPackage(); + } + + public MetaAnnotatedClass[] getInterfaces() { + return to(target.getInterfaces()); + } + + public Type[] getGenericInterfaces() { + return target.getGenericInterfaces(); + } + + public MetaAnnotatedClass getComponentType() { + return to(target.getComponentType()); + } + + public int getModifiers() { + return target.getModifiers(); + } + + public Object[] getSigners() { + return target.getSigners(); + } + + public MetaAnnotatedMethod getEnclosingMethod() { + return to(target.getEnclosingMethod()); + } + + public MetaAnnotatedConstructor getEnclosingConstructor() { + return to(target.getEnclosingConstructor()); + } + + public MetaAnnotatedClass getDeclaringClass() { + return to(target.getDeclaringClass()); + } + + public MetaAnnotatedClass getEnclosingClass() { + return to(target.getEnclosingClass()); + } + + public String getSimpleName() { + return target.getSimpleName(); + } + + public String getCanonicalName() { + return target.getCanonicalName(); + } + + public boolean isAnonymousClass() { + return target.isAnonymousClass(); + } + + public boolean isLocalClass() { + return target.isLocalClass(); + } + + public boolean isMemberClass() { + return target.isMemberClass(); + } + + public MetaAnnotatedClass[] getClasses() { + return to(target.getClasses()); + } + + public MetaAnnotatedField[] getFields() throws SecurityException { + return to(target.getFields()); + } + + public MetaAnnotatedMethod[] getMethods() throws SecurityException { + return to(target.getMethods()); + } + + public MetaAnnotatedConstructor[] getConstructors() throws SecurityException { + return to(target.getConstructors()); + } + + public MetaAnnotatedField getField(String name) throws NoSuchFieldException, SecurityException { + return to(target.getField(name)); + } + + public MetaAnnotatedMethod getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException { + return to(target.getMethod(name, parameterTypes)); + } + + public MetaAnnotatedConstructor getConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException { + return new MetaAnnotatedConstructor(target.getConstructor(parameterTypes)); + } + + public MetaAnnotatedClass[] getDeclaredClasses() throws SecurityException { + return to(target.getDeclaredClasses()); + } + + public MetaAnnotatedField[] getDeclaredFields() throws SecurityException { + return to(target.getDeclaredFields()); + } + + public MetaAnnotatedMethod[] getDeclaredMethods() throws SecurityException { + return to(target.getDeclaredMethods()); + } + + public MetaAnnotatedConstructor[] getDeclaredConstructors() throws SecurityException { + return to(target.getDeclaredConstructors()); + } + + public MetaAnnotatedField getDeclaredField(String name) throws NoSuchFieldException, SecurityException { + return to(target.getDeclaredField(name)); + } + + public MetaAnnotatedMethod getDeclaredMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException { + return to(target.getDeclaredMethod(name, parameterTypes)); + } + + public MetaAnnotatedConstructor getDeclaredConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException { + return new MetaAnnotatedConstructor(target.getDeclaredConstructor(parameterTypes)); + } + + public InputStream getResourceAsStream(String name) { + return target.getResourceAsStream(name); + } + + public URL getResource(String name) { + return target.getResource(name); + } + + public ProtectionDomain getProtectionDomain() { + return target.getProtectionDomain(); + } + + public boolean desiredAssertionStatus() { + return target.desiredAssertionStatus(); + } + + public boolean isEnum() { + return target.isEnum(); + } + + public T[] getEnumConstants() { + return target.getEnumConstants(); + } + + public T cast(Object obj) { + return target.cast(obj); + } + + public Class asSubclass(Class clazz) { + return target.asSubclass(clazz); + } + + private MetaAnnotatedMethod[] to(Method[] a) { + MetaAnnotatedMethod[] b = new MetaAnnotatedMethod[a.length]; + for (int i = 0; i < a.length; i++) { + b[i] = new MetaAnnotatedMethod(a[i]); + } + return b; + } + + private MetaAnnotatedMethod to(Method method) { + return new MetaAnnotatedMethod(method); + } + + private MetaAnnotatedConstructor[] to(Constructor[] a) { + MetaAnnotatedConstructor[] b = new MetaAnnotatedConstructor[a.length]; + for (int i = 0; i < a.length; i++) { + b[i] = new MetaAnnotatedConstructor(a[i]); + } + return b; + } + + private MetaAnnotatedConstructor to(Constructor constructor) { + return new MetaAnnotatedConstructor(constructor); + } + + private MetaAnnotatedClass[] to(Class[] a) { + MetaAnnotatedClass[] b = new MetaAnnotatedClass[a.length]; + for (int i = 0; i < a.length; i++) { + b[i] = to(a[i]); + } + return b; + } + + private MetaAnnotatedField[] to(Field[] a) { + MetaAnnotatedField[] b = new MetaAnnotatedField[a.length]; + for (int i = 0; i < a.length; i++) { + b[i] = new MetaAnnotatedField(a[i]); + } + return b; + } + + private MetaAnnotatedField to(Field field) { + return new MetaAnnotatedField(field); + } + +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedConstructor.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedConstructor.java new file mode 100644 index 00000000..2cfc9d77 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedConstructor.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedConstructor extends MetaAnnotatedElement> implements AnnotatedMethod> { + + private Annotation[][] parameterAnnotations; + + public MetaAnnotatedConstructor(Constructor target) { + super(target, unroll(target)); + this.parameterAnnotations = unrollParameters(target.getParameterAnnotations()); + } + + public Annotation[][] getParameterAnnotations() { + return parameterAnnotations; + } + + public Class getDeclaringClass() { + return get().getDeclaringClass(); + } + + public String getName() { + return get().getName(); + } + + public int getModifiers() { + return get().getModifiers(); + } + + public Class[] getParameterTypes() { + return get().getParameterTypes(); + } + + public java.lang.reflect.Type[] getGenericParameterTypes() { + return get().getGenericParameterTypes(); + } + + public Class[] getExceptionTypes() { + return get().getExceptionTypes(); + } + + public java.lang.reflect.Type[] getGenericExceptionTypes() { + return get().getGenericExceptionTypes(); + } + + public String toGenericString() { + return get().toGenericString(); + } + + public boolean isVarArgs() { + return get().isVarArgs(); + } + + public boolean isSynthetic() { + return get().isSynthetic(); + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedElement.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedElement.java new file mode 100644 index 00000000..9dd267bf --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedElement.java @@ -0,0 +1,292 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; + +/** +* @version $Rev$ $Date$ +*/ +public class MetaAnnotatedElement implements AnnotatedElement, MetaAnnotated { + protected final Map, MetaAnnotation> annotations = new HashMap, MetaAnnotation>(); + protected final T target; + + public MetaAnnotatedElement(T element) { + this(element, unroll(element)); + } + + MetaAnnotatedElement(T target, Map, MetaAnnotation> annotations) { + this.target = target; + this.annotations.putAll(annotations); + } + + public T get() { + return target; + } + + public Annotation[] getDeclaredAnnotations() { + return target.getDeclaredAnnotations(); + } + + public boolean isAnnotationPresent(Class annotationClass) { + return annotations.containsKey(annotationClass); + } + + public T getAnnotation(Class annotationClass) { + MetaAnnotation annotation = (MetaAnnotation) annotations.get(annotationClass); + return (annotation == null) ? null : annotation.get(); + } + + public Annotation[] getAnnotations() { + Annotation[] annotations = new Annotation[this.annotations.size()]; + + int i = 0; + for (MetaAnnotation annotation : this.annotations.values()) { + annotations[i++] = annotation.get(); + } + + return annotations; + } + + public Collection> getMetaAnnotations() { + return Collections.unmodifiableCollection(annotations.values()); + } + + @Override + public boolean equals(Object obj) { + return get().equals(obj); + } + + @Override + public int hashCode() { + return get().hashCode(); + } + + @Override + public String toString() { + return get().toString(); + } + + + private static void unroll(Class clazz, int depth, Map, MetaAnnotation> found) { + if (!isMetaAnnotation(clazz)) return; + + for (Annotation annotation : getDeclaredMetaAnnotations(clazz)) { + Class type = annotation.annotationType(); + + MetaAnnotation existing = found.get(type); + + if (existing != null) { + + if (existing.getDepth() > depth) { + + // OVERWRITE + + found.put(type, new MetaAnnotation(annotation, depth)); + + unroll(type, depth + 1, found); + + } else if (existing.getDepth() < depth) { + + // IGNORE + + // ignore, what we have already is higher priority + + } else { + + // CONFLICT + + // They are the same depth and therefore conflicting + existing.getConflicts().add(new MetaAnnotation(annotation, depth)); + + } + + } else { + + // NEW + + found.put(type, new MetaAnnotation(annotation, depth)); + + unroll(type, depth + 1, found); + + } + } + } + + private static Collection getDeclaredMetaAnnotations(Class clazz) { + + Map map = new HashMap(); + + // pull in the annotations declared on this annotation + + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + map.put(annotation.annotationType(), annotation); + } + + List groups = new ArrayList(); + + Class metatype = getMetatype(clazz); + if (metatype != null) { + try { + Class def = clazz.getClassLoader().loadClass(clazz.getName() + "$$"); + + List elements = new ArrayList(); + + elements.addAll(asList(def.getDeclaredFields())); + elements.addAll(asList(def.getDeclaredConstructors())); + elements.addAll(asList(def.getDeclaredMethods())); + + for (Method method : def.getDeclaredMethods()) { + for (Annotation[] array : method.getParameterAnnotations()) { + groups.add(array); + } + } + + for (Constructor constructor : def.getDeclaredConstructors()) { + for (Annotation[] array : constructor.getParameterAnnotations()) { + groups.add(array); + } + } + + for (AnnotatedElement element : elements) { + groups.add(element.getDeclaredAnnotations()); + } + + for (Annotation[] annotations : groups) { + if (contains(annotations, clazz)) { + for (Annotation annotation : annotations) { + map.put(annotation.annotationType(), annotation); + } + } + } + } catch (ClassNotFoundException e) { + // inner class is optional + } + } + + map.remove(Target.class); + map.remove(Retention.class); + map.remove(Documented.class); + map.remove(metatype); + map.remove(clazz); + + return map.values(); + } + + private static boolean contains(Annotation[] annotations, Class clazz) { + for (Annotation annotation : annotations) { + if (clazz.equals(annotation.annotationType())) return true; + } + return false; + } + + private static Class getMetatype(Class clazz) { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + Class type = annotation.annotationType(); + + if (isMetatypeAnnotation(type)) return type; + } + + return null; + } + + private static boolean isMetaAnnotation(Class clazz) { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + if (isMetatypeAnnotation(annotation.annotationType())) return true; + } + + return false; + } + + private static boolean isMetatypeAnnotation(Class type) { + if (type.getName().equals("javax.annotation.Metatype")) return true; + if (isSelfAnnotated(type, "Metatype")) return true; + + for (Annotation annotation : type.getAnnotations()) { + if (type.getName().equals("javax.annotation.Metaroot")) return true; + if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true; + } + + return false; + } + + private static boolean isSelfAnnotated(Class type, String name) { + return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type); + } + + private static boolean validTarget(Class type) { + final Target target = type.getAnnotation(Target.class); + + if (target == null) return false; + + final ElementType[] targets = target.value(); + + return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE; + } + + protected static Map, MetaAnnotation> unroll(AnnotatedElement element) { + return unroll(element.getDeclaredAnnotations()); + } + + protected static Map, MetaAnnotation> unroll(Annotation[] annotations) { + final Map, MetaAnnotation> map = new HashMap, MetaAnnotation>(); + + for (Annotation annotation : annotations) { + + map.put(annotation.annotationType(), new MetaAnnotation(annotation, 0)); + + unroll(annotation.annotationType(), 1, map); + + } + + return map; + } + + protected Annotation[][] unrollParameters(Annotation[][] parameterAnnotations) { + final Annotation[][] unrolledParameters = new Annotation[parameterAnnotations.length][]; + + int i = 0; + for (Annotation[] annotations : parameterAnnotations) { + final Map, MetaAnnotation> map = unroll(annotations); + + int j = 0; + + final Annotation[] unrolled = new Annotation[map.size()]; + for (MetaAnnotation metaAnnotation : map.values()) { + unrolled[j++] = metaAnnotation.get(); + } + + unrolledParameters[i++] = unrolled; + } + return unrolledParameters; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedField.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedField.java new file mode 100644 index 00000000..23f68594 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedField.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +/** +* @version $Rev$ $Date$ +*/ +public class MetaAnnotatedField extends MetaAnnotatedElement implements AnnotatedMember { + + public MetaAnnotatedField(Field field) { + super(field, unroll(field)); + } + + public Class getDeclaringClass() { + return get().getDeclaringClass(); + } + + public String getName() { + return get().getName(); + } + + public int getModifiers() { + return get().getModifiers(); + } + + public boolean isSynthetic() { + return get().isSynthetic(); + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedMethod.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedMethod.java new file mode 100644 index 00000000..7c17ba63 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotatedMethod.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** +* @version $Rev$ $Date$ +*/ +public class MetaAnnotatedMethod extends MetaAnnotatedElement implements AnnotatedMethod { + + private final Annotation[][] parameterAnnotations; + + public MetaAnnotatedMethod(Method method) { + super(method, unroll(method)); + + this.parameterAnnotations = unrollParameters(method.getParameterAnnotations()); + } + + public Annotation[][] getParameterAnnotations() { + return parameterAnnotations; + } + + public Class getDeclaringClass() { + return target.getDeclaringClass(); + } + + public String getName() { + return target.getName(); + } + + public int getModifiers() { + return target.getModifiers(); + } + + public Class[] getParameterTypes() { + return target.getParameterTypes(); + } + + public java.lang.reflect.Type[] getGenericParameterTypes() { + return target.getGenericParameterTypes(); + } + + public Class[] getExceptionTypes() { + return target.getExceptionTypes(); + } + + public java.lang.reflect.Type[] getGenericExceptionTypes() { + return target.getGenericExceptionTypes(); + } + + public String toGenericString() { + return target.toGenericString(); + } + + public boolean isVarArgs() { + return target.isVarArgs(); + } + + public boolean isSynthetic() { + return target.isSynthetic(); + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotation.java b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotation.java new file mode 100644 index 00000000..17870dba --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotation.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.xbean.finder; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +/** +* @version $Rev$ $Date$ +*/ +public class MetaAnnotation { + private final T annotation; + private final int depth; + + private final List> conflicts = new ArrayList>(); + + MetaAnnotation(T annotation, int depth) { + this.annotation = annotation; + this.depth = depth; + } + + public T get() { + return annotation; + } + + public int getDepth() { + return depth; + } + + public List> getConflicts() { + return conflicts; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/ResourceFinder.java b/xbean-finder/src/main/java/org/apache/xbean/finder/ResourceFinder.java new file mode 100644 index 00000000..ec4c1eb9 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/ResourceFinder.java @@ -0,0 +1,1117 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collections; +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.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @author David Blevins + * @version $Rev$ $Date$ + */ +public class ResourceFinder { + + private final URL[] urls; + private final String path; + private final ClassLoader classLoader; + private final List resourcesNotLoaded = new ArrayList(); + + public ResourceFinder(URL... urls) { + this(null, Thread.currentThread().getContextClassLoader(), urls); + } + + public ResourceFinder(String path) { + this(path, Thread.currentThread().getContextClassLoader(), null); + } + + public ResourceFinder(String path, URL... urls) { + this(path, Thread.currentThread().getContextClassLoader(), urls); + } + + public ResourceFinder(String path, ClassLoader classLoader) { + this(path, classLoader, null); + } + + public ResourceFinder(String path, ClassLoader classLoader, URL... urls) { + if (path == null){ + path = ""; + } else if (path.length() > 0 && !path.endsWith("/")) { + path += "/"; + } + this.path = path; + + if (classLoader == null) { + classLoader = Thread.currentThread().getContextClassLoader(); + } + this.classLoader = classLoader; + + for (int i = 0; urls != null && i < urls.length; i++) { + URL url = urls[i]; + if (url == null || isDirectory(url) || url.getProtocol().equals("jar")) { + continue; + } + try { + urls[i] = new URL("jar", "", -1, url.toString() + "!/"); + } catch (MalformedURLException e) { + } + } + this.urls = (urls == null || urls.length == 0)? null : urls; + } + + private static boolean isDirectory(URL url) { + String file = url.getFile(); + return (file.length() > 0 && file.charAt(file.length() - 1) == '/'); + } + + /** + * Returns a list of resources that could not be loaded in the last invoked findAvailable* or + * mapAvailable* methods. + *

    + * The list will only contain entries of resources that match the requirements + * of the last invoked findAvailable* or mapAvailable* methods, but were unable to be + * loaded and included in their results. + *

    + * The list returned is unmodifiable and the results of this method will change + * after each invocation of a findAvailable* or mapAvailable* methods. + *

    + * This method is not thread safe. + */ + public List getResourcesNotLoaded() { + return Collections.unmodifiableList(resourcesNotLoaded); + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public URL find(String uri) throws IOException { + String fullUri = path + uri; + + URL resource = getResource(fullUri); + if (resource == null) { + throw new IOException("Could not find resource '" + path + uri + "'"); + } + + return resource; + } + + public List findAll(String uri) throws IOException { + String fullUri = path + uri; + + Enumeration resources = getResources(fullUri); + List list = new ArrayList(); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + list.add(url); + } + return list; + } + + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find String + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + /** + * Reads the contents of the URL as a {@link String}'s and returns it. + * + * @param uri + * @return a stringified content of a resource + * @throws IOException if a resource pointed out by the uri param could not be find + * @see ClassLoader#getResource(String) + */ + public String findString(String uri) throws IOException { + String fullUri = path + uri; + + URL resource = getResource(fullUri); + if (resource == null) { + throw new IOException("Could not find a resource in : " + fullUri); + } + + return readContents(resource); + } + + /** + * Reads the contents of the found URLs as a list of {@link String}'s and returns them. + * + * @param uri + * @return a list of the content of each resource URL found + * @throws IOException if any of the found URLs are unable to be read. + */ + public List findAllStrings(String uri) throws IOException { + String fulluri = path + uri; + + List strings = new ArrayList(); + + Enumeration resources = getResources(fulluri); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + String string = readContents(url); + strings.add(string); + } + return strings; + } + + /** + * Reads the contents of the found URLs as a Strings and returns them. + * Individual URLs that cannot be read are skipped and added to the + * list of 'resourcesNotLoaded' + * + * @param uri + * @return a list of the content of each resource URL found + * @throws IOException if classLoader.getResources throws an exception + */ + public List findAvailableStrings(String uri) throws IOException { + resourcesNotLoaded.clear(); + String fulluri = path + uri; + + List strings = new ArrayList(); + + Enumeration resources = getResources(fulluri); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + try { + String string = readContents(url); + strings.add(string); + } catch (IOException notAvailable) { + resourcesNotLoaded.add(url.toExternalForm()); + } + } + return strings; + } + + /** + * Reads the contents of all non-directory URLs immediately under the specified + * location and returns them in a map keyed by the file name. + *

    + * Any URLs that cannot be read will cause an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/serializables/one + * META-INF/serializables/two + * META-INF/serializables/three + * META-INF/serializables/four/foo.txt + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAvailableStrings("serializables"); + * map.contains("one"); // true + * map.contains("two"); // true + * map.contains("three"); // true + * map.contains("four"); // false + * + * @param uri + * @return a list of the content of each resource URL found + * @throws IOException if any of the urls cannot be read + */ + public Map mapAllStrings(String uri) throws IOException { + Map strings = new HashMap(); + Map resourcesMap = getResourcesMap(uri); + for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + URL url = (URL) entry.getValue(); + String value = readContents(url); + strings.put(name, value); + } + return strings; + } + + /** + * Reads the contents of all non-directory URLs immediately under the specified + * location and returns them in a map keyed by the file name. + *

    + * Individual URLs that cannot be read are skipped and added to the + * list of 'resourcesNotLoaded' + *

    + * Example classpath: + *

    + * META-INF/serializables/one + * META-INF/serializables/two # not readable + * META-INF/serializables/three + * META-INF/serializables/four/foo.txt + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAvailableStrings("serializables"); + * map.contains("one"); // true + * map.contains("two"); // false + * map.contains("three"); // true + * map.contains("four"); // false + * + * @param uri + * @return a list of the content of each resource URL found + * @throws IOException if classLoader.getResources throws an exception + */ + public Map mapAvailableStrings(String uri) throws IOException { + resourcesNotLoaded.clear(); + Map strings = new HashMap(); + Map resourcesMap = getResourcesMap(uri); + for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + URL url = (URL) entry.getValue(); + try { + String value = readContents(url); + strings.put(name, value); + } catch (IOException notAvailable) { + resourcesNotLoaded.add(url.toExternalForm()); + } + } + return strings; + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Class + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + /** + * Executes {@link #findString(String)} assuming the contents URL found is the name of + * a class that should be loaded and returned. + * + * @param uri + * @return + * @throws IOException + * @throws ClassNotFoundException + */ + public Class findClass(String uri) throws IOException, ClassNotFoundException { + String className = findString(uri); + return classLoader.loadClass(className); + } + + /** + * Executes findAllStrings assuming the strings are + * the names of a classes that should be loaded and returned. + *

    + * Any URL or class that cannot be loaded will cause an exception to be thrown. + * + * @param uri + * @return + * @throws IOException + * @throws ClassNotFoundException + */ + public List> findAllClasses(String uri) throws IOException, ClassNotFoundException { + List> classes = new ArrayList>(); + List strings = findAllStrings(uri); + for (String className : strings) { + Class clazz = classLoader.loadClass(className); + classes.add(clazz); + } + return classes; + } + + /** + * Executes findAvailableStrings assuming the strings are + * the names of a classes that should be loaded and returned. + *

    + * Any class that cannot be loaded will be skipped and placed in the + * 'resourcesNotLoaded' collection. + * + * @param uri + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public List> findAvailableClasses(String uri) throws IOException { + resourcesNotLoaded.clear(); + List> classes = new ArrayList>(); + List strings = findAvailableStrings(uri); + for (String className : strings) { + try { + Class clazz = classLoader.loadClass(className); + classes.add(clazz); + } catch (Exception notAvailable) { + resourcesNotLoaded.add(className); + } + } + return classes; + } + + /** + * Executes mapAllStrings assuming the value of each entry in the + * map is the name of a class that should be loaded. + *

    + * Any class that cannot be loaded will be cause an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/xmlparsers/xerces + * META-INF/xmlparsers/crimson + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAvailableStrings("xmlparsers"); + * map.contains("xerces"); // true + * map.contains("crimson"); // true + * Class xercesClass = map.get("xerces"); + * Class crimsonClass = map.get("crimson"); + * + * @param uri + * @return + * @throws IOException + * @throws ClassNotFoundException + */ + public Map> mapAllClasses(String uri) throws IOException, ClassNotFoundException { + Map> classes = new HashMap>(); + Map map = mapAllStrings(uri); + for (Map.Entry entry : map.entrySet()) { + String string = entry.getKey(); + String className = entry.getValue(); + Class clazz = classLoader.loadClass(className); + classes.put(string, clazz); + } + return classes; + } + + /** + * Executes mapAvailableStrings assuming the value of each entry in the + * map is the name of a class that should be loaded. + *

    + * Any class that cannot be loaded will be skipped and placed in the + * 'resourcesNotLoaded' collection. + *

    + * Example classpath: + *

    + * META-INF/xmlparsers/xerces + * META-INF/xmlparsers/crimson + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAvailableStrings("xmlparsers"); + * map.contains("xerces"); // true + * map.contains("crimson"); // true + * Class xercesClass = map.get("xerces"); + * Class crimsonClass = map.get("crimson"); + * + * @param uri + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public Map> mapAvailableClasses(String uri) throws IOException { + resourcesNotLoaded.clear(); + Map> classes = new HashMap>(); + Map map = mapAvailableStrings(uri); + for (Map.Entry entry : map.entrySet()) { + String string = entry.getKey(); + String className = entry.getValue(); + try { + Class clazz = classLoader.loadClass(className); + classes.put(string, clazz); + } catch (Exception notAvailable) { + resourcesNotLoaded.add(className); + } + } + return classes; + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Implementation + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + /** + * Assumes the class specified points to a file in the classpath that contains + * the name of a class that implements or is a subclass of the specfied class. + *

    + * Any class that cannot be loaded will be cause an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream + * META-INF/java.io.OutputStream + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Class clazz = finder.findImplementation(java.io.InputStream.class); + * clazz.getName(); // returns "org.acme.AcmeInputStream" + * + * @param interfase a superclass or interface + * @return + * @throws IOException if the URL cannot be read + * @throws ClassNotFoundException if the class found is not loadable + * @throws ClassCastException if the class found is not assignable to the specified superclass or interface + */ + public Class findImplementation(Class interfase) throws IOException, ClassNotFoundException { + String className = findString(interfase.getName()); + Class impl = classLoader.loadClass(className); + if (!interfase.isAssignableFrom(impl)) { + throw new ClassCastException("Class not of type: " + interfase.getName()); + } + return impl; + } + + /** + * Assumes the class specified points to a file in the classpath that contains + * the name of a class that implements or is a subclass of the specfied class. + *

    + * Any class that cannot be loaded or assigned to the specified interface will be cause + * an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream + * META-INF/java.io.InputStream # contains the classname org.widget.NeatoInputStream + * META-INF/java.io.InputStream # contains the classname com.foo.BarInputStream + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List classes = finder.findAllImplementations(java.io.InputStream.class); + * classes.contains("org.acme.AcmeInputStream"); // true + * classes.contains("org.widget.NeatoInputStream"); // true + * classes.contains("com.foo.BarInputStream"); // true + * + * @param interfase a superclass or interface + * @return + * @throws IOException if the URL cannot be read + * @throws ClassNotFoundException if the class found is not loadable + * @throws ClassCastException if the class found is not assignable to the specified superclass or interface + */ + public List> findAllImplementations(Class interfase) throws IOException, ClassNotFoundException { + List> implementations = new ArrayList>(); + List strings = findAllStrings(interfase.getName()); + for (String className : strings) { + Class impl = classLoader.loadClass(className).asSubclass(interfase); + implementations.add(impl); + } + return implementations; + } + + /** + * Assumes the class specified points to a file in the classpath that contains + * the name of a class that implements or is a subclass of the specfied class. + *

    + * Any class that cannot be loaded or are not assignable to the specified class will be + * skipped and placed in the 'resourcesNotLoaded' collection. + *

    + * Example classpath: + *

    + * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream + * META-INF/java.io.InputStream # contains the classname org.widget.NeatoInputStream + * META-INF/java.io.InputStream # contains the classname com.foo.BarInputStream + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List classes = finder.findAllImplementations(java.io.InputStream.class); + * classes.contains("org.acme.AcmeInputStream"); // true + * classes.contains("org.widget.NeatoInputStream"); // true + * classes.contains("com.foo.BarInputStream"); // true + * + * @param interfase a superclass or interface + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public List> findAvailableImplementations(Class interfase) throws IOException { + resourcesNotLoaded.clear(); + List> implementations = new ArrayList>(); + List strings = findAvailableStrings(interfase.getName()); + for (String className : strings) { + try { + Class impl = classLoader.loadClass(className); + if (interfase.isAssignableFrom(impl)) { + implementations.add(impl.asSubclass(interfase)); + } else { + resourcesNotLoaded.add(className); + } + } catch (Exception notAvailable) { + resourcesNotLoaded.add(className); + } + } + return implementations; + } + + /** + * Assumes the class specified points to a directory in the classpath that holds files + * containing the name of a class that implements or is a subclass of the specfied class. + *

    + * Any class that cannot be loaded or assigned to the specified interface will be cause + * an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/java.net.URLStreamHandler/jar + * META-INF/java.net.URLStreamHandler/file + * META-INF/java.net.URLStreamHandler/http + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class); + * Class jarUrlHandler = map.get("jar"); + * Class fileUrlHandler = map.get("file"); + * Class httpUrlHandler = map.get("http"); + * + * @param interfase a superclass or interface + * @return + * @throws IOException if the URL cannot be read + * @throws ClassNotFoundException if the class found is not loadable + * @throws ClassCastException if the class found is not assignable to the specified superclass or interface + */ + public Map> mapAllImplementations(Class interfase) throws IOException, ClassNotFoundException { + Map> implementations = new HashMap>(); + Map map = mapAllStrings(interfase.getName()); + for (Map.Entry entry : map.entrySet()) { + String string = entry.getKey(); + String className = entry.getValue(); + Class impl = classLoader.loadClass(className).asSubclass(interfase); + implementations.put(string, impl); + } + return implementations; + } + + /** + * Assumes the class specified points to a directory in the classpath that holds files + * containing the name of a class that implements or is a subclass of the specfied class. + *

    + * Any class that cannot be loaded or are not assignable to the specified class will be + * skipped and placed in the 'resourcesNotLoaded' collection. + *

    + * Example classpath: + *

    + * META-INF/java.net.URLStreamHandler/jar + * META-INF/java.net.URLStreamHandler/file + * META-INF/java.net.URLStreamHandler/http + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class); + * Class jarUrlHandler = map.get("jar"); + * Class fileUrlHandler = map.get("file"); + * Class httpUrlHandler = map.get("http"); + * + * @param interfase a superclass or interface + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public Map> mapAvailableImplementations(Class interfase) throws IOException { + resourcesNotLoaded.clear(); + Map> implementations = new HashMap>(); + Map map = mapAvailableStrings(interfase.getName()); + for (Map.Entry entry : map.entrySet()) { + String string = entry.getKey(); + String className = entry.getValue(); + try { + Class impl = classLoader.loadClass(className); + if (interfase.isAssignableFrom(impl)) { + implementations.put(string, impl.asSubclass(interfase)); + } else { + resourcesNotLoaded.add(className); + } + } catch (Exception notAvailable) { + resourcesNotLoaded.add(className); + } + } + return implementations; + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Properties + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + /** + * Finds the corresponding resource and reads it in as a properties file + *

    + * Example classpath: + *

    + * META-INF/widget.properties + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * Properties widgetProps = finder.findProperties("widget.properties"); + * + * @param uri + * @return + * @throws IOException if the URL cannot be read or is not in properties file format + */ + public Properties findProperties(String uri) throws IOException { + String fulluri = path + uri; + + URL resource = getResource(fulluri); + if (resource == null) { + throw new IOException("Could not find resource: " + fulluri); + } + + return loadProperties(resource); + } + + /** + * Finds the corresponding resources and reads them in as a properties files + *

    + * Any URL that cannot be read in as a properties file will cause an exception to be thrown. + *

    + * Example classpath: + *

    + * META-INF/app.properties + * META-INF/app.properties + * META-INF/app.properties + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List appProps = finder.findAllProperties("app.properties"); + * + * @param uri + * @return + * @throws IOException if the URL cannot be read or is not in properties file format + */ + public List findAllProperties(String uri) throws IOException { + String fulluri = path + uri; + + List properties = new ArrayList(); + + Enumeration resources = getResources(fulluri); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + Properties props = loadProperties(url); + properties.add(props); + } + return properties; + } + + /** + * Finds the corresponding resources and reads them in as a properties files + *

    + * Any URL that cannot be read in as a properties file will be added to the + * 'resourcesNotLoaded' collection. + *

    + * Example classpath: + *

    + * META-INF/app.properties + * META-INF/app.properties + * META-INF/app.properties + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List appProps = finder.findAvailableProperties("app.properties"); + * + * @param uri + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public List findAvailableProperties(String uri) throws IOException { + resourcesNotLoaded.clear(); + String fulluri = path + uri; + + List properties = new ArrayList(); + + Enumeration resources = getResources(fulluri); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + try { + Properties props = loadProperties(url); + properties.add(props); + } catch (Exception notAvailable) { + resourcesNotLoaded.add(url.toExternalForm()); + } + } + return properties; + } + + /** + * Finds the corresponding resources and reads them in as a properties files + *

    + * Any URL that cannot be read in as a properties file will cause an exception to be thrown. + *

    + * Example classpath: + *

    + - META-INF/jdbcDrivers/oracle.properties + - META-INF/jdbcDrivers/mysql.props + - META-INF/jdbcDrivers/derby + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List driversList = finder.findAvailableProperties("jdbcDrivers"); + * Properties oracleProps = driversList.get("oracle.properties"); + * Properties mysqlProps = driversList.get("mysql.props"); + * Properties derbyProps = driversList.get("derby"); + * + * @param uri + * @return + * @throws IOException if the URL cannot be read or is not in properties file format + */ + public Map mapAllProperties(String uri) throws IOException { + Map propertiesMap = new HashMap(); + Map map = getResourcesMap(uri); + for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String string = (String) entry.getKey(); + URL url = (URL) entry.getValue(); + Properties properties = loadProperties(url); + propertiesMap.put(string, properties); + } + return propertiesMap; + } + + /** + * Finds the corresponding resources and reads them in as a properties files + *

    + * Any URL that cannot be read in as a properties file will be added to the + * 'resourcesNotLoaded' collection. + *

    + * Example classpath: + *

    + * META-INF/jdbcDrivers/oracle.properties + * META-INF/jdbcDrivers/mysql.props + * META-INF/jdbcDrivers/derby + *

    + * ResourceFinder finder = new ResourceFinder("META-INF/"); + * List driversList = finder.findAvailableProperties("jdbcDrivers"); + * Properties oracleProps = driversList.get("oracle.properties"); + * Properties mysqlProps = driversList.get("mysql.props"); + * Properties derbyProps = driversList.get("derby"); + * + * @param uri + * @return + * @throws IOException if classLoader.getResources throws an exception + */ + public Map mapAvailableProperties(String uri) throws IOException { + resourcesNotLoaded.clear(); + Map propertiesMap = new HashMap(); + Map map = getResourcesMap(uri); + for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String string = (String) entry.getKey(); + URL url = (URL) entry.getValue(); + try { + Properties properties = loadProperties(url); + propertiesMap.put(string, properties); + } catch (Exception notAvailable) { + resourcesNotLoaded.add(url.toExternalForm()); + } + } + return propertiesMap; + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Map Resources + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public Map getResourcesMap(String uri) throws IOException { + String basePath = path + uri; + + Map resources = new HashMap(); + if (!basePath.endsWith("/")) { + basePath += "/"; + } + Enumeration urls = getResources(basePath); + + while (urls.hasMoreElements()) { + URL location = urls.nextElement(); + + try { + if (location.getProtocol().equals("jar")) { + + readJarEntries(location, basePath, resources); + + } else if (location.getProtocol().equals("file")) { + + readDirectoryEntries(location, resources); + + } + } catch (Exception e) { + } + } + + return resources; + } + + private static void readDirectoryEntries(URL location, Map resources) throws MalformedURLException { + File dir = new File(decode(location.getPath())); + if (dir.isDirectory()) { + File[] files = dir.listFiles(); + for (File file : files) { + if (!file.isDirectory()) { + String name = file.getName(); + URL url = file.toURI().toURL(); + resources.put(name, url); + } + } + } + } + + private static void readJarEntries(URL location, String basePath, Map resources) throws IOException { + JarURLConnection conn = (JarURLConnection) location.openConnection(); + JarFile jarfile = null; + jarfile = conn.getJarFile(); + + Enumeration entries = jarfile.entries(); + while (entries != null && entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (entry.isDirectory() || !name.startsWith(basePath) || name.length() == basePath.length()) { + continue; + } + + name = name.substring(basePath.length()); + + if (name.contains("/")) { + continue; + } + + URL resource = new URL(location, name); + resources.put(name, resource); + } + } + + private Properties loadProperties(URL resource) throws IOException { + InputStream in = resource.openStream(); + + BufferedInputStream reader = null; + try { + reader = new BufferedInputStream(in); + Properties properties = new Properties(); + properties.load(reader); + + return properties; + } finally { + try { + in.close(); + reader.close(); + } catch (Exception e) { + } + } + } + + private String readContents(URL resource) throws IOException { + InputStream in = resource.openStream(); + BufferedInputStream reader = null; + StringBuffer sb = new StringBuffer(); + + try { + reader = new BufferedInputStream(in); + + int b = reader.read(); + while (b != -1) { + sb.append((char) b); + b = reader.read(); + } + + return sb.toString().trim(); + } finally { + try { + in.close(); + reader.close(); + } catch (Exception e) { + } + } + } + + public URL getResource(String fullUri) { + if (urls == null){ + return classLoader.getResource(fullUri); + } + return findResource(fullUri, urls); + } + + private Enumeration getResources(String fulluri) throws IOException { + if (urls == null) { + return classLoader.getResources(fulluri); + } + Vector resources = new Vector(); + for (URL url : urls) { + URL resource = findResource(fulluri, url); + if (resource != null){ + resources.add(resource); + } + } + return resources.elements(); + } + + private URL findResource(String resourceName, URL... search) { + for (int i = 0; i < search.length; i++) { + URL currentUrl = search[i]; + if (currentUrl == null) { + continue; + } + + try { + String protocol = currentUrl.getProtocol(); + if (protocol.equals("jar")) { + /* + * If the connection for currentUrl or resURL is + * used, getJarFile() will throw an exception if the + * entry doesn't exist. + */ + URL jarURL = ((JarURLConnection) currentUrl.openConnection()).getJarFileURL(); + JarFile jarFile; + JarURLConnection juc; + try { + juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection(); + jarFile = juc.getJarFile(); + } catch (IOException e) { + // Don't look for this jar file again + search[i] = null; + throw e; + } + + try { + juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection(); + jarFile = juc.getJarFile(); + String entryName; + if (currentUrl.getFile().endsWith("!/")) { + entryName = resourceName; + } else { + String file = currentUrl.getFile(); + int sepIdx = file.lastIndexOf("!/"); + if (sepIdx == -1) { + // Invalid URL, don't look here again + search[i] = null; + continue; + } + sepIdx += 2; + StringBuffer sb = new StringBuffer(file.length() - sepIdx + resourceName.length()); + sb.append(file.substring(sepIdx)); + sb.append(resourceName); + entryName = sb.toString(); + } + if (entryName.equals("META-INF/") && jarFile.getEntry("META-INF/MANIFEST.MF") != null) { + return targetURL(currentUrl, "META-INF/MANIFEST.MF"); + } + if (jarFile.getEntry(entryName) != null) { + return targetURL(currentUrl, resourceName); + } + } finally { + if (!juc.getUseCaches()) { + try { + jarFile.close(); + } catch (Exception e) { + } + } + } + + } else if (protocol.equals("file")) { + String baseFile = currentUrl.getFile(); + String host = currentUrl.getHost(); + int hostLength = 0; + if (host != null) { + hostLength = host.length(); + } + StringBuffer buf = new StringBuffer(2 + hostLength + baseFile.length() + resourceName.length()); + + if (hostLength > 0) { + buf.append("//").append(host); + } + // baseFile always ends with '/' + buf.append(baseFile); + String fixedResName = resourceName; + // Do not create a UNC path, i.e. \\host + while (fixedResName.startsWith("/") || fixedResName.startsWith("\\")) { + fixedResName = fixedResName.substring(1); + } + buf.append(fixedResName); + String filename = buf.toString(); + File file = new File(filename); + File file2 = new File(decode(filename)); + + if (file.exists() || file2.exists()) { + return targetURL(currentUrl, fixedResName); + } + } else { + URL resourceURL = targetURL(currentUrl, resourceName); + URLConnection urlConnection = resourceURL.openConnection(); + + try { + urlConnection.getInputStream().close(); + } catch (SecurityException e) { + return null; + } + // HTTP can return a stream on a non-existent file + // So check for the return code; + if (!resourceURL.getProtocol().equals("http")) { + return resourceURL; + } + + int code = ((HttpURLConnection) urlConnection).getResponseCode(); + if (code >= 200 && code < 300) { + return resourceURL; + } + } + } catch (MalformedURLException e) { + // Keep iterating through the URL list + } catch (IOException e) { + } catch (SecurityException e) { + } + } + return null; + } + + private URL targetURL(URL base, String name) throws MalformedURLException { + StringBuffer sb = new StringBuffer(base.getFile().length() + name.length()); + sb.append(base.getFile()); + sb.append(name); + String file = sb.toString(); + return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null); + } + + public static String decode(String fileName) { + if (fileName.indexOf('%') == -1) return fileName; + + StringBuilder result = new StringBuilder(fileName.length()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + for (int i = 0; i < fileName.length();) { + char c = fileName.charAt(i); + + if (c == '%') { + out.reset(); + do { + if (i + 2 >= fileName.length()) { + throw new IllegalArgumentException("Incomplete % sequence at: " + i); + } + + int d1 = Character.digit(fileName.charAt(i + 1), 16); + int d2 = Character.digit(fileName.charAt(i + 2), 16); + + if (d1 == -1 || d2 == -1) { + throw new IllegalArgumentException("Invalid % sequence (" + fileName.substring(i, i + 3) + ") at: " + String.valueOf(i)); + } + + out.write((byte) ((d1 << 4) + d2)); + + i += 3; + + } while (i < fileName.length() && fileName.charAt(i) == '%'); + + + result.append(out.toString()); + + continue; + } else { + result.append(c); + } + + i++; + } + return result.toString(); + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/UriSet.java b/xbean-finder/src/main/java/org/apache/xbean/finder/UriSet.java new file mode 100644 index 00000000..c6163778 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/UriSet.java @@ -0,0 +1,149 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import org.apache.xbean.finder.filter.Filter; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.apache.xbean.finder.filter.Filters.invert; +import static org.apache.xbean.finder.filter.Filters.patterns; + +/** + * @version $Rev$ $Date$ + */ +public class UriSet implements Iterable { + + private final Map URIs; + + public UriSet(URI... URIs) { + this(Arrays.asList(URIs)); + } + + public UriSet(Collection URIs) { + this.URIs = new HashMap(); + for (URI location : URIs) { + this.URIs.put(location.toASCIIString(), location); + } + } + + private UriSet(Map URIs) { + this.URIs = URIs; + } + + public UriSet include(String pattern) { + return filter(patterns(pattern)); + } + + public UriSet include(UriSet URISet) { + Map URIs = new HashMap(this.URIs); + URIs.putAll(URISet.URIs); + return new UriSet(URIs); + } + + + public UriSet include(URI URI) { + Map URIs = new HashMap(this.URIs); + URIs.put(URI.toASCIIString(), URI); + return new UriSet(URIs); + } + + public UriSet exclude(UriSet URISet) { + Map URIs = new HashMap(this.URIs); + Map parentURIs = URISet.URIs; + for (String URI : parentURIs.keySet()) { + URIs.remove(URI); + } + return new UriSet(URIs); + } + + public UriSet exclude(URI URI) { + Map URIs = new HashMap(this.URIs); + URIs.remove(URI.toASCIIString()); + return new UriSet(URIs); + } + + public UriSet exclude(File file) { + return exclude(relative(file)); + } + + public UriSet exclude(String pattern) { + return filter(invert(patterns(pattern))); + } + + public UriSet excludePaths(String pathString) { + String[] paths = pathString.split(File.pathSeparator); + UriSet URISet = this; + for (String path : paths) { + File file = new File(path); + URISet = URISet.exclude(file); + } + return URISet; + } + + public UriSet filter(Filter filter) { + Map URIs = new HashMap(); + for (Map.Entry entry : this.URIs.entrySet()) { + String URI = entry.getKey(); + if (filter.accept(URI)) { + URIs.put(URI, entry.getValue()); + } + } + return new UriSet(URIs); + } + + public UriSet matching(String pattern) { + return filter(patterns(pattern)); + } + + public UriSet relative(File file) { + String URIPath = file.toURI().toASCIIString(); + Map URIs = new HashMap(); + for (Map.Entry entry : this.URIs.entrySet()) { + String URI = entry.getKey(); + if (URI.startsWith(URIPath) || URI.startsWith("jar:" + URIPath)) { + URIs.put(URI, entry.getValue()); + } + } + return new UriSet(URIs); + } + + public List getURIs() { + return new ArrayList(URIs.values()); + } + + public int size() { + return URIs.size(); + } + + public Iterator iterator() { + return getURIs().iterator(); + } + + @Override + public String toString() { + return super.toString() + "[" + URIs.size() + "]"; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/UrlSet.java b/xbean-finder/src/main/java/org/apache/xbean/finder/UrlSet.java new file mode 100644 index 00000000..9409f62d --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/UrlSet.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.xbean.finder; + +import org.apache.xbean.finder.filter.Filter; + +import java.net.URL; +import java.net.MalformedURLException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.io.IOException; +import java.io.File; + +import static org.apache.xbean.finder.filter.Filters.invert; +import static org.apache.xbean.finder.filter.Filters.patterns; + +/** + * @version $Rev$ $Date$ + */ +public class UrlSet implements Iterable { + + private final Map urls; + + public UrlSet(ClassLoader classLoader) throws IOException { + this(getUrls(classLoader)); + } + + public UrlSet(URL... urls){ + this(Arrays.asList(urls)); + } + /** + * Ignores all URLs that are not "jar" or "file" + * @param urls + */ + public UrlSet(Collection urls){ + this.urls = new HashMap(); + for (URL location : urls) { + try { +// if (location.getProtocol().equals("file")) { +// try { +// // See if it's actually a jar +// URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); +// JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); +// juc.getJarFile(); +// location = jarUrl; +// } catch (IOException e) { +// } +// this.urls.put(location.toExternalForm(), location); +// } + this.urls.put(location.toExternalForm(), location); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private UrlSet(Map urls) { + this.urls = urls; + } + + public UrlSet include(UrlSet urlSet){ + Map urls = new HashMap(this.urls); + urls.putAll(urlSet.urls); + return new UrlSet(urls); + } + + + public UrlSet include(URL url){ + Map urls = new HashMap(this.urls); + urls.put(url.toExternalForm(), url); + return new UrlSet(urls); + } + + public UrlSet exclude(UrlSet urlSet) { + Map urls = new HashMap(this.urls); + Map parentUrls = urlSet.urls; + for (String url : parentUrls.keySet()) { + urls.remove(url); + } + return new UrlSet(urls); + } + + public UrlSet exclude(URL url) { + Map urls = new HashMap(this.urls); + urls.remove(url.toExternalForm()); + return new UrlSet(urls); + } + + public UrlSet exclude(ClassLoader parent) throws IOException { + return exclude(new UrlSet(parent)); + } + + public UrlSet exclude(File file) throws MalformedURLException { + return exclude(relative(file)); + } + + public UrlSet exclude(String pattern) throws MalformedURLException { + return filter(invert(patterns(pattern))); + } + + /** + * Calls excludePaths(System.getProperty("java.ext.dirs")) + * @return + * @throws MalformedURLException + */ + public UrlSet excludeJavaExtDirs() throws MalformedURLException { + String extDirs = System.getProperty("java.ext.dirs"); + return extDirs == null ? this : excludePaths(extDirs); + } + + /** + * Calls excludePaths(System.getProperty("java.endorsed.dirs")) + * + * @return + * @throws MalformedURLException + */ + public UrlSet excludeJavaEndorsedDirs() throws MalformedURLException { + String endorsedDirs = System.getProperty("java.endorsed.dirs"); + return endorsedDirs == null ? this : excludePaths(endorsedDirs); + } + + public UrlSet excludeJavaHome() throws MalformedURLException { + String path = System.getProperty("java.home"); + + File java = new File(path); + + if (path.matches("/System/Library/Frameworks/JavaVM.framework/Versions/[^/]+/Home")){ + java = java.getParentFile(); + } + + return exclude(java); + } + + public UrlSet excludePaths(String pathString) throws MalformedURLException { + String[] paths = pathString.split(File.pathSeparator); + UrlSet urlSet = this; + for (String path : paths) { + File file = new File(path); + urlSet = urlSet.exclude(file); + } + return urlSet; + } + + public UrlSet filter(Filter filter) { + Map urls = new HashMap(); + for (Map.Entry entry : this.urls.entrySet()) { + String url = entry.getKey(); + if (filter.accept(url)){ + urls.put(url, entry.getValue()); + } + } + return new UrlSet(urls); + } + + public UrlSet matching(String pattern) { + return filter(patterns(pattern)); + } + + public UrlSet relative(File file) throws MalformedURLException { + String urlPath = file.toURI().toURL().toExternalForm(); + Map urls = new HashMap(); + for (Map.Entry entry : this.urls.entrySet()) { + String url = entry.getKey(); + if (url.startsWith(urlPath) || url.startsWith("jar:"+urlPath)){ + urls.put(url, entry.getValue()); + } + } + return new UrlSet(urls); + } + + public List getUrls() { + return new ArrayList(urls.values()); + } + + public int size() { + return urls.size(); + } + + public Iterator iterator() { + return getUrls().iterator(); + } + + private static List getUrls(ClassLoader classLoader) throws IOException { + List list = new ArrayList(); + ArrayList urls = Collections.list(classLoader.getResources("META-INF")); + for (URL url : urls) { + String externalForm = url.toExternalForm(); + int i = externalForm.lastIndexOf("META-INF"); + externalForm = externalForm.substring(0, i); + url = new URL(externalForm); + list.add(url); + } + list.addAll(Collections.list(classLoader.getResources(""))); + return list; + } + + @Override + public String toString() { + return super.toString() + "[" + urls.size() + "]"; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/Archive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/Archive.java new file mode 100644 index 00000000..82eb04f3 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/Archive.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.xbean.finder.archive; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @version $Rev$ $Date$ + */ +public interface Archive extends Iterable { + + InputStream getBytecode(String className) throws IOException, ClassNotFoundException; + + Class loadClass(String className) throws ClassNotFoundException; + + public interface Entry { + String getName(); + InputStream getBytecode() throws IOException; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ArchiveIterator.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ArchiveIterator.java new file mode 100644 index 00000000..8dd64b5c --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ArchiveIterator.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.xbean.finder.archive; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +/** +* @version $Rev$ $Date$ +*/ +public class ArchiveIterator implements Iterator { + private final Iterator classes; + private final Archive archive; + + public ArchiveIterator(Archive archive, Iterator classes) { + this.archive = archive; + this.classes = classes; + } + + public boolean hasNext() { + return classes.hasNext(); + } + + public Archive.Entry next() { + final String name = classes.next(); + return new Archive.Entry() { + public String getName() { + return name; + } + + public InputStream getBytecode() throws IOException { + try { + return archive.getBytecode(name); + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + }; + } + + public void remove() { + classes.remove(); + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/BundleArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/BundleArchive.java new file mode 100644 index 00000000..b648c6d8 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/BundleArchive.java @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import org.apache.xbean.osgi.bundle.util.BundleResourceFinder; +import org.apache.xbean.osgi.bundle.util.ResourceDiscoveryFilter; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.PackageAdmin; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Iterator; +import java.util.zip.ZipEntry; + +/** + * TODO Unfinished + * @version $Rev$ $Date$ + */ +public class BundleArchive implements Archive { + + private final Bundle bundle; + + public BundleArchive(PackageAdmin packageAdmin, Bundle bundle) throws Exception { + this(packageAdmin, bundle, BundleResourceFinder.FULL_DISCOVERY_FILTER); + } + + public BundleArchive(PackageAdmin packageAdmin, Bundle bundle, ResourceDiscoveryFilter discoveryFilter) throws Exception { + this.bundle = bundle; + BundleResourceFinder bundleResourceFinder = new BundleResourceFinder(packageAdmin, bundle, "", ".class", discoveryFilter); + bundleResourceFinder.find(new AnnotationFindingCallback()); + } + + public Iterator iterator() { + return Collections.EMPTY_LIST.iterator(); + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + + URL resource = bundle.getResource(className); + if (resource != null) return resource.openStream(); + + throw new ClassNotFoundException(className); + } + + public Class loadClass(String s) throws ClassNotFoundException { + return bundle.loadClass(s); + } + + private class AnnotationFindingCallback implements BundleResourceFinder.ResourceFinderCallback { + + public boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception { + InputStream in = url.openStream(); + try { + //TODO +// readClassDef(in); + } finally { + in.close(); + } + return true; + } + + + public boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream in) throws Exception { + //TODO +// readClassDef(in); + return true; + } + } + + +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClassesArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClassesArchive.java new file mode 100644 index 00000000..eba373b1 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClassesArchive.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.xbean.finder.archive; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * @version $Rev$ $Date$ + */ +public class ClassesArchive implements Archive { + + private final Set loaders = new LinkedHashSet(); + private final Map> classes = new LinkedHashMap>(); + + public ClassesArchive(Class... classes) { + this(Arrays.asList(classes)); + } + + public ClassesArchive(Iterable> classes) { + assert classes != null; + + for (Class clazz : classes) { + if (clazz == null) continue; + if (clazz.getClassLoader() == null) continue; + this.classes.put(clazz.getName(), clazz); + loaders.add(clazz.getClassLoader()); + } + } + + public Iterator iterator() { + return new ArchiveIterator(this, classes.keySet().iterator()); + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + assert className != null; + + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + for (ClassLoader loader : loaders) { + URL resource = loader.getResource(className); + if (resource != null) return new BufferedInputStream(resource.openStream()); + } + + throw new ClassNotFoundException(className); + } + + public Class loadClass(String className) throws ClassNotFoundException { + Class clazz = classes.get(className); + if (clazz != null) return clazz; + + for (ClassLoader loader : loaders) { + try { + return loader.loadClass(className); + } catch (ClassNotFoundException e) { + } + } + + throw new ClassNotFoundException(className); + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClasspathArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClasspathArchive.java new file mode 100644 index 00000000..b1a42fe1 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/ClasspathArchive.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.xbean.finder.archive; + +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Supports JarArchive and FileArchive URLs + * + * @version $Rev$ $Date$ + */ +public class ClasspathArchive extends CompositeArchive { + + private final List urls = new ArrayList(); + private final ClassLoader loader; + + public ClasspathArchive(ClassLoader loader, URL... urls) { + this(loader, Arrays.asList(urls)); + } + + public ClasspathArchive(ClassLoader loader, Iterable urls) { + super(archives(loader, urls)); + this.loader = loader; + + } + + public static List archives(ClassLoader loader, Iterable urls) { + List archives = new ArrayList(); + + for (URL location : urls) { + try { + archives.add(archive(loader, location)); + } catch (Exception e) { + // TODO This is what we did before, so not too urgent to change, but not ideal + e.printStackTrace(); + } + } + + return archives; + } + + public static Archive archive(ClassLoader loader, URL location) { + + if (location.getProtocol().equals("jar")) { + + return new JarArchive(loader, location); + + } else if (location.getProtocol().equals("file")) { + + try { + + // See if it's actually a jar + + URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); + JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); + juc.getJarFile(); + + return new JarArchive(loader, jarUrl); + + } catch (IOException e) { + + return new FileArchive(loader, location); + + } + } + + throw new UnsupportedOperationException("unsupported archive type: " + location); + } + + public static List archives(ClassLoader loader, URL... urls) { + return archives(loader, Arrays.asList(urls)); + } + + @Override + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + + URL resource = loader.getResource(className); + if (resource != null) return resource.openStream(); + + throw new ClassNotFoundException(className); + } + + @Override + public Class loadClass(String className) throws ClassNotFoundException { + return loader.loadClass(className); + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/CompositeArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/CompositeArchive.java new file mode 100644 index 00000000..542f6328 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/CompositeArchive.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.xbean.finder.archive; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * @version $Rev$ $Date$ + */ +public class CompositeArchive implements Archive { + + private final List archives = new ArrayList(); + + public CompositeArchive(Archive... archives) { + this(Arrays.asList(archives)); + } + + public CompositeArchive(Iterable archives) { + for (Archive archive : archives) { + this.archives.add(archive); + } + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + for (Archive archive : archives) { + try { + return archive.getBytecode(className); + } catch (ClassNotFoundException e) { + } + } + + throw new ClassNotFoundException(className); + } + + public Class loadClass(String className) throws ClassNotFoundException { + for (Archive archive : archives) { + try { + return archive.loadClass(className); + } catch (ClassNotFoundException e) { + } + } + + throw new ClassNotFoundException(className); + } + + public Iterator iterator() { + if (archives.size() == 1) return archives.get(0).iterator(); + return new CompositeIterator(archives); + } + + private static class CompositeIterator implements Iterator { + + private Iterator archives; + private Iterator current; + + private CompositeIterator(Iterable archives) { + this.archives = archives.iterator(); + if (this.archives.hasNext()) { + current = this.archives.next().iterator(); + } + } + + public boolean hasNext() { + if (current == null) return false; + if (current.hasNext()) return true; + + if (archives.hasNext()) { + current = archives.next().iterator(); + return hasNext(); + } + return false; + } + + public Entry next() { + if (!hasNext()) throw new NoSuchElementException(); + + return current.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FileArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FileArchive.java new file mode 100644 index 00000000..666c1a48 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FileArchive.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.xbean.finder.archive; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class FileArchive implements Archive { + + private final ClassLoader loader; + private final String basePackage; + private final File dir; + private List list; + + public FileArchive(ClassLoader loader, URL url) { + this.loader = loader; + this.basePackage = ""; + this.dir = toFile(url); + } + + public FileArchive(ClassLoader loader, File dir) { + this.loader = loader; + this.basePackage = ""; + this.dir = dir; + } + + public FileArchive(ClassLoader loader, URL url, String basePackage) { + this.loader = loader; + this.basePackage = basePackage; + this.dir = toFile(url); + } + + public FileArchive(ClassLoader loader, File dir, String basePackage) { + this.loader = loader; + this.basePackage = basePackage; + this.dir = dir; + } + + public File getDir() { + return dir; + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + + URL resource = loader.getResource(className); + if (resource != null) return new BufferedInputStream(resource.openStream()); + + throw new ClassNotFoundException(className); + } + + + public Class loadClass(String className) throws ClassNotFoundException { + return loader.loadClass(className); + } + + public Iterator iterator() { + return new ArchiveIterator(this, _iterator()); + } + + public Iterator _iterator() { + if (list != null) return list.iterator(); + + list = file(dir); + return list.iterator(); + } + + private List file(File dir) { + List classNames = new ArrayList(); + if (dir.isDirectory()) { + scanDir(dir, classNames, (basePackage.length() > 0) ? (basePackage + ".") : basePackage); + } + return classNames; + } + + private void scanDir(File dir, List classNames, String packageName) { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + scanDir(file, classNames, packageName + file.getName() + "."); + } else if (file.getName().endsWith(".class")) { + String name = file.getName(); + name = name.replaceFirst(".class$", ""); + if (name.contains(".")) continue; + classNames.add(packageName + name); + } + } + } + + private static File toFile(URL url) { + if (!"file".equals(url.getProtocol())) throw new IllegalArgumentException("not a file url: " + url); + String path = url.getFile(); + File dir = new File(decode(path)); + if (dir.getName().equals("META-INF")) { + dir = dir.getParentFile(); // Scrape "META-INF" off + } + return dir; + } + + public static String decode(String fileName) { + if (fileName.indexOf('%') == -1) return fileName; + + StringBuilder result = new StringBuilder(fileName.length()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + for (int i = 0; i < fileName.length();) { + char c = fileName.charAt(i); + + if (c == '%') { + out.reset(); + do { + if (i + 2 >= fileName.length()) { + throw new IllegalArgumentException("Incomplete % sequence at: " + i); + } + + int d1 = Character.digit(fileName.charAt(i + 1), 16); + int d2 = Character.digit(fileName.charAt(i + 2), 16); + + if (d1 == -1 || d2 == -1) { + throw new IllegalArgumentException("Invalid % sequence (" + fileName.substring(i, i + 3) + ") at: " + String.valueOf(i)); + } + + out.write((byte) ((d1 << 4) + d2)); + + i += 3; + + } while (i < fileName.length() && fileName.charAt(i) == '%'); + + + result.append(out.toString()); + + continue; + } else { + result.append(c); + } + + i++; + } + return result.toString(); + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FilteredArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FilteredArchive.java new file mode 100644 index 00000000..6c88a1b4 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/FilteredArchive.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.xbean.finder.archive; + +import org.apache.xbean.finder.filter.Filter; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * @version $Rev$ $Date$ + */ +public class FilteredArchive implements Archive { + + private final Archive archive; + + private final Filter filter; + + public FilteredArchive(Archive archive, Filter filter) { + this.archive = archive; + this.filter = filter; + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + return archive.getBytecode(className); + } + + public Class loadClass(String className) throws ClassNotFoundException { + return archive.loadClass(className); + } + + public Iterator iterator() { + return new FilteredIterator(archive.iterator()); + } + + private final class FilteredIterator implements Iterator { + private final Iterator it; + + private Entry next; + + private FilteredIterator(Iterator it) { + this.it = it; + } + + public boolean hasNext() { + if (next != null) return true; + if (!it.hasNext()) return false; + seek(); + return hasNext(); + } + + public Entry next() { + if (!hasNext()) throw new NoSuchElementException(); + + Entry s = next; + next = null; + + return s; + } + + public void remove() { + it.remove(); + } + + private void seek() { + while (next == null && it.hasNext()) { + + next = it.next(); + + if (filter.accept(next.getName())) return; + + next = null; + } + } + + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/archive/JarArchive.java b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/JarArchive.java new file mode 100644 index 00000000..a0136f8a --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/archive/JarArchive.java @@ -0,0 +1,156 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +/** + * @version $Rev$ $Date$ + */ +public class JarArchive implements Archive { + + private final ClassLoader loader; + private final URL url; + private List list; + private final JarFile jar; + + public JarArchive(ClassLoader loader, URL url) { +// if (!"jar".equals(url.getProtocol())) throw new IllegalArgumentException("not a jar url: " + url); + + try { + this.loader = loader; + this.url = url; + URL u = url; + + String jarPath = url.getFile(); + if (jarPath.contains("!")) { + jarPath = jarPath.substring(0, jarPath.indexOf("!")); + u = new URL(jarPath); + } + jar = new JarFile(u.getFile().replace("%20", " ")); // no more an url + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public URL getUrl() { + return url; + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + int pos = className.indexOf("<"); + if (pos > -1) { + className = className.substring(0, pos); + } + pos = className.indexOf(">"); + if (pos > -1) { + className = className.substring(0, pos); + } + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + + ZipEntry entry = jar.getEntry(className); + if (entry == null) throw new ClassNotFoundException(className); + + return jar.getInputStream(entry); + } + + + public Class loadClass(String className) throws ClassNotFoundException { + return loader.loadClass(className); + } + + public Iterator iterator() { + return new JarIterator(); + } + + private class JarIterator implements Iterator { + + private final Enumeration stream; + private Entry next; + + private JarIterator() { + stream = jar.entries(); + } + + private boolean advance() { + if (next != null) return true; + + if (!stream.hasMoreElements()) return false; + + final JarEntry entry = stream.nextElement(); + + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + return advance(); + } + + final String className = entry.getName().replaceFirst(".class$", ""); + + if (className.contains(".")) { + return advance(); + } + + next = new ClassEntry(entry, className.replace('/', '.')); + + return true; + } + + public boolean hasNext() { + return advance(); + } + + public Entry next() { + if (!hasNext()) throw new NoSuchElementException(); + Entry entry = next; + next = null; + return entry; + } + + public void remove() { + throw new UnsupportedOperationException("remove"); + } + + private class ClassEntry implements Entry { + private final String name; + private final JarEntry entry; + + private ClassEntry(JarEntry entry, String name) { + this.name = name; + this.entry = entry; + } + + public String getName() { + return name; + } + + public InputStream getBytecode() throws IOException { + return jar.getInputStream(entry); + } + } + } +} + diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ClassFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ClassFilter.java new file mode 100644 index 00000000..a4a4e392 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ClassFilter.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.xbean.finder.filter; + +/** + * @version $Rev$ $Date$ + */ +public class ClassFilter implements Filter { + + private final String name; + + public ClassFilter(String name) { + assert name != null; + this.name = name; + } + + public String getName() { + return name; + } + + public boolean accept(String name) { + return this.name.equals(name); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ClassFilter that = (ClassFilter) o; + + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return "ClassFilter{" + + "name='" + name + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ContainsFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ContainsFilter.java new file mode 100644 index 00000000..0c65b454 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ContainsFilter.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.xbean.finder.filter; + +/** + * @version $Rev$ $Date$ + */ +public class ContainsFilter implements Filter { + + private final String token; + + public ContainsFilter(String token) { + assert token != null; + this.token = token; + } + + public String getToken() { + return token; + } + + public boolean accept(String name) { + return name.contains(token); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ContainsFilter that = (ContainsFilter) o; + + return token.equals(that.token); + } + + @Override + public int hashCode() { + return token.hashCode(); + } + + @Override + public String toString() { + return "ContainsFilter{" + + "token='" + token + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ExcludeIncludeFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ExcludeIncludeFilter.java new file mode 100644 index 00000000..97afe5f6 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/ExcludeIncludeFilter.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.xbean.finder.filter; + +/** + * First, all Exclude directives are evaluated; if any match, the className is denied unless it also matches an Include directive. + * Any classNames which do not match any Include or Exclude directives are permitted. + */ +public class ExcludeIncludeFilter implements Filter { + + private final Filter include; + private final Filter exclude; + + public ExcludeIncludeFilter(Filter include, Filter exclude) { + this.include = include; + this.exclude = exclude; + } + + public boolean accept(String name) { + if (exclude.accept(name)) return include.accept(name); + return true; + } + + @Override + public String toString() { + return "Exclude." + exclude + + " Include." + include; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filter.java new file mode 100644 index 00000000..e9bd42fc --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filter.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.xbean.finder.filter; + +/** +* @version $Rev$ $Date$ +*/ +public interface Filter { + boolean accept(String name); +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/FilterList.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/FilterList.java new file mode 100644 index 00000000..1e1f8698 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/FilterList.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.xbean.finder.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class FilterList implements Filter { + + private final List filters = new ArrayList(); + + public FilterList(Filter... filters) { + this(Arrays.asList(filters)); + } + + public FilterList(Iterable filters) { + for (Filter filter : filters) { + this.filters.add(filter); + } + } + + public boolean accept(String name) { + for (Filter filter : filters) { + if (filter.accept(name)) return true; + } + + return false; + } + + public List getFilters() { + return filters; + } + + @Override + public String toString() { + return "FilterList{" + + "filters=" + filters + + '}'; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filters.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filters.java new file mode 100644 index 00000000..008e2c2f --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/Filters.java @@ -0,0 +1,162 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @version $Rev$ $Date$ + */ +public class Filters { + private static final Filter NONE = new Filter() { + public boolean accept(String name) { + return false; + } + }; + + public static Filter packages(String... packages) { + List filters = new ArrayList(); + for (String s : packages) { + filters.add(new PackageFilter(s)); + } + + return optimize(filters); + } + + public static Filter classes(String... classes) { + List filters = new ArrayList(); + for (String s : classes) { + filters.add(new ClassFilter(s)); + } + + return optimize(filters); + } + + public static Filter prefixes(String... prefixes) { + List filters = new ArrayList(); + for (String s : prefixes) { + filters.add(new PrefixFilter(s)); + } + + return optimize(filters); + } + + public static Filter tokens(String... tokens) { + List filters = new ArrayList(); + for (String s : tokens) { + filters.add(new ContainsFilter(s)); + } + + return optimize(filters); + } + + public static Filter suffixes(String... suffixes) { + List filters = new ArrayList(); + for (String s : suffixes) { + filters.add(new SuffixFilter(s)); + } + + return optimize(filters); + } + + public static Filter patterns(String... patterns) { + List filters = new ArrayList(); + for (String s : patterns) { + filters.add(new PatternFilter(s)); + } + + return optimize(filters); + } + + + public static Filter optimize(Filter... filters) { + return optimize(Arrays.asList(filters)); + } + + public static Filter optimize(List... filterss) { + Set unwrapped = new LinkedHashSet(); + + for (List filters : filterss) { + unwrap(filters, unwrapped); + } + + if (unwrapped.size() > 1) { + Iterator iterator = unwrapped.iterator(); + while (iterator.hasNext()) { + Filter filter = iterator.next(); + if (filter == NONE) iterator.remove(); + } + } + + if (unwrapped.size() == 0) return NONE; + if (unwrapped.size() == 1) return unwrapped.iterator().next(); + return new FilterList(unwrapped); + } + + /** + * Will invert the meaning of this filter by wrapping it with + * a filter that negates the return of the accept method. + * + * If the passed in filter is already wrapped, it will be + * unwrapped and returned. This is to prevent endless wrapping + * if the invert method is called many times. + * + * @param filter + * @return + */ + public static Filter invert(Filter filter) { + if (filter instanceof NegativeFilter) { + NegativeFilter negativeFilter = (NegativeFilter) filter; + return negativeFilter.getFilter(); + } + + return new NegativeFilter(filter); + } + + private static void unwrap(List filters, Set unwrapped) { + for (Filter filter : filters) { + if (filter instanceof FilterList) { + FilterList filterList = (FilterList) filter; + unwrap(filterList.getFilters(), unwrapped); + } else { + unwrapped.add(filter); + } + } + } + + private static final class NegativeFilter implements Filter { + private final Filter filter; + + public NegativeFilter(Filter filter) { + this.filter = filter; + } + + public boolean accept(String name) { + return !filter.accept(name); + } + + public Filter getFilter() { + return filter; + } + } + +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/IncludeExcludeFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/IncludeExcludeFilter.java new file mode 100644 index 00000000..b2d3a811 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/IncludeExcludeFilter.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.xbean.finder.filter; + +/** + * First, all Include directives are evaluated; at least one must match, or the className is rejected. + * Next, all Exclude directives are evaluated. If any matches, the className is rejected. + * Last, any classNames which do not match an Include or a Exclude directive are denied by default. + */ +public class IncludeExcludeFilter implements Filter { + + private Filter include; + private Filter exclude; + + public IncludeExcludeFilter(Filter include, Filter exclude) { + this.include = include; + this.exclude = exclude; + } + + public boolean accept(String name) { + if (include.accept(name)) return !exclude.accept(name); + return false; + } + + @Override + public String toString() { + return "Include." + include + + " Exclude." + exclude; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PackageFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PackageFilter.java new file mode 100644 index 00000000..b3942481 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PackageFilter.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.xbean.finder.filter; + +/** + * @version $Rev$ $Date$ + */ +public class PackageFilter implements Filter { + + private final String packageName; + + public PackageFilter(String packageName) { + assert packageName != null; + if (!packageName.endsWith(".")) packageName += "."; + this.packageName = packageName; + } + + public String getPackageName() { + return packageName; + } + + public boolean accept(String name) { + return name.startsWith(packageName); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PackageFilter that = (PackageFilter) o; + + return packageName.equals(that.packageName); + } + + @Override + public int hashCode() { + return packageName.hashCode(); + } + + @Override + public String toString() { + return "PackageFilter{" + + "package='" + packageName + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PatternFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PatternFilter.java new file mode 100644 index 00000000..88e54f40 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PatternFilter.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.xbean.finder.filter; + +import java.util.regex.Pattern; + +/** + * @version $Rev$ $Date$ + */ +public class PatternFilter implements Filter { + + private final Pattern pattern; + + public PatternFilter(String expression) { + this(Pattern.compile(expression)); + } + + public PatternFilter(Pattern pattern) { + assert pattern != null; + this.pattern = pattern; + } + + public Pattern getPattern() { + return pattern; + } + + public boolean accept(String name) { + return pattern.matcher(name).matches(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PatternFilter that = (PatternFilter) o; + + return pattern.pattern().equals(that.pattern.pattern()); + } + + @Override + public int hashCode() { + return pattern.hashCode(); + } + + @Override + public String toString() { + return "PatternFilter{" + + "pattern=" + pattern + + '}'; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PrefixFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PrefixFilter.java new file mode 100644 index 00000000..2cb2e873 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/PrefixFilter.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.xbean.finder.filter; + +/** + * @version $Rev$ $Date$ + */ +public class PrefixFilter implements Filter { + + private final String prefix; + + public PrefixFilter(String prefix) { + assert prefix != null; + this.prefix = prefix; + } + + public String getPrefix() { + return prefix; + } + + public boolean accept(String name) { + return name.startsWith(prefix); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PrefixFilter that = (PrefixFilter) o; + + return prefix.equals(that.prefix); + } + + @Override + public int hashCode() { + return prefix.hashCode(); + } + + @Override + public String toString() { + return "PrefixFilter{" + + "prefix='" + prefix + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/filter/SuffixFilter.java b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/SuffixFilter.java new file mode 100644 index 00000000..d7e8c6ee --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/filter/SuffixFilter.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.xbean.finder.filter; + +/** + * @version $Rev$ $Date$ + */ +public class SuffixFilter implements Filter { + + private final String suffix; + + public SuffixFilter(String suffix) { + assert suffix != null; + this.suffix = suffix; + } + + public String getSuffix() { + return suffix; + } + + public boolean accept(String name) { + return name.endsWith(suffix); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SuffixFilter that = (SuffixFilter) o; + + return suffix.equals(that.suffix); + } + + @Override + public int hashCode() { + return suffix.hashCode(); + } + + @Override + public String toString() { + return "SuffixFilter{" + + "suffix='" + suffix + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/util/Classes.java b/xbean-finder/src/main/java/org/apache/xbean/finder/util/Classes.java new file mode 100644 index 00000000..5cd6d254 --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/util/Classes.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.xbean.finder.util; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Classes { + + private static final Map, Class> primitiveWrappers = new HashMap, Class>(); + private static final HashMap primitives = new HashMap(); + + static { + primitives.put("boolean", boolean.class); + primitives.put("byte", byte.class); + primitives.put("char", char.class); + primitives.put("short", short.class); + primitives.put("int", int.class); + primitives.put("long", long.class); + primitives.put("float", float.class); + primitives.put("double", double.class); + + primitiveWrappers.put(boolean.class, Boolean.class); + primitiveWrappers.put(byte.class, Byte.class); + primitiveWrappers.put(char.class, Character.class); + primitiveWrappers.put(double.class, Double.class); + primitiveWrappers.put(float.class, Float.class); + primitiveWrappers.put(int.class, Integer.class); + primitiveWrappers.put(long.class, Long.class); + primitiveWrappers.put(short.class, Short.class); + } + + public static boolean equals(String classNameA, String classNameB) { + return classNameA.equals(classNameB); + } + + public static Class forName(String string, ClassLoader classLoader) throws ClassNotFoundException { + int arrayDimentions = 0; + while (string.endsWith("[]")){ + string = string.substring(0, string.length() - 2); + arrayDimentions++; + } + + Class clazz = primitives.get(string); + + if (clazz == null) clazz = Class.forName(string, true, classLoader); + + if (arrayDimentions == 0){ + return clazz; + } + return Array.newInstance(clazz, new int[arrayDimentions]).getClass(); + } + + public static String packageName(Class clazz){ + return packageName(clazz.getName()); + } + + public static String packageName(String clazzName){ + int i = clazzName.lastIndexOf('.'); + if (i > 0){ + return clazzName.substring(0, i); + } else { + return ""; + } + } + + public static List getSimpleNames(Class... classes){ + List list = new ArrayList(); + for (Class aClass : classes) { + list.add(aClass.getSimpleName()); + } + + return list; + } + + public static Class deprimitivize(Class fieldType) { + return fieldType = fieldType.isPrimitive() ? primitiveWrappers.get(fieldType): fieldType; + } +} diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/util/SingleLinkedList.java b/xbean-finder/src/main/java/org/apache/xbean/finder/util/SingleLinkedList.java new file mode 100644 index 00000000..432a6d2e --- /dev/null +++ b/xbean-finder/src/main/java/org/apache/xbean/finder/util/SingleLinkedList.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.xbean.finder.util; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +public class SingleLinkedList implements List { + + private Entry entry; + private int size = 0; + + private class Entry { + + private E value; + private Entry next; + + private Entry(E value, Entry next) { + this.value = value; + this.next = next; + } + } + + + public int size() { + return size; + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean contains(Object o) { + if (o == null) { + for (E e : this) { + if (null == e) return true; + } + } else { + for (E e : this) { + if (o.equals(e)) return true; + } + } + + return false; + } + + public Iterator iterator() { + return values(); + } + + public Object[] toArray() { + final Object[] array = new Object[size]; + return toArray(array); + } + + public T[] toArray(T[] a) { + if (a.length < size) a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); + + Object[] array = a; + int i = 0; + + for (E e : this) { + array[i++] = e; + } + + return (T[]) array; + } + + public boolean add(E e) { + this.entry = new Entry(e, this.entry); + size++; + return true; + } + + public boolean remove(Object o) { + throw new UnsupportedOperationException("remove"); + } + + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException("containsAll"); + } + + public boolean addAll(Collection c) { + throw new UnsupportedOperationException("addAll"); + } + + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException("addAll"); + } + + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("removeAll"); + } + + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("retainAll"); + } + + public void clear() { + this.entry = null; + this.size = 0; + } + + public E get(int index) { + bounds(index); + int i = size; + for (E e : this) { + if (--i == index) return e; + } + + throw new IllegalStateException("statement should not be reachable"); + } + + public E set(int index, E element) { + bounds(index); + int i = size; + + for (Entry entry : entries()) { + if (--i == index) { + final E old = entry.value; + entry.value = element; + return old; + } + } + + throw new IllegalStateException("statement should not be reachable"); + } + + public void add(int index, E element) { + throw new UnsupportedOperationException("add"); + } + + public E remove(int index) { + throw new UnsupportedOperationException("remove"); + } + + public int indexOf(Object o) { + throw new UnsupportedOperationException("indexOf"); + } + + public int lastIndexOf(Object o) { + throw new UnsupportedOperationException("lastIndexOf"); + } + + public ListIterator listIterator() { + throw new UnsupportedOperationException("listIterator"); + } + + public ListIterator listIterator(int index) { + throw new UnsupportedOperationException("listIterator"); + } + + public List subList(int fromIndex, int toIndex) { + throw new UnsupportedOperationException("subList"); + } + + private void bounds(int index) { + if (index >= size) throw new IndexOutOfBoundsException(index + " [size " + size + "]"); + if (index < 0) throw new IndexOutOfBoundsException(index + " [size " + size + "]"); + } + + + private Iterator values() { + return new Values(this.entry); + } + + private Entries entries() { + return new Entries(this.entry); + } + + + private class Values implements Iterator { + + private Entry current; + + private Values(Entry current) { + this.current = current; + } + + public boolean hasNext() { + return current != null; + } + + public E next() { + if (current == null) throw new NoSuchElementException(); + + final E v = current.value; + + this.current = current.next; + + return v; + } + + public void remove() { + throw new UnsupportedOperationException("remove"); + } + } + + private class Entries implements Iterator>, Iterable> { + private Entry current; + + private Entries(Entry current) { + this.current = current; + } + + public Iterator> iterator() { + return this; + } + + public boolean hasNext() { + return current != null; + } + + public Entry next() { + if (current == null) throw new NoSuchElementException(); + + final Entry value = this.current; + + this.current = current.next; + + return value; + } + + public void remove() { + throw new UnsupportedOperationException("remove"); + } + } +} diff --git a/xbean-finder/src/site/site.xml b/xbean-finder/src/site/site.xml new file mode 100644 index 00000000..0b5a5bd8 --- /dev/null +++ b/xbean-finder/src/site/site.xml @@ -0,0 +1,37 @@ + + + + + + + + + + ${parentProject} + + ${modules} + + ${reports} + + + + + + diff --git a/xbean-finder/src/test/java/org/acme/BarUrlHandler.java b/xbean-finder/src/test/java/org/acme/BarUrlHandler.java new file mode 100644 index 00000000..bf7cc7f2 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/BarUrlHandler.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.acme; + +import java.net.URLConnection; +import java.net.URL; +import java.io.IOException; + +/** + * @version $Revision$ $Date$ + */ +public class BarUrlHandler extends java.net.URLStreamHandler { + protected URLConnection openConnection(URL u) throws IOException { + throw new IOException("bar"); + } +} diff --git a/xbean-finder/src/test/java/org/acme/ClassAnnotatedClass.java b/xbean-finder/src/test/java/org/acme/ClassAnnotatedClass.java new file mode 100644 index 00000000..92178a67 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/ClassAnnotatedClass.java @@ -0,0 +1,7 @@ +package org.acme; + +import org.acme.bar.ClassAnnotation; + +@ClassAnnotation +public class ClassAnnotatedClass { +} diff --git a/xbean-finder/src/test/java/org/acme/FooUrlHandler.java b/xbean-finder/src/test/java/org/acme/FooUrlHandler.java new file mode 100644 index 00000000..83d7068a --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/FooUrlHandler.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.acme; + +import java.net.URLConnection; +import java.net.URL; +import java.io.IOException; + +/** + * @version $Revision$ $Date$ + */ +public class FooUrlHandler extends java.net.URLStreamHandler { + protected URLConnection openConnection(URL u) throws IOException { + throw new IOException("foo"); + } +} diff --git a/xbean-finder/src/test/java/org/acme/One.java b/xbean-finder/src/test/java/org/acme/One.java new file mode 100644 index 00000000..d6eba648 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/One.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.acme; + +import java.io.Externalizable; +import java.io.ObjectOutput; +import java.io.IOException; +import java.io.ObjectInput; + +/** + * @version $Rev$ $Date$ + */ +public class One implements Externalizable { + public void writeExternal(ObjectOutput out) throws IOException { + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + } + + public String toString() { + return "one"; + } +} diff --git a/xbean-finder/src/test/java/org/acme/Three.java b/xbean-finder/src/test/java/org/acme/Three.java new file mode 100644 index 00000000..358b3eb6 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/Three.java @@ -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. + */ +package org.acme; + +/** + * @version $Revision$ $Date$ + */ +public class Three { + public String toString() { + return "three"; + } +} diff --git a/xbean-finder/src/test/java/org/acme/Two.java b/xbean-finder/src/test/java/org/acme/Two.java new file mode 100644 index 00000000..0ae3be4e --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/Two.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.acme; + +import java.io.Externalizable; +import java.io.ObjectOutput; +import java.io.IOException; +import java.io.ObjectInput; + +/** + * @version $Revision$ $Date$ + */ +public class Two implements Externalizable { + public void writeExternal(ObjectOutput out) throws IOException { + } + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + } + + public String toString() { + return "two"; + } +} diff --git a/xbean-finder/src/test/java/org/acme/bar/AnnType.java b/xbean-finder/src/test/java/org/acme/bar/AnnType.java new file mode 100644 index 00000000..44627f03 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/AnnType.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.ANNOTATION_TYPE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface AnnType { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/ClassAnnotation.java b/xbean-finder/src/test/java/org/acme/bar/ClassAnnotation.java new file mode 100644 index 00000000..4f7a4eac --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/ClassAnnotation.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.acme.bar; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * @version $Revision: 469417 $ $Date: 2006-10-31 09:50:58 +0100 (mar. 31 oct. 2006) $ + */ +@java.lang.annotation.Target(value = {ElementType.TYPE}) +@java.lang.annotation.Retention(value = RetentionPolicy.CLASS) +public @interface ClassAnnotation { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Construct.java b/xbean-finder/src/test/java/org/acme/bar/Construct.java new file mode 100644 index 00000000..0f18e98e --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Construct.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.CONSTRUCTOR}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Construct { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Field.java b/xbean-finder/src/test/java/org/acme/bar/Field.java new file mode 100644 index 00000000..eada3fde --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Field.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.FIELD}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Field { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/FullyAnnotated.java b/xbean-finder/src/test/java/org/acme/bar/FullyAnnotated.java new file mode 100644 index 00000000..08ba7d81 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/FullyAnnotated.java @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +import java.util.List; +import java.util.ArrayList; + +/** + * @version $Revision$ $Date$ + */ +@Type public class FullyAnnotated,Beeer> { + @Field private String field; + @Field private char[] characters; + @Field private String[] strings; + @Field private String[][] moreStrings; + @Field private List stringList; + @Field private Cheese spam; + @Field private Direction direction; + + @Construct public FullyAnnotated(@ParamA String constructorParam, @ParamB @Optional int anInt) { + this.field = constructorParam; + this.stringList = new ArrayList(); + } + + @Method public void doIt(int i, boolean b, double d, short s){} + + @Method public void doMore(Cheese cheese, Fun fun){} + + @Type enum Direction { + NORTH, SOUTH, EAST, WEST + } + + public Direction getDirection() { + return direction; + } + + public void setDirection(Direction direction) { + this.direction = direction; + } + + public Cheese getSpam() { + return spam; + } + + public void setSpam(Cheese spam) { + this.spam = spam; + } + + public void setSpam(Object spam) { + this.spam = (Cheese)spam; + } + + @Get @Method public String getField() { + @Variable String theField = this.field; + return theField; + } + + @Set @Method public void setField(@ParamB String methodParam) { + this.field = methodParam; + } + + @Get @Method public char[] getCharacters() { + return characters; + } + + @Set @Method public void setCharacters(char[] characters) { + this.characters = characters; + } + + @Get @Method public String[] getStrings() { + return strings; + } + + @Set @Method public void setStrings(String[] strings) { + this.strings = strings; + } + + @Get @Method public String[][] getMoreStrings() { + return moreStrings; + } + + @Set @Method public void setMoreStrings(@ParamA String[][] moreStrings) { + this.moreStrings = moreStrings; + } + + @Get @Method public List getStringList() { + return stringList; + } + + @Set @Method public void setStringList(List stringList) { + this.stringList = stringList; + } + + @Set @Method public void setStringList(ArrayList stringList) { + this.stringList = stringList; + } +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Get.java b/xbean-finder/src/test/java/org/acme/bar/Get.java new file mode 100644 index 00000000..6507a91a --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Get.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.METHOD}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Get { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Method.java b/xbean-finder/src/test/java/org/acme/bar/Method.java new file mode 100644 index 00000000..2a97f22b --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Method.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.METHOD}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Method { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Optional.java b/xbean-finder/src/test/java/org/acme/bar/Optional.java new file mode 100644 index 00000000..a6a5aca4 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Optional.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Optional { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Package.java b/xbean-finder/src/test/java/org/acme/bar/Package.java new file mode 100644 index 00000000..706efd09 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Package.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PACKAGE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Package { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/ParamA.java b/xbean-finder/src/test/java/org/acme/bar/ParamA.java new file mode 100644 index 00000000..c3c80f86 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/ParamA.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface ParamA { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/ParamB.java b/xbean-finder/src/test/java/org/acme/bar/ParamB.java new file mode 100644 index 00000000..449277ec --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/ParamB.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface ParamB { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Set.java b/xbean-finder/src/test/java/org/acme/bar/Set.java new file mode 100644 index 00000000..5aa50322 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Set.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.METHOD}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Set { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Type.java b/xbean-finder/src/test/java/org/acme/bar/Type.java new file mode 100644 index 00000000..ea950bff --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Type.java @@ -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. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.TYPE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +@AnnType +public @interface Type { +} diff --git a/xbean-finder/src/test/java/org/acme/bar/Variable.java b/xbean-finder/src/test/java/org/acme/bar/Variable.java new file mode 100644 index 00000000..0dcec452 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/bar/Variable.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.bar; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.LOCAL_VARIABLE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Variable { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Blue.java b/xbean-finder/src/test/java/org/acme/foo/Blue.java new file mode 100644 index 00000000..7032bd08 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Blue.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@Color public class Blue implements Primary { + @Color public static class Navy{} + @Color public static class Sky{} +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Color.java b/xbean-finder/src/test/java/org/acme/foo/Color.java new file mode 100644 index 00000000..d96f83c4 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Color.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.TYPE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Color { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Deployable.java b/xbean-finder/src/test/java/org/acme/foo/Deployable.java new file mode 100644 index 00000000..2c437fb5 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Deployable.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PACKAGE}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Deployable { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/FamilyHalloween.java b/xbean-finder/src/test/java/org/acme/foo/FamilyHalloween.java new file mode 100644 index 00000000..34c67446 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/FamilyHalloween.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Rev$ $Date$ + */ +public class FamilyHalloween extends Halloween { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/FunnyFamilyHalloween.java b/xbean-finder/src/test/java/org/acme/foo/FunnyFamilyHalloween.java new file mode 100644 index 00000000..6127b00b --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/FunnyFamilyHalloween.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Rev$ $Date$ + */ +public class FunnyFamilyHalloween extends FamilyHalloween { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/GenericHoliday.java b/xbean-finder/src/test/java/org/acme/foo/GenericHoliday.java new file mode 100644 index 00000000..9f4d3beb --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/GenericHoliday.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Revision: 469417 $ $Date: 2006-10-31 09:50:58 +0100 (Tue, 31 Oct 2006) $ + */ +@Holiday public interface GenericHoliday { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Green.java b/xbean-finder/src/test/java/org/acme/foo/Green.java new file mode 100644 index 00000000..c60a67dd --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Green.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.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@Color public class Green implements Primary { + @Color public static class Emerald extends Green {} + + @Property public void myMethod(){ + } +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Halloween.java b/xbean-finder/src/test/java/org/acme/foo/Halloween.java new file mode 100644 index 00000000..4eb0c9a9 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Halloween.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ + +@Holiday public class Halloween {} diff --git a/xbean-finder/src/test/java/org/acme/foo/Holiday.java b/xbean-finder/src/test/java/org/acme/foo/Holiday.java new file mode 100644 index 00000000..ebaf59a1 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Holiday.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.acme.foo; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @version $Revision$ $Date$ + */ +@Target(value = {java.lang.annotation.ElementType.TYPE}) +@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Holiday { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Primary.java b/xbean-finder/src/test/java/org/acme/foo/Primary.java new file mode 100644 index 00000000..71c91e5d --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Primary.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +public interface Primary {} diff --git a/xbean-finder/src/test/java/org/acme/foo/Property.java b/xbean-finder/src/test/java/org/acme/foo/Property.java new file mode 100644 index 00000000..3fa2eb72 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Property.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.METHOD}) +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Property { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Red.java b/xbean-finder/src/test/java/org/acme/foo/Red.java new file mode 100644 index 00000000..508171da --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Red.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@Color public class Red implements Primary { + @Color public static class CandyApple{} + @Color public static class Pink{} +} diff --git a/xbean-finder/src/test/java/org/acme/foo/StringGenericHoliday.java b/xbean-finder/src/test/java/org/acme/foo/StringGenericHoliday.java new file mode 100644 index 00000000..42c6ec3e --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/StringGenericHoliday.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Rev$ $Date$ + */ +public class StringGenericHoliday implements GenericHoliday { +} diff --git a/xbean-finder/src/test/java/org/acme/foo/Thanksgiving.java b/xbean-finder/src/test/java/org/acme/foo/Thanksgiving.java new file mode 100644 index 00000000..f25bedf9 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/Thanksgiving.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@Holiday public class Thanksgiving {} diff --git a/xbean-finder/src/test/java/org/acme/foo/ValentinesDay.java b/xbean-finder/src/test/java/org/acme/foo/ValentinesDay.java new file mode 100644 index 00000000..f77ffcfb --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/ValentinesDay.java @@ -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. + */ +package org.acme.foo; + +/** + * @version $Revision$ $Date$ + */ +@Holiday public class ValentinesDay {} diff --git a/xbean-finder/src/test/java/org/acme/foo/package-info.java b/xbean-finder/src/test/java/org/acme/foo/package-info.java new file mode 100644 index 00000000..c27c9dab --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/foo/package-info.java @@ -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. + */ +@Deployable +package org.acme.foo; diff --git a/xbean-finder/src/test/java/org/acme/javaURLContextFactory.java b/xbean-finder/src/test/java/org/acme/javaURLContextFactory.java new file mode 100644 index 00000000..9fb5b974 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/javaURLContextFactory.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.acme; + +import javax.naming.spi.ObjectFactory; +import javax.naming.Name; +import javax.naming.Context; +import java.util.Hashtable; + +/** + * @version $Revision$ $Date$ + */ +public class javaURLContextFactory implements ObjectFactory { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + return "java"; + } +} diff --git a/xbean-finder/src/test/java/org/acme/kernelURLContextFactory.java b/xbean-finder/src/test/java/org/acme/kernelURLContextFactory.java new file mode 100644 index 00000000..ffe52364 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/kernelURLContextFactory.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.acme; + +import javax.naming.spi.ObjectFactory; +import javax.naming.Name; +import javax.naming.Context; +import java.util.Hashtable; + +/** + * @version $Revision$ $Date$ + */ +public class kernelURLContextFactory implements ObjectFactory { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + return "kernel"; + } +} diff --git a/xbean-finder/src/test/java/org/acme/ldapURLContextFactory.java b/xbean-finder/src/test/java/org/acme/ldapURLContextFactory.java new file mode 100644 index 00000000..37962466 --- /dev/null +++ b/xbean-finder/src/test/java/org/acme/ldapURLContextFactory.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.acme; + +import javax.naming.spi.ObjectFactory; +import javax.naming.Name; +import javax.naming.Context; +import java.util.Hashtable; + +/** + * @version $Revision$ $Date$ + */ +public class ldapURLContextFactory implements ObjectFactory { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + return "ldap"; + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/ClassAnnotationFinderTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassAnnotationFinderTest.java new file mode 100644 index 00000000..a3494b03 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassAnnotationFinderTest.java @@ -0,0 +1,27 @@ +package org.apache.xbean.finder; + +import org.acme.ClassAnnotatedClass; +import org.acme.bar.ClassAnnotation; +import org.apache.xbean.finder.archive.ClassesArchive; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class ClassAnnotationFinderTest { + @Test + public void checkClassAnnotationIsNotFound() { + final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ClassAnnotatedClass.class)); + final List> annotations = finder.findAnnotatedClasses(ClassAnnotation.class); + assertEquals(0, annotations.size()); + } + + @Test + public void checkClassAnnotationIsFound() { + final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ClassAnnotatedClass.class), false); + final List> annotations = finder.findAnnotatedClasses(ClassAnnotation.class); + assertEquals(1, annotations.size()); + assertEquals(ClassAnnotatedClass.class, annotations.iterator().next()); + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderDepthTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderDepthTest.java new file mode 100644 index 00000000..85568374 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderDepthTest.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.xbean.finder; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +/** + * @version $Rev$ $Date$ + */ +public class ClassFinderDepthTest extends TestCase { + + + public static interface Hue { + } + + public static interface Saturation { + } + + public static interface Brightness { + } + + public static interface HSB extends Hue, Saturation, Brightness { + } + + public static class Color implements HSB { + } + + public static class Red extends Color { + } + + public static class Crimson extends Red { + } + + // added to ensure there are classes that shouldn't match + + public static class Shape { + } + + public static class Square extends Shape { + } + + public void testFindSubclassesIncomplete() throws Exception { + final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Crimson.class, Square.class)).link(); + + assertSubclasses(finder, Color.class, Red.class, Crimson.class); + assertSubclasses(finder, Red.class, Crimson.class); + assertSubclasses(finder, Crimson.class); + + assertSubclasses(finder, Shape.class, Square.class); + assertSubclasses(finder, Square.class); + } + + public void testFindImplementations() throws Exception { + final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Crimson.class, Square.class)).link(); + + assertImplementations(finder, HSB.class, Color.class, Red.class, Crimson.class); + assertImplementations(finder, Hue.class, HSB.class, Color.class, Red.class, Crimson.class); + assertImplementations(finder, Saturation.class, HSB.class, Color.class, Red.class, Crimson.class); + } + + private void assertSubclasses(AnnotationFinder finder, Class clazz, Class... subclasses) { + final List> classes = new ArrayList(finder.findSubclasses(clazz)); + + for (Class subclass : subclasses) { + assertContains(classes, subclass); + } + assertSize(classes, subclasses.length); + } + + private void assertImplementations(AnnotationFinder finder, Class clazz, Class... implementations) { + final List> classes = new ArrayList(finder.findImplementations(clazz)); + + for (Class subclass : implementations) { + assertContains(classes, subclass); + } + assertSize(classes, implementations.length); + } + + private void assertSize(List list, int size) { + assertEquals(size, list.size()); + } + + private void assertContains(List> classes, Class clazz) { + assertTrue("missing " + clazz.getSimpleName(), classes.contains(clazz)); + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderTest.java new file mode 100644 index 00000000..6f9ff965 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/ClassFinderTest.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.xbean.finder; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; +import org.acme.bar.AnnType; +import org.acme.bar.Construct; +import org.acme.bar.FullyAnnotated; +import org.acme.bar.Get; +import org.acme.bar.ParamA; +import org.acme.bar.Type; +import org.acme.foo.Blue; +import org.acme.foo.Color; +import org.acme.foo.Deployable; +import org.acme.foo.FamilyHalloween; +import org.acme.foo.FunnyFamilyHalloween; +import org.acme.foo.GenericHoliday; +import org.acme.foo.Green; +import org.acme.foo.Halloween; +import org.acme.foo.Holiday; +import org.acme.foo.Primary; +import org.acme.foo.Property; +import org.acme.foo.Red; +import org.acme.foo.StringGenericHoliday; +import org.acme.foo.Thanksgiving; +import org.acme.foo.ValentinesDay; + +/** + * @author David Blevins + * @version $Rev$ $Date$ + */ +public class ClassFinderTest extends TestCase { + private ClassFinder classFinder; + + + public void setUp() throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + UrlSet urlSet = new UrlSet(classLoader); + + if (classLoader.getParent() != null){ + urlSet = urlSet.exclude(classLoader.getParent()); + } + + urlSet = urlSet.excludeJavaHome(); + + classFinder = new ClassFinder(classLoader, urlSet.getUrls()); + } + + public void testFindAnnotatedPackages() throws Exception { + List packages = classFinder.findAnnotatedPackages(Deployable.class); + + assertNotNull(packages); + assertEquals(1, packages.size()); + assertTrue(packages.contains(Red.class.getPackage())); + } + + public void testFindAnnotatedClasses() throws Exception { + + Class[] expected = {Halloween.class, Thanksgiving.class, ValentinesDay.class, GenericHoliday.class}; + List> actual = classFinder.findAnnotatedClasses(Holiday.class); + + assertNotNull(actual); + assertEquals(expected.length, actual.size()); + for (Class clazz : expected) { + assertTrue(clazz.getName(), actual.contains(clazz)); + } + + Class[] expected2 = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, Red.CandyApple.class, Red.Pink.class}; + actual = classFinder.findAnnotatedClasses(Color.class); + + assertNotNull(actual); + assertEquals(expected2.length, actual.size()); + for (Class clazz : expected2) { + assertTrue(clazz.getName(), actual.contains(clazz)); + } + + Class[] expected3 = {Type.class}; + actual = classFinder.findAnnotatedClasses(AnnType.class); + + assertNotNull(actual); + assertEquals(expected3.length, actual.size()); + for (Class clazz : expected3) { + assertTrue(clazz.getName(), actual.contains(clazz)); + } + } + + public void testFindInheritedAnnotatedClassesInherited() throws Exception { + Class[] expected = {FunnyFamilyHalloween.class, FamilyHalloween.class, Halloween.class, Thanksgiving.class, ValentinesDay.class, GenericHoliday.class, StringGenericHoliday.class}; + List> actual = classFinder.findInheritedAnnotatedClasses(Holiday.class); + + assertNotNull(actual); + assertEquals(expected.length, actual.size()); + for (Class clazz : expected) { + assertTrue(clazz.getName(), actual.contains(clazz)); + } + + expected = new Class[]{Halloween.class, Thanksgiving.class, ValentinesDay.class, GenericHoliday.class}; + actual = classFinder.findAnnotatedClasses(Holiday.class); + assertNotNull(actual); + assertEquals(expected.length, actual.size()); + for (Class clazz : expected) { + assertTrue(clazz.getName(), actual.contains(clazz)); + } + } + + public void testFindAnnotatedMethods() throws Exception { + List methods = classFinder.findAnnotatedMethods(Get.class); + assertNotNull("methods", methods); + assertEquals("methods.size", 5, methods.size()); + + // Annotated parameters don't count + methods = classFinder.findAnnotatedMethods(ParamA.class); + assertNotNull("methods", methods); + assertEquals("methods.size", 0, methods.size()); + + // Neither do annotated constructors + methods = classFinder.findAnnotatedMethods(Construct.class); + assertNotNull("methods", methods); + assertEquals("methods.size", 0, methods.size()); + } + + public void testFindAnnotatedConstructors() throws Exception { + List constructors = classFinder.findAnnotatedConstructors(Construct.class); + assertNotNull("constructors", constructors); + assertEquals("constructors.size", 1, constructors.size()); + } + + public void testFindAnnotatedFields() throws Exception { + List fields = classFinder.findAnnotatedFields(org.acme.bar.Field.class); + assertNotNull("fields", fields); + assertEquals("fields.size", 7, fields.size()); + } + + public void testClassListConstructor() throws Exception { + Class[] classes = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, + Red.CandyApple.class, Red.Pink.class, Halloween.class, Holiday.class, Deployable.class, Primary.class, + Property.class, Thanksgiving.class, ValentinesDay.class, FullyAnnotated.class, Type.class, + GenericHoliday.class, StringGenericHoliday.class}; + + classFinder = new ClassFinder(classes); + + testFindAnnotatedClasses(); + testFindAnnotatedConstructors(); + testFindAnnotatedFields(); + testFindAnnotatedMethods(); + testFindAnnotatedPackages(); + } + public void testFindClassesInPackage() throws Exception{ + List> classesInPackage = classFinder.findClassesInPackage("org.acme.foo", false); + Class[] classesArray = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, + Red.CandyApple.class, Red.Pink.class, Halloween.class, Holiday.class, Deployable.class, Primary.class, + Property.class, Thanksgiving.class, ValentinesDay.class}; + List> classes = Arrays.asList(classesArray); + assertEquals(true, classesInPackage.containsAll(classes)); + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/FiltersTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/FiltersTest.java new file mode 100644 index 00000000..69e0686e --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/FiltersTest.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.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.filter.ClassFilter; +import org.apache.xbean.finder.filter.ExcludeIncludeFilter; +import org.apache.xbean.finder.filter.Filter; +import org.apache.xbean.finder.filter.FilterList; +import org.apache.xbean.finder.filter.Filters; +import org.apache.xbean.finder.filter.IncludeExcludeFilter; + +/** + * @version $Rev$ $Date$ + */ +public class FiltersTest extends TestCase { + public void setUp() throws Exception { + super.setUp(); + } + + public void testPackages() throws Exception { + Filter filter = Filters.packages("org.foo", "org.bar"); + + assertTrue(filter.accept("org.foo.Red")); + assertTrue(filter.accept("org.bar.Orange")); + + assertFalse(filter.accept("org.fooo.Orange")); + assertFalse(filter.accept("org.barr.Orange")); + assertFalse(filter.accept("org")); + assertFalse(filter.accept("")); + } + + public void testClasses() throws Exception { + Filter filter = Filters.classes("org.foo.Red", "org.foo.Blue"); + + assertTrue(filter.accept("org.foo.Red")); + assertTrue(filter.accept("org.foo.Blue")); + + assertFalse(filter.accept("org.foo.Orange")); + assertFalse(filter.accept("org.foo.Redd")); + assertFalse(filter.accept("")); + } + + public void testPatterns() throws Exception { + Filter filter = Filters.patterns("org\\.foo\\..*", ".*\\.Blue"); + + assertTrue(filter.accept("org.foo.Red")); + assertTrue(filter.accept("org.foo.Blue")); + assertTrue(filter.accept("org.bar.Blue")); + + assertFalse(filter.accept("com.foo.Orange")); + assertFalse(filter.accept("net.foo.Redd")); + assertFalse(filter.accept("")); + } + + public void testOptimize() throws Exception { + + ClassFilter foo = new ClassFilter("foo"); + ClassFilter foo2 = new ClassFilter("foo"); + ClassFilter foo3 = new ClassFilter("foo"); + + FilterList filter = new FilterList( + new FilterList( + new FilterList( + new FilterList( + foo, + new FilterList( + foo, + new FilterList( + new FilterList(foo, foo2, foo3) + ) + + ) + ) + + ) + ) + ); + + assertSame(foo, Filters.optimize(filter)); + } + + public void testIncludeExclude() { + Filter filter = new IncludeExcludeFilter(Filters.packages("org.foo", "org.bar"), Filters.packages("org.foo.util")); + + assertTrue(filter.accept("org.foo.Red")); + assertTrue(filter.accept("org.bar.Red")); + + assertFalse(filter.accept("com.bar.Red")); + assertFalse(filter.accept("org.foo.util.Blue")); + } + + public void testExcludeInclude() { + Filter filter = new ExcludeIncludeFilter(Filters.packages("org.foo.util"), Filters.packages("org.foo", "org.bar")); + + assertFalse(filter.accept("org.foo.Red")); + assertFalse(filter.accept("org.bar.Red")); + + assertTrue(filter.accept("com.bar.Red")); + assertTrue(filter.accept("org.foo.util.Blue")); + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/FinderSelectTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/FinderSelectTest.java new file mode 100644 index 00000000..cad0892f --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/FinderSelectTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import org.apache.xbean.finder.archive.ClassesArchive; +import org.junit.Test; + +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class FinderSelectTest { + + @Test + public void test() throws Exception { + + final AnnotationFinder all = new AnnotationFinder(new ClassesArchive(Red.class, Green.class, Blue.class)); + + final AnnotationFinder finder = all.select(Red.class.getName()); + final List> classes = finder.findAnnotatedClasses(Color.class); + + for (Class aClass : classes) { + System.out.println(aClass); + } + + } + + + + @java.lang.annotation.Target(value = {java.lang.annotation.ElementType.TYPE}) + @java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) + public @interface Color { + } + + + @Color + public static class Red { + + } + + @Color + public static class Green { + + } + + @Color + public static class Blue { + + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java new file mode 100644 index 00000000..d2663bd8 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java @@ -0,0 +1,274 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Basic assertions: + *

    + * - getDeclaredAnnotations should not include meta-annotations + * - meta-annotations can be recursive + * - the most top-level value is the one returned from getAnnotation() + * + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedClassTest extends TestCase { + + public void test() throws Exception { + AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Fake.class, Store.class, Farm.class, None.class)).link(); + + Map, Annotated>> map = new HashMap, Annotated>>(); + + List>> metas = finder.findMetaAnnotatedClasses(Color.class); + for (Annotated> meta : metas) { + Annotated> oldValue = map.put(meta.get(), meta); + assertNull("no duplicates allowed", oldValue); + } + + // MetaAnnotation classes themselves are not included + assertNull(map.get(Red.class)); + assertNull(map.get(Crimson.class)); + + // Check the negative scenario + assertFalse(map.containsKey(None.class)); + + // Check the positive scenarios + + { // Circle + Annotated> target = map.get(Circle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("white", target.getAnnotation(Color.class).value()); + } + + { // Square + Annotated> target = map.get(Square.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + } + + { // Triangle + Annotated> target = map.get(Triangle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Crimson.class)); + assertTrue(target.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, target.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, target.getAnnotations())); + } + + { // Fake -- should not get more than we asked for + Annotated> target = map.get(Fake.class); + assertNull(target); + + List>> list = finder.findMetaAnnotatedClasses(NotMeta.class); + assertEquals(1, list.size()); + + target = list.get(0); + assertNotNull(target); + + assertTrue(!target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) == null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(!contains(Color.class, target.getAnnotations())); + } + + + { // Circular - Egg wins + Annotated> target = map.get(Store.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("egg", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + { // Circular - Chicken wins + Annotated> target = map.get(Farm.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("chicken", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Metatype + @Color("red") + // one level deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Red { + } + + @Metatype + @Red + // two levels deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Crimson { + } + + @Red + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface NotMeta { + } + + @Metatype + @Color("egg") + @Chicken + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Egg { + } + + + @Metatype + @Color("chicken") + @Egg + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Chicken { + } + + + @Red + // -> @Color + public static class Square { + } + + @Red + // will be covered up by @Color + @Color("white") + public static class Circle { + } + + @Crimson + // -> @Red -> @Color + public static class Triangle { + + } + + // always good to have a fake in there + + public static class None { + + } + + @NotMeta + public static class Fake { + + } + + @Egg + public static class Store { + + } + + @Chicken + public static class Farm { + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorParametersTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorParametersTest.java new file mode 100644 index 00000000..3086becf --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorParametersTest.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedConstructorParametersTest extends TestCase { + + public void test() throws Exception { + + final Class[] classes = new Class[]{Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class}; + + final Map> map = new HashMap>(); + + for (Class clazz : classes) { + final MetaAnnotatedClass annotatedClass = new MetaAnnotatedClass(clazz); + + for (MetaAnnotatedConstructor method : annotatedClass.getConstructors()) { + map.put(annotatedClass.getSimpleName().toLowerCase(), method); + } + } + + // Check the positive scenarios + { + Annotation[] annotations = getAnnotations(map, "circle"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + + assertEquals("white", get(Color.class, annotations).value()); + + assertEquals(2, annotations.length); + } + + { + Annotation[] annotations = getAnnotations(map, "square"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + + assertEquals("red", get(Color.class, annotations).value()); + + assertEquals(2, annotations.length); + } + + { + Annotation[] annotations = getAnnotations(map, "triangle"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + assertTrue(contains(Crimson.class, annotations)); + + assertEquals("red", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + + { // Circular - Egg wins + Annotation[] annotations = getAnnotations(map, "store"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Egg.class, annotations)); + assertTrue(contains(Chicken.class, annotations)); + + assertEquals("egg", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + + { // Circular - Chicken wins + Annotation[] annotations = getAnnotations(map, "farm"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Egg.class, annotations)); + assertTrue(contains(Chicken.class, annotations)); + + assertEquals("chicken", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + } + + private Annotation[] getAnnotations(Map> map, String key) { + final MetaAnnotatedConstructor constructor = (MetaAnnotatedConstructor) map.get(key); + + assertNotNull(constructor); + + return constructor.getParameterAnnotations()[0]; + } + + public T get(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (annotation.annotationType() == type) return (T) annotation; + } + return null; + } + + private boolean contains(Class type, Annotation[] annotations) { + return get(type, annotations) != null; + } + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Red { + public interface $ { + + // one level deep + public void method( + @Red + @Color("red") + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Crimson { + public interface $ { + + // two levels deep + public void method( + @Crimson + @Red + Object object); + } + } + + // Green is intentionally not used in the classes + // passed directly to the finder to ensure that + // the finder is capable of following the path to + // the root annotation even when some of the + // annotations in the path are not strictly part + // of the archive + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Green { + public interface $ { + + // two levels deep + public void method( + @Green + @Color("green") + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface DarkGreen { + public interface $ { + + public void method( + @DarkGreen + @Green + Object object); + } + } + + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Forrest { + public interface $ { + + public void method( + @Forrest + @DarkGreen + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Chicken { + public interface $ { + + public void method( + @Chicken + @Color("chicken") + @Egg + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Egg { + public interface $ { + + public void method( + @Egg + @Color("egg") + @Chicken + Object object); + } + } + + public static class Square { + + public Square(@Red Object object) { + } + } + + public static class Circle { + + public Circle(@Red @Color("white") Object object) { + } + } + + public static class Triangle { + + public Triangle(@Crimson Object object) { + } + } + + public static class Oval { + + public Oval(@Forrest Object object) { + } + } + + // always good to have a fake in there + public static class None { + + public None(Object object) { + } + } + + public static class Store { + + public Store(@Egg Object object) { + } + } + + public static class Farm { + + public Farm(@Chicken Object object) { + } + } + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorTest.java new file mode 100644 index 00000000..d68461b2 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedConstructorTest.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedConstructorTest extends TestCase { + + public void test() throws Exception { + + final Class[] classes = new Class[]{Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class}; + + final Map> map = new HashMap>(); + + for (Class clazz : classes) { + final MetaAnnotatedClass annotatedClass = new MetaAnnotatedClass(clazz); + + for (MetaAnnotatedConstructor constructor : annotatedClass.getConstructors()) { + map.put(annotatedClass.getSimpleName().toLowerCase(), constructor); + } + } + + // Check the positive scenarios + { + final java.lang.reflect.AnnotatedElement element = map.get("circle"); + assertNotNull(element); + + assertTrue(element.isAnnotationPresent(Color.class)); + assertTrue(element.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, element.getDeclaredAnnotations())); + assertTrue(contains(Color.class, element.getAnnotations())); + assertEquals("white", element.getAnnotation(Color.class).value()); + + assertTrue(element.isAnnotationPresent(Red.class)); + assertTrue(element.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, element.getDeclaredAnnotations())); + assertTrue(contains(Red.class, element.getAnnotations())); + + assertEquals(2, element.getDeclaredAnnotations().length); + assertEquals(2, element.getAnnotations().length); + } + + { + final java.lang.reflect.AnnotatedElement target = map.get("square"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(2, target.getAnnotations().length); + } + + { + final java.lang.reflect.AnnotatedElement annotated = map.get("triangle"); + assertNotNull(annotated); + + assertTrue(annotated.isAnnotationPresent(Color.class)); + assertTrue(annotated.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Color.class, annotated.getAnnotations())); + assertEquals("red", annotated.getAnnotation(Color.class).value()); + + assertTrue(annotated.isAnnotationPresent(Red.class)); + assertTrue(annotated.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Red.class, annotated.getAnnotations())); + + assertTrue(annotated.isAnnotationPresent(Crimson.class)); + assertTrue(annotated.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, annotated.getAnnotations())); + + assertEquals(1, annotated.getDeclaredAnnotations().length); + assertEquals(3, annotated.getAnnotations().length); + } + + { // Circular - Egg wins + final java.lang.reflect.AnnotatedElement annotated = map.get("store"); + assertNotNull(annotated); + + assertTrue(annotated.isAnnotationPresent(Color.class)); + assertTrue(annotated.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Color.class, annotated.getAnnotations())); + assertEquals("egg", annotated.getAnnotation(Color.class).value()); + + assertTrue(annotated.isAnnotationPresent(Egg.class)); + assertTrue(annotated.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, annotated.getAnnotations())); + + assertTrue(annotated.isAnnotationPresent(Chicken.class)); + assertTrue(annotated.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, annotated.getAnnotations())); + + assertEquals(1, annotated.getDeclaredAnnotations().length); + assertEquals(3, annotated.getAnnotations().length); + } + + { // Circular - Chicken wins + final java.lang.reflect.AnnotatedElement annotated = map.get("farm"); + assertNotNull(annotated); + + assertTrue(annotated.isAnnotationPresent(Color.class)); + assertTrue(annotated.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Color.class, annotated.getAnnotations())); + assertEquals("chicken", annotated.getAnnotation(Color.class).value()); + + assertTrue(annotated.isAnnotationPresent(Egg.class)); + assertTrue(annotated.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, annotated.getAnnotations())); + + assertTrue(annotated.isAnnotationPresent(Chicken.class)); + assertTrue(annotated.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, annotated.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, annotated.getAnnotations())); + + assertEquals(1, annotated.getDeclaredAnnotations().length); + assertEquals(3, annotated.getAnnotations().length); + } + + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Red { + public class $ { + + @Red + @Color("red") // one level deep + public $(){} + } + } + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Crimson { + public class $ { + + @Crimson + @Red // two levels deep + public $(){} + } + } + + // Green is intentionally not used in the classes + // passed directly to the finder to ensure that + // the finder is capable of following the path to + // the root annotation even when some of the + // annotations in the path are not strictly part + // of the archive + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Green { + public class $ { + + @Green + @Color("green") // two levels deep + public $(){} + } + } + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface DarkGreen { + public class $ { + + @DarkGreen + @Green + public $(){} + } + } + + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Forrest { + public class $ { + + @Forrest + @DarkGreen + public $(){} + } + } + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Chicken { + public class $ { + + @Chicken + @Color("chicken") + @Egg + public $(){} + } + } + + @Metatype + @Target({CONSTRUCTOR}) + @Retention(RUNTIME) + public static @interface Egg { + public class $ { + + @Egg + @Color("egg") + @Chicken + public $(){} + } + } + + public static class Square { + + @Red // -> @Color + public Square(String s, int i) { + } + } + + public static class Circle { + + @Red // will be covered up by @Color + @Color("white") + public Circle(int i) { + } + } + + public static class Triangle { + + @Crimson // -> @Red -> @Color + public Triangle(boolean... b) { + } + } + + public static class Oval { + + @Forrest // -> @Green -> @Color + public Oval(boolean... b) { + } + } + + // always good to have a fake in there + public static class None { + + public None(List l) { + } + } + + public static class Store { + + @Egg + public Store() { + } + + } + + public static class Farm { + + @Chicken + public Farm() { + } + + } + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedFieldTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedFieldTest.java new file mode 100644 index 00000000..4ac55465 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedFieldTest.java @@ -0,0 +1,326 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedFieldTest extends TestCase { + + public void test() throws Exception { + AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class)).link(); + + Map> map = new HashMap>(); + + List> fields = finder.findMetaAnnotatedFields(Color.class); + for (Annotated field : fields) { + Annotated oldValue = map.put(field.get().getName(), field); + assertNull("no duplicates allowed", oldValue); + } + + // Check the negative scenario + assertFalse(map.containsKey("none")); + + // Check the positive scenarios + { + Annotated target = map.get("circle"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("white", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertEquals(2, target.getDeclaredAnnotations().length); + assertEquals(2, target.getAnnotations().length); + } + + { + Annotated target = map.get("square"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(2, target.getAnnotations().length); + } + + { + Annotated target = map.get("triangle"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Crimson.class)); + assertTrue(target.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, target.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + { // Circular - Egg wins + Annotated target = map.get("store"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("egg", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + { // Circular - Chicken wins + Annotated target = map.get("farm"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("chicken", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Color { + String value () default ""; + } + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Red { + public class $ { + + @Red + @Color("red") // one level deep + private Object field; + } + } + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Crimson { + public class $ { + + @Crimson + @Red // two levels deep + private Object field; + } + } + + // Green is intentionally not used in the classes + // passed directly to the finder to ensure that + // the finder is capable of following the path to + // the root annotation even when some of the + // annotations in the path are not strictly part + // of the archive + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Green { + public class $ { + + @Green + @Color("green") // two levels deep + private Object field; + } + } + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface DarkGreen { + public class $ { + + @DarkGreen + @Green + private Object field; + } + } + + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Forrest { + public class $ { + + @Forrest + @DarkGreen + private Object field; + } + } + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Chicken { + public class $ { + + @Chicken + @Color("chicken") + @Egg + private Object field; + } + } + + @Metatype + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Egg { + public class $ { + + @Egg + @Color("egg") + @Chicken + private Object field; + } + } + + public static class Square { + + @Red // -> @Color + private Object square; + } + + public static class Circle { + + @Red // will be covered up by @Color + @Color("white") + private Object circle; + } + + public static class Triangle { + + @Crimson // -> @Red -> @Color + private Object triangle; + } + + public static class Oval { + + @Forrest // -> @Green -> @Color + private Object oval; + } + + // always good to have a fake in there + public static class None { + + private Object none; + } + + public static class Store { + + @Egg + private Object store; + + } + + public static class Farm { + + @Chicken + private Object farm; + + } + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodParametersTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodParametersTest.java new file mode 100644 index 00000000..97c0146b --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodParametersTest.java @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedMethodParametersTest extends TestCase { + + public void test() throws Exception { + + final Class[] classes = new Class[]{Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class}; + + final Map> map = new HashMap>(); + + for (Class clazz : classes) { + final MetaAnnotatedClass annotatedClass = new MetaAnnotatedClass(clazz); + + for (MetaAnnotatedMethod method : annotatedClass.getMethods()) { + map.put(method.getName(), method); + } + } + + // Check the positive scenarios + { + Annotation[] annotations = getAnnotations(map, "circle"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + + assertEquals("white", get(Color.class, annotations).value()); + + assertEquals(2, annotations.length); + } + + { + Annotation[] annotations = getAnnotations(map, "square"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + + assertEquals("red", get(Color.class, annotations).value()); + + assertEquals(2, annotations.length); + } + + { + Annotation[] annotations = getAnnotations(map, "triangle"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Red.class, annotations)); + assertTrue(contains(Crimson.class, annotations)); + + assertEquals("red", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + + { // Circular - Egg wins + Annotation[] annotations = getAnnotations(map, "store"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Egg.class, annotations)); + assertTrue(contains(Chicken.class, annotations)); + + assertEquals("egg", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + + { // Circular - Chicken wins + Annotation[] annotations = getAnnotations(map, "farm"); + + assertTrue(contains(Color.class, annotations)); + assertTrue(contains(Egg.class, annotations)); + assertTrue(contains(Chicken.class, annotations)); + + assertEquals("chicken", get(Color.class, annotations).value()); + + assertEquals(3, annotations.length); + } + + } + + private Annotation[] getAnnotations(Map> map, String key) { + final MetaAnnotatedMethod method = (MetaAnnotatedMethod) map.get(key); + + assertNotNull(method); + + return method.getParameterAnnotations()[0]; + } + + public T get(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (annotation.annotationType() == type) return (T) annotation; + } + return null; + } + + private boolean contains(Class type, Annotation[] annotations) { + return get(type, annotations) != null; + } + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Red { + public interface $ { + + // one level deep + public void method( + @Red + @Color("red") + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Crimson { + public interface $ { + + // two levels deep + public void method( + @Crimson + @Red + Object object); + } + } + + // Green is intentionally not used in the classes + // passed directly to the finder to ensure that + // the finder is capable of following the path to + // the root annotation even when some of the + // annotations in the path are not strictly part + // of the archive + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Green { + public interface $ { + + // two levels deep + public void method( + @Green + @Color("green") + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface DarkGreen { + public interface $ { + + public void method( + @DarkGreen + @Green + Object object); + } + } + + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Forrest { + public interface $ { + + public void method( + @Forrest + @DarkGreen + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Chicken { + public interface $ { + + public void method( + @Chicken + @Color("chicken") + @Egg + Object object); + } + } + + @Metatype + @Target({PARAMETER}) + @Retention(RUNTIME) + public static @interface Egg { + public interface $ { + + public void method( + @Egg + @Color("egg") + @Chicken + Object object); + } + } + + public static class Square { + + public void square(@Red Object object) { + } + } + + public static class Circle { + + public void circle(@Red @Color("white") Object object) { + } + } + + public static class Triangle { + + public void triangle(@Crimson Object object) { + } + } + + public static class Oval { + + public void oval(@Forrest Object object) { + } + } + + // always good to have a fake in there + public static class None { + + public void none(Object object) { + } + } + + public static class Store { + + + public void store(@Egg Object object) { + } + + } + + public static class Farm { + + + public void farm(@Chicken Object object) { + } + + } + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java new file mode 100644 index 00000000..2f2209c2 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java @@ -0,0 +1,326 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @version $Rev$ $Date$ + */ +public class MetaAnnotatedMethodTest extends TestCase { + + public void test() throws Exception { + AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class)).link(); + + Map> map = new HashMap>(); + + List> methods = finder.findMetaAnnotatedMethods(Color.class); + for (Annotated method : methods) { + Annotated oldValue = map.put(method.get().getName(), method); + assertNull("no duplicates allowed", oldValue); + } + + // Check the negative scenario + assertFalse(map.containsKey("none")); + + // Check the positive scenarios + { + Annotated target = map.get("circle"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("white", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertEquals(2, target.getDeclaredAnnotations().length); + assertEquals(2, target.getAnnotations().length); + } + + { + Annotated target = map.get("square"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(2, target.getAnnotations().length); + } + + { + Annotated target = map.get("triangle"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Crimson.class)); + assertTrue(target.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, target.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + { // Circular - Egg wins + Annotated target = map.get("store"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("egg", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + { // Circular - Chicken wins + Annotated target = map.get("farm"); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("chicken", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + + assertEquals(1, target.getDeclaredAnnotations().length); + assertEquals(3, target.getAnnotations().length); + } + + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Color { + String value () default ""; + } + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Red { + public interface $ { + + @Red + @Color("red") // one level deep + public void method(); + } + } + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Crimson { + public interface $ { + + @Crimson + @Red // two levels deep + public void method(); + } + } + + // Green is intentionally not used in the classes + // passed directly to the finder to ensure that + // the finder is capable of following the path to + // the root annotation even when some of the + // annotations in the path are not strictly part + // of the archive + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Green { + public interface $ { + + @Green + @Color("green") // two levels deep + public void method(); + } + } + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface DarkGreen { + public interface $ { + + @DarkGreen + @Green + public void method(); + } + } + + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Forrest { + public interface $ { + + @Forrest + @DarkGreen + public void method(); + } + } + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Chicken { + public interface $ { + + @Chicken + @Color("chicken") + @Egg + public void method(); + } + } + + @Metatype + @Target({METHOD}) + @Retention(RUNTIME) + public static @interface Egg { + public interface $ { + + @Egg + @Color("egg") + @Chicken + public void method(); + } + } + + public static class Square { + + @Red // -> @Color + public void square(String s, int i){} + } + + public static class Circle { + + @Red // will be covered up by @Color + @Color("white") + public void circle(int i){} + } + + public static class Triangle { + + @Crimson // -> @Red -> @Color + public void triangle(boolean... b){} + } + + public static class Oval { + + @Forrest // -> @Green -> @Color + public void oval(boolean... b){} + } + + // always good to have a fake in there + public static class None { + + public void none(List l){} + } + + public static class Store { + + @Egg + public void store(){} + + } + + public static class Farm { + + @Chicken + public void farm(){} + + } + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/ResourceFinderTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/ResourceFinderTest.java new file mode 100644 index 00000000..d300572f --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/ResourceFinderTest.java @@ -0,0 +1,470 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +/** + * @version $Rev$ $Date$ + */ + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import junit.framework.TestCase; +import org.acme.BarUrlHandler; +import org.acme.FooUrlHandler; +import org.acme.One; +import org.acme.Three; +import org.acme.Two; +import org.acme.javaURLContextFactory; +import org.acme.kernelURLContextFactory; +import org.acme.ldapURLContextFactory; +import org.apache.xbean.finder.archive.Archives; + +public class ResourceFinderTest extends TestCase { + ResourceFinder resourceFinder = new ResourceFinder("META-INF/"); + + public void testGetResourcesMap1() throws Exception { + Map resourcesMap = resourceFinder.getResourcesMap(""); + Set> entries = resourcesMap.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + URL value = entry.getValue(); + + assertTrue("key not a directory", !key.contains("/")); + assertTrue("contains META-INF/", value.getPath().contains("META-INF/")); + assertTrue("ends with META-INF/" + key, value.getPath().endsWith("META-INF/" + key)); + assertTrue("value not a directory", !value.getPath().endsWith("/")); + } + } + + public void testGetResourcesMap2() throws Exception { + String token = "tvshows"; + Map resourcesMap = resourceFinder.getResourcesMap(token); + Set> entries = resourcesMap.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + URL value = entry.getValue(); + + assertTrue("key not a directory", !key.contains("/")); + assertTrue("contains META-INF/", value.getPath().contains("META-INF/")); + assertTrue("ends with META-INF/" + token + "/" + key, value.getPath().endsWith("META-INF/" + token + "/" + key)); + assertTrue("value not a directory", !value.getPath().endsWith("/")); + } + + assertTrue("map contains simpsons.properties", resourcesMap.containsKey("simpsons.properties")); + assertTrue("map contains familyguy.properties", resourcesMap.containsKey("familyguy.properties")); + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find String + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public void testFindString() throws Exception { + String expected = One.class.getName(); + String actual = resourceFinder.findString("java.io.Serializable"); + assertEquals(expected, actual); + } + + public void testFindAllStrings() throws Exception { + List manifests; + try { + manifests = resourceFinder.findAllStrings("MANIFEST.MF"); + } catch (Exception thisIsLegal) { + return; + } + + assertTrue("manifests found", manifests.size() > 1); + for (String manifest : manifests) { + assertTrue("starts with 'Manifest-Version'", manifest.startsWith("Manifest-Version")); + } + } + + public void testFindAvailableStrings() throws Exception { + List manifests = resourceFinder.findAvailableStrings("MANIFEST.MF"); + + assertTrue("manifests found", manifests.size() > 1); + for (String manifest : manifests) { + assertTrue("starts with 'Manifest-Version'", manifest.startsWith("Manifest-Version")); + } + } + + public void testMapAllStrings() throws Exception { + Map resourcesMap = resourceFinder.mapAllStrings("serializables"); + + assertEquals("map size", 3, resourcesMap.size()); + assertTrue("map contains key 'one'", resourcesMap.containsKey("one")); + assertEquals(One.class.getName(), resourcesMap.get("one")); + + assertTrue("map contains key 'two'", resourcesMap.containsKey("two")); + assertEquals(Two.class.getName(), resourcesMap.get("two")); + + assertTrue("map contains key 'three'", resourcesMap.containsKey("three")); + assertEquals(Three.class.getName(), resourcesMap.get("three")); + } + + public void testMapAvailableStrings() throws Exception { + Map resourcesMap = resourceFinder.mapAvailableStrings("serializables"); + + assertEquals("map size", 3, resourcesMap.size()); + assertTrue("map contains key 'one'", resourcesMap.containsKey("one")); + assertEquals(One.class.getName(), resourcesMap.get("one")); + + assertTrue("map contains key 'two'", resourcesMap.containsKey("two")); + assertEquals(Two.class.getName(), resourcesMap.get("two")); + + assertTrue("map contains key 'three'", resourcesMap.containsKey("three")); + assertEquals(Three.class.getName(), resourcesMap.get("three")); + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Class + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public void testFindClass() throws Exception { + Class actual = resourceFinder.findClass("java.io.Serializable"); + assertEquals(One.class, actual); + + try { + resourceFinder.findClass("java.io.OutputStream"); + fail("ClassNotFoundException should be thrown"); + } catch (ClassNotFoundException success) { + // pass + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testFindAllClasses() throws Exception { + List> classes = resourceFinder.findAllClasses("java.io.Serializable"); + assertEquals("size", 1, classes.size()); + assertEquals(One.class, classes.get(0)); + + try { + resourceFinder.findAllClasses("java.io.OutputStream"); + fail("ClassNotFoundException should be thrown"); + } catch (ClassNotFoundException success) { + // pass + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testFindAvailableClasses() throws Exception { + List> classes = resourceFinder.findAvailableClasses("java.io.Serializable"); + assertEquals("size", 1, classes.size()); + assertEquals(One.class, classes.get(0)); + + classes = resourceFinder.findAvailableClasses("java.io.OutputStream"); + assertEquals("size", 0, classes.size()); + } + + public void testMapAllClasses() throws Exception { + Map> resourcesMap = resourceFinder.mapAllClasses("serializables"); + + assertEquals("map size", 3, resourcesMap.size()); + assertTrue("map contains key 'one'", resourcesMap.containsKey("one")); + assertEquals(One.class, resourcesMap.get("one")); + + assertTrue("map contains key 'two'", resourcesMap.containsKey("two")); + assertEquals(Two.class, resourcesMap.get("two")); + + assertTrue("map contains key 'three'", resourcesMap.containsKey("three")); + assertEquals(Three.class, resourcesMap.get("three")); + + try { + resourceFinder.mapAllClasses("externalizables"); + fail("ClassNotFoundException should be thrown"); + } catch (ClassNotFoundException success) { + // pass + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testMapAvailableClasses() throws Exception { + Map> resourcesMap = resourceFinder.mapAvailableClasses("externalizables"); + + assertEquals("map size", 2, resourcesMap.size()); + assertTrue("map contains key 'one'", resourcesMap.containsKey("one")); + assertEquals(One.class, resourcesMap.get("one")); + + assertTrue("map contains key 'two'", resourcesMap.containsKey("two")); + assertEquals(Two.class, resourcesMap.get("two")); + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Implementation + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public void testFindImplementation() throws Exception { + Class expected = One.class; + Class actual = resourceFinder.findImplementation(java.io.Serializable.class); + assertEquals(expected, actual); + + try { + resourceFinder.findImplementation(java.io.InputStream.class); + fail("ClassCastException should be thrown"); + } catch (ClassCastException success) { + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testFindAllImplementations() throws Exception { + List> classes = resourceFinder.findAllImplementations(java.io.Serializable.class); + assertEquals("size", 1, classes.size()); + assertEquals(One.class, classes.get(0)); + + try { + resourceFinder.findAllImplementations(java.io.InputStream.class); + fail("ClassNotFoundException should be thrown"); + } catch (ClassCastException success) { + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testMapAllImplementations() throws Exception { + Map> resourcesMap = resourceFinder.mapAllImplementations(javax.naming.spi.ObjectFactory.class); + + assertEquals("map size", 3, resourcesMap.size()); + assertTrue("map contains key 'java'", resourcesMap.containsKey("java")); + assertEquals(javaURLContextFactory.class, resourcesMap.get("java")); + + assertTrue("map contains key 'kernel'", resourcesMap.containsKey("kernel")); + assertEquals(kernelURLContextFactory.class, resourcesMap.get("kernel")); + + assertTrue("map contains key 'ldap'", resourcesMap.containsKey("ldap")); + assertEquals(ldapURLContextFactory.class, resourcesMap.get("ldap")); + + try { + resourceFinder.mapAllImplementations(java.net.URLStreamHandler.class); + fail("ClassNotFoundException should be thrown"); + } catch (ClassCastException success) { + // pass + } catch (Exception e) { + fail("Wrong exception type was thrown: " + e.getClass().getName()); + } + } + + public void testMapAvailableImplementations() throws Exception { + Map> resourcesMap = resourceFinder.mapAvailableImplementations(java.net.URLStreamHandler.class); + + assertEquals("map size", 2, resourcesMap.size()); + assertTrue("map contains key 'bar'", resourcesMap.containsKey("bar")); + assertEquals(BarUrlHandler.class, resourcesMap.get("bar")); + + assertTrue("map contains key 'foo'", resourcesMap.containsKey("foo")); + assertEquals(FooUrlHandler.class, resourcesMap.get("foo")); + } + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + // + // Find Properties + // + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + public void testFindProperties() throws Exception { + Properties properties = resourceFinder.findProperties("tvshows/familyguy.properties"); + assertNotNull("properties", properties); + validateFamilyGuy(properties); + + properties = resourceFinder.findProperties("tvshows/simpsons.properties"); + assertNotNull("properties", properties); + validateSimpsons(properties); + } + + public void testFindAllProperties() throws Exception { + List propertiesLists = resourceFinder.findAllProperties("tvshows/familyguy.properties"); + assertNotNull("properties", propertiesLists); + assertEquals("list size", 1, propertiesLists.size()); + + Properties properties = propertiesLists.get(0); + validateFamilyGuy(properties); + } + + public void testFindAvailableProperties() throws Exception { + List propertiesLists = resourceFinder.findAvailableProperties("tvshows/familyguy.properties"); + assertNotNull("properties", propertiesLists); + assertEquals("list size", 1, propertiesLists.size()); + + Properties properties = propertiesLists.get(0); + validateFamilyGuy(properties); + } + + public void testMapAllProperties() throws Exception { + Map propertiesMap = resourceFinder.mapAllProperties("tvshows"); + assertNotNull("properties", propertiesMap); + assertEquals("map size", 2, propertiesMap.size()); + + assertTrue("contains 'familyguy.properties'", propertiesMap.containsKey("familyguy.properties")); + validateFamilyGuy(propertiesMap.get("familyguy.properties")); + + assertTrue("contains 'simpsons.properties'", propertiesMap.containsKey("simpsons.properties")); + validateSimpsons(propertiesMap.get("simpsons.properties")); + + try { + resourceFinder.mapAllProperties("movies"); + } catch (Exception success) { + } + + } + + public void testMapAvailableProperties() throws Exception { + Map propertiesMap = resourceFinder.mapAvailableProperties("movies"); + assertNotNull("properties", propertiesMap); + assertEquals("map size", 2, propertiesMap.size()); + + assertTrue("contains 'serenity.properties'", propertiesMap.containsKey("serentity.properties")); + Properties properties = propertiesMap.get("serentity.properties"); + assertEquals("director", "Joss Whedon", properties.getProperty("director")); + assertEquals("star", "Nathan Fillion", properties.getProperty("star")); + assertEquals("year", "2005", properties.getProperty("year")); + + assertTrue("contains 'kingkong.properties'", propertiesMap.containsKey("kingkong.properties")); + properties = propertiesMap.get("kingkong.properties"); + assertEquals("director", "Peter Jackson", properties.getProperty("director")); + assertEquals("star", "Naomi Watts", properties.getProperty("star")); + assertEquals("year", "2005", properties.getProperty("year")); + } + + + public void testWebinfJar() throws Exception { + + Map map = new HashMap(); + map.put("WEB-INF/beans.xml", ""); + + final File jarFile = Archives.jarArchive(map); + + final URL jarFileUrl = jarFile.toURI().toURL(); + final ResourceFinder finder = new ResourceFinder(jarFileUrl); + + final URL beansXmlUrl = finder.find("WEB-INF/beans.xml"); + + assertNotNull(beansXmlUrl); + } + + + private static void readJarEntries(URL location, String basePath, Map resources) throws IOException { + JarURLConnection conn = (JarURLConnection) location.openConnection(); + JarFile jarfile = null; + jarfile = conn.getJarFile(); + + Enumeration entries = jarfile.entries(); + while (entries != null && entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (entry.isDirectory() || !name.startsWith(basePath) || name.length() == basePath.length()) { + continue; + } + + name = name.substring(basePath.length()); + + if (name.contains("/")) { + continue; + } + + URL resource = new URL(location, name); + resources.put(name, resource); + } + } + + + + private void validateSimpsons(Properties properties) { + assertEquals("props size", 6, properties.size()); + assertEquals("creator", "Matt Groening", properties.getProperty("creator")); + assertEquals("father", "Homer", properties.getProperty("father")); + assertEquals("mother", "Marge", properties.getProperty("mother")); + assertEquals("son", "Bart", properties.getProperty("son")); + assertEquals("daughter", "Lisa", properties.getProperty("daughter")); + assertEquals("baby", "Maggie", properties.getProperty("baby")); + } + + private void validateFamilyGuy(Properties properties) { + assertEquals("props size", 6, properties.size()); + assertEquals("creator", "Seth MacFarlane", properties.getProperty("creator")); + assertEquals("father", "Peter", properties.getProperty("father")); + assertEquals("mother", "Lois", properties.getProperty("mother")); + assertEquals("son", "Chris", properties.getProperty("son")); + assertEquals("daughter", "Meg", properties.getProperty("daughter")); + assertEquals("baby", "Stewie", properties.getProperty("baby")); + } + + + /* + * Disable test because it's failing its purpose: + * - when running in maven in a clean build, no urls are found + * so the test runs with the ResourceFinder using the classloader + * instead of urls + * - when running on a non clean build in maven, one url is found, + * but the test fails + + public void testUrlConstructor() throws Exception { + List all = resourceFinder.findAll("MANIFEST.MF"); + + List urls = new ArrayList(); + for (URL url : all) { + if (url.getPath().contains("xbean-finder")){ + urls.add(url); + } + } + + resourceFinder = new ResourceFinder("META-INF/", urls.toArray(new URL[]{})); + testGetResourcesMap1(); + testGetResourcesMap2(); + testFindString(); + testFindAllStrings(); + testFindAvailableStrings(); + testMapAllStrings(); + testMapAvailableStrings(); + testFindClass(); + testFindAllClasses(); + testFindAvailableClasses(); + testMapAllClasses(); + testMapAvailableClasses(); + testFindImplementation(); + testFindAllImplementations(); + testMapAllImplementations(); + testMapAvailableImplementations(); + testFindProperties(); + testFindAllProperties(); + testFindAvailableProperties(); + testMapAllProperties(); + testMapAvailableProperties(); + } + */ + + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/RootMetaAnnotatedClassTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/RootMetaAnnotatedClassTest.java new file mode 100644 index 00000000..54ff4bb5 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/RootMetaAnnotatedClassTest.java @@ -0,0 +1,280 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Basic assertions: + *

    + * - getDeclaredAnnotations should not include meta-annotations + * - meta-annotations can be recursive + * - the most top-level value is the one returned from getAnnotation() + * + * @version $Rev$ $Date$ + */ +public class RootMetaAnnotatedClassTest extends TestCase { + + public void test() throws Exception { + AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Fake.class, Store.class, Farm.class, None.class)).link(); + + Map, Annotated>> map = new HashMap, Annotated>>(); + + List>> metas = finder.findMetaAnnotatedClasses(Color.class); + for (Annotated> meta : metas) { + Annotated> oldValue = map.put(meta.get(), meta); + assertNull("no duplicates allowed", oldValue); + } + + // MetaAnnotation classes themselves are not included + assertNull(map.get(Red.class)); + assertNull(map.get(Crimson.class)); + + // Check the negative scenario + assertFalse(map.containsKey(None.class)); + + // Check the positive scenarios + + { // Circle + Annotated> target = map.get(Circle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("white", target.getAnnotation(Color.class).value()); + } + + { // Square + Annotated> target = map.get(Square.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + } + + { // Triangle + Annotated> target = map.get(Triangle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Crimson.class)); + assertTrue(target.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, target.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, target.getAnnotations())); + } + + { // Fake -- should not get more than we asked for + Annotated> target = map.get(Fake.class); + assertNull(target); + + List>> list = finder.findMetaAnnotatedClasses(NotMeta.class); + assertEquals(1, list.size()); + + target = list.get(0); + assertNotNull(target); + + assertTrue(!target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) == null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(!contains(Color.class, target.getAnnotations())); + } + + + { // Circular - Egg wins + Annotated> target = map.get(Store.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("egg", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + { // Circular - Chicken wins + Annotated> target = map.get(Farm.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("chicken", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metaroot + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metaroot { + } + + @Metaroot + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Stereotype { + } + + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Stereotype + @Color("red") + // one level deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Red { + } + + @Stereotype + @Red + // two levels deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Crimson { + } + + @Red + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface NotMeta { + } + + @Stereotype + @Color("egg") + @Chicken + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Egg { + } + + + @Stereotype + @Color("chicken") + @Egg + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Chicken { + } + + + @Red + // -> @Color + public static class Square { + } + + @Red + // will be covered up by @Color + @Color("white") + public static class Circle { + } + + @Crimson + // -> @Red -> @Color + public static class Triangle { + + } + + // always good to have a fake in there + + public static class None { + + } + + @NotMeta + public static class Fake { + + } + + @Egg + public static class Store { + + } + + @Chicken + public static class Farm { + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/SelectMetaAnnotatedClassTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/SelectMetaAnnotatedClassTest.java new file mode 100644 index 00000000..5f3511e6 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/SelectMetaAnnotatedClassTest.java @@ -0,0 +1,284 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.archive.ClassesArchive; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Basic assertions: + *

    + * - getDeclaredAnnotations should not include meta-annotations + * - meta-annotations can be recursive + * - the most top-level value is the one returned from getAnnotation() + * + * @version $Rev$ $Date$ + */ +public class SelectMetaAnnotatedClassTest extends TestCase { + + private AnnotationFinder finder; + private Map, Annotated>> map; + + public void test() throws Exception { + finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Fake.class, Store.class, Farm.class, None.class)).link(); + + // MetaAnnotation classes themselves are not included + assertNull(get(Red.class)); + assertNull(get(Crimson.class)); + assertNull(get(None.class)); + + // Check the positive scenarios + + { // Circle + Annotated> target = get(Circle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("white", target.getAnnotation(Color.class).value()); + } + + { // Square + Annotated> target = get(Square.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + } + + { // Triangle + Annotated> target = get(Triangle.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("red", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Red.class)); + assertTrue(target.getAnnotation(Red.class) != null); + assertTrue(!contains(Red.class, target.getDeclaredAnnotations())); + assertTrue(contains(Red.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Crimson.class)); + assertTrue(target.getAnnotation(Crimson.class) != null); + assertTrue(contains(Crimson.class, target.getDeclaredAnnotations())); + assertTrue(contains(Crimson.class, target.getAnnotations())); + } + + { // Fake -- should not get more than we asked for + Annotated> target = get(Fake.class); + assertNull(target); + + List>> list = finder.findMetaAnnotatedClasses(NotMeta.class); + assertEquals(1, list.size()); + + target = list.get(0); + assertNotNull(target); + + assertTrue(!target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) == null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(!contains(Color.class, target.getAnnotations())); + } + + + { // Circular - Egg wins + Annotated> target = get(Store.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("egg", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(!contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + { // Circular - Chicken wins + Annotated> target = get(Farm.class); + assertNotNull(target); + + assertTrue(target.isAnnotationPresent(Color.class)); + assertTrue(target.getAnnotation(Color.class) != null); + assertTrue(!contains(Color.class, target.getDeclaredAnnotations())); + assertTrue(contains(Color.class, target.getAnnotations())); + assertEquals("chicken", target.getAnnotation(Color.class).value()); + + assertTrue(target.isAnnotationPresent(Egg.class)); + assertTrue(target.getAnnotation(Egg.class) != null); + assertTrue(!contains(Egg.class, target.getDeclaredAnnotations())); + assertTrue(contains(Egg.class, target.getAnnotations())); + + assertTrue(target.isAnnotationPresent(Chicken.class)); + assertTrue(target.getAnnotation(Chicken.class) != null); + assertTrue(contains(Chicken.class, target.getDeclaredAnnotations())); + assertTrue(contains(Chicken.class, target.getAnnotations())); + } + + } + +// private Annotated> get(Class key) { +// return map.get(key); +// } +// + private Annotated> get(Class key) { + final List>> all = finder.findMetaAnnotatedClasses(Color.class); + final AnnotationFinder select = finder.select(key); + final List>> metas = select.findMetaAnnotatedClasses(Color.class); + + assertFalse(all.size() == metas.size()); + for (Annotated> meta : metas) { + if (meta.get() == key) return meta; + } + + return null; + } + + private boolean contains(Class type, Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.annotationType())) return true; + } + return false; + } + + + // 100% your own annotations, even the @Metatype annotation + // Any annotation called @Metatype and annotated with itself works + @Metatype + @Retention(RetentionPolicy.RUNTIME) + @Target(ANNOTATION_TYPE) + public @interface Metatype { + } + + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Color { + String value() default ""; + } + + @Metatype + @Color("red") + // one level deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Red { + } + + @Metatype + @Red + // two levels deep + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Crimson { + } + + @Red + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface NotMeta { + } + + @Metatype + @Color("egg") + @Chicken + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Egg { + } + + + @Metatype + @Color("chicken") + @Egg + // Circular + @Target(value = {TYPE}) + @Retention(value = RUNTIME) + public static @interface Chicken { + } + + + @Red + // -> @Color + public static class Square { + } + + @Red + // will be covered up by @Color + @Color("white") + public static class Circle { + } + + @Crimson + // -> @Red -> @Color + public static class Triangle { + + } + + // always good to have a fake in there + + public static class None { + + } + + @NotMeta + public static class Fake { + + } + + @Egg + public static class Store { + + } + + @Chicken + public static class Farm { + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/SingleLinkedListTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/SingleLinkedListTest.java new file mode 100644 index 00000000..81b31331 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/SingleLinkedListTest.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.xbean.finder; + +import junit.framework.TestCase; +import org.apache.xbean.finder.util.SingleLinkedList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class SingleLinkedListTest extends TestCase { + private SingleLinkedList list; + private List expected; + + @Override + protected void setUp() throws Exception { + list = new SingleLinkedList(); + list.add("one"); + list.add("two"); + list.add("three"); + list.add("four"); + list.add("five"); + + expected = Arrays.asList("five", "four", "three", "two", "one"); + } + + public void testIterator() throws Exception { + ArrayList arrayList = new ArrayList(); + for (String s : list) { + arrayList.add(s); + } + + assertEquals(expected, arrayList); + } + + public void testArrayListConstructor() throws Exception { + ArrayList arrayList = new ArrayList(list); + + assertEquals(expected, arrayList); + } + + public void testLinkedListConstructor() throws Exception { + LinkedList linkedList = new LinkedList(list); + + assertEquals(expected, linkedList); + } + + public void testToArrayWithWrongSize() { + final String[] strings = list.toArray(new String[0]); + + assertEquals(expected, Arrays.asList(strings)); + } + + public void testToArrayWithRightSize() { + final String[] strings = list.toArray(new String[5]); + + assertEquals(expected, Arrays.asList(strings)); + } + + public void testToArray() { + final Object[] strings = list.toArray(); + + assertEquals(expected, Arrays.asList(strings)); + } + + public void testContains() { + assertTrue(list.contains("five")); + assertFalse(list.contains("foo")); + } + + public void testContainsNull() { + assertFalse(list.contains(null)); + } + + public void testGet() { + int i = 0; + assertEquals("one", list.get(i++)); + assertEquals("two", list.get(i++)); + assertEquals("three", list.get(i++)); + assertEquals("four", list.get(i++)); + assertEquals("five", list.get(i++)); + } + + public void testGetInvalid() { + + try { + list.get(-1); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // pass + } + + + try { + list.get(5); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // pass + } + } + + + public void testSet() { + int i = 0; + assertEquals("one", list.set(i++, "uno")); + assertEquals("two", list.set(i++, "dos")); + assertEquals("three", list.set(i++, "tres")); + assertEquals("four", list.set(i++, "quatro")); + assertEquals("five", list.set(i++, "cinco")); + + i = 0; + assertEquals("uno", list.get(i++)); + assertEquals("dos", list.get(i++)); + assertEquals("tres", list.get(i++)); + assertEquals("quatro", list.get(i++)); + assertEquals("cinco", list.get(i++)); + } + + public void testSetInvalid() { + + try { + list.set(-1, null); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // pass + } + + + try { + list.set(5, null); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // pass + } + } + + public void testIsEmpty() { + SingleLinkedList temp = new SingleLinkedList(); + assertTrue(temp.isEmpty()); + assertEquals(0, temp.size()); + temp.add("one"); + assertFalse(temp.isEmpty()); + assertEquals(1, temp.size()); + temp.clear(); + assertTrue(temp.isEmpty()); + assertEquals(0, temp.size()); + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/UrlSetTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/UrlSetTest.java new file mode 100644 index 00000000..c5cf45f0 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/UrlSetTest.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.xbean.finder; + +import junit.framework.TestCase; + +import java.net.URL; +import java.util.List; +import java.io.File; + +/** + * @version $Rev$ $Date$ + */ +public class UrlSetTest extends TestCase { + private UrlSet urlSet; + private URL[] originalUrls; + + protected void setUp() throws Exception { + originalUrls = new URL[]{ + new URL("file:/Users/dblevins/work/xbean/trunk/xbean-finder/target/classes/"), + new URL("file:/Users/dblevins/work/xbean/trunk/xbean-finder/target/test-classes/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/.compatibility/14compatibility.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/charsets.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/dt.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jce.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jconsole.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jsse.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/laf.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/ui.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/deploy.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/apple_provider.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/dnsns.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/localedata.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunjce_provider.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunpkcs11.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/plugin.jar!/"), + new URL("jar:file:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/sa-jdi.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/CoreAudio.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/MRJToolkit.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/QTJSupport.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/QTJava.zip!/"), + new URL("jar:file:/System/Library/Java/Extensions/dns_sd.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/j3daudio.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/j3dcore.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/j3dutils.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/jai_codec.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/jai_core.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/mlibwrapper_jai.jar!/"), + new URL("jar:file:/System/Library/Java/Extensions/vecmath.jar!/"), + new URL("jar:file:/Users/dblevins/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar!/"), + }; + urlSet = new UrlSet(originalUrls); + } + + public void testAll() throws Exception { + + assertEquals("Urls.size()", 32, urlSet.getUrls().size()); + + UrlSet homeSet = urlSet.matching(".*Home.*"); + assertEquals("HomeSet.getUrls().size()", 8, homeSet.getUrls().size()); + +// homeSet = urlSet.relative(new File("/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home")); +// assertEquals("HomeSet.getUrls().size()", 8, homeSet.getUrls().size()); + + UrlSet urlSet2 = urlSet.exclude(homeSet); + assertEquals("Urls.size()", 24, urlSet2.getUrls().size()); + + UrlSet xbeanSet = urlSet.matching(".*xbean.*"); + assertEquals("XbeanSet.getUrls().size()", 2, xbeanSet.getUrls().size()); + + UrlSet junitSet = urlSet.matching(".*junit.*"); + assertEquals("JunitSet.getUrls().size()", 1, junitSet.getUrls().size()); + + UrlSet mergedSet = homeSet.include(xbeanSet); + assertEquals("MergedSet.getUrls().size()", 10, mergedSet.getUrls().size()); + + mergedSet.include(junitSet); + assertEquals("MergedSet.getUrls().size()", 10, mergedSet.getUrls().size()); + + UrlSet mergedSet2 = mergedSet.include(junitSet); + assertEquals("MergedSet2.getUrls().size()", 11, mergedSet2.getUrls().size()); + + UrlSet filteredSet = urlSet.exclude(".*System/Library.*"); + assertEquals("FilteredSet.getUrls().size()", 3, filteredSet.getUrls().size()); + + filteredSet.exclude(junitSet); + assertEquals("FilteredSet.getUrls().size()", 3, filteredSet.getUrls().size()); + + UrlSet filteredSet2 = filteredSet.exclude(junitSet); + assertEquals("FilteredSet2.getUrls().size()", 2, filteredSet2.getUrls().size()); + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/Archives.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/Archives.java new file mode 100644 index 00000000..00cfe065 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/Archives.java @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +/** +* @version $Rev$ $Date$ +*/ +public class Archives { + + public static File fileArchive(Class[] classes) throws IOException { + return fileArchive(new HashMap(), classes); + } + + public static File fileArchive(Map entries, Class... classes) throws IOException { + + ClassLoader loader = Archives.class.getClassLoader(); + + File classpath = File.createTempFile("path with spaces", "classes"); + + assertTrue(classpath.delete()); + assertTrue(classpath.mkdirs()); + + + for (Class clazz : classes) { + String name = clazz.getName().replace('.', File.separatorChar) + ".class"; + File file = new File(classpath, name); + + File d = file.getParentFile(); + + if (!d.exists()) assertTrue(d.getAbsolutePath(), d.mkdirs()); + + URL resource = loader.getResource(name); + assertNotNull(resource); + + InputStream in = new BufferedInputStream(resource.openStream()); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + + int i = -1; + while ((i = in.read()) != -1) { + out.write(i); + } + + out.close(); + in.close(); + } + + for (Map.Entry entry : entries.entrySet()) { + + final String key = entry.getKey().replace('/', File.separatorChar); + + final File file = new File(classpath, key); + + File d = file.getParentFile(); + + if (!d.exists()) assertTrue(d.getAbsolutePath(), d.mkdirs()); + + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + + out.write(entry.getValue().getBytes()); + + out.close(); + } + + return classpath; + } + + public static File jarArchive(Class... classes) throws IOException { + return jarArchive(new HashMap(), classes); + } + + public static File jarArchive(Map entries, Class... classes) throws IOException { + + ClassLoader loader = Archives.class.getClassLoader(); + + File classpath = File.createTempFile("path with spaces", ".jar"); + + // Create the ZIP file + ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(classpath))); + + for (Class clazz : classes) { + String name = clazz.getName().replace('.', '/') + ".class"; + + URL resource = loader.getResource(name); + assertNotNull(resource); + + // Add ZIP entry to output stream. + out.putNextEntry(new ZipEntry(name)); + + InputStream in = new BufferedInputStream(resource.openStream()); + + int i = -1; + while ((i = in.read()) != -1) { + out.write(i); + } + + // Complete the entry + out.closeEntry(); + } + + for (Map.Entry entry : entries.entrySet()) { + + out.putNextEntry(new ZipEntry(entry.getKey())); + + out.write(entry.getValue().getBytes()); + } + + // Complete the ZIP file + out.close(); + return classpath; + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClassesArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClassesArchiveTest.java new file mode 100644 index 00000000..2771f907 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClassesArchiveTest.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.xbean.finder.archive; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class ClassesArchiveTest extends TestCase { + private ClassesArchive archive; + + @Override + protected void setUp() throws Exception { + archive = new ClassesArchive(Red.class, Green.class, Blue.class); + } + + public void testGetBytecode() throws Exception { + + assertNotNull(archive.getBytecode(Blue.class.getName())); + assertNotNull(archive.getBytecode(Green.class.getName())); + assertNotNull(archive.getBytecode(Red.class.getName())); + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testLoadClass() throws Exception { + assertEquals(Blue.class, archive.loadClass(Blue.class.getName())); + assertEquals(Green.class, archive.loadClass(Green.class.getName())); + assertEquals(Red.class, archive.loadClass(Red.class.getName())); + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testIterator() throws Exception { + List classes = new ArrayList(); + for (Archive.Entry entry : archive) { + classes.add(entry.getName()); + } + + assertFalse(0 == classes.size()); + assertTrue(classes.contains(Blue.class.getName())); + assertTrue(classes.contains(Red.class.getName())); + assertTrue(classes.contains(Green.class.getName())); + assertEquals(3, classes.size()); + } + + public static class Red { + } + + public static class Green { + } + + public static class Blue { + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClasspathArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClasspathArchiveTest.java new file mode 100644 index 00000000..80636e00 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/ClasspathArchiveTest.java @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import junit.framework.TestCase; +import org.acme.foo.Blue; +import org.acme.foo.Green; +import org.acme.foo.Red; +import org.apache.xbean.finder.UrlSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class ClasspathArchiveTest extends TestCase { + + + private ClasspathArchive archive; + private final Class[] classes = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, Red.CandyApple.class, Red.Pink.class}; + + @Override + protected void setUp() throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + UrlSet urlSet = new UrlSet(classLoader); + + if (classLoader.getParent() != null) { + urlSet = urlSet.exclude(classLoader.getParent()); + } + + urlSet = urlSet.excludeJavaHome(); + + archive = new ClasspathArchive(classLoader, urlSet.getUrls()); + } + + public void testGetBytecode() throws Exception { + + for (Class clazz : classes) { + assertNotNull(clazz.getName(), archive.getBytecode(clazz.getName())); + } + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testLoadClass() throws Exception { + for (Class clazz : classes) { + assertEquals(clazz.getName(), clazz, archive.loadClass(clazz.getName())); + } + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testArchives() throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + UrlSet urlSet = new UrlSet(classLoader); + + if (classLoader.getParent() != null) { + urlSet = urlSet.exclude(classLoader.getParent()); + } + + urlSet = urlSet.excludeJavaHome(); + + List list = ClasspathArchive.archives(classLoader, urlSet.getUrls()); + + + // At least classes, test-classes and junit.jar should be here + assertTrue(list.size() >= 3); + + // target/classes/ and target/test-classes/ + assertTrue(sublist(list, FileArchive.class).size() >= 2); + + // junit + assertTrue(sublist(list, JarArchive.class).size() >= 1); + } + + private List sublist(List list, Class type) { + List ts = new ArrayList(); + for (Archive archive : list) { + + if (type.isAssignableFrom(archive.getClass())) { + T t = (T) archive; + ts.add(t); + } + } + return ts; + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeArchiveTest.java new file mode 100644 index 00000000..7e208fa3 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeArchiveTest.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.xbean.finder.archive; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class CompositeArchiveTest extends TestCase { + private CompositeArchive archive; + Class[] classes = {Blue.class, Green.class, Red.class}; + + @Override + protected void setUp() throws Exception { + archive = new CompositeArchive( + new ClassesArchive(Red.class, Green.class), + new ClassesArchive(Blue.class) + ); + } + + public void testGetBytecode() throws Exception { + + for (Class clazz : classes) { + assertNotNull(clazz.getName(), archive.getBytecode(clazz.getName())); + } + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testLoadClass() throws Exception { + for (Class clazz : classes) { + assertEquals(clazz.getName(), clazz, archive.loadClass(clazz.getName())); + } + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testIterator() throws Exception { + List classes = new ArrayList(); + for (Archive.Entry entry : archive) { + classes.add(entry.getName()); + } + + assertFalse(0 == classes.size()); + assertTrue(classes.contains(Blue.class.getName())); + assertTrue(classes.contains(Red.class.getName())); + assertTrue(classes.contains(Green.class.getName())); + assertEquals(3, classes.size()); + } + + + public static class Red { + } + + public static class Green { + } + + public static class Blue { + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeJarArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeJarArchiveTest.java new file mode 100644 index 00000000..294e6787 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/CompositeJarArchiveTest.java @@ -0,0 +1,100 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import junit.framework.TestCase; +import org.apache.xbean.finder.filter.Filter; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class CompositeJarArchiveTest extends TestCase { + private CompositeArchive archive; + Class[] classes = {Blue.class, Green.class, Red.class}; + + @Override + protected void setUp() throws Exception { + + final File one = Archives.jarArchive(Red.class, Green.class); + final File two = Archives.jarArchive(Blue.class); + + archive = new CompositeArchive( + new FilteredArchive(new CompositeArchive(new JarArchive(this.getClass().getClassLoader(), one.toURI().toURL())), new MyFilter()), + new FilteredArchive(new CompositeArchive(new JarArchive(this.getClass().getClassLoader(), two.toURI().toURL())), new MyFilter()) + ); + } + + public void testGetBytecode() throws Exception { + + for (Class clazz : classes) { + assertNotNull(clazz.getName(), archive.getBytecode(clazz.getName())); + } + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testLoadClass() throws Exception { + for (Class clazz : classes) { + assertEquals(clazz.getName(), clazz, archive.loadClass(clazz.getName())); + } + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + public void testIterator() throws Exception { + List classes = new ArrayList(); + for (Archive.Entry entry : archive) { + classes.add(entry.getName()); + } + + assertFalse(0 == classes.size()); + assertTrue(classes.contains(Blue.class.getName())); + assertTrue(classes.contains(Red.class.getName())); + assertTrue(classes.contains(Green.class.getName())); + assertEquals(3, classes.size()); + } + + + public static class Red { + } + + public static class Green { + } + + public static class Blue { + } + + private static class MyFilter implements Filter { + public boolean accept(String name) { + return true; + } + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FileArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FileArchiveTest.java new file mode 100644 index 00000000..43daad26 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FileArchiveTest.java @@ -0,0 +1,123 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.finder.archive; + +import org.acme.foo.Blue; +import org.acme.foo.Green; +import org.acme.foo.Red; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +/** + * @version $Rev$ $Date$ + */ +public class FileArchiveTest { + + private static final Class[] classes = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, Red.CandyApple.class, Red.Pink.class}; + private static File classpath; + private FileArchive archive; + + @BeforeClass + public static void classSetUp() throws Exception { + + classpath = Archives.fileArchive(classes); + } + + @Before + public void setUp() throws Exception { + URL[] urls = {classpath.toURI().toURL()}; + + archive = new FileArchive(new URLClassLoader(urls), urls[0]); + } + + @Test + public void testBasePackageName() throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Enumeration resources = classLoader.getResources("org/acme/foo"); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + FileArchive fileArchive = new FileArchive(classLoader, url, "org.acme.foo"); + Iterator iterator = fileArchive.iterator(); + while ( iterator.hasNext() ) { + assertTrue(iterator.next().getName().startsWith("org.acme.foo")); + } + } + } + + @Test + public void testGetBytecode() throws Exception { + + for (Class clazz : classes) { + assertNotNull(clazz.getName(), archive.getBytecode(clazz.getName())); + } + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + @Test + public void testLoadClass() throws Exception { + for (Class clazz : classes) { + assertEquals(clazz.getName(), clazz, archive.loadClass(clazz.getName())); + } + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + @Test + public void testIterator() throws Exception { + List actual = new ArrayList(); + for (Archive.Entry entry : archive) { + actual.add(entry.getName()); + } + + assertFalse(0 == actual.size()); + + for (Class clazz : classes) { + assertTrue(clazz.getName(), actual.contains(Blue.class.getName())); + } + + assertEquals(classes.length, actual.size()); + } + + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FilteredArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FilteredArchiveTest.java new file mode 100644 index 00000000..04ab799b --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/FilteredArchiveTest.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.xbean.finder.archive; + +import junit.framework.TestCase; +import org.apache.xbean.finder.filter.Filter; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class FilteredArchiveTest extends TestCase { + + private MockArchive archive; + + public void testNothingFiltered() throws Exception { + + assertEquals(archive, new FilteredArchive(archive, new Nothing())); + } + + public void testFilterHalf() throws Exception { + + List list = list(new FilteredArchive(archive, new Half())); + + assertEquals(25, list.size()); + } + + public void testFilterAll() throws Exception { + + List list = list(new FilteredArchive(archive, new All())); + + assertEquals(0, list.size()); + } + + public static void assertEquals(Iterable expectedList, Iterable actualList) { + final Iterator expected = expectedList.iterator(); + final Iterator actual = actualList.iterator(); + + int i = 0; + while (expected.hasNext() && actual.hasNext()) { + assertEquals(expected.next().getName(), actual.next().getName()); + i++; + } + + assertEquals(i + " items were the same", expected.hasNext(), actual.hasNext()); + } + + public static class Nothing implements Filter { + + public boolean accept(String name) { + return true; + } + } + + public static class All implements Filter { + + public boolean accept(String name) { + return false; + } + } + + public static class Half implements Filter { + + private boolean accept; + + public boolean accept(String name) { + return accept = !accept; + } + } + + public static List list(Iterable iterable) { + List list = new ArrayList(); + + for (Archive.Entry t : iterable) { + list.add(t.getName()); + } + + return list; + } + + + @Override + protected void setUp() throws Exception { + // 50 entries + archive = new MockArchive( + "org.apache.xbean.asm.AnnotationVisitor", + "org.apache.xbean.asm.AnnotationWriter", + "org.apache.xbean.asm.Attribute", + "org.apache.xbean.asm.ClassAdapter", + "org.apache.xbean.asm.commons.AdviceAdapter", + "org.apache.xbean.asm.commons.AnalyzerAdapter", + "org.apache.xbean.asm.commons.GeneratorAdapter", + "org.apache.xbean.asm.commons.JSRInlinerAdapter", + "org.apache.xbean.asm.commons.RemappingAnnotationAdapter", + "org.apache.xbean.asm.commons.RemappingClassAdapter", + "org.apache.xbean.asm.commons.RemappingFieldAdapter", + "org.apache.xbean.asm.commons.RemappingMethodAdapter", + "org.apache.xbean.asm.commons.RemappingSignatureAdapter", + "org.apache.xbean.asm.commons.SerialVersionUIDAdder", + "org.apache.xbean.asm.MethodAdapter", + "org.apache.xbean.asm.tree.AnnotationNode", + "org.apache.xbean.asm.tree.MultiANewArrayInsnNode", + "org.apache.xbean.finder.AnnotatedMember", + "org.apache.xbean.finder.AnnotatedMethod", + "org.apache.xbean.finder.AnnotatedTarget", + "org.apache.xbean.finder.AnnotationFinder", + "org.apache.xbean.finder.archive.Archive", + "org.apache.xbean.finder.BundleAnnotationFinder", + "org.apache.xbean.finder.archive.BundleArchive", + "org.apache.xbean.finder.BundleAssignableClassFinder", + "org.apache.xbean.finder.archive.ClassesArchive", + "org.apache.xbean.finder.archive.ClasspathArchive", + "org.apache.xbean.finder.MetaAnnotation", + "org.apache.xbean.finder.PackageFilteredArchive", + "org.apache.xbean.finder.PrefixFilteredArchive", + "org.apache.xbean.finder.RegexFilteredArchive", + "org.acme.bar.AnnType", + "org.acme.bar.FullyAnnotated", + "org.acme.bar.ParamA", + "org.apache.xbean.finder.MetaAnnotatedClassTest", + "org.apache.xbean.finder.MetaAnnotatedMethodTest", + "org.apache.xbean.finder.BundleAnnotationFinder", + "org.apache.xbean.finder.BundleAssignableClassFinder", + "org.apache.xbean.naming.context.ContextAccess", + "org.apache.xbean.naming.context.ContextAccessControlList", + "org.apache.xbean.propertyeditor.ArrayConverter", + "org.apache.xbean.propertyeditor.ArrayListEditor", + "org.apache.xbean.propertyeditor.Inet4AddressEditor", + "org.apache.xbean.propertyeditor.Inet6AddressEditor", + "org.apache.xbean.propertyeditor.InetAddressEditor", + "org.apache.xbean.recipe.AllPropertiesRecipe", + "org.apache.xbean.recipe.ArrayRecipe", + "org.apache.xbean.recipe.AsmParameterNameLoader", + "org.apache.xbean.recipe.MissingAccessorException", + "org.apache.xbean.recipe.XbeanAsmParameterNameLoader" + ); + } +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/JarArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/JarArchiveTest.java new file mode 100644 index 00000000..a4ae0918 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/JarArchiveTest.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.xbean.finder.archive; + +import org.acme.foo.Blue; +import org.acme.foo.Green; +import org.acme.foo.Red; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +/** + * @version $Rev$ $Date$ + */ +public class JarArchiveTest { + + private static final Class[] classes = {Blue.class, Blue.Navy.class, Blue.Sky.class, Green.class, Green.Emerald.class, Red.class, Red.CandyApple.class, Red.Pink.class}; + private static File classpath; + private JarArchive archive; + + @BeforeClass + public static void classSetUp() throws Exception { + + classpath = Archives.jarArchive(classes); + } + + @Before + public void setUp() throws Exception { + + URL[] urls = {new URL("jar:" + classpath.toURI().toURL() + "!/")}; + + archive = new JarArchive(new URLClassLoader(urls), urls[0]); + } + + + @Test + public void testGetBytecode() throws Exception { + + for (Class clazz : classes) { + assertNotNull(clazz.getName(), archive.getBytecode(clazz.getName())); + } + + try { + archive.getBytecode("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + @Test + public void testLoadClass() throws Exception { + for (Class clazz : classes) { + assertEquals(clazz.getName(), clazz, archive.loadClass(clazz.getName())); + } + + try { + archive.loadClass("Fake"); + fail("ClassNotFoundException should have been thrown"); + } catch (ClassNotFoundException e) { + // pass + } + } + + @Test + public void testIterator() throws Exception { + List actual = new ArrayList(); + for (Archive.Entry entry : archive) { + actual.add(entry.getName()); + } + + assertFalse(0 == actual.size()); + + for (Class clazz : classes) { + assertTrue(clazz.getName(), actual.contains(clazz.getName())); + } + + assertEquals(classes.length, actual.size()); + } + + +} \ No newline at end of file diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MockArchive.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MockArchive.java new file mode 100644 index 00000000..7a9fc59f --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MockArchive.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.xbean.finder.archive; + +import org.apache.xbean.finder.archive.Archive; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class MockArchive implements Archive { + private final List list = new ArrayList(); + + public MockArchive(String... classNames) { + this(Arrays.asList(classNames)); + } + + public MockArchive(Iterable classNames) { + for (String className : classNames) { + list.add(className); + } + } + + public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { + return null; + } + + public Class loadClass(String className) throws ClassNotFoundException { + return null; + } + + public Iterator iterator() { + return new ArchiveIterator(this, list.iterator()); + } + +} diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/util/IOUtil.java b/xbean-finder/src/test/java/org/apache/xbean/finder/util/IOUtil.java new file mode 100644 index 00000000..5f016971 --- /dev/null +++ b/xbean-finder/src/test/java/org/apache/xbean/finder/util/IOUtil.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.xbean.finder.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.Flushable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; + +/** + * @version $Rev$ $Date$ + */ +public class IOUtil { + public static String readString(URL url) throws IOException { + InputStream in = url.openStream(); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + return reader.readLine(); + } finally { + close(in); + } + } + + public static String readString(File file) throws IOException { + FileReader in = new FileReader(file); + try { + BufferedReader reader = new BufferedReader(in); + return reader.readLine(); + } finally { + close(in); + } + } + + public static void writeString(File file, String string) throws IOException { + BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); + bufferedWriter.write(string); + bufferedWriter.newLine(); + close(bufferedWriter); + } + + public static void copy(File from, OutputStream to) throws IOException { + copy(read(from), to); + } + + public static void copy(InputStream from, OutputStream to) throws IOException { + try { + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = from.read(buffer)) != -1) { + to.write(buffer, 0, length); + } + } finally { + close(from); + close(to); + } + + } + + public static void close(Closeable closeable) throws IOException { + if (closeable == null) return; + try { + if (closeable instanceof Flushable) { + ((Flushable) closeable).flush(); + } + } catch (IOException e) { + } + try { + closeable.close(); + } catch (IOException e) { + } + } + + public static OutputStream write(File destination) throws FileNotFoundException { + OutputStream out = new FileOutputStream(destination); + return new BufferedOutputStream(out, 32768); + } + + public static InputStream read(File source) throws FileNotFoundException { + InputStream in = new FileInputStream(source); + return new BufferedInputStream(in, 32768); + } +} diff --git a/xbean-finder/src/test/resources/META-INF/externalizables/one b/xbean-finder/src/test/resources/META-INF/externalizables/one new file mode 100644 index 00000000..cf3474e3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/externalizables/one @@ -0,0 +1 @@ +org.acme.One \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/externalizables/three b/xbean-finder/src/test/resources/META-INF/externalizables/three new file mode 100644 index 00000000..c343100a --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/externalizables/three @@ -0,0 +1 @@ +org.openejb.ClassThatDoesntExist \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/externalizables/two b/xbean-finder/src/test/resources/META-INF/externalizables/two new file mode 100644 index 00000000..6b1951b0 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/externalizables/two @@ -0,0 +1 @@ +org.acme.Two \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.io.InputStream b/xbean-finder/src/test/resources/META-INF/java.io.InputStream new file mode 100644 index 00000000..cf3474e3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.io.InputStream @@ -0,0 +1 @@ +org.acme.One \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.io.OutputStream b/xbean-finder/src/test/resources/META-INF/java.io.OutputStream new file mode 100644 index 00000000..c343100a --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.io.OutputStream @@ -0,0 +1 @@ +org.openejb.ClassThatDoesntExist \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.io.Serializable b/xbean-finder/src/test/resources/META-INF/java.io.Serializable new file mode 100644 index 00000000..cf3474e3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.io.Serializable @@ -0,0 +1 @@ +org.acme.One \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/bar b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/bar new file mode 100644 index 00000000..784873d2 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/bar @@ -0,0 +1 @@ +org.acme.BarUrlHandler \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/baz b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/baz new file mode 100644 index 00000000..1ac2eee0 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/baz @@ -0,0 +1 @@ +org.acme.Three \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/foo b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/foo new file mode 100644 index 00000000..bd28e835 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/java.net.URLStreamHandler/foo @@ -0,0 +1 @@ +org.acme.FooUrlHandler \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/java b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/java new file mode 100644 index 00000000..5a12376f --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/java @@ -0,0 +1 @@ +org.acme.javaURLContextFactory \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/kernel b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/kernel new file mode 100644 index 00000000..3f367b58 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/kernel @@ -0,0 +1 @@ +org.acme.kernelURLContextFactory \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/ldap b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/ldap new file mode 100644 index 00000000..3022d0f3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/javax.naming.spi.ObjectFactory/ldap @@ -0,0 +1 @@ +org.acme.ldapURLContextFactory \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/movies/ishtar.properties b/xbean-finder/src/test/resources/META-INF/movies/ishtar.properties new file mode 100644 index 00000000..d885a824 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/movies/ishtar.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. +# +################################################################################ +# Malformed \uxxxx encoding +\u999 \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/movies/kingkong.properties b/xbean-finder/src/test/resources/META-INF/movies/kingkong.properties new file mode 100644 index 00000000..0ceaad79 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/movies/kingkong.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. +# +################################################################################ +director=Peter Jackson +star=Naomi Watts +year=2005 diff --git a/xbean-finder/src/test/resources/META-INF/movies/serentity.properties b/xbean-finder/src/test/resources/META-INF/movies/serentity.properties new file mode 100644 index 00000000..25fbaab5 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/movies/serentity.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. +# +################################################################################ +director=Joss Whedon +star=Nathan Fillion +year=2005 diff --git a/xbean-finder/src/test/resources/META-INF/serializables/one b/xbean-finder/src/test/resources/META-INF/serializables/one new file mode 100644 index 00000000..cf3474e3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/serializables/one @@ -0,0 +1 @@ +org.acme.One \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/serializables/three b/xbean-finder/src/test/resources/META-INF/serializables/three new file mode 100644 index 00000000..1ac2eee0 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/serializables/three @@ -0,0 +1 @@ +org.acme.Three \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/serializables/two b/xbean-finder/src/test/resources/META-INF/serializables/two new file mode 100644 index 00000000..6b1951b0 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/serializables/two @@ -0,0 +1 @@ +org.acme.Two \ No newline at end of file diff --git a/xbean-finder/src/test/resources/META-INF/tvshows/familyguy.properties b/xbean-finder/src/test/resources/META-INF/tvshows/familyguy.properties new file mode 100644 index 00000000..3ba78aae --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/tvshows/familyguy.properties @@ -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. +# +################################################################################ +creator=Seth MacFarlane +father=Peter +mother=Lois +son=Chris +daughter=Meg +baby=Stewie diff --git a/xbean-finder/src/test/resources/META-INF/tvshows/simpsons.properties b/xbean-finder/src/test/resources/META-INF/tvshows/simpsons.properties new file mode 100644 index 00000000..d1250ed3 --- /dev/null +++ b/xbean-finder/src/test/resources/META-INF/tvshows/simpsons.properties @@ -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. +# +################################################################################ +creator=Matt Groening +father=Homer +mother=Marge +son=Bart +daughter=Lisa +baby=Maggie diff --git a/xbean-naming/pom.xml b/xbean-naming/pom.xml new file mode 100644 index 00000000..2ca0505b --- /dev/null +++ b/xbean-naming/pom.xml @@ -0,0 +1,34 @@ + + + + + + + xbean + org.apache.xbean + 3.12 + + 4.0.0 + xbean-naming + bundle + Apache XBean :: Naming + xbean-naming implements a flexible non-persistent jndi provider + + diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractContext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractContext.java new file mode 100644 index 00000000..85b6a100 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractContext.java @@ -0,0 +1,860 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.CompositeName; +import javax.naming.Context; +import javax.naming.InvalidNameException; +import javax.naming.Name; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.NotContextException; +import javax.naming.LinkRef; +import javax.naming.NameNotFoundException; +import javax.naming.InitialContext; +import javax.naming.OperationNotSupportedException; +import javax.naming.NameClassPair; +import javax.naming.Binding; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Map; + +public abstract class AbstractContext implements Context, NestedContextFactory, Serializable { + private static final long serialVersionUID = 6481918425692261483L; + private final String nameInNamespace; + private final Name parsedNameInNamespace; + private final ContextAccess contextAccess; + private final boolean modifiable; + private final ThreadLocal inCall = new ThreadLocal(); + + protected AbstractContext(String nameInNamespace) { + this(nameInNamespace, ContextAccess.MODIFIABLE); + } + + public AbstractContext(String nameInNamespace, ContextAccess contextAccess) { + this.nameInNamespace = nameInNamespace; + try { + this.parsedNameInNamespace = getNameParser().parse(nameInNamespace); + } catch (NamingException e) { + throw new RuntimeException(e); + } + this.contextAccess = contextAccess; + this.modifiable = contextAccess.isModifiable(getParsedNameInNamespace()); + } + + public void close() throws NamingException { + //Ignore. Explicitly do not close the context + } + + protected ContextAccess getContextAccess() { + return contextAccess; + } + + // + // Lookup Binding + // + + /** + * Gets the object bound to the name. The name may contain slashes. + * @param name the name + * @return the object bound to the name, or null if not found + */ + protected Object getDeepBinding(String name) { + return null; + } + + /** + * Gets the object bound to the name. The name will not contain slashes. + * @param name the name + * @return the object bound to the name, or null if not found + * @throws javax.naming.NamingException on error + */ + protected Object getBinding(String name) throws NamingException { + Map bindings = getBindings(); + return bindings.get(name); + } + + /** + * Finds the specified entry. Normally there is no need to override this method; instead you should + * simply implement the getDeepBindings(String) and getBindings(String) method. + * + * This method will follow links except for the final element which is always just returned without + * inspection. This means this method can be used to implement lookupLink. + * + * @param stringName the string version of the name; maybe null + * @param parsedName the parsed name; may be null + * @return the value bound to the name + * @throws NamingException if no value is bound to that name or if a problem occurs during the lookup + */ + protected Object lookup(String stringName, Name parsedName) throws NamingException { + if (stringName == null && parsedName == null) { + throw new IllegalArgumentException("Both stringName and parsedName are null"); + } + if (stringName == null) stringName = parsedName.toString(); + + // try to look up the name directly (this is the fastest path) + Object directLookup = getDeepBinding(stringName); + if (directLookup != null) { + return ContextUtil.resolve(directLookup, stringName, parsedName, this); + } + + // if the parsed name has no parts, they are asking for the current context + if (parsedName == null) parsedName = getNameParser().parse(stringName); + if (parsedName.isEmpty()) { + return this; + } + + // we didn't find an entry, pop the first element off the parsed name and attempt to + // get a context from the bindings and delegate to that context + Object localValue; + String firstNameElement = parsedName.get(0); + if (firstNameElement.length() == 0) { + // the element is null... this is normally caused by looking up with a trailing '/' character + localValue = this; + } else { + localValue = getBinding(firstNameElement); + } + + if (localValue != null) { + + // if the name only had one part, we've looked up everything + if (parsedName.size() == 1) { + localValue = ContextUtil.resolve(localValue, stringName, parsedName, this); + return localValue; + } + + // if we have a link ref, follow it + if (localValue instanceof LinkRef) { + LinkRef linkRef = (LinkRef) localValue; + localValue = lookup(linkRef.getLinkName()); + } + + // we have more to lookup so we better have a context object + if (!(localValue instanceof Context)) { + throw new NameNotFoundException(stringName); + } + + // delegate to the sub-context + return ((Context) localValue).lookup(parsedName.getSuffix(1)); + } + + // if we didn't find an entry, it may be an absolute name + Object value = faultLookup(stringName, parsedName); + if (value != null) { + return value; + } + if (parsedName.size() > 1) { + throw new NotContextException(stringName); + } else { + throw new NameNotFoundException(stringName); + } + } + + /** + * When a value can not be found within this context, this method is called as a last ditch effort befrore + * thowing a null pointer exception. + * @param stringName the string version of the name; will not be null + * @param parsedName the parsed name; will not be null + * @return the value or null if no fault value could be found + */ + protected Object faultLookup(String stringName, Name parsedName) { + if (!stringName.startsWith(nameInNamespace) && stringName.indexOf(':') > 0 && inCall.get() == null) { + inCall.set(parsedName); + try { + Context ctx = new InitialContext(); + return ctx.lookup(parsedName); + } catch (NamingException ignored) { + // thrown below + } finally { + inCall.set(null); + } + } + return null; + } + + protected Context lookupFinalContext(Name name) throws NamingException { + Object value; + try { + value = lookup(name.getPrefix(name.size() - 1)); + } catch (NamingException e) { + throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " does not exist"); + } + + if (value == null) { + throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " does not exist"); + } else if (!(value instanceof Context)) { + throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " is not a context"); + } else { + return (Context) value; + } + } + + // + // List Bindings + // + + /** + * Gets a map of the bindings for the current node (i.e., no names with slashes). + * This method must not return null. + * + * @return a Map from binding name to binding value + * @throws NamingException if a problem occurs while getting the bindigns + */ + protected abstract Map getBindings() throws NamingException; + + // + // Add Binding + // + + protected void addDeepBinding(Name name, Object value, boolean rebind, boolean createIntermediateContexts) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + if (value == null) throw new NullPointerException("value is null for name: " + name); + + if (name.isEmpty()) { + throw new InvalidNameException("Name is empty"); + } + + if (name.size() == 1) { + addBinding(name.get(0), value, rebind); + return; + } + + if (!createIntermediateContexts) { + Context context = lookupFinalContext(name); + + String lastSegment = name.get(name.size() - 1); + addBinding(context, lastSegment, value, rebind); + } else { + Context currentContext = this; + for (int i = 0; i < name.size(); i++) { + String part = name.get(i); + + // empty path parts are not allowed + if (part.length() == 0) { + // this could be supported but it would be tricky + throw new InvalidNameException("Name part " + i + " is empty: " + name); + } + + // Is this the last element in the name? + if (i == name.size() - 1) { + // we're at the end... (re)bind the value into the parent context + addBinding(currentContext, part, value, rebind); + + // all done... this is redundant but makes the code more readable + break; + } else { + Object currentValue = getBinding(currentContext, part); + if (currentValue == null) { + // the next step in the tree is not present, so create everything down + // and add it to the current bindings + Context subcontext = createSubcontextTree(name.getPrefix(i).toString(), name.getSuffix(i), value); + addBinding(currentContext, part, subcontext, rebind); + + // all done + break; + } else { + // the current value must be a nested subcontext + if (!(currentValue instanceof Context)) { + throw new NotContextException("Expected an instance of context to be bound at " + + part + " but found an instance of " + currentValue.getClass().getName()); + } + currentContext = (Context) currentValue; + // now we recurse into the current context + } + } + } + } + } + + /** + * Gets the value bound to the specified name within the specified context. If the specified context is an + * AbstractContext this method will call the faster getBinding method, otherwise it will call lookup. + * + * @param context the context to get the binding from + * @param name the binding name + * @return the bound value or null if no value was bound + */ + private static Object getBinding(Context context, String name) { + try { + if (context instanceof AbstractContext) { + AbstractContext abstractContext = (AbstractContext) context; + return abstractContext.getBinding(name); + } else { + return context.lookup(name); + } + } catch (NamingException e) { + return null; + } + } + + /** + * Binds the specified value to the specified name within the specified context. If the specified context is an + * AbstractContext and is a nested subcontext, this method will call the direct addBinding method, otherwise it + * will call public (re)bind method. + * + * @param context the context to add the binding to + * @param name the binding name + * @param value the value to bind + * @param rebind if true, this method will replace any exsiting binding, otherwise a NamingException will be thrown + * @throws NamingException if a problem occurs while (re)binding + */ + protected void addBinding(Context context, String name, Object value, boolean rebind) throws NamingException { + if (context == this || (context instanceof AbstractContext && isNestedSubcontext(context))) { + AbstractContext abstractContext = (AbstractContext) context; + abstractContext.addBinding(name, value, rebind); + } else { + if (rebind) { + context.rebind(name, value); + } else { + context.bind(name, value); + } + } + } + + protected abstract boolean addBinding(String name, Object value, boolean rebind) throws NamingException; + + /** + * Creates a context tree which will be rooted at the specified path and contain a single entry located down + * a path specified by the name. All necessary intermediate contexts will be created using the createContext method. + * @param path the path to the context that will contains this context + * @param name the name under which the value should be bound + * @param value the value + * @return a context with the value bound at the specified name + * @throws NamingException if a problem occurs while creating the subcontext tree + */ + protected Context createSubcontextTree(String path, Name name, Object value) throws NamingException { + if (path == null) throw new NullPointerException("path is null"); + if (name == null) throw new NullPointerException("name is null"); + if (name.size() < 2) throw new InvalidNameException("name must have at least 2 parts " + name); + + if (path.length() > 0 && !path.endsWith("/")) path += "/"; + + for (int i = name.size() - 1; i > 0; i--) { + String fullPath = path + name.getPrefix(i); + String key = name.get(i); + value = createNestedSubcontext(fullPath, Collections.singletonMap(key, value)); + } + return (Context) value; + } + + + // + // Remove Binding + // + + /** + * Removes the binding from the context. The name will not contain a path and the value will not + * be a nested context although it may be a foreign context. + * @param name name under which the value should be bound + * @param removeNotEmptyContext ??? TODO figure this out + * @return whether removal was successful + * @throws NamingException if a problem occurs during the bind such as a value already being bound + */ + protected abstract boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException; + + protected void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { + removeDeepBinding(name, pruneEmptyContexts, false); + } + + protected void removeDeepBinding(Name name, boolean pruneEmptyContexts, boolean removeNotEmptyContext) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new InvalidNameException("Name is empty"); + } + + if (name.size() == 1) { + removeBinding(name.get(0), removeNotEmptyContext); + return; + } + + if (!pruneEmptyContexts) { + Context context = lookupFinalContext(name); + context.unbind(name.getSuffix(name.size() - 1)); + } else { + // we serch the tree for a target context and name to remove + // this is normally the last context in the tree and the final name part, but + // it may be farther up the path if the intervening nodes are empty + Context targetContext = this; + String targetName = name.get(0); + + Context currentContext = this; + for (int i = 0; i < name.size(); i++) { + String part = name.get(i); + + // empty path parts are not allowed + if (part.length() == 0) { + throw new InvalidNameException("Name part " + i + " is empty: " + name); + } + + // update targets + if (getSize(currentContext) > 1) { + targetContext = currentContext; + targetName = part; + } + + + // Is this the last element in the name? + if (i == name.size() - 1) { + // we're at the end... unbind value + unbind(targetContext, targetName, true); + + // all done... this is redundant but makes the code more readable + break; + } else { + Object currentValue = getBinding(currentContext, part); + if (currentValue == null) { + // path not found we are done, but first prune the empty contexts + if (targetContext != currentContext) { + unbind(targetContext, targetName, false); + } + break; + } else { + // the current value must be a context + if (!(currentValue instanceof Context)) { + throw new NotContextException("Expected an instance of context to be bound at " + + part + " but found an instance of " + currentValue.getClass().getName()); + } + currentContext = (Context) currentValue; + // now we recurse into the current context + } + } + } + } + } + + protected static boolean isEmpty(Context context) throws NamingException { + if (context instanceof AbstractContext) { + AbstractContext abstractContext = (AbstractContext) context; + Map currentBindings = abstractContext.getBindings(); + return currentBindings.isEmpty(); + } else { + NamingEnumeration namingEnumeration = context.list(""); + return namingEnumeration.hasMore(); + } + } + + protected static int getSize(Context context) throws NamingException { + if (context instanceof AbstractContext) { + AbstractContext abstractContext = (AbstractContext) context; + Map currentBindings = abstractContext.getBindings(); + return currentBindings.size(); + } else { + NamingEnumeration namingEnumeration = context.list(""); + int size = 0; + while (namingEnumeration.hasMore()) size++; + return size; + } + } + + /** + * Unbinds any value bound to the specified name within the specified context. If the specified context is an + * AbstractContext and is a nested context, this method will call the direct removeBinding method, otherwise it + * will call public unbind. + * + * @param context the context to remove the binding from + * @param name the binding name + * @param removeNotEmptyContext ??? TODO figure this out + * @throws NamingException if a problem occurs while unbinding + */ + private void unbind(Context context, String name, boolean removeNotEmptyContext) throws NamingException { + if (context == this || (context instanceof AbstractContext && isNestedSubcontext(context))) { + AbstractContext abstractContext = (AbstractContext) context; + abstractContext.removeBinding(name, removeNotEmptyContext); + } else { + context.unbind(name); + } + } + + // + // Environment + // + + /** + * Always returns a new (empty) Hashtable. + * @return a new (empty) Hashtable + */ + public Hashtable getEnvironment() { + return new Hashtable(); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + if (propName == null) throw new NullPointerException("propName is null"); + if (propVal == null) throw new NullPointerException("propVal is null"); + + Map env = getEnvironment(); + return env.put(propName, propVal); + } + + public Object removeFromEnvironment(String propName) throws NamingException { + if (propName == null) throw new NullPointerException("propName is null"); + + Map env = getEnvironment(); + return env.remove(propName); + } + + // + // Name handling + // + + /** + * Gets the name of this context withing the global namespace. This method may return null + * if the location of the node in the global namespace is not known + * @return the name of this context within the global namespace or null if unknown. + */ + public String getNameInNamespace() { + return nameInNamespace; + } + + /** + * Gets the name of this context withing the global namespace. This method may return null + * if the location of the node in the global namespace is not known + * @return the name of this context within the global namespace or null if unknown. + */ + protected Name getParsedNameInNamespace() { + return parsedNameInNamespace; + } + + /** + * Gets the name of a path withing the global namespace context. + * @param path path to extend + * @return full path in namespace + */ + protected String getNameInNamespace(String path) { + String nameInNamespace = getNameInNamespace(); + if (nameInNamespace == null || nameInNamespace.length() == 0) { + return path; + } else { + return nameInNamespace + "/" + path; + } + } + + /** + * Gets the name of a path withing the global namespace context. + * @param path path to extend + * @return full path in namespace + * @throws javax.naming.NamingException on error + */ + protected Name getNameInNamespace(Name path) throws NamingException { + Name nameInNamespace = getParsedNameInNamespace(); + if (nameInNamespace == null || nameInNamespace.size() == 0) { + return path; + } else { + return composeName(nameInNamespace, path); + } + } + + /** + * A parser that can turn Strings into javax.naming.Name objects. + * @return ContextUtil.NAME_PARSER + */ + protected NameParser getNameParser() { + return ContextUtil.NAME_PARSER; + } + + public NameParser getNameParser(Name name) { + return getNameParser(); + } + + public NameParser getNameParser(String name) { + return getNameParser(); + } + + public Name composeName(Name name, Name prefix) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + if (prefix == null) throw new NullPointerException("prefix is null"); + + Name result = (Name) prefix.clone(); + result.addAll(name); + return result; + } + + public String composeName(String name, String prefix) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + if (prefix == null) throw new NullPointerException("prefix is null"); + + CompositeName result = new CompositeName(prefix); + result.addAll(new CompositeName(name)); + return result.toString(); + } + + // + // Lookup + // + + public Object lookup(String name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + Object value = lookup(name, null); + + // if we got a link back we need to resolve it + if (value instanceof LinkRef) { + LinkRef linkRef = (LinkRef) value; + value = lookup(linkRef.getLinkName()); + } + + return value; + } + + public Object lookup(Name name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + Object value = lookup(null, name); + + + // if we got a link back we need to resolve it + if (value instanceof LinkRef) { + LinkRef linkRef = (LinkRef) value; + value = lookup(linkRef.getLinkName()); + } + + return value; + } + + public Object lookupLink(String name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + return lookup(name, null); + } + + public Object lookupLink(Name name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + return lookup(null, name); + } + + // + // Bind, rebind, rename and unbind + // + + public void bind(String name, Object obj) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.length() == 0) { + throw new NameAlreadyBoundException("Cannot bind to an empty name (this context)"); + } + bind(new CompositeName(name), obj); + } + + public void bind(Name name, Object obj) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new NameAlreadyBoundException("Cannot bind to an empty name (this context)"); + } + addDeepBinding(name, obj, false, false); + } + + public void rebind(String name, Object obj) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + rebind(new CompositeName(name), obj); + } + + public void rebind(Name name, Object obj) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new NameAlreadyBoundException("Cannot rebind an empty name (this context)"); + } + addDeepBinding(name, obj, true, false); + } + + public void rename(String oldName, String newName) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (oldName == null) throw new NullPointerException("oldName is null"); + if (newName == null) throw new NullPointerException("newName is null"); + rename(new CompositeName(oldName), new CompositeName(newName)); + } + + public void rename(Name oldName, Name newName) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (oldName == null || newName == null) { + throw new NullPointerException("name is null"); + } else if (oldName.isEmpty() || newName.isEmpty()) { + throw new NameAlreadyBoundException("Name cannot be empty"); + } + this.bind(newName, this.lookup(oldName)); + this.unbind(oldName); + } + + public void unbind(String name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + unbind(new CompositeName(name)); + } + + public void unbind(Name name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new InvalidNameException("Cannot unbind empty name"); + } + removeDeepBinding(name, false); + } + + // + // List + // + + protected NamingEnumeration list() throws NamingException { + Map bindings = getBindings(); + return new ContextUtil.ListEnumeration(bindings); + } + + protected NamingEnumeration listBindings() throws NamingException { + Map bindings = getBindings(); + return new ContextUtil.ListBindingEnumeration(bindings, this); + } + + public NamingEnumeration list(String name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + // if the name is empty, list the current context + if (name.length() == 0) { + return list(); + } + + // lookup the target context + Object target; + try { + target = lookup(name); + } catch (NamingException e) { + throw new NotContextException(name); + } + + if (target == this) { + return list(); + } else if (target instanceof Context) { + return ((Context) target).list(""); + } else { + throw new NotContextException("The name " + name + " cannot be listed"); + } + } + + public NamingEnumeration list(Name name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + // if the name is empty, list the current context + if (name.isEmpty()) { + return list(); + } + + // lookup the target context + Object target; + try { + target = lookup(name); + } catch (NamingException e) { + throw new NotContextException(name.toString()); + } + + if (target == this) { + return list(); + } else if (target instanceof Context) { + return ((Context) target).list(""); + } else { + throw new NotContextException("The name " + name + " cannot be listed"); + } + } + + public NamingEnumeration listBindings(String name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + // if the name is empty, list the current context + if (name.length() == 0) { + return listBindings(); + } + + // lookup the target context + Object target; + try { + target = lookup(name); + } catch (NamingException e) { + throw new NotContextException(name); + } + + if (target == this) { + return listBindings(); + } else if (target instanceof Context) { + return ((Context) target).listBindings(""); + } else { + throw new NotContextException("The name " + name + " cannot be listed"); + } + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + if (name == null) throw new NullPointerException("name is null"); + + // if the name is empty, list the current context + if (name.isEmpty()) { + return listBindings(); + } + + // lookup the target context + Object target; + try { + target = lookup(name); + } catch (NamingException e) { + throw new NotContextException(name.toString()); + } + + if (target == this) { + return listBindings(); + } else if (target instanceof Context) { + return ((Context) target).listBindings(""); + } else { + throw new NotContextException("The name " + name + " cannot be listed"); + } + } + + // + // Subcontexts + // + + public Context createSubcontext(String name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + return createSubcontext(new CompositeName(name)); + } + + public Context createSubcontext(Name name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new NameAlreadyBoundException("Cannot create a subcontext if the name is empty"); + } + Context abstractContext = createNestedSubcontext(name.toString(), Collections.EMPTY_MAP); + addDeepBinding(name, abstractContext, false, false); + return abstractContext; + } + + public void destroySubcontext(String name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + destroySubcontext(new CompositeName(name)); + } + + public void destroySubcontext(Name name) throws NamingException { + if (!modifiable) throw new OperationNotSupportedException("Context is read only"); + if (name == null) throw new NullPointerException("name is null"); + if (name.isEmpty()) { + throw new InvalidNameException("Cannot destroy subcontext with empty name"); + } + unbind(name); + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractFederatedContext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractFederatedContext.java new file mode 100644 index 00000000..4446d4a0 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/AbstractFederatedContext.java @@ -0,0 +1,184 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NamingException; + +/** + * @version $Rev$ $Date$ + */ +public abstract class AbstractFederatedContext extends AbstractContext { + private final ContextFederation contextFederation; + private final AbstractFederatedContext masterContext; + + public AbstractFederatedContext() { + this("", ContextAccess.MODIFIABLE); + } + + public AbstractFederatedContext(String nameInNamespace) { + this(nameInNamespace, ContextAccess.MODIFIABLE); + } + + public AbstractFederatedContext(String nameInNamespace, ContextAccess contextAccess) { + super(nameInNamespace, contextAccess); + this.masterContext = this; + this.contextFederation = new ContextFederation(this); + } + + public AbstractFederatedContext(String nameInNamespace, ContextAccess contextAccess, Set federatedContexts) { + super(nameInNamespace, contextAccess); + this.masterContext = this; + this.contextFederation = new ContextFederation(this, federatedContexts); + } + + public AbstractFederatedContext(AbstractFederatedContext masterContext, String nameInNamespace) throws NamingException { + super(nameInNamespace, masterContext.getContextAccess()); + this.masterContext = masterContext; + this.contextFederation = this.masterContext.contextFederation.createSubcontextFederation(nameInNamespace, this); + } + + protected Object faultLookup(String stringName, Name parsedName) { + Object value = contextFederation.lookup(parsedName); + if (value != null) { + return value; + } + return super.faultLookup(stringName, parsedName); + } + + @Override + protected Object getDeepBinding(String name) { + try { + Object value = contextFederation.getFederatedBinding(name); + if (value instanceof Context) { + return null; + } + return value; + } catch (NamingException e) { + return null; + } + } + + @Override + protected Object getBinding(String name) throws NamingException { + Object value = contextFederation.getFederatedBinding(name); + if (value instanceof Context) { + return createNestedSubcontext(name, getBindings(name)); + } + if (value == null) { + value = getWrapperBindings().get(name); + } + return value; + } + + protected final Map getBindings() throws NamingException { + return getBindings(""); + } + + protected final Map getBindings(String name) throws NamingException { + Map bindings = contextFederation.getFederatedBindings(name); + bindings.putAll(getWrapperBindings()); + return bindings; + } + + protected abstract Map getWrapperBindings() throws NamingException; + + protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + if (!(value instanceof Context && !isNestedSubcontext(value))) { + return contextFederation.addBinding(name, value, rebind); + } else if (!isNestedSubcontext(value)) { + Context federatedContext = (Context) value; + + // if we already have a context bound at the specified value + Object existingValue = getBinding(name); + if (existingValue != null) { + if (!(existingValue instanceof AbstractFederatedContext)) { + throw new NameAlreadyBoundException(name); + } + + AbstractFederatedContext nestedContext = (AbstractFederatedContext) existingValue; + addFederatedContext(nestedContext, federatedContext); + return true; + } else { + AbstractFederatedContext nestedContext = (AbstractFederatedContext) createNestedSubcontext(name, Collections.emptyMap()); + addFederatedContext(nestedContext, federatedContext); + + // call back into this method using the new nested context + // this gives subclasses a chance to handle the binding + return addBinding(name, nestedContext, rebind); + } + } + + return false; + } + + protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + return contextFederation.removeBinding(name); + } + + protected static void addFederatedContext(AbstractFederatedContext wrappingContext, Context innerContext) throws NamingException { + wrappingContext.contextFederation.addContext(innerContext); + for (Map.Entry entry : wrappingContext.getWrapperBindings().entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof AbstractFederatedContext) { + AbstractFederatedContext nestedContext = (AbstractFederatedContext) value; + + Name parsedName = wrappingContext.getNameParser().parse(name); + Name nameInNamespace = wrappingContext.getNameInNamespace(parsedName); + + VirtualSubcontext virtualSubcontext = new VirtualSubcontext(nameInNamespace, innerContext); + addFederatedContext(nestedContext, virtualSubcontext); + } + } + } + + protected static void removeFederatedContext(AbstractFederatedContext wrappingContext, Context innerContext) throws NamingException { + wrappingContext.contextFederation.removeContext(innerContext); + for (Map.Entry entry : wrappingContext.getWrapperBindings().entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof AbstractFederatedContext) { + AbstractFederatedContext nestedContext = (AbstractFederatedContext) value; + + Name parsedName = wrappingContext.getNameParser().parse(name); + Name nameInNamespace = wrappingContext.getNameInNamespace(parsedName); + + VirtualSubcontext virtualSubcontext = new VirtualSubcontext(nameInNamespace, innerContext); + removeFederatedContext(nestedContext, virtualSubcontext); + } + } + } + + public boolean isNestedSubcontext(Object value) { + if (value instanceof AbstractFederatedContext) { + AbstractFederatedContext context = (AbstractFederatedContext) value; + return getMasterContext() == context.getMasterContext(); + } + return false; + } + + protected AbstractFederatedContext getMasterContext() { + return masterContext; + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccess.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccess.java new file mode 100644 index 00000000..0cbeb1b5 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccess.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.xbean.naming.context; + +import javax.naming.Name; + +/** + * @version $Rev$ $Date$ + */ +public interface ContextAccess { + ContextAccess MODIFIABLE = new ContextAccess() { + public boolean isModifiable(Name name) { + return true; + } + }; + + ContextAccess UNMODIFIABLE = new ContextAccess() { + public boolean isModifiable(Name name) { + return false; + } + }; + + boolean isModifiable(Name name); +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccessControlList.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccessControlList.java new file mode 100644 index 00000000..d3f70c9b --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextAccessControlList.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.naming.Name; +import javax.naming.NamingException; + +/** + * @version $Rev$ $Date$ + */ +public class ContextAccessControlList implements ContextAccess { + private final boolean defaultAllow; + private final List allow; + private final List deny; + + public ContextAccessControlList(boolean defaultAllow, List allow, List deny) { + this.defaultAllow = defaultAllow; + this.allow = toACL(allow); + this.deny = toACL(deny); + } + + private List toACL(List input) { + if (input == null) return Collections.emptyList(); + + ArrayList list = new ArrayList(input.size()); + for (Object value : input) { + if (value instanceof Name) { + list.add((Name) value); + } else if (value instanceof String) { + String string = (String) value; + Name name; + try { + name = ContextUtil.NAME_PARSER.parse(string); + } catch (NamingException e) { + throw new IllegalArgumentException("error while parsing name: " + value); + } + list.add(name); + } else { + throw new IllegalArgumentException("name is not an instance of Name or String: " + value); + } + } + return Collections.unmodifiableList(list); + } + + public boolean isModifiable(Name name) { + if (name == null) throw new NullPointerException("name is null"); + if (defaultAllow) { + // allow by default, so allow if it wasn't explicitly denied or was explicitly allowed + return !isDenied(name) || isAllowed(name); + } else { + // deny by default, so allow if it was explicitly allowed or wasn't explicitly denied + return isAllowed(name) && !isDenied(name); + } + } + + protected boolean isAllowed(Name name) { + if (name == null) throw new NullPointerException("name is null"); + for (Name prefix : allow) { + if (name.startsWith(prefix)) { + return true; + } + } + + return false; + } + + protected boolean isDenied(Name name) { + if (name == null) throw new NullPointerException("name is null"); + for (Name prefix : deny) { + if (name.startsWith(prefix)) { + return true; + } + } + + return false; + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFederation.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFederation.java new file mode 100644 index 00000000..650a9400 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFederation.java @@ -0,0 +1,191 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.NotContextException; +import javax.naming.OperationNotSupportedException; + +/** + * @version $Rev$ $Date$ + */ +public class ContextFederation { + private final Context actualContext; + private final AtomicReference> federatedContextRef = new AtomicReference>(Collections.emptySet()); + public static final int MAX_WRITE_ATTEMPTS = 10; + + public ContextFederation(Context actualContext) { + this.actualContext = actualContext; + } + + public ContextFederation(Context actualContext, Set federatedContexts) { + this.actualContext = actualContext; + Set copy = new LinkedHashSet(federatedContexts); + federatedContextRef.set(Collections.unmodifiableSet(copy)); + } + + public void addContext(Context context) { + Set federatedContext; + Set newFederatedContext; + for (int i = 0; i < MAX_WRITE_ATTEMPTS; i++) { + federatedContext = getFederatedContexts(); + + newFederatedContext = new LinkedHashSet(federatedContext); + newFederatedContext.add(context); + newFederatedContext = Collections.unmodifiableSet(newFederatedContext); + if (federatedContextRef.compareAndSet(federatedContext, newFederatedContext)) { + return; + } + } + throw new RuntimeException("Unable to update federatedContextRef within " + MAX_WRITE_ATTEMPTS + " attempts"); + } + + public void removeContext(Context context) { + Set federatedContext; + Set newFederatedContext; + for (int i = 0; i < MAX_WRITE_ATTEMPTS; i++) { + federatedContext = getFederatedContexts(); + + newFederatedContext = new LinkedHashSet(federatedContext); + newFederatedContext.remove(context); + newFederatedContext = Collections.unmodifiableSet(newFederatedContext); + if (federatedContextRef.compareAndSet(federatedContext, newFederatedContext)) { + return; + } + } + throw new RuntimeException("Unable to update federatedContextRef within " + MAX_WRITE_ATTEMPTS + " attempts"); + } + + public Set getFederatedContexts() { + return federatedContextRef.get(); + } + + public Object getFederatedBinding(String name) throws NamingException { + for (Context context : getFederatedContexts()) { + + try { + Object value = context.lookup(name); + if (value != null) { + return value; + } + } catch (NamingException e) { + // ignore + } + } + return null; + } + + public Map getFederatedBindings(String name) throws NamingException { + Map bindings = new HashMap(); + for (Context context : getFederatedContexts()) { + + // list federated context + try { + NamingEnumeration namingEnumeration = context.listBindings(name); + + // add to bindings + while (namingEnumeration.hasMoreElements()) { + Binding binding = (Binding) namingEnumeration.nextElement(); + String bindingName = binding.getName(); + + // don't overwrite existing bindings + if (!bindings.containsKey(bindingName)) { + try { + bindings.put(bindingName, binding.getObject()); + } catch (RuntimeException e) { + // if this is a wrapped NamingException, unwrap and throw the original + Throwable cause = e.getCause(); + if (cause != null && cause instanceof NamingException ) { + throw (NamingException)cause; + } + // Wrap this into a RuntimeException. + throw (NamingException)new NamingException("Could retrieve bound instance " + name).initCause(e); + } + } + } + } catch (NotContextException e) { + //this context does not include the supplied name + } + } + return bindings; + } + + protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + for (Context context : getFederatedContexts()) { + + try { + if (rebind) { + context.rebind(name, value); + } else { + context.bind(name, value); + } + return true; + } catch (OperationNotSupportedException ignored) { + } + } + return false; + } + + protected boolean removeBinding(String name) throws NamingException { + for (Context context : getFederatedContexts()) { + + try { + context.unbind(name); + return true; + } catch (OperationNotSupportedException ignored) { + } + } + return false; + } + + public Object lookup(Name name) { + for (Context federatedContext : getFederatedContexts()) { + try { + Object value = federatedContext.lookup(name); + if (value instanceof Context) { + return new VirtualSubcontext(name, actualContext); + } else { + return value; + } + } catch (NamingException ignored) { + } + } + return null; + } + + public ContextFederation createSubcontextFederation(String subcontextName, Context actualSubcontext) throws NamingException { + Name parsedSubcontextName = actualContext.getNameParser("").parse(subcontextName); + + ContextFederation subcontextFederation = new ContextFederation(actualSubcontext); + for (Context federatedContext : getFederatedContexts()) { + VirtualSubcontext virtualSubcontext = new VirtualSubcontext(parsedSubcontextName, federatedContext); + subcontextFederation.addContext(virtualSubcontext); + } + return subcontextFederation; + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFlyweight.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFlyweight.java new file mode 100644 index 00000000..28f0d756 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextFlyweight.java @@ -0,0 +1,157 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NameParser; +import javax.naming.NameClassPair; +import javax.naming.Binding; + +import java.util.Hashtable; + +/** + * @version $Rev$ $Date$ + */ +public abstract class ContextFlyweight implements Context { + protected abstract Context getContext() throws NamingException; + + protected Name getName(Name name) throws NamingException { + return name; + } + + protected String getName(String name) throws NamingException { + return name; + } + + public void close() throws NamingException { + } + + public String getNameInNamespace() throws NamingException { + return getContext().getNameInNamespace(); + } + + public Object lookup(Name name) throws NamingException { + return getContext().lookup(getName(name)); + } + + public Object lookup(String name) throws NamingException { + return getContext().lookup(getName(name)); + } + + public void bind(Name name, Object obj) throws NamingException { + getContext().bind(getName(name), obj); + } + + public void bind(String name, Object obj) throws NamingException { + getContext().bind(getName(name), obj); + } + + public void rebind(Name name, Object obj) throws NamingException { + getContext().rebind(getName(name), obj); + } + + public void rebind(String name, Object obj) throws NamingException { + getContext().rebind(getName(name), obj); + } + + public void unbind(Name name) throws NamingException { + getContext().unbind(getName(name)); + } + + public void unbind(String name) throws NamingException { + getContext().unbind(getName(name)); + } + + public void rename(Name oldName, Name newName) throws NamingException { + getContext().rename(getName(oldName), getName(newName)); + } + + public void rename(String oldName, String newName) throws NamingException { + getContext().rename(getName(oldName), getName(newName)); + } + + public NamingEnumeration list(Name name) throws NamingException { + return getContext().list(getName(name)); + } + + public NamingEnumeration list(String name) throws NamingException { + return getContext().list(getName(name)); + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + return getContext().listBindings(getName(name)); + } + + public NamingEnumeration listBindings(String name) throws NamingException { + return getContext().listBindings(getName(name)); + } + + public void destroySubcontext(Name name) throws NamingException { + getContext().destroySubcontext(getName(name)); + } + + public void destroySubcontext(String name) throws NamingException { + getContext().destroySubcontext(getName(name)); + } + + public Context createSubcontext(Name name) throws NamingException { + return getContext().createSubcontext(getName(name)); + } + + public Context createSubcontext(String name) throws NamingException { + return getContext().createSubcontext(getName(name)); + } + + public Object lookupLink(Name name) throws NamingException { + return getContext().lookupLink(getName(name)); + } + + public Object lookupLink(String name) throws NamingException { + return getContext().lookupLink(getName(name)); + } + + public NameParser getNameParser(Name name) throws NamingException { + return getContext().getNameParser(getName(name)); + } + + public NameParser getNameParser(String name) throws NamingException { + return getContext().getNameParser(getName(name)); + } + + public Name composeName(Name name, Name prefix) throws NamingException { + return getContext().composeName(name, prefix); + } + + public String composeName(String name, String prefix) throws NamingException { + return getContext().composeName(name, prefix); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + return getContext().addToEnvironment(propName, propVal); + } + + public Object removeFromEnvironment(String propName) throws NamingException { + return getContext().removeFromEnvironment(propName); + } + + public Hashtable getEnvironment() throws NamingException { + return getContext().getEnvironment(); + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextUtil.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextUtil.java new file mode 100644 index 00000000..fd61cf6a --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ContextUtil.java @@ -0,0 +1,309 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import javax.naming.Binding; +import javax.naming.CompoundName; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.spi.NamingManager; + +import org.apache.xbean.naming.reference.SimpleReference; + +/** + * @version $Rev$ $Date$ + */ +public final class ContextUtil { + private ContextUtil() { + } + + public final static NameParser NAME_PARSER = new SimpleNameParser(); + + public static Name parseName(String name) throws NamingException { + return NAME_PARSER.parse(name); + } + + public static Object resolve(Object value, String stringName, Name parsedName, Context nameCtx) throws NamingException { + if (!(value instanceof Reference)) { + return value; + } + + Reference reference = (Reference) value; + + // for SimpleReference we can just call the getContext method + if (reference instanceof SimpleReference) { + try { + return ((SimpleReference) reference).getContent(); + } catch (NamingException e) { + throw e; + } catch (Exception e) { + throw (NamingException) new NamingException("Could not look up : " + stringName == null? parsedName.toString(): stringName).initCause(e); + } + } + + // for normal References we have to do it the slow way + try { + if (parsedName == null) { + parsedName = NAME_PARSER.parse(stringName); + } + return NamingManager.getObjectInstance(reference, parsedName, nameCtx, nameCtx.getEnvironment()); + } catch (NamingException e) { + throw e; + } catch (Exception e) { + throw (NamingException) new NamingException("Could not look up : " + stringName == null? parsedName.toString(): stringName).initCause(e); + } + } + + public static Map listToMap(NamingEnumeration enumeration) { + Map result = new HashMap(); + while (enumeration.hasMoreElements()) { + NameClassPair nameClassPair = (NameClassPair) enumeration.nextElement(); + String name = nameClassPair.getName(); + result.put(name, nameClassPair.getClassName()); + } + return result; + } + + public static Map listBindingsToMap(NamingEnumeration enumeration) { + Map result = new HashMap(); + while (enumeration.hasMoreElements()) { + Binding binding = (Binding) enumeration.nextElement(); + String name = binding.getName(); + result.put(name, binding.getObject()); + } + return result; + } + + public static final class ListEnumeration implements NamingEnumeration { + private final Iterator iterator; + + public ListEnumeration(Map localBindings) { + this.iterator = localBindings.entrySet().iterator(); + } + + public boolean hasMore() { + return iterator.hasNext(); + } + + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + public NameClassPair next() { + return nextElement(); + } + + public NameClassPair nextElement() { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + Object value = entry.getValue(); + String className; + if (value instanceof Reference) { + Reference reference = (Reference) value; + className = reference.getClassName(); + } else { + className = value.getClass().getName(); + } + return new NameClassPair(name, className); + } + + public void close() { + } + } + + public static final class ListBindingEnumeration implements NamingEnumeration { + private final Iterator iterator; + private final Context context; + + public ListBindingEnumeration(Map localBindings, Context context) { + this.iterator = localBindings.entrySet().iterator(); + this.context = context; + } + + public boolean hasMore() { + return iterator.hasNext(); + } + + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + public Binding next() { + return nextElement(); + } + + public Binding nextElement() { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + Object value = entry.getValue(); + return new ReadOnlyBinding(name, value, context); + } + + public void close() { + } + } + + public static final class ReadOnlyBinding extends Binding { + private final Object value; + private final Context context; + private final boolean isRelative; + + public ReadOnlyBinding(String name, Object value, Context context) { + this(name, value, false, context); + } + + public ReadOnlyBinding(String name, Object value, boolean isRelative, Context context) { + super(name, value); + this.value = value; + this.context = context; + this.isRelative = isRelative; + } + + public void setName(String name) { + throw new UnsupportedOperationException("Context is read only"); + } + + public String getClassName() { + if (value instanceof Reference) { + Reference reference = (Reference) value; + return reference.getClassName(); + } + return value.getClass().getName(); + } + + public void setClassName(String name) { + throw new UnsupportedOperationException("Context is read only"); + } + + public Object getObject() { + try { + return resolve(value, getName(), null, context); + } catch (NamingException e) { + throw new RuntimeException(e); + } + } + + public void setObject(Object obj) { + throw new UnsupportedOperationException("Context is read only"); + } + + public boolean isRelative() { + return isRelative; + } + + public void setRelative(boolean r) { + throw new UnsupportedOperationException("Context is read only"); + } + } + + + private static final class SimpleNameParser implements NameParser { + private static final Properties PARSER_PROPERTIES = new Properties(); + + static { + PARSER_PROPERTIES.put("jndi.syntax.direction", "left_to_right"); + PARSER_PROPERTIES.put("jndi.syntax.separator", "/"); + } + + + private SimpleNameParser() { + } + + public Name parse(String name) throws NamingException { + return new CompoundName(name, PARSER_PROPERTIES); + } + } + + public static Map createBindings(Map absoluteBindings, NestedContextFactory factory) throws NamingException { + // create a tree of Nodes using the absolute bindings + Node node = buildMapTree(absoluteBindings); + + // convert the node tree into a tree of context objects + + return ContextUtil.createBindings(null, node, factory); + } + + private static Map createBindings(String nameInNameSpace, Node node, NestedContextFactory factory) throws NamingException { + Map bindings = new HashMap(node.size()); + for (Map.Entry entry : node.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + + // if this is a nested node we need to create a context for the node + if (value instanceof Node) { + Node nestedNode = (Node) value; + + // recursive call create bindings to cause building the context depth first + String path = nameInNameSpace == null ? name : nameInNameSpace + "/" + name; + + Map nestedBindings = createBindings(path, nestedNode, factory); + Context nestedContext = factory.createNestedSubcontext(path, nestedBindings); + bindings.put(name, nestedContext); + } else { + bindings.put(name, value); + } + } + return bindings; + } + + + /** + * Do nothing subclass of hashmap used to differentiate between a Map in the tree an a nested element during tree building + */ + public static final class Node extends HashMap { + } + + public static Node buildMapTree(Map absoluteBindings) throws NamingException { + Node rootContext = new Node(); + + for (Map.Entry entry : absoluteBindings.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + + Node parentContext = rootContext; + + Name compoundName = ContextUtil.parseName(name); + for (Enumeration parts = compoundName.getAll(); parts.hasMoreElements();) { + String part = (String) parts.nextElement(); + // the last element in the path is the name of the value + if (parts.hasMoreElements()) { + // nest node into parent + Node bindings = (Node) parentContext.get(part); + if (bindings == null) { + bindings = new Node(); + parentContext.put(part, bindings); + } + + parentContext = bindings; + } + } + + parentContext.put(compoundName.get(compoundName.size() - 1), value); + } + return rootContext; + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableContext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableContext.java new file mode 100644 index 00000000..116668fc --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableContext.java @@ -0,0 +1,173 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.OperationNotSupportedException; + +import org.apache.xbean.naming.reference.CachingReference; + +/** + * + * @version $Rev: 417891 $ $Date: 2006-06-28 15:45:07 -0700 (Wed, 28 Jun 2006) $ + */ +public class ImmutableContext extends AbstractContext { + private final Map localBindings; + private final Map absoluteIndex; + + public ImmutableContext(Map bindings) throws NamingException { + this("", bindings, true); + } + + public ImmutableContext(Map bindings, boolean cacheReferences) throws NamingException { + this("", bindings, cacheReferences); + } + + public ImmutableContext(String nameInNamespace, Map bindings, boolean cacheReferences) throws NamingException { + super(nameInNamespace, ContextAccess.UNMODIFIABLE); + + if (cacheReferences) { + bindings = CachingReference.wrapReferences(bindings, this); + } + + Map localBindings = ContextUtil.createBindings(bindings, this); + this.localBindings = Collections.unmodifiableMap(localBindings); + + Map globalBindings = ImmutableContext.buildAbsoluteIndex("", localBindings); + this.absoluteIndex = Collections.unmodifiableMap(globalBindings); + } + + private static Map buildAbsoluteIndex(String nameInNamespace, Map bindings) { + String path = nameInNamespace; + if (path.length() > 0) { + path += "/"; + } + + Map globalBindings = new HashMap(); + for (Map.Entry entry : bindings.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof NestedImmutableContext) { + NestedImmutableContext nestedContext = (NestedImmutableContext) value; + globalBindings.putAll(ImmutableContext.buildAbsoluteIndex(nestedContext.getNameInNamespace(), nestedContext.localBindings)); + } + globalBindings.put(path + name, value); + } + return globalBindings; + } + + protected Object getDeepBinding(String name) { + return absoluteIndex.get(name); + } + + protected Map getBindings() { + return localBindings; + } + + protected final void addDeepBinding(String name, Object value, boolean createIntermediateContexts) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + public boolean isNestedSubcontext(Object value) { + if (value instanceof NestedImmutableContext) { + NestedImmutableContext context = (NestedImmutableContext) value; + return this == context.getImmutableContext(); + } + return false; + } + + public Context createNestedSubcontext(String path, Map bindings) { + return new NestedImmutableContext(path, bindings); + } + + /** + * Nested context which shares the absolute index map in MapContext. + */ + public final class NestedImmutableContext extends AbstractContext { + private final Map localBindings; + private final String pathWithSlash; + + public NestedImmutableContext(String path, Map bindings) { + super(ImmutableContext.this.getNameInNamespace(path), ContextAccess.UNMODIFIABLE); + + path = getNameInNamespace(); + if (!path.endsWith("/")) path += "/"; + this.pathWithSlash = path; + + this.localBindings = Collections.unmodifiableMap(bindings); + } + + protected Object getDeepBinding(String name) { + String absoluteName = pathWithSlash + name; + return absoluteIndex.get(absoluteName); + } + + protected Map getBindings() { + return localBindings; + } + + protected final void addDeepBinding(String name, Object value, boolean createIntermediateContexts) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected final boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + public boolean isNestedSubcontext(Object value) { + if (value instanceof NestedImmutableContext) { + NestedImmutableContext context = (NestedImmutableContext) value; + return getImmutableContext() == context.getImmutableContext(); + } + return false; + } + + public Context createNestedSubcontext(String path, Map bindings) { + return new NestedImmutableContext(path, bindings); + } + + protected ImmutableContext getImmutableContext() { + return ImmutableContext.this; + } + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableFederatedContext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableFederatedContext.java new file mode 100644 index 00000000..739e93b4 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/ImmutableFederatedContext.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.xbean.naming.context; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.OperationNotSupportedException; + +/** + * @version $Rev$ $Date$ + */ +public class ImmutableFederatedContext extends AbstractFederatedContext { + + public ImmutableFederatedContext(String nameInNamespace, Set federatedContexts) { + super(nameInNamespace, ContextAccess.UNMODIFIABLE, federatedContexts); + } + + public void federateContext(Context context) throws NamingException { + addFederatedContext(this, context); + } + + public void unfederateContext(Context context) throws NamingException { + removeFederatedContext(this, context); + } + + @Override + protected Map getWrapperBindings() throws NamingException { + return Collections.emptyMap(); + } + + public Context createNestedSubcontext(String path, Map bindings) throws NamingException { + return new NestedImmutableFederatedContext(path, bindings); + } + + /** + * Nested context which shares the absolute index map in MapContext. + */ + public class NestedImmutableFederatedContext extends AbstractFederatedContext { + private final AtomicReference> bindingsRef; + private final String pathWithSlash; + + public NestedImmutableFederatedContext(String path, Map bindings) throws NamingException { + super(ImmutableFederatedContext.this, path); + + path = getNameInNamespace(); + if (!path.endsWith("/")) path += "/"; + this.pathWithSlash = path; + + this.bindingsRef = new AtomicReference>(Collections.unmodifiableMap(bindings)); + } + + public Context createNestedSubcontext(String path, Map bindings) throws NamingException { + return new NestedImmutableFederatedContext(getNameInNamespace(path), bindings); + } + + protected Object getDeepBinding(String name) { + String absoluteName = pathWithSlash + name; + return ImmutableFederatedContext.this.getDeepBinding(absoluteName); + } + + protected Map getWrapperBindings() throws NamingException { + return bindingsRef.get(); + } + + protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + + protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + throw new OperationNotSupportedException("Context is immutable"); + } + } + +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/NestedContextFactory.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/NestedContextFactory.java new file mode 100644 index 00000000..c776ae75 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/NestedContextFactory.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.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.NamingException; +import java.util.Map; + +/** + * @version $Rev$ $Date$ + */ +public interface NestedContextFactory { + /** + * Is the specified value an instance of a nested context + * @param value the value to inspect + * @return true if the specified value an instance of a nested context; false otherwise + */ + boolean isNestedSubcontext(Object value); + + /** + * Creates a nested subcontext instance. This does not cause the nested context to be bound. + * @param path the path to the new nested context + * @param bindings the initial bindings for the context + * @return the new nested context + * @throws javax.naming.NamingException on error + */ + Context createNestedSubcontext(String path, Map bindings) throws NamingException; +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/VirtualSubcontext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/VirtualSubcontext.java new file mode 100644 index 00000000..1b319f7c --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/VirtualSubcontext.java @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.NamingEnumeration; +import javax.naming.NameParser; +import javax.naming.Binding; +import javax.naming.NameClassPair; + +import java.util.Hashtable; + +/** + * @version $Rev$ $Date$ + */ +public class VirtualSubcontext extends ContextFlyweight { + private final Name nameInContext; + private final Context context; + + public VirtualSubcontext(Name nameInContext, Context context) throws NamingException { + if (context instanceof VirtualSubcontext) { + VirtualSubcontext virtualSubcontext = (VirtualSubcontext) context; + this.nameInContext = virtualSubcontext.getName(nameInContext); + this.context = virtualSubcontext.context; + } else { + this.nameInContext = nameInContext; + this.context = context; + } + } + + @Override + protected Context getContext() throws NamingException { + return context; + } + + @Override + protected Name getName(Name name) throws NamingException { + return context.composeName(nameInContext, name); + } + + @Override + protected String getName(String name) throws NamingException { + Name parsedName = context.getNameParser("").parse(name); + return context.composeName(nameInContext, parsedName).toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + VirtualSubcontext that = (VirtualSubcontext) o; + + if (context != null ? !context.equals(that.context) : that.context != null) return false; + if (nameInContext != null ? !nameInContext.equals(that.nameInContext) : that.nameInContext != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = nameInContext != null ? nameInContext.hashCode() : 0; + result = 31 * result + (context != null ? context.hashCode() : 0); + return result; + } + + public void close() throws NamingException { + context.close(); + } + + public String getNameInNamespace() throws NamingException { + Name parsedNameInNamespace = context.getNameParser("").parse(context.getNameInNamespace()); + return context.composeName(parsedNameInNamespace, nameInContext).toString(); + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/context/WritableContext.java b/xbean-naming/src/main/java/org/apache/xbean/naming/context/WritableContext.java new file mode 100644 index 00000000..7ed55680 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/context/WritableContext.java @@ -0,0 +1,285 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Hashtable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.naming.Context; +import javax.naming.ContextNotEmptyException; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NamingException; +import javax.naming.Referenceable; +import javax.naming.Reference; +import javax.naming.spi.NamingManager; + +import org.apache.xbean.naming.reference.CachingReference; + +/** + * @version $Rev$ $Date$ + */ +public class WritableContext extends AbstractFederatedContext { + private final Lock writeLock = new ReentrantLock(); + private final AtomicReference> bindingsRef; + private final AtomicReference> indexRef; + private final boolean cacheReferences; + private final boolean supportReferenceable; + private final boolean checkDereferenceDifferent; + private final boolean assumeDereferenceBound; + + public WritableContext() throws NamingException { + this("", Collections.emptyMap(), ContextAccess.MODIFIABLE, false); + } + + public WritableContext(String nameInNamespace) throws NamingException { + this(nameInNamespace, Collections.emptyMap(), ContextAccess.MODIFIABLE, false); + } + + public WritableContext(String nameInNamespace, Map bindings) throws NamingException { + this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, false); + } + + public WritableContext(String nameInNamespace, Map bindings, boolean cacheReferences) throws NamingException { + this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, cacheReferences); + } + + public WritableContext(String nameInNamespace, Map bindings, ContextAccess contextAccess) throws NamingException { + this(nameInNamespace, bindings, contextAccess, false); + } + + public WritableContext(String nameInNamespace, Map bindings, ContextAccess contextAccess, boolean cacheReferences) throws NamingException { + this(nameInNamespace, bindings, contextAccess, cacheReferences, true, true, false); + } + public WritableContext(String nameInNamespace, + Map bindings, + ContextAccess contextAccess, + boolean cacheReferences, + boolean supportReferenceable, + boolean checkDereferenceDifferent, + boolean assumeDereferenceBound) throws NamingException { + super(nameInNamespace, contextAccess); + + this.cacheReferences = cacheReferences; + if (this.cacheReferences) { + bindings = CachingReference.wrapReferences(bindings, this); + } + this.supportReferenceable = supportReferenceable; + this.checkDereferenceDifferent = checkDereferenceDifferent; + this.assumeDereferenceBound = assumeDereferenceBound; + + Map localBindings = ContextUtil.createBindings(bindings, this); + + this.bindingsRef = new AtomicReference>(Collections.unmodifiableMap(localBindings)); + this.indexRef = new AtomicReference>(Collections.unmodifiableMap(buildIndex("", localBindings))); + } + + protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + if (super.addBinding(name, value, rebind)) { + return true; + } + + addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind); + return true; + } + + protected void addBinding(AtomicReference> bindingsRef, String name, String nameInNamespace, Object value, boolean rebind) throws NamingException { + if (supportReferenceable && value instanceof Referenceable) { + Reference ref = ((Referenceable)value).getReference(); + if (ref != null) { + if (checkDereferenceDifferent) { + try { + Object o = NamingManager.getObjectInstance(ref, null, null, new Hashtable()); + if (!value.equals(o)) { + value = ref; + } + } catch (Exception e) { + if (!assumeDereferenceBound) { + value = ref; + } + } + } else { + value = ref; + } + } + + } + if (cacheReferences) { + value = CachingReference.wrapReference(name, value, this); + } + + writeLock.lock(); + try { + Map bindings = bindingsRef.get(); + + if (!rebind && bindings.containsKey(name)) { + throw new NameAlreadyBoundException(name); + } + Map newBindings = new HashMap(bindings); + newBindings.put(name,value); + bindingsRef.set(newBindings); + + addToIndex(nameInNamespace, value); + } finally { + writeLock.unlock(); + } + } + + private void addToIndex(String name, Object value) { + Map index = indexRef.get(); + Map newIndex = new HashMap(index); + newIndex.put(name, value); + if (value instanceof NestedWritableContext) { + NestedWritableContext nestedcontext = (NestedWritableContext) value; + Map newIndexValues = buildIndex(name, nestedcontext.bindingsRef.get()); + newIndex.putAll(newIndexValues); + } + indexRef.set(newIndex); + } + + protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + if (super.removeBinding(name, removeNotEmptyContext)) { + return true; + } + removeBinding(bindingsRef, name, getNameInNamespace(name), removeNotEmptyContext); + return true; + } + + private boolean removeBinding(AtomicReference> bindingsRef, String name, String nameInNamespace, boolean removeNotEmptyContext) throws NamingException { + writeLock.lock(); + try { + Map bindings = bindingsRef.get(); + if (!bindings.containsKey(name)) { + // remove is idempotent meaning remove succeededs even if there was no value bound + return false; + } + + Map newBindings = new HashMap(bindings); + Object oldValue = newBindings.remove(name); + if (!removeNotEmptyContext && oldValue instanceof Context && !isEmpty((Context)oldValue)) { + throw new ContextNotEmptyException(name); + } + bindingsRef.set(newBindings); + + Map newIndex = removeFromIndex(nameInNamespace); + indexRef.set(newIndex); + return true; + } finally { + writeLock.unlock(); + } + } + + private Map removeFromIndex(String name) { + Map index = indexRef.get(); + Map newIndex = new HashMap(index); + Object oldValue = newIndex.remove(name); + if (oldValue instanceof NestedWritableContext) { + NestedWritableContext nestedcontext = (NestedWritableContext) oldValue; + Map removedIndexValues = buildIndex(name, nestedcontext.bindingsRef.get()); + for (String key : removedIndexValues.keySet()) { + newIndex.remove(key); + } + } + return newIndex; + } + + public Context createNestedSubcontext(String path, Map bindings) throws NamingException { + if (getNameInNamespace().length() > 0) { + path = getNameInNamespace() + "/" + path; + } + return new NestedWritableContext(path, bindings); + } + + private static Map buildIndex(String nameInNamespace, Map bindings) { + String path = nameInNamespace; + if (path.length() > 0 && !path.endsWith("/")) { + path += "/"; + } + + Map absoluteIndex = new HashMap(); + for (Map.Entry entry : bindings.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof NestedWritableContext) { + NestedWritableContext nestedContext = (NestedWritableContext) value; + absoluteIndex.putAll(buildIndex(nestedContext.pathWithSlash, nestedContext.bindingsRef.get())); + } + absoluteIndex.put(path + name, value); + } + return absoluteIndex; + } + + protected Object getDeepBinding(String name) { + Map index = indexRef.get(); + return index.get(name); + } + + protected Map getWrapperBindings() throws NamingException { + return bindingsRef.get(); + } + + /** + * Nested context which shares the absolute index map in MapContext. + */ + public class NestedWritableContext extends AbstractFederatedContext { + private final AtomicReference> bindingsRef; + private final String pathWithSlash; + + public NestedWritableContext(String path, Map bindings) throws NamingException { + super(WritableContext.this, path); + + path = getNameInNamespace(); + if (!path.endsWith("/")) path += "/"; + this.pathWithSlash = path; + + this.bindingsRef = new AtomicReference>(Collections.unmodifiableMap(bindings)); + } + + public Context createNestedSubcontext(String path, Map bindings) throws NamingException { + return new NestedWritableContext(getNameInNamespace(path), bindings); + } + + protected Object getDeepBinding(String name) { + String absoluteName = pathWithSlash + name; + return WritableContext.this.getDeepBinding(absoluteName); + } + + protected Map getWrapperBindings() throws NamingException { + return bindingsRef.get(); + } + + protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { + if (super.addBinding(name, value, rebind)) { + return true; + } + + WritableContext.this.addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind); + return true; + } + + protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { + if (WritableContext.this.removeBinding(bindingsRef, name, getNameInNamespace(name), removeNotEmptyContext)) { + return true; + } + return super.removeBinding(name, false); + } + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/global/GlobalContextManager.java b/xbean-naming/src/main/java/org/apache/xbean/naming/global/GlobalContextManager.java new file mode 100644 index 00000000..9220dbe1 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/global/GlobalContextManager.java @@ -0,0 +1,106 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.global; + +import org.apache.xbean.naming.context.ContextFlyweight; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.OperationNotSupportedException; +import javax.naming.NoInitialContextException; +import javax.naming.spi.InitialContextFactory; +import javax.naming.spi.ObjectFactory; +import java.util.Hashtable; + +/** + * The GlobalContextManager contains the static global context object. JNDI effectively requires a single global static + * to resolve the root context, and this class manages that static. This class is also an URLContextFactory and + * an InitialContextFactory which returns the registered global context. + * + * To use this factory simply set the following system property or pass the property in the environment to new InitialContext: + * + * java.naming.factory.initial = org.apache.xbean.naming.global.GlobalContextManager + * + * @version $Rev$ $Date$ + */ +public class GlobalContextManager implements ObjectFactory, InitialContextFactory { + private static Context DEFAULT_CONTEXT = new DefaultGlobalContext(); + private static Context globalContext; + + /** + * Gets the global context. This context is the root of all contexts and will contain entries such as "java:comp". + * @return the global context + */ + public static synchronized Context getGlobalContext() { + if (globalContext == null) return DEFAULT_CONTEXT; + return globalContext; + } + + /** + * Sets the global context. To invoke this method the calling code must have "setFactory" RuntimePermission. + * @param globalContext the new global context + */ + public static synchronized void setGlobalContext(Context globalContext) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + GlobalContextManager.globalContext = globalContext; + } + + /** + * Returns the Context registered with the GlobalManager. This method is equivalent to: + * + * return GlobalContextManager.getGlobalContext(); + * + * @param obj must be null + * @param name ignored + * @param nameCtx ignored + * @param environment ignored + * @return GlobalManager.getGlobalContext() + * @throws javax.naming.OperationNotSupportedException if obj is not null + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + if (obj == null) { + return GlobalContextManager.getGlobalContext(); + } else { + throw new OperationNotSupportedException(); + } + } + + + /** + * Returns the Context registered with the GlobalManager. This method is equivalent to: + * + * return GlobalContextManager.getGlobalContext(); + * + * @param environment ignored + * @return GlobalContextManager.getGlobalContext() + */ + public Context getInitialContext(Hashtable environment) { + return GlobalContextManager.getGlobalContext(); + } + + private static class DefaultGlobalContext extends ContextFlyweight { + protected Context getContext() throws NoInitialContextException { + synchronized (GlobalContextManager.class) { + if (globalContext == null) throw new NoInitialContextException("Global context has not been set"); + return globalContext; + } + } + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/java/javaURLContextFactory.java b/xbean-naming/src/main/java/org/apache/xbean/naming/java/javaURLContextFactory.java new file mode 100644 index 00000000..cc5c4871 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/java/javaURLContextFactory.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.xbean.naming.java; + +import org.apache.xbean.naming.global.GlobalContextManager; + +/** + * URLContextFactory for the java: namespace. + * + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class javaURLContextFactory extends GlobalContextManager { + public javaURLContextFactory() { + System.out.println(getClass().getName()); + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/reference/CachingReference.java b/xbean-naming/src/main/java/org/apache/xbean/naming/reference/CachingReference.java new file mode 100644 index 00000000..c4e0bd7f --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/reference/CachingReference.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.xbean.naming.reference; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Context; + +import org.apache.xbean.naming.context.ContextUtil; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class CachingReference extends SimpleReference { + + public static Object wrapReference(String fullName, Object value, Context context) { + if (value instanceof Reference && !(value instanceof CachingReference)) { + return new CachingReference(fullName, (Reference)value, context); + } + return value; + } + + public static Map wrapReferences(Map bindings, Context context) { + LinkedHashMap newBindings = new LinkedHashMap(bindings); + for (Map.Entry entry : bindings.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Reference && !(value instanceof CachingReference)) { + newBindings.put(name, new CachingReference(name, (Reference) value, context)); + } + } + return newBindings; + } + + private final Object lock = new Object(); + private final String stringName; + private final Context context; + private final Reference reference; + private final String className; + private Object value; + + public CachingReference(String fullName, Reference reference, Context context) { + this.stringName = fullName; + this.reference = reference; + className = reference.getClassName(); + this.context = context; + } + + public Object getContent() throws NamingException { + synchronized(lock) { + if (value == null) { + value = ContextUtil.resolve(reference, stringName, null, context); + } + return value; + } + } + + public String getClassName() { + return className; + } +} diff --git a/xbean-naming/src/main/java/org/apache/xbean/naming/reference/SimpleReference.java b/xbean-naming/src/main/java/org/apache/xbean/naming/reference/SimpleReference.java new file mode 100644 index 00000000..9bf1e453 --- /dev/null +++ b/xbean-naming/src/main/java/org/apache/xbean/naming/reference/SimpleReference.java @@ -0,0 +1,142 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.reference; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.NamingException; +import javax.naming.spi.ObjectFactory; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public abstract class SimpleReference extends Reference { + private static final Enumeration EMPTY_ENUMERATION = new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public RefAddr nextElement() { + throw new NoSuchElementException(); + } + }; + + public SimpleReference() { + super(null); + } + + /** + * Gets the actual referenced Object. + * @return the referenced object + * @throws javax.naming.NamingException on error + */ + public abstract Object getContent() throws NamingException; + + /** + * We will atleast return an Object. Subclasses may want to provide a more specific class. + * @return "java.lang.Object" + */ + public String getClassName() { + return "java.lang.Object"; + } + + /** + * If the JNDI context does not understand simple references, this method will be called + * to obtain the class name of a factory. This factory in turn understands the simple + * reference. This style is much slower because JNDI will use reflection to load and + * create this class. + * @return factory class name + */ + public final String getFactoryClassName() { + return SimpleObjectFactory.class.getName(); + } + + // + // Disabled methods that we no longer need + // + public final String getFactoryClassLocation() { + return null; + } + + public final RefAddr get(String addrType) { + return null; + } + + public final RefAddr get(int posn) { + throw new ArrayIndexOutOfBoundsException(posn); + } + + public final Enumeration getAll() { + return EMPTY_ENUMERATION; + } + + public final int size() { + return 0; + } + + public final void add(RefAddr addr) { + throw new UnsupportedOperationException("SimpleReference has no addresses so none can be added"); + } + + public final void add(int posn, RefAddr addr) { + throw new UnsupportedOperationException("SimpleReference has no addresses so none can be added"); + } + + public final Object remove(int posn) { + throw new ArrayIndexOutOfBoundsException(posn); + } + + public final void clear() { + } + + // + // Reset the java.lang.Object methods back to default implementations + // + public boolean equals(Object obj) { + return this == obj; + } + + public int hashCode() { + return System.identityHashCode(this); + } + + public String toString() { + return getClass().getName() + "@" + Integer.toHexString(hashCode()); + } + + public Object clone() { + throw new UnsupportedOperationException("SimpleReference can not be cloned"); + } + + /** + * Simply calls getContent() on the SimpleReference + */ + public static final class SimpleObjectFactory implements ObjectFactory { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + if (obj instanceof SimpleReference) { + SimpleReference reference = (SimpleReference) obj; + return reference.getContent(); + } + return null; + } + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/AbstractContextTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/AbstractContextTest.java new file mode 100644 index 00000000..85a349c5 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/AbstractContextTest.java @@ -0,0 +1,420 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import junit.framework.TestCase; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.OperationNotSupportedException; +import java.util.Map; +import java.util.Iterator; +import java.util.TreeSet; + +/** + * @version $Rev$ $Date$ + */ +public abstract class AbstractContextTest extends TestCase { + public static Name parse(String name) throws NamingException { + return ContextUtil.NAME_PARSER.parse(name); + } + public static void assertEq(Map expected, Context actual) throws NamingException { + AbstractContextTest.assertEq(ContextUtil.buildMapTree(expected), actual, actual, null); + } + + public static void assertEq(Map expected, String pathInExpected, Context actual) throws NamingException { + ContextUtil.Node node = ContextUtil.buildMapTree(expected); + Name parsedName = actual.getNameParser("").parse(pathInExpected); + for (int i = 0; i < parsedName.size(); i++) { + String part = parsedName.get(i); + Object value = node.get(part); + if (value == null) { + throw new NamingException("look for " + parsedName.getPrefix(i+1) + " in node tree is null "); + } + node = (ContextUtil.Node) value; + } + + AbstractContextTest.assertEq(node, actual, actual, null); + } + + private static void assertEq(ContextUtil.Node node, Context rootContext, Context currentContext, String path) throws NamingException { + for (Iterator iterator = node.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String expectedName = (String) entry.getKey(); + Object expectedValue = entry.getValue(); + + String fullName = path == null ? expectedName : path + "/" + expectedName; + + // verify we can lookup by string name and parsed name using the root context and current context + Object value = AbstractContextTest.assertLookup(expectedValue, currentContext, expectedName); + Object absoluteValue = AbstractContextTest.assertLookup(expectedValue, rootContext, fullName); + assertSame(fullName, value, absoluteValue); + + if (expectedValue instanceof ContextUtil.Node) { + ContextUtil.Node expectedNode = (ContextUtil.Node) expectedValue; + + // verufy listing of this context returns the expected results + AbstractContextTest.assertList(expectedNode, currentContext, expectedName); + AbstractContextTest.assertList(expectedNode, rootContext, fullName); + + AbstractContextTest.assertEq(expectedNode, rootContext, (Context) value, fullName); + } + } + } + + public static Object assertLookup(Object expectedValue, Context context, String name) throws NamingException { + Object value = context.lookup(name); + + String contextName = context.getNameInNamespace(); + if (contextName == null || contextName.length() == 0) contextName = ""; + + assertNotNull("lookup of " + name + " on " + contextName + " returned null", value); + + if (expectedValue instanceof ContextUtil.Node) { + assertTrue("Expected lookup of " + name + " on " + contextName + " to return a Context, but got a " + value.getClass().getName(), + value instanceof Context); + } else { + assertEquals("lookup of " + name + " on " + contextName, expectedValue, value); + } + + Name parsedName = context.getNameParser("").parse(name); + Object valueFromParsedName = context.lookup(parsedName); + assertSame("lookup of " + name + " on " + contextName + " using a parsed name", value, valueFromParsedName); + + return value; + } + + public static void assertList(ContextUtil.Node node, Context context, String name) throws NamingException { + String contextName = context.getNameInNamespace(); + if (contextName == null || contextName.length() == 0) contextName = ""; + + AbstractContextTest.assertListResults(node, context.list(name), contextName, name, false); + AbstractContextTest.assertListResults(node, context.listBindings(name), contextName, name, true); + + Name parsedName = context.getNameParser("").parse(name); + AbstractContextTest.assertListResults(node, context.list(parsedName), contextName, "parsed name " + name, false); + AbstractContextTest.assertListResults(node, context.listBindings(parsedName), contextName, "parsed name " + name, true); + } + + public static void assertListResults(ContextUtil.Node node, NamingEnumeration enumeration, String contextName, String name, boolean wasListBinding) { + Map actualValues; + if (wasListBinding) { + actualValues = ContextUtil.listBindingsToMap(enumeration); + } else { + actualValues = ContextUtil.listToMap(enumeration); + } + + for (Iterator iterator = node.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String expectedName = (String) entry.getKey(); + Object expectedValue = entry.getValue(); + + Object actualValue = actualValues.get(expectedName); + + assertNotNull("list of " + name + " on " + contextName + " did not find value for " + name, actualValue); + if (wasListBinding) { + if (expectedValue instanceof ContextUtil.Node) { + assertTrue("Expected list of " + name + " on " + contextName + " result value for " + expectedName + " to return a Context, but got a " + actualValue.getClass().getName(), + actualValue instanceof Context); + } else { + assertEquals("list of " + name + " on " + contextName + " for value for " + expectedName, expectedValue, actualValue); + } + } else { + if (!(expectedValue instanceof ContextUtil.Node)) { + assertEquals("list of " + name + " on " + contextName + " for value for " + expectedName, expectedValue.getClass().getName(), actualValue); + } else { + // can't really test this since it the value is the name of a nested node class + } + } + } + + TreeSet extraNames = new TreeSet(actualValues.keySet()); + extraNames.removeAll(node.keySet()); + if (!extraNames.isEmpty()) { + fail("list of " + name + " on " + contextName + " did not find values: " + extraNames); + } + } + + public static Context lookupSubcontext(Context context, String name) throws NamingException { + Object value = context.lookup(name); + assertTrue("Expected an instance of Context from look up of " + name + " in context " + context.getNameInNamespace(), + value instanceof Context); + return (Context) value; + } + + public static void assertHasBinding(Context context, String name) { + String nameInNamespace = null; + try { + nameInNamespace = context.getNameInNamespace(); + } catch (NamingException e) { + throw new RuntimeException("getNameInNamespace threw a NamingException", e); + } + + try { + Object value = context.lookup(name); + if (value != null) { + fail("Lookup of " + name + " on context " + nameInNamespace + " return null"); + } + } catch (NamingException e) { + fail("Lookup of " + name + " on context " + nameInNamespace + " threw " + e.getClass().getName()); + } + } + + public static void assertHasBinding(Context context, Name name) { + String nameInNamespace = null; + try { + nameInNamespace = context.getNameInNamespace(); + } catch (NamingException e) { + throw new RuntimeException("getNameInNamespace threw a NamingException", e); + } + + try { + Object value = context.lookup(name); + if (value != null) { + fail("Lookup of " + name + " on context " + nameInNamespace + " return null"); + } + } catch (NamingException e) { + fail("Lookup of " + name + " on context " + nameInNamespace + " threw " + e.getClass().getName()); + } + } + + public static void assertNoBinding(Context context, String name) { + String nameInNamespace = null; + try { + nameInNamespace = context.getNameInNamespace(); + } catch (NamingException e) { + throw new RuntimeException("getNameInNamespace threw a NamingException", e); + } + + try { + Object value = context.lookup(name); + if (value == null) { + fail("Context " + nameInNamespace + " has a binding for " + name); + } + } catch (NamingException expected) { + } + } + + public static void assertNoBinding(Context context, Name name) { + String nameInNamespace = null; + try { + nameInNamespace = context.getNameInNamespace(); + } catch (NamingException e) { + throw new RuntimeException("getNameInNamespace threw a NamingException", e); + } + + try { + Object value = context.lookup(name); + if (value == null) { + fail("Context " + nameInNamespace + " has a binding for " + name); + } + } catch (NamingException expected) { + } + } + + public static void assertUnmodifiable(Context context) throws Exception { + Object value = "VALUE"; + String nameString = "TEST_NAME"; + Name name = context.getNameParser("").parse(nameString); + + // + // bind + // + try { + context.bind(nameString, value); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.bind(name, value); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + // + // rebind + // + try { + context.rebind(nameString, value); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.rebind(name, value); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + // + // rename + // + String newNameString = "NEW_TEST_NAME"; + Name newName = context.getNameParser("").parse(newNameString); + try { + context.rename(nameString, newNameString); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.rename(name, newName); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + // + // unbind + // + try { + context.unbind(nameString); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.unbind(name); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + // + // createSubcontext + // + try { + context.createSubcontext(nameString); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.createSubcontext(name); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + // + // destroySubcontext + // + try { + context.destroySubcontext(nameString); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + + try { + context.destroySubcontext(name); + fail("Expected an OperationNotSupportedException"); + } catch(OperationNotSupportedException expected) { + } + } + + public static void assertModifiable(Context context) throws Exception { + Object value = "VALUE"; + Object newValue = "NEW_VALUE"; + + String nameString = "TEST_NAME"; + Name name = context.getNameParser("").parse(nameString); + + String newNameString = "NEW_TEST_NAME"; + Name newName = context.getNameParser("").parse(newNameString); + + assertNoBinding(context, nameString); + assertNoBinding(context, name); + + // + // bind / unbind + // + context.bind(nameString, value); + assertSame(value, context.lookup(nameString)); + context.unbind(nameString); + + assertNoBinding(context, nameString); + + context.bind(name, value); + assertSame(value, context.lookup(name)); + context.unbind(name); + + assertNoBinding(context, name); + + // + // rebind + // + context.bind(nameString, value); + assertSame(value, context.lookup(nameString)); + context.rebind(nameString, newValue); + assertSame(newValue, context.lookup(nameString)); + context.unbind(nameString); + + assertNoBinding(context, nameString); + + context.bind(name, value); + assertSame(value, context.lookup(name)); + context.rebind(name, newValue); + assertSame(newValue, context.lookup(name)); + context.unbind(name); + + assertNoBinding(context, name); + + // + // rename + // + context.bind(nameString, value); + assertSame(value, context.lookup(nameString)); + context.rename(nameString, newNameString); + assertSame(value, context.lookup(newNameString)); + assertNoBinding(context, nameString); + context.unbind(newNameString); + + assertNoBinding(context, nameString); + + context.bind(name, value); + assertSame(value, context.lookup(name)); + context.rename(name, newName); + assertSame(value, context.lookup(newName)); + assertNoBinding(context, name); + context.unbind(newName); + + assertNoBinding(context, name); + + // + // createSubcontext / destroySubcontext + // + context.createSubcontext(nameString); + assertTrue(context.lookup(nameString) instanceof Context); + context.destroySubcontext(nameString); + + assertNoBinding(context, nameString); + + context.createSubcontext(name); + assertTrue(context.lookup(name) instanceof Context); + context.destroySubcontext(name); + + assertNoBinding(context, name); + } + + public static boolean bindingExists(Context context, Name contextName) { + try { + return context.lookup(contextName) != null; + } catch (NamingException e) { + } + return false; + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/ContextAccessControlListTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ContextAccessControlListTest.java new file mode 100644 index 00000000..4e6136fd --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ContextAccessControlListTest.java @@ -0,0 +1,227 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class ContextAccessControlListTest extends AbstractContextTest { + private ContextAccessControlList allowDenyACL; + private ContextAccessControlList denyAllowACL; + private Context allowDenyContext; + private WritableContext denyAllowContext; + + public void testAllowDeny() throws Exception { + // outside of listed + assertTrue(allowDenyACL.isModifiable(parse("foo"))); + + // explicitly allowed + assertTrue(allowDenyACL.isModifiable(parse("allow"))); + + // child of explicitly allowed + assertTrue(allowDenyACL.isModifiable(parse("allow/foo"))); + + // explicitly denied + assertFalse(allowDenyACL.isModifiable(parse("deny"))); + + // child of explicitly denied + assertFalse(allowDenyACL.isModifiable(parse("deny/foo"))); + + // parent of denied + assertTrue(allowDenyACL.isModifiable(parse("a/b"))); + assertTrue(allowDenyACL.isModifiable(parse("one/two"))); + + // explicitly denied + assertFalse(allowDenyACL.isModifiable(parse("a/b/c"))); + assertFalse(allowDenyACL.isModifiable(parse("one/two/three"))); + + // child of denied + assertFalse(allowDenyACL.isModifiable(parse("a/b/c/foo"))); + assertFalse(allowDenyACL.isModifiable(parse("one/two/three/foo"))); + assertFalse(allowDenyACL.isModifiable(parse("a/b/c/d"))); + assertFalse(allowDenyACL.isModifiable(parse("one/two/three/four"))); + + // deny override + assertTrue(allowDenyACL.isModifiable(parse("a/b/c/d/e"))); + assertTrue(allowDenyACL.isModifiable(parse("one/two/three/four/five"))); + + // child of deny override + assertTrue(allowDenyACL.isModifiable(parse("a/b/c/d/e/foo"))); + assertTrue(allowDenyACL.isModifiable(parse("one/two/three/four/five/foo"))); + } + + public void testDenyAllow() throws Exception { + // outside of listed + assertFalse(denyAllowACL.isModifiable(parse("foo"))); + + // explicitly allowed + assertTrue(denyAllowACL.isModifiable(parse("allow"))); + + // child of explicitly allowed + assertTrue(denyAllowACL.isModifiable(parse("allow/foo"))); + + // explicitly denied + assertFalse(denyAllowACL.isModifiable(parse("deny"))); + + // child of explicitly denied + assertFalse(denyAllowACL.isModifiable(parse("deny/foo"))); + + // parent of allowed + assertFalse(denyAllowACL.isModifiable(parse("a/b"))); + assertFalse(denyAllowACL.isModifiable(parse("one/two"))); + + // explicitly allowed + assertTrue(denyAllowACL.isModifiable(parse("a/b/c"))); + assertTrue(denyAllowACL.isModifiable(parse("one/two/three"))); + + // child of allowed + assertTrue(denyAllowACL.isModifiable(parse("a/b/c/foo"))); + assertTrue(denyAllowACL.isModifiable(parse("one/two/three/foo"))); + assertTrue(denyAllowACL.isModifiable(parse("a/b/c/d"))); + assertTrue(denyAllowACL.isModifiable(parse("one/two/three/four"))); + + // allow override + assertFalse(denyAllowACL.isModifiable(parse("a/b/c/d/e"))); + assertFalse(denyAllowACL.isModifiable(parse("one/two/three/four/five"))); + + // children of allow override + assertFalse(denyAllowACL.isModifiable(parse("a/b/c/d/e/foo"))); + assertFalse(denyAllowACL.isModifiable(parse("one/two/three/four/five/foo"))); + } + + public void testAllowDenyContext() throws Exception { + // outside of listed + assertModifiable(lookupSubcontext(allowDenyContext, "foo")); + + // explicitly allowed + assertModifiable(lookupSubcontext(allowDenyContext, "allow")); + + // child of explicitly allowed + assertModifiable(lookupSubcontext(allowDenyContext, "allow/foo")); + + // explicitly denied + assertUnmodifiable(lookupSubcontext(allowDenyContext, "deny")); + + // child of explicitly denied + assertUnmodifiable(lookupSubcontext(allowDenyContext, "deny/foo")); + + // parent of denied + assertModifiable(lookupSubcontext(allowDenyContext, "a/b")); + assertModifiable(lookupSubcontext(allowDenyContext, "one/two")); + + // explicitly denied + assertUnmodifiable(lookupSubcontext(allowDenyContext, "a/b/c")); + assertUnmodifiable(lookupSubcontext(allowDenyContext, "one/two/three")); + + // child of denied + assertUnmodifiable(lookupSubcontext(allowDenyContext, "a/b/c/foo")); + assertUnmodifiable(lookupSubcontext(allowDenyContext, "one/two/three/foo")); + assertUnmodifiable(lookupSubcontext(allowDenyContext, "a/b/c/d")); + assertUnmodifiable(lookupSubcontext(allowDenyContext, "one/two/three/four")); + + // deny override + assertModifiable(lookupSubcontext(allowDenyContext, "a/b/c/d/e")); + assertModifiable(lookupSubcontext(allowDenyContext, "one/two/three/four/five")); + + // child of deny override + assertModifiable(lookupSubcontext(allowDenyContext, "a/b/c/d/e/foo")); + assertModifiable(lookupSubcontext(allowDenyContext, "one/two/three/four/five/foo")); + } + + public void testDenyAllowContext() throws Exception { + // outside of listed + assertUnmodifiable(lookupSubcontext(denyAllowContext, "foo")); + + // explicitly allowed + assertModifiable(lookupSubcontext(denyAllowContext, "allow")); + + // child of explicitly allowed + assertModifiable(lookupSubcontext(denyAllowContext, "allow/foo")); + + // explicitly denied + assertUnmodifiable(lookupSubcontext(denyAllowContext, "deny")); + + // child of explicitly denied + assertUnmodifiable(lookupSubcontext(denyAllowContext, "deny/foo")); + + // parent of allowed + assertUnmodifiable(lookupSubcontext(denyAllowContext, "a/b")); + assertUnmodifiable(lookupSubcontext(denyAllowContext, "one/two")); + + // explicitly allowed + assertModifiable(lookupSubcontext(denyAllowContext, "a/b/c")); + assertModifiable(lookupSubcontext(denyAllowContext, "one/two/three")); + + // child of allowed + assertModifiable(lookupSubcontext(denyAllowContext, "a/b/c/foo")); + assertModifiable(lookupSubcontext(denyAllowContext, "one/two/three/foo")); + assertModifiable(lookupSubcontext(denyAllowContext, "a/b/c/d")); + assertModifiable(lookupSubcontext(denyAllowContext, "one/two/three/four")); + + // allow override + assertUnmodifiable(lookupSubcontext(denyAllowContext, "a/b/c/d/e")); + assertUnmodifiable(lookupSubcontext(denyAllowContext, "one/two/three/four/five")); + + // children of allow override + assertUnmodifiable(lookupSubcontext(denyAllowContext, "a/b/c/d/e/foo")); + assertUnmodifiable(lookupSubcontext(denyAllowContext, "one/two/three/four/five/foo")); + } + + protected void setUp() throws Exception { + super.setUp(); + + allowDenyACL = new ContextAccessControlList(true, + Arrays.asList(new String[]{"allow", "a/b/c/d/e", "one/two/three/four/five"}), + Arrays.asList(new String[]{"deny", "a/b/c", "one/two/three"})); + + denyAllowACL = new ContextAccessControlList(false, + Arrays.asList(new String[]{"allow", "a/b/c", "one/two/three"}), + Arrays.asList(new String[]{"deny", "a/b/c/d/e", "one/two/three/four/five"})); + + + Map map = new HashMap(); + + // outside of listed + map.put("foo/value", "bar"); + + // explicitly allowed + map.put("allow/foo/cheese", "cheddar"); + + // explicitly denied + map.put("deny/foo/cheese", "american"); + + // child of denied + map.put("a/b/c/foo/value", "bar"); + map.put("one/two/three/foo/value", "bar"); + + // child of deny override + map.put("a/b/c/d/e/foo/value", "bar"); + map.put("one/two/three/four/five/foo/value", "bar"); + + allowDenyContext = new WritableContext("", map, allowDenyACL); + assertEq(map, allowDenyContext); + + denyAllowContext = new WritableContext("", map, denyAllowACL); + assertEq(map, denyAllowContext); + } + +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/FederationTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/FederationTest.java new file mode 100644 index 00000000..51823853 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/FederationTest.java @@ -0,0 +1,398 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.Name; +import javax.naming.NameAlreadyBoundException; +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class FederationTest extends AbstractContextTest { + private Context rootContext; + private MutableContext unmodifibleContext; + private Context writableContext; + private Map rootBindings; + private Map unmodifibleBindings; + private Map writableBindings; + + private final class MutableContext extends WritableContext { + public MutableContext(Map bindings) throws NamingException { + super("", bindings, ContextAccess.UNMODIFIABLE); + } + + public void addDeepBinding(Name name, Object value, boolean rebind, boolean createIntermediateContexts) throws NamingException { + super.addDeepBinding(name, value, rebind, createIntermediateContexts); + } + + protected void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { + super.removeDeepBinding(name, pruneEmptyContexts); + } + } + + public void setUp() throws Exception { + super.setUp(); + + rootBindings = new HashMap(); + rootBindings.put("string", "blah"); + rootBindings.put("nested/context/string", "blah"); + rootBindings.put("java:comp/env/string", "blah"); + rootBindings.put("java:comp/env/one", new Integer(1)); + rootBindings.put("java:comp/env/two", new Integer(2)); + rootBindings.put("java:comp/env/three", new Integer(3)); + + rootContext = new WritableContext(); + FederationTest.bind(rootContext, rootBindings); + + assertEq(rootBindings, rootContext); + + unmodifibleBindings = new HashMap(); + unmodifibleBindings.put("string", "blah"); + unmodifibleBindings.put("one", new Integer(1)); + unmodifibleBindings.put("two", new Integer(2)); + unmodifibleBindings.put("three", new Integer(3)); + + unmodifibleContext = new MutableContext(unmodifibleBindings); + assertEq(unmodifibleBindings, unmodifibleContext); + + rootContext.bind("java:comp/unmodifible", unmodifibleContext); + putAllBindings(rootBindings, "java:comp/unmodifible", unmodifibleBindings); + + writableBindings = new HashMap(); + writableBindings.put("string", "blah"); + writableBindings.put("one", new Integer(1)); + writableBindings.put("two", new Integer(2)); + writableBindings.put("three", new Integer(3)); + + writableContext = new WritableContext("", writableBindings); + assertEq(writableBindings, writableContext); + + rootContext.bind("java:comp/writable", writableContext); + putAllBindings(rootBindings, "java:comp/writable", writableBindings); + } + + public void testBasic() throws Exception { + assertEq(rootBindings, rootContext); + } + + public void testMutability() throws Exception { + assertModifiable(rootContext); + assertUnmodifiable(unmodifibleContext); + assertModifiable(writableContext); + assertModifiable(lookupSubcontext(rootContext, "java:comp/unmodifible")); + assertModifiable(lookupSubcontext(rootContext, "java:comp/writable")); + } + + public void testBindDirect() throws Exception { + // + // bind directly into the unmodifible context + unmodifibleContext.addDeepBinding(parse("DIRECT"), "DIRECT_VALUE", false, true); + + // visible from root context + rootBindings.put("java:comp/unmodifible/DIRECT", "DIRECT_VALUE"); + assertEq(rootBindings, rootContext); + + // visible from unmodifibleContext + unmodifibleBindings.put("DIRECT", "DIRECT_VALUE"); + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testBindWrapper() throws Exception { + // + // bind over the top of a value in the subcontext + rootContext.rebind("java:comp/unmodifible/three", "three"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/three", "three"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + + // + // bind into root context OVER the unmodifible context + rootContext.bind("java:comp/unmodifible/TEST", "TEST_VALUE"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/TEST", "TEST_VALUE"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testBindInner() throws Exception { + // bind into root context OVER the writable context + rootContext.bind("java:comp/writable/TEST", "TEST_VALUE"); + + // visible from root context + rootBindings.put("java:comp/writable/TEST", "TEST_VALUE"); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.put("TEST", "TEST_VALUE"); + assertEq(writableBindings, writableContext); + } + + public void testRebindDirect() throws Exception { + // + // bind directly into the unmodifible context + unmodifibleContext.addDeepBinding(parse("DIRECT"), "DIRECT_VALUE", true, true); + + // visible from root context + rootBindings.put("java:comp/unmodifible/DIRECT", "DIRECT_VALUE"); + assertEq(rootBindings, rootContext); + + // visible from unmodifibleContext + unmodifibleBindings.put("DIRECT", "DIRECT_VALUE"); + assertEq(unmodifibleBindings, unmodifibleContext); + + // + // bind over the top of a value in the subcontext + unmodifibleContext.addDeepBinding(parse("three"), "three", true, true); + + // visible from root context + rootBindings.put("java:comp/unmodifible/three", "three"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + unmodifibleBindings.put("three", "three"); + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testRebindWrapper() throws Exception { + // + // bind into root context OVER the unmodifible context + rootContext.rebind("java:comp/unmodifible/TEST", "TEST_VALUE"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/TEST", "TEST_VALUE"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + + // + // bind into root context OVER the unmodifible context + rootContext.rebind("java:comp/unmodifible/TEST", "NEW_TEST_VALUE"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/TEST", "NEW_TEST_VALUE"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + + // + // bind over the top of a value in the subcontext + rootContext.rebind("java:comp/unmodifible/three", "three"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/three", "three"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + + rootContext.rebind("java:comp/unmodifible/three", "thirty-three"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/three", "thirty-three"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testRebindInner() throws Exception { + // + // rebind new name into writable context + rootContext.rebind("java:comp/writable/three", "three"); + + // visible from root context + rootBindings.put("java:comp/writable/three", "three"); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.put("three", "three"); + assertEq(writableBindings, writableContext); + + // + // rebind new value into writable context + rootContext.rebind("java:comp/writable/three", "thirty-three"); + + // visible from root context + rootBindings.put("java:comp/writable/three", "thirty-three"); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.put("three", "thirty-three"); + assertEq(writableBindings, writableContext); + } + + public void testUnbindWrapper() throws Exception { + // todo ths should throw an exception... value is not removable + // unbind value under unmodifible... no exception occurs since unbind is idempotent + rootContext.unbind("java:comp/unmodifible/three"); + + // no change in root context + assertEq(rootBindings, rootContext); + + // no change in unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testUnbindDirect() throws Exception { + // unbind directly from the unmodifible context + unmodifibleContext.removeDeepBinding(parse("three"), true); + + // visible from root context + rootBindings.remove("java:comp/unmodifible/three"); + assertEq(rootBindings, rootContext); + + // visible from unmodifibleContext + unmodifibleBindings.remove("three"); + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testUnbindInner() throws Exception { + // unbind directly from the unmodifible context + rootContext.unbind("java:comp/writable/three"); + + // visible from root context + rootBindings.remove("java:comp/writable/three"); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.remove("three"); + assertEq(writableBindings, writableContext); + } + + public void testRenameInner() throws Exception { + // + // rename in inner context + rootContext.rename("java:comp/writable/three", "java:comp/writable/four"); + + // visible from root context + rootBindings.remove("java:comp/writable/three"); + rootBindings.put("java:comp/writable/four", new Integer(3)); + assertEq(rootBindings, rootContext); + + // visible from unmodifibleContext + writableBindings.remove("three"); + writableBindings.put("four", new Integer(3)); + assertEq(writableBindings, writableContext); + + // + // rename from inner to wrapper + rootContext.rename("java:comp/writable/two", "java:comp/unmodifible/four"); + + // visible from root context + rootBindings.remove("java:comp/writable/two"); + rootBindings.put("java:comp/unmodifible/four", new Integer(2)); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.remove("two"); + assertEq(writableBindings, writableContext); + + // not visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + + // + // rename from wrapper to inner + rootContext.rename("java:comp/unmodifible/four", "java:comp/writable/two"); + + // visible from root context + rootBindings.remove("java:comp/unmodifible/four"); + rootBindings.put("java:comp/writable/two", new Integer(2)); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.put("two", new Integer(2)); + assertEq(writableBindings, writableContext); + + // not visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testCreateSubcontextWrapper() throws Exception { + // + // createSubcontext in wrapper + rootContext.createSubcontext("java:comp/unmodifible/context"); + rootContext.bind("java:comp/unmodifible/context/foo", "bar"); + + // visible from root context + rootBindings.put("java:comp/unmodifible/context/foo", "bar"); + assertEq(rootBindings, rootContext); + + // not-visible from unmodifibleContext + assertEq(unmodifibleBindings, unmodifibleContext); + } + + public void testCreateSubcontextInner() throws Exception { + // bind into root context OVER the writable context + rootContext.bind("java:comp/writable/TEST", "TEST_VALUE"); + + // visible from root context + rootBindings.put("java:comp/writable/TEST", "TEST_VALUE"); + assertEq(rootBindings, rootContext); + + // visible from writableContext + writableBindings.put("TEST", "TEST_VALUE"); + assertEq(writableBindings, writableContext); + } + + public static void putAllBindings(Map rootBindings, String nestedPath, Map nestedBindings) { + for (Iterator iterator = nestedBindings.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + Object value = entry.getValue(); + String fullName = nestedPath + "/" + name; + rootBindings.put(fullName, value); + } + } + + public static void bind(Context context, Map bindings) throws NamingException { + for (Iterator iterator = bindings.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + Object value = entry.getValue(); + Name parsedName = context.getNameParser("").parse(name); + for (int i =1; i < parsedName.size(); i++) { + Name contextName = parsedName.getPrefix(i); + if (!FederationTest.bindingExists(context, contextName)) { + context.createSubcontext(contextName); + } + } + context.bind(name, value); + } + } + + public static boolean bindingExists(Context context, Name contextName) { + try { + return context.lookup(contextName) != null; + } catch (NamingException e) { + } + return false; + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableContextTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableContextTest.java new file mode 100644 index 00000000..3494698e --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableContextTest.java @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import java.util.HashMap; +import java.util.Map; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class ImmutableContextTest extends AbstractContextTest { + private static final String STRING_VAL = "some string"; + + public void testBasic() throws Exception { + Map map = new HashMap(); + map.put("string", STRING_VAL); + map.put("nested/context/string", STRING_VAL); + map.put("a/b/c/d/e/string", STRING_VAL); + map.put("a/b/c/d/e/one", 1); + map.put("a/b/c/d/e/two", 2); + map.put("a/b/c/d/e/three", 3); + map.put("a/a/b/c/d/e/three", 3); + map.put("a/b/b/c/d/e/three", 3); + + Context context = new ImmutableContext(map); + + assertEq(map, context); + + assertEquals("a", ((Context)context.lookup("a")).getNameInNamespace()); + assertEquals("a/a", ((Context)context.lookup("a/a")).getNameInNamespace()); + assertEquals("a/b/b", ((Context)context.lookup("a/b/b")).getNameInNamespace()); + assertEquals("a/b", ((Context)context.lookup("a/b")).getNameInNamespace()); + assertEquals("a/b/c", ((Context)context.lookup("a/b/c")).getNameInNamespace()); + assertEquals("a/b/c/d", ((Context)context.lookup("a/b/c/d")).getNameInNamespace()); + assertEquals("a/b/c/d/e", ((Context)context.lookup("a/b/c/d/e")).getNameInNamespace()); + + } + + public void testNameInNamespace() throws Exception { + Map map = new HashMap(); + map.put("string", STRING_VAL); + map.put("nested/context/string", STRING_VAL); + map.put("a/b/c/d/e/string", STRING_VAL); + map.put("a/b/c/d/e/one", 1); + map.put("a/b/c/d/e/two", 2); + map.put("a/b/c/d/e/three", 3); + map.put("a/a/b/c/d/e/three", 3); + map.put("a/b/b/c/d/e/three", 3); + + Context context = new ImmutableContext("a", map, false); + assertEquals("a/a", ((Context)context.lookup("a")).getNameInNamespace()); + assertEquals("a/a/a", ((Context)context.lookup("a/a")).getNameInNamespace()); + assertEquals("a/a/b/b", ((Context)context.lookup("a/b/b")).getNameInNamespace()); + assertEquals("a/a/b", ((Context)context.lookup("a/b")).getNameInNamespace()); + assertEquals("a/a/b/c", ((Context)context.lookup("a/b/c")).getNameInNamespace()); + assertEquals("a/a/b/c/d", ((Context)context.lookup("a/b/c/d")).getNameInNamespace()); + assertEquals("a/a/b/c/d/e", ((Context)context.lookup("a/b/c/d/e")).getNameInNamespace()); + + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableFederatedContextTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableFederatedContextTest.java new file mode 100644 index 00000000..90f6c9eb --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ImmutableFederatedContextTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.naming.context; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; + +/** + * @version $Rev$ $Date$ + */ +public class ImmutableFederatedContextTest extends TestCase { + + public void testJavaContextFederation() throws Exception { + Context comp = new ImmutableContext(Collections.singletonMap("comp/env/foo", "foo")); + Context module = new ImmutableContext(Collections.singletonMap("module/env/bar", "bar")); + Context application = new ImmutableContext(Collections.singletonMap("application/env/baz", "baz")); + Context global1 = new ImmutableContext(Collections.singletonMap("global/env/foo1", "foo1")); + Context global2 = new ImmutableContext(Collections.singletonMap("global/env/foo2", "foo2")); + + Set globals = new LinkedHashSet(); + ImmutableFederatedContext global = new ImmutableFederatedContext("", globals); + + Set locals = new LinkedHashSet(); + locals.add(comp); + locals.add(module); + locals.add(application); + locals.add(global); + ImmutableFederatedContext w = new ImmutableFederatedContext("", locals); + + assertEquals("foo", w.lookup("comp/env/foo")); + assertEquals("bar", w.lookup("module/env/bar")); + assertEquals("baz", w.lookup("application/env/baz")); + try { + w.lookup("global/env/foo1"); + fail("foo1 not yet bound"); + } catch (NamingException e) { + + } + global.federateContext(global1); + assertEquals("foo1", w.lookup("global/env/foo1")); + try { + w.lookup("global/env/foo2"); + fail("foo2 not yet bound"); + } catch (NamingException e) { + + } + + global.federateContext(global2); + assertEquals("foo2", w.lookup("global/env/foo2")); + + Context c = (Context) w.lookup("global"); + assertEquals("foo1", c.lookup("env/foo1")); + assertEquals("foo2", c.lookup("env/foo2")); + + global.unfederateContext(global2); + try { + w.lookup("global/env/foo2"); + fail("foo2 not yet bound"); + } catch (NamingException e) { + + } + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/ReferenceableTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ReferenceableTest.java new file mode 100644 index 00000000..b9f126b2 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/ReferenceableTest.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.xbean.naming.context; + +import javax.naming.Context; + +import org.apache.xbean.naming.referenceable.Foo; +import org.apache.xbean.naming.referenceable.FooFactory; + +/** + * @version $Rev$ $Date$ + */ +public class ReferenceableTest extends AbstractContextTest { + + public void testReferenceable() throws Exception { + Context context = new WritableContext(); + context.createSubcontext("bar"); + + Foo foo1 = new Foo("foo1"); + context.bind("bar/foo1", foo1); + Object o1 = context.lookup("bar/foo1"); + assertEquals(foo1, o1); +// assertNotSame(foo1, o1); + } + public void testReferenceable2() throws Exception { + Context context = new WritableContext(); + context.createSubcontext("bar"); + + Foo foo1 = new Foo("foo1"); + FooFactory fooFactory1 = new FooFactory(foo1); + context.bind("bar/foo1", fooFactory1); + Object o1 = context.lookup("bar/foo1"); + assertEquals(foo1, o1); + assertNotSame(foo1, o1); + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/UnmodifiableContextTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/UnmodifiableContextTest.java new file mode 100644 index 00000000..3705f9f0 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/UnmodifiableContextTest.java @@ -0,0 +1,121 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.Name; +import javax.naming.NameParser; +import java.util.HashMap; +import java.util.Map; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class UnmodifiableContextTest extends AbstractContextTest { + private static final String STRING_VAL = "some string"; + + private final class MutableContext extends WritableContext { + public MutableContext(Map bindings) throws NamingException { + super("", bindings, ContextAccess.UNMODIFIABLE); + } + + public void addDeepBinding(Name name, Object value, boolean rebind, boolean createIntermediateContexts) throws NamingException { + super.addDeepBinding(name, value, rebind, createIntermediateContexts); + } + + protected void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { + super.removeDeepBinding(name, pruneEmptyContexts); + } + } + + public void testBasic() throws Exception { + Map map = new HashMap(); + map.put("string", STRING_VAL); + map.put("nested/context/string", STRING_VAL); + map.put("a/b/c/d/e/string", STRING_VAL); + map.put("a/b/c/d/e/one", new Integer(1)); + map.put("a/b/c/d/e/two", new Integer(2)); + map.put("a/b/c/d/e/three", new Integer(3)); + + Context context = new WritableContext("", map, ContextAccess.UNMODIFIABLE); + + assertEq(map, context); + assertUnmodifiable(context); + } + + public void testAddBinding() throws Exception { + Map map = new HashMap(); + map.put("string", STRING_VAL); + map.put("nested/context/string", STRING_VAL); + map.put("a/b/c/d/e/string", STRING_VAL); + map.put("a/b/c/d/e/one", new Integer(1)); + map.put("a/b/c/d/e/two", new Integer(2)); + map.put("a/b/c/d/e/three", new Integer(3)); + + MutableContext context = new MutableContext(map); + + assertEq(map, context); + assertUnmodifiable(context); + + // add a new deep tree + map.put("uno/dos/tres", new Integer(123)); + NameParser parser = context.getNameParser(); + context.addDeepBinding(parser.parse("uno/dos/tres"), new Integer(123), false, true); + + assertEq(map, context); + assertUnmodifiable(context); + + // modify an existing context + map.put("a/b/c/d/e/four", new Integer(4)); + context.addDeepBinding(parser.parse("a/b/c/d/e/four"), new Integer(4), false, true); + + assertEq(map, context); + assertUnmodifiable(context); + } + + + public void testRemoveBinding() throws Exception { + Map map = new HashMap(); + map.put("string", STRING_VAL); + map.put("nested/context/string", STRING_VAL); + map.put("a/b/c/d/e/string", STRING_VAL); + map.put("a/b/c/d/e/one", new Integer(1)); + map.put("a/b/c/d/e/two", new Integer(2)); + map.put("a/b/c/d/e/three", new Integer(3)); + + MutableContext context = new MutableContext(map); + + assertEq(map, context); + assertUnmodifiable(context); + + // remove from an exisitng node + map.remove("a/b/c/d/e/three"); + NameParser parser = context.getNameParser(); + context.removeDeepBinding(parser.parse("a/b/c/d/e/three"), true); + + assertEq(map, context); + assertUnmodifiable(context); + + // remove a deep single element element... empty nodes should be removed + map.remove("nested/context/string"); + context.removeDeepBinding(parser.parse("nested/context/string"), true); + + assertEq(map, context); + assertUnmodifiable(context); + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/context/WritableContextTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/context/WritableContextTest.java new file mode 100644 index 00000000..57cf86f5 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/context/WritableContextTest.java @@ -0,0 +1,1010 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.context; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameNotFoundException; +import javax.naming.NotContextException; +import javax.naming.NameAlreadyBoundException; +import javax.naming.InvalidNameException; +import javax.naming.ContextNotEmptyException; +import javax.naming.NamingException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ + */ +public class WritableContextTest extends AbstractContextTest { + private static final String STRING_VAL = "some string"; + private Map bindings; + private Context context; + + public void setUp() throws Exception { + super.setUp(); + + // initialize the bindings map + bindings = new HashMap(); + bindings.put("string", WritableContextTest.STRING_VAL); + bindings.put("nested/context/string", WritableContextTest.STRING_VAL); + bindings.put("a/b/c/d/e/string", WritableContextTest.STRING_VAL); + bindings.put("a/b/c/d/e/one", new Integer(1)); + bindings.put("a/b/c/d/e/two", new Integer(2)); + bindings.put("a/b/c/d/e/three", new Integer(3)); + + // create the contxt + context = new WritableContext(); + + // bind the bindings + for (Iterator iterator = bindings.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String name = (String) entry.getKey(); + Object value = entry.getValue(); + Name parsedName = context.getNameParser("").parse(name); + for (int i =1; i < parsedName.size(); i++) { + Name contextName = parsedName.getPrefix(i); + if (!bindingExists(context, contextName)) { + context.createSubcontext(contextName); + } + } + context.bind(name, value); + } + } + + + public void testLookupAndList() throws Exception { + assertEq(bindings, context); + } + + public void testLookupExceptions() throws Exception{ + // + // lookup a non-existing value of an exisitng context + // + try { + context.lookup("a/b/c/d/e/NoSuchValue"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + assertEq(bindings, context); + + try { + context.lookup(parse("a/b/c/d/e/NoSuchValue")); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + assertEq(bindings, context); + + // + // lookup a non-existing value of a non-exisitng context + // + try { + context.list("a/b/NoSuchContext/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.list(parse("a/b/NoSuchContext/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // lookup null + // + try { + context.lookup((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.lookup((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testLookupLinkExceptions() throws Exception{ + // + // lookupLink a non-existing value of an exisitng context + // + try { + context.lookupLink("a/b/c/d/e/NoSuchValue"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + assertEq(bindings, context); + + try { + context.lookupLink(parse("a/b/c/d/e/NoSuchValue")); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + assertEq(bindings, context); + + // + // lookupLink a non-existing value of a non-exisitng context + // + try { + context.list("a/b/NoSuchContext/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.list(parse("a/b/NoSuchContext/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // lookupLink null + // + try { + context.lookupLink((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.lookupLink((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testListExceptions() throws Exception{ + // + // list a non-existing subcontext of an exisitng context + // + try { + context.list("a/b/c/d/e/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.list(parse("a/b/c/d/e/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // list a non-existing subcontext of a non-exisitng context + // + try { + context.list("a/b/NoSuchContext/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.list(parse("a/b/NoSuchContext/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // list a binding that is a value instead of a context + // + try { + context.list("a/b/c/d/e/three"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.list(parse("a/b/c/d/e/three")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // list null + // + try { + context.list((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.list((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testListBindingsExceptions() throws Exception{ + // + // listBindings a non-existing subcontext of an exisitng context + // + try { + context.listBindings("a/b/c/d/e/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.listBindings(parse("a/b/c/d/e/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // listBindings a non-existing subcontext of a non-exisitng context + // + try { + context.listBindings("a/b/NoSuchContext/NoSuchContext"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.listBindings(parse("a/b/NoSuchContext/NoSuchContext")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // listBindings a binding that is a value instead of a context + // + try { + context.listBindings("a/b/c/d/e/three"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.listBindings(parse("a/b/c/d/e/three")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // listBindings null + // + try { + context.listBindings((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.listBindings((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testBind() throws Exception { + // bind(String) + bindings.put("a/b/c/d/e/forty-two", new Integer(42)); + context.bind("a/b/c/d/e/forty-two", new Integer(42)); + + assertEq(bindings, context); + + // bind(Name) + bindings.put("a/b/c/d/e/forty-four", new Integer(44)); + context.bind(parse("a/b/c/d/e/forty-four"), new Integer(44)); + + assertEq(bindings, context); + } + + public void testBindExceptions() throws Exception{ + // + // bind over existing value of an exisitng context + // + try { + context.bind("a/b/c/d/e/three", "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.bind(parse("a/b/c/d/e/three"), "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // bind over root context + // + try { + context.bind("", "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.bind(parse(""), "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // bind to non-existing context + // + try { + context.bind("a/b/NoSuchContext/name", "value"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.bind(parse("a/b/NoSuchContext/name"), "value"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // bind null + // + try { + context.bind((String)null, "value"); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.bind((Name)null, "value"); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testUnbind() throws Exception { + // unbind(String) + bindings.remove("a/b/c/d/e/three"); + context.unbind("a/b/c/d/e/three"); + + assertEq(bindings, context); + + // unbind(Name) + bindings.remove("a/b/c/d/e/two"); + context.unbind(parse("a/b/c/d/e/two")); + + assertEq(bindings, context); + } + + public void testUnbindExceptions() throws Exception{ + // + // unbind non empty context + // + try { + context.unbind("a/b/c"); + fail("Expected ContextNotEmptyException"); + } catch (ContextNotEmptyException expected) { + } + assertEq(bindings, context); + + try { + context.unbind(parse("a/b/c")); + fail("Expected ContextNotEmptyException"); + } catch (ContextNotEmptyException expected) { + } + assertEq(bindings, context); + + // + // unbind root context + // + try { + context.unbind(""); + fail("Expected InvalidNameException"); + } catch (InvalidNameException expected) { + } + assertEq(bindings, context); + + try { + context.unbind(parse("")); + fail("Expected InvalidNameException"); + } catch (InvalidNameException expected) { + } + assertEq(bindings, context); + + // + // unbind non-existing context + // + try { + context.unbind("a/b/NoSuchContext/name"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.unbind(parse("a/b/NoSuchContext/name")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // unbind null + // + try { + context.unbind((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.unbind((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testRebind() throws Exception { + // rebind(String) + bindings.put("a/b/c/d/e/three", new Integer(33)); + context.rebind("a/b/c/d/e/three", new Integer(33)); + + assertEq(bindings, context); + + // rebind(Name) + bindings.put("a/b/c/d/e/three", new Integer(33333)); + context.rebind(parse("a/b/c/d/e/three"), new Integer(33333)); + + assertEq(bindings, context); + + // rebind(String) - New Value + bindings.put("a/b/c/d/e/forty-two", new Integer(42)); + context.rebind("a/b/c/d/e/forty-two", new Integer(42)); + + assertEq(bindings, context); + + // rebind(Name) - New Value + bindings.put("a/b/c/d/e/forty-four", new Integer(44)); + context.rebind(parse("a/b/c/d/e/forty-four"), new Integer(44)); + + assertEq(bindings, context); + } + + public void testRebindExceptions() throws Exception{ + // + // rebind over root context + // + try { + context.rebind("", "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.rebind(parse(""), "value"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // rebind to non-existing context + // + try { + context.rebind("a/b/NoSuchContext/name", "value"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.rebind(parse("a/b/NoSuchContext/name"), "value"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // rebind null + // + try { + context.rebind((String)null, "value"); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rebind((Name)null, "value"); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testRename() throws Exception { + // rename(String, String) + Object value = bindings.remove("a/b/c/d/e/three"); + bindings.put("a/b/c/d/e/boo", value); + context.rename("a/b/c/d/e/three", "a/b/c/d/e/boo"); + + assertEq(bindings, context); + + // rename(Name, Name) + value = bindings.remove("a/b/c/d/e/boo"); + bindings.put("a/b/c/d/e/moo", value); + context.rename(parse("a/b/c/d/e/boo"), parse("a/b/c/d/e/moo")); + + assertEq(bindings, context); + } + + public void testRenameExceptions() throws Exception{ + // + // rename over existing value of an exisitng context + // + try { + context.rename("a/b/c/d/e/one", "a/b/c/d/e/three"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.rename(parse("a/b/c/d/e/one"), parse("a/b/c/d/e/three")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // rename over root context + // + try { + context.rename("a/b/c/d/e/one", ""); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.rename(parse("a/b/c/d/e/one"), parse("")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // rename to non-existing context + // + try { + context.rename("a/b/c/d/e/one", "a/b/NoSuchContext/name"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.rename(parse("a/b/c/d/e/one"), parse("a/b/NoSuchContext/name")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // rename null + // + try { + context.rename(null, "SOME_NAME"); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rename(null, parse("SOME_NAME")); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rename("SOME_NAME", null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rename(null, parse("SOME_NAME")); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rename((String)null, (String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.rename((Name)null, (Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + + assertEq(bindings, context); + } + + public void testCreateSubcontext() throws Exception { + // createSubcontext(String) + bindings.put("a/b/c/d/e/f/foo", "bar"); + context.createSubcontext("a/b/c/d/e/f"); + context.bind("a/b/c/d/e/f/foo", "bar"); + + assertEq(bindings, context); + + // createSubcontext(Name) + bindings.put("a/b/c/d/e/f2/foo", "bar"); + context.createSubcontext(parse("a/b/c/d/e/f2")); + context.bind(parse("a/b/c/d/e/f2/foo"), "bar"); + + assertEq(bindings, context); + } + + public void testCreateSubcontextExceptions() throws Exception{ + // + // createSubcontext over existing value of an exisitng context + // + try { + context.createSubcontext("a/b/c/d/e/three"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext(parse("a/b/c/d/e/three")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // createSubcontext over existing context + // + try { + context.createSubcontext("a/b/c"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext(parse("a/b/c")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // createSubcontext over root context + // + try { + context.createSubcontext(""); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext(parse("")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // createSubcontext in a non-existing context + // + try { + context.createSubcontext("a/b/NoSuchContext/name"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext(parse("a/b/NoSuchContext/name")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // createSubcontext null + // + try { + context.createSubcontext((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testDestroySubcontext() throws Exception { + // destroySubcontext(String) + bindings.put("a/b/c/d/e/f/foo", "bar"); + context.createSubcontext("a/b/c/d/e/f"); + context.bind("a/b/c/d/e/f/foo", "bar"); + assertEq(bindings, context); + + bindings.remove("a/b/c/d/e/f/foo"); + context.unbind("a/b/c/d/e/f/foo"); + context.destroySubcontext("a/b/c/d/e/f"); + + assertEq(bindings, context); + + // destroySubcontext(Name) + bindings.put("a/b/c/d/e/f2/foo", "bar"); + context.createSubcontext(parse("a/b/c/d/e/f2")); + context.bind(parse("a/b/c/d/e/f2/foo"), "bar"); + assertEq(bindings, context); + + bindings.remove("a/b/c/d/e/f2/foo"); + context.unbind(parse("a/b/c/d/e/f2/foo")); + context.destroySubcontext(parse("a/b/c/d/e/f2")); + + assertEq(bindings, context); + } + + public void testDestroySubcontextExceptions() throws Exception{ + // + // destroySubcontext a value (not a context) + // + try { + context.createSubcontext("a/b/c/d/e/three"); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + try { + context.createSubcontext(parse("a/b/c/d/e/three")); + fail("Expected NameAlreadyBoundException"); + } catch (NameAlreadyBoundException expected) { + } + assertEq(bindings, context); + + // + // destroySubcontext non empty context + // + try { + context.destroySubcontext("a/b/c"); + fail("Expected ContextNotEmptyException"); + } catch (ContextNotEmptyException expected) { + } + assertEq(bindings, context); + + try { + context.destroySubcontext(parse("a/b/c")); + fail("Expected ContextNotEmptyException"); + } catch (ContextNotEmptyException expected) { + } + assertEq(bindings, context); + + // + // destroySubcontext root context + // + try { + context.destroySubcontext(""); + fail("Expected InvalidNameException"); + } catch (InvalidNameException expected) { + } + assertEq(bindings, context); + + try { + context.destroySubcontext(parse("")); + fail("Expected InvalidNameException"); + } catch (InvalidNameException expected) { + } + assertEq(bindings, context); + + // + // destroySubcontext non-existing context + // + try { + context.destroySubcontext("a/b/NoSuchContext/name"); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + try { + context.destroySubcontext(parse("a/b/NoSuchContext/name")); + fail("Expected NotContextException"); + } catch (NotContextException expected) { + } + assertEq(bindings, context); + + // + // destroySubcontext null + // + try { + context.destroySubcontext((String)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + + try { + context.destroySubcontext((Name)null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + assertEq(bindings, context); + } + + public void testLookupSubContext() throws Exception { + Context ctx = (Context) context.lookup("a/b/c"); + String s = (String) ctx.lookup("d/e/string"); + assertEquals(s, WritableContextTest.STRING_VAL); + } + + public void testDeepBinding() throws Exception { + WritableContext w = new WritableContext("jca:"); + w.addDeepBinding(w.getNameParser("").parse("test/test/GBean/resourceSource"), WritableContextTest.STRING_VAL, false, true); + assertEquals(WritableContextTest.STRING_VAL, w.lookup("test/test/GBean/resourceSource")); + w.rebind("test/test/GBean/resourceSource", 1); + assertEquals(new Integer(1), w.lookup("test/test/GBean/resourceSource")); + } + + public void testRemoveDeepBinding_Leaf() throws Exception { + WritableContext w = new WritableContext("jca:"); + // Test when only one object + w.addDeepBinding(w.getNameParser("").parse("test/test/GBean/resourceSource"), WritableContextTest.STRING_VAL, true, true); + assertEquals(WritableContextTest.STRING_VAL, w.lookup("test/test/GBean/resourceSource")); + w.removeDeepBinding(w.getNameParser("").parse("test/test/GBean/resourceSource"), true, true); + try { + w.lookup("test"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + w.addDeepBinding(w.getNameParser("").parse("test/test/GBean/resourceSource"), WritableContextTest.STRING_VAL, true, true); + assertEquals(WritableContextTest.STRING_VAL, w.lookup("test/test/GBean/resourceSource")); + w.addDeepBinding(w.getNameParser("").parse("test/test/GBean/rresourceSource2"), new Integer(2), true, true); + assertEquals(new Integer(2), w.lookup("test/test/GBean/rresourceSource2")); + + w.removeDeepBinding(w.getNameParser("").parse("test/test/GBean/resourceSource"), true, true); + assertEquals(new Integer(2), w.lookup("test/test/GBean/rresourceSource2")); + try { + w.lookup("test/test/GBean/resourceSource"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + w.removeDeepBinding(w.getNameParser("").parse("test/test/GBean/rresourceSource2"), true, true); + try { + w.lookup("test"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + } + + public void testRemoveDeepBinding_Intermediate() throws Exception { + WritableContext w = new WritableContext("jca:"); + // Test when only one object + w.addDeepBinding(w.getNameParser("").parse("test/test1/GBean/resourceSource"), WritableContextTest.STRING_VAL, true, true); + assertEquals(WritableContextTest.STRING_VAL, w.lookup("test/test1/GBean/resourceSource")); + w.removeDeepBinding(w.getNameParser("").parse("test/test1/GBean/resourceSource"), true, true); + try { + w.lookup("test"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + w.addDeepBinding(w.getNameParser("").parse("test/test1/GBean/resourceSource"), WritableContextTest.STRING_VAL, true, true); + assertEquals(WritableContextTest.STRING_VAL, w.lookup("test/test1/GBean/resourceSource")); + w.addDeepBinding(w.getNameParser("").parse("test/test2/GBean/rresourceSource2"), new Integer(2), true, true); + assertEquals(new Integer(2), w.lookup("test/test2/GBean/rresourceSource2")); + + w.removeDeepBinding(w.getNameParser("").parse("test/test1/GBean/resourceSource"), true, true); + assertEquals(new Integer(2), w.lookup("test/test2/GBean/rresourceSource2")); + try { + w.lookup("test/test1"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + w.removeDeepBinding(w.getNameParser("").parse("test/test2/GBean/rresourceSource2"), true, true); + try { + w.lookup("test"); + fail("Expected NameNotFoundException"); + } catch (NameNotFoundException expected) { + } + } + + public void test2PathsCreateSubcontext() throws Exception { + WritableContext w = new WritableContext(); + doBind(w, "a/b/c", "c"); + doBind(w, "a/b/b/c", "c"); + assertEquals("a", ((Context)w.lookup("a")).getNameInNamespace()); + assertEquals("a/b", ((Context)w.lookup("a/b")).getNameInNamespace()); + assertEquals("a/b/b", ((Context)w.lookup("a/b/b")).getNameInNamespace()); + + w = new WritableContext("a"); + doBind(w, "a/b/c", "c"); + doBind(w, "a/b/b/c", "c"); + assertEquals("a/a", ((Context)w.lookup("a")).getNameInNamespace()); + assertEquals("a/a/b", ((Context)w.lookup("a/b")).getNameInNamespace()); + assertEquals("a/a/b/b", ((Context)w.lookup("a/a/b/b")).getNameInNamespace()); + } + + private void doBind(Context context, String nameString, Object value) throws NamingException { + Name name = context.getNameParser("").parse(nameString); + Context current = context; + for (int i = 0; i< name.size() - 1; i++) { + String part = name.get(i); + try { + Object o = current.lookup(part); + if (!(o instanceof Context)) { + throw new NamingException("not a context at " + part +" found: " + o); + } + current = (Context) o; + } catch (NamingException e) { + current = current.createSubcontext(part); + } + } + current.bind(name.get(name.size() - 1), value); + } + +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/global/GlobalContextManagerTest.java b/xbean-naming/src/test/java/org/apache/xbean/naming/global/GlobalContextManagerTest.java new file mode 100644 index 00000000..836bb4b8 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/global/GlobalContextManagerTest.java @@ -0,0 +1,94 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.naming.global; + +import org.apache.xbean.naming.context.AbstractContextTest; +import org.apache.xbean.naming.context.ImmutableContext; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NoInitialContextException; +import java.util.Hashtable; +import java.util.Map; +import java.util.HashMap; + +/** + * @version $Rev$ $Date$ + */ +public class GlobalContextManagerTest extends AbstractContextTest { + public void testNoGlobalContextSet() throws Exception { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, GlobalContextManager.class.getName()); + + try { + InitialContext initialContext = new InitialContext(env); + initialContext.lookup(""); + fail("expected a NoInitialContextException"); + } catch (NoInitialContextException expected) { + } + } + + public void testDefaultInitialContext() throws Exception { + Map bindings = new HashMap(); + + bindings.put("string", "foo"); + bindings.put("nested/context/string", "bar"); + bindings.put("a/b/c/d/e/string", "beer"); + bindings.put("a/b/c/d/e/one", new Integer(1)); + bindings.put("a/b/c/d/e/two", new Integer(2)); + bindings.put("a/b/c/d/e/three", new Integer(3)); + bindings.put("java:comp/env/foo", new Integer(42)); + bindings.put("foo:bar/baz", "caz"); + ImmutableContext immutableContext = new ImmutableContext(bindings); + + assertEq(bindings, immutableContext); + + GlobalContextManager.setGlobalContext(immutableContext); + + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, GlobalContextManager.class.getName()); + + InitialContext initialContext = new InitialContext(env); + + assertEq(bindings, initialContext); + } + + public void testPackageDir() throws Exception { + Map bindings = new HashMap(); + + bindings.put("java:comp/string", "foo"); + bindings.put("java:comp/nested/context/string", "bar"); + bindings.put("java:comp/a/b/c/d/e/string", "beer"); + bindings.put("java:comp/a/b/c/d/e/one", new Integer(1)); + bindings.put("java:comp/a/b/c/d/e/two", new Integer(2)); + bindings.put("java:comp/a/b/c/d/e/three", new Integer(3)); + bindings.put("java:comp/env/foo", new Integer(42)); + bindings.put("java:comp/baz", "caz"); + ImmutableContext immutableContext = new ImmutableContext(bindings); + + assertEq(bindings, immutableContext); + + GlobalContextManager.setGlobalContext(immutableContext); + + Hashtable env = new Hashtable(); + env.put(Context.URL_PKG_PREFIXES, "org.apache.xbean.naming"); + + Context ctx = (Context) new InitialContext(env).lookup("java:comp"); + + assertEq(bindings, "java:comp", ctx); + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/Foo.java b/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/Foo.java new file mode 100644 index 00000000..f96486b9 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/Foo.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.xbean.naming.referenceable; + +import javax.naming.Referenceable; +import javax.naming.Reference; +import javax.naming.NamingException; +import javax.naming.StringRefAddr; + +/** + * @version $Rev$ $Date$ + */ +public class Foo implements Referenceable { + + private final String value; + + public Foo(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public Reference getReference() throws NamingException { + return new Reference(Foo.class.getName(), + new StringRefAddr("value", value), + FooFactory.class.getName(), + null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Foo foo = (Foo) o; + + if (value != null ? !value.equals(foo.value) : foo.value != null) return false; + + return true; + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } +} diff --git a/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/FooFactory.java b/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/FooFactory.java new file mode 100644 index 00000000..966d7337 --- /dev/null +++ b/xbean-naming/src/test/java/org/apache/xbean/naming/referenceable/FooFactory.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.xbean.naming.referenceable; + +import java.util.Hashtable; + +import javax.naming.spi.ObjectFactory; +import javax.naming.Referenceable; +import javax.naming.Reference; +import javax.naming.NamingException; +import javax.naming.Name; +import javax.naming.Context; +import javax.naming.RefAddr; + +/** + * @version $Rev$ $Date$ + */ +public class FooFactory implements Referenceable, ObjectFactory { + + private Foo foo; + + public FooFactory() { + } + + public FooFactory(Foo foo) { + this.foo = foo; + } + + public Foo getFoo() { + return foo; + } + + public void setFoo(Foo foo) { + this.foo = foo; + } + + public Reference getReference() throws NamingException { + return foo.getReference(); + } + + public Object getObjectInstance(Object o, Name name, Context context, Hashtable hashtable) throws Exception { + if (o instanceof Reference) { + Reference ref = (Reference) o; + if (Foo.class.getName().equals(ref.getClassName())) { + RefAddr addr = ref.get("value"); + return new Foo((String) addr.getContent()); + } + } + return null; + } + +} diff --git a/xbean-reflect/pom.xml b/xbean-reflect/pom.xml new file mode 100644 index 00000000..ea109f30 --- /dev/null +++ b/xbean-reflect/pom.xml @@ -0,0 +1,116 @@ + + + + + + + 4.0.0 + + xbean + org.apache.xbean + 3.12 + + xbean-reflect + bundle + Apache XBean :: Reflect + xbean-reflect provides very flexible ways to create objects and graphs of objects for DI frameworks + + + asm + asm + 3.2 + provided + true + + + asm + asm-commons + 3.2 + provided + true + + + org.apache.xbean + xbean-asm-shaded + 3.12 + provided + true + + + log4j + log4j + 1.2.12 + compile + true + + + commons-logging + commons-logging-api + 1.1 + compile + true + + + + + + + org.apache.felix + maven-bundle-plugin + 2.0.0 + true + + + !org.apache.xbean.asm.*,org.apache.xbean.*;version=${project.version} + *,org.apache.log4j;resolution:=optional,org.apache.commons.logging;resolution:=optional,org.objectweb.asm;resolution:=optional;version=3.1,org.objectweb.asm.commons;resolution:=optional;version=3.1,org.apache.xbean.asm;resolution:=optional;version=3.1,org.apache.xbean.asm.commons;resolution:=optional;version=3.1 + + + + + + + + debug + + + DEBUG + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.2 + + pertest + + -Xdebug -Xnoagent -Djava.compiler=NONE + -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 + -enableassertions + + ${basedir}/target + + + + + + + diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java new file mode 100644 index 00000000..d5dcea75 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.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.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.lang.reflect.Array; + +/** + * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ + */ +public abstract class AbstractCollectionConverter extends AbstractConverter { + private final PropertyEditor editor; + + public AbstractCollectionConverter(Class type) { + super(type); + this.editor = new StringEditor(); + } + + public AbstractCollectionConverter(Class type, PropertyEditor editor) { + super(type); + + if (editor == null) throw new NullPointerException("editor is null"); + this.editor = editor; + } + + protected final Object toObjectImpl(String text) { + List list = CollectionUtil.toList(text, editor); + if (list == null) { + return null; + } + Object collection = createCollection(list); + return collection; + } + + protected abstract Object createCollection(List list); + + protected final String toStringImpl(Object value) { + Collection values; + if (value.getClass().isArray()) { + values = new ArrayList(Array.getLength(value)); + for (int i = 0; i < Array.getLength(value); i++) { + values.add(Array.get(value, i)); + } + } else { + values = (Collection) value; + } + + String text = CollectionUtil.toString(values, editor); + return text; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractConverter.java new file mode 100644 index 00000000..adc37723 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractConverter.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.xbean.propertyeditor; + +import java.beans.PropertyEditorSupport; + +/** + * A base class for converters. This class handles all converter methods, and redirects all conversion requests to + * toStringImpl and toObjectImpl. These methods can assume that the supplied value or text is never null, and that + * type checking has been applied to the value. + * + * @version $Rev: 6680 $ + */ +public abstract class AbstractConverter extends PropertyEditorSupport implements Converter { + private final Class type; + private final boolean trim; + + /** + * Creates an abstract converter for the specified type. + * + * @param type type of the property editor + */ + protected AbstractConverter(Class type) { + this(type, true); + } + + protected AbstractConverter(Class type, boolean trim) { + super(); + if (type == null) throw new NullPointerException("type is null"); + this.type = type; + this.trim = trim; + } + + public final Class getType() { + return type; + } + + public final String getAsText() { + Object value = super.getValue(); + String text = toString(value); + return text; + } + + public final void setAsText(String text) { + Object value = toObject((trim) ? text.trim() : text); + super.setValue(value); + } + + public final Object getValue() { + Object value = super.getValue(); + return value; + } + + public final void setValue(Object value) { + // Don't validate the type. Type validation is not required by spec and some setters (e.g. Spring) expect this. + super.setValue(value); + } + + public final String toString(Object value) { + if (value == null) { + return null; + } + // Don't validate the type. Type validation is not required by spec and some setters (e.g. Spring) expect this. + return toStringImpl(value); + } + + public final Object toObject(String text) { + if (text == null) { + return null; + } + + Object value = toObjectImpl((trim) ? text.trim() : text); + return value; + } + + /** + * Converts the supplied object to text. The supplied object will always be an instance of the editor type, and + * specifically will never be null or a String (unless this is the String editor). + * + * @param value an instance of the editor type + * @return the text equivalent of the value + */ + protected String toStringImpl(Object value) { + String text = value.toString(); + return text; + } + + /** + * Converts the supplied text in to an instance of the editor type. The text will never be null, and trim() will + * already have been called. + * + * @param text the text to convert + * @return an instance of the editor type + */ + protected abstract Object toObjectImpl(String text); +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractMapConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractMapConverter.java new file mode 100644 index 00000000..964a3b33 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractMapConverter.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.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.util.Map; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public abstract class AbstractMapConverter extends AbstractConverter { + private final PropertyEditor keyEditor; + private final PropertyEditor valueEditor; + + public AbstractMapConverter(Class type) { + super(type); + this.keyEditor = new StringEditor(); + this.valueEditor = new StringEditor(); + } + + protected AbstractMapConverter(Class type, PropertyEditor keyEditor, PropertyEditor valueEditor) { + super(type); + this.keyEditor = keyEditor; + this.valueEditor = valueEditor; + } + + /** + * Treats the text value of this property as an input stream that + * is converted into a Property bundle. + * + * @return a Properties object + * @throws PropertyEditorException An error occurred creating the Properties object. + */ + protected final Object toObjectImpl(String text) { + Map map = CollectionUtil.toMap(text, keyEditor, valueEditor); + if (map == null) { + return null; + } + Map finalMap = createMap(map); + return finalMap; + } + + protected abstract Map createMap(Map map); + + protected final String toStringImpl(Object value) { + Map map = (Map) value; + String text = CollectionUtil.toString(map, keyEditor, valueEditor); + return text; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayConverter.java new file mode 100644 index 00000000..86fd1774 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayConverter.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.lang.reflect.Array; +import java.util.List; +import java.util.ListIterator; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ + */ +public final class ArrayConverter extends AbstractCollectionConverter { + public ArrayConverter(Class type, PropertyEditor editor) { + super(type, editor); + + if (!type.isArray()) { + throw new IllegalArgumentException("type is not an array " + type.getSimpleName()); + } + + if (type.getComponentType().isArray()) { + throw new IllegalArgumentException("type is a multi-dimensional array " + type.getSimpleName()); + } + + if (editor == null) throw new NullPointerException("editor is null"); + } + + protected Object createCollection(List list) { + Object array = Array.newInstance(getType().getComponentType(), list.size()); + for (ListIterator iterator = list.listIterator(); iterator.hasNext();) { + Object item = iterator.next(); + int index = iterator.previousIndex(); + Array.set(array, index, item); + } + return array; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayListEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayListEditor.java new file mode 100644 index 00000000..ac213f01 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ArrayListEditor.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.xbean.propertyeditor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class ArrayListEditor extends AbstractCollectionConverter { + public ArrayListEditor() { + super(ArrayList.class); + } + + protected Object createCollection(List list) { + return new ArrayList(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigDecimalEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigDecimalEditor.java new file mode 100644 index 00000000..e909fd44 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigDecimalEditor.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.xbean.propertyeditor; + +import java.math.BigDecimal; + +/** + * A property editor for BigDecimal typed properties. + * + * @version $Rev: 6680 $ + */ +public class BigDecimalEditor extends AbstractConverter { + public BigDecimalEditor() { + super(BigDecimal.class); + } + + /** + * Convert the text value of the property into a BigDecimal instance. + * + * @return a BigDecimal object constructed from the property text value. + */ + protected Object toObjectImpl(String text) { + try { + // just instantiate a BigDecimal instance from the test string value. + return new BigDecimal(text); + } catch (NumberFormatException e) { + // any format errors show up as a NumberFormatException, which we turn into a PropertyEditorException. + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigIntegerEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigIntegerEditor.java new file mode 100644 index 00000000..dc3791bf --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BigIntegerEditor.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.math.BigInteger; + +/** + * A property editor for BigDecimal typed properties. + * + * @version $Rev: 6680 $ + */ +public class BigIntegerEditor extends AbstractConverter { + public BigIntegerEditor() { + super(BigInteger.class); + } + + protected Object toObjectImpl(String value) { + try { + // just instantiate a BigDecimal instance from the test string value. + return new BigInteger(value); + } catch (NumberFormatException e) { + // any format errors show up as a NumberFormatException, which we turn into a PropertyEditorException. + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BooleanEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BooleanEditor.java new file mode 100644 index 00000000..73f1022b --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/BooleanEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class BooleanEditor extends AbstractConverter { + public BooleanEditor() { + super(Boolean.class); + } + + protected Object toObjectImpl(String text) { + try { + return Boolean.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ByteEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ByteEditor.java new file mode 100644 index 00000000..1a646f66 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ByteEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class ByteEditor extends AbstractConverter { + public ByteEditor() { + super(Byte.class); + } + + protected Object toObjectImpl(String text) { + try { + return Byte.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CharacterEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CharacterEditor.java new file mode 100644 index 00000000..0b11518c --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CharacterEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class CharacterEditor extends AbstractConverter { + public CharacterEditor() { + super(Character.class, false); + } + + protected Object toObjectImpl(String text) { + try { + if (text.length() != 1) { + throw new IllegalArgumentException("wrong size: " + text); + } + return new Character(text.charAt(0)); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ClassEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ClassEditor.java new file mode 100644 index 00000000..247d686a --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ClassEditor.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.xbean.propertyeditor; + +/** + * A property editor for converting class names into class object instances + * + * @version $Rev: 6680 $ + */ +public class ClassEditor extends AbstractConverter { + public ClassEditor() { + super(Class.class); + } + + /** + * Return a resolved class using the text value of the property as the name. + * The class is loading using the current context class loader, using the resolution + * rules defined by ClassLoading.java. + * + * @return a Class object created from the text value of the name. + * @throws PropertyEditorException Unable to resolve the Class object from the name. + */ + protected Object toObjectImpl(String text) { + try { + // load this using the current thread's context loader. + return Class.forName(text, true, Thread.currentThread().getContextClassLoader()); + } catch (Exception e) { + // not found exceptions are just turned into wrapped property exceptions. + throw new PropertyEditorException("Unable to resolve class " + text, e); + } + } + + protected String toStringImpl(Object value) { + Class type = (Class) value; + String text = type.getSimpleName(); + return text; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CollectionUtil.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CollectionUtil.java new file mode 100644 index 00000000..613bbd89 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CollectionUtil.java @@ -0,0 +1,166 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Collection; +import java.util.Properties; +import java.util.Map; +import java.util.LinkedHashMap; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class CollectionUtil { + public static List toList(String text, PropertyEditor componentEditor) { + if (text.length() == 0) { + return null; + } + + // text may be surrounded with [ and ] + if (text.startsWith("[") && text.endsWith("]")) { + text = text.substring(1, text.length() - 1).trim(); + } + + List list = new LinkedList(); + + if (text.length() > 0) { + StringTokenizer stok = new StringTokenizer(text, ","); + while (stok.hasMoreTokens()) { + String innerText = stok.nextToken(); + Object value = componentToObject(innerText, componentEditor); + list.add(value); + } + } + + return list; + } + + public static String toString(Collection values, PropertyEditor componentEditor) { + if (values.size() == 0) { + return "[]"; + } + + StringBuffer result = new StringBuffer(); + result.append("["); + int i = 0; + for (Iterator iterator = values.iterator(); iterator.hasNext();) { + Object object = iterator.next(); + String text = componentToString(object, componentEditor); + + if (i > 0) { + result.append(","); + } + result.append(text); + i++; + } + result.append("]"); + return result.toString(); + } + + public static final Map toMap(String text, PropertyEditor keyEditor, PropertyEditor valueEditor) { + Properties properties = new Properties(); + try { + ByteArrayInputStream stream = new ByteArrayInputStream(text.getBytes()); + properties.load(stream); + } catch (IOException e) { + // any errors here are just a property exception + throw new PropertyEditorException(e); + } + + // run the properties through the editors + Map map = new LinkedHashMap(properties.size()); + for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String keyText = (String) entry.getKey(); + String valueText = (String) entry.getValue(); + + Object keyObject = componentToObject(keyText, keyEditor); + Object valueObject = componentToObject(valueText, valueEditor); + + map.put(keyObject, valueObject); + } + return map; + } + + public static final String toString(Map map, PropertyEditor keyEditor, PropertyEditor valueEditor) { + // run the properties through the editors + Properties properties = new Properties(); + for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + Object keyObject = entry.getKey(); + Object valueObject = entry.getValue(); + + String keyText = componentToString(keyObject, keyEditor); + String valueText = componentToString(valueObject, valueEditor); + + properties.setProperty(keyText, valueText); + } + + // write using the properties object + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + properties.store(out, null); + String text = new String(out.toByteArray()); + return text; + } catch (IOException e) { + // any errors here are just a property exception + throw new PropertyEditorException(e); + } + } + + private static final String componentToString(Object value, PropertyEditor componentEditor) { + if (value == null) { + return null; + } + if (componentEditor instanceof Converter) { + Converter converter = (Converter) componentEditor; + Class type = converter.getType(); + if (!type.isInstance(value)) { + throw new PropertyEditorException("Value is not an instance of " + type.getSimpleName() + ": " + value.getClass().getName()); + } + return converter.toString(value); + } else { + componentEditor.setValue(value); + String text = componentEditor.getAsText(); + return text; + } + } + + private static final Object componentToObject(String text, PropertyEditor componentEditor) { + if (text == null) { + return null; + } + + if (componentEditor instanceof Converter) { + Converter converter = (Converter) componentEditor; + Object value = converter.toObject(text.trim()); + return value; + } else { + componentEditor.setAsText(text); + Object value = componentEditor.getValue(); + return value; + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CommonsLoggingConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CommonsLoggingConverter.java new file mode 100644 index 00000000..f9550d9b --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/CommonsLoggingConverter.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.xbean.propertyeditor; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Rev$ $Date$ + */ +public class CommonsLoggingConverter extends AbstractConverter { + public CommonsLoggingConverter() { + super(Log.class); + } + + protected Object toObjectImpl(String text) { + return LogFactory.getLog(text); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java new file mode 100644 index 00000000..d80df324 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.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.xbean.propertyeditor; + +import java.beans.PropertyEditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public interface Converter extends PropertyEditor { + /** + * Gets the the type of object supported by this converter. + * @return + */ + Class getType(); + + /** + * Converts the supplied object to text. If value is null, null will be returned. If value is not an instance of + * the this converter's type, a PropertyEditorException will be thrown. + * + * @param value an instance of the editor type + * @return the text equivalent of the value + * @throws PropertyEditorException if an error occurs while converting the value to a String (this is very rare) + */ + String toString(Object value) throws PropertyEditorException; + + /** + * Converts the supplied text in to an instance of the editor type. If text is null, null will be returned. + * + * @param text the text to convert + * @return an instance of the editor type + * @throws PropertyEditorException if an error occurs while converting the text to an object + */ + Object toObject(String text) throws PropertyEditorException; +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DateEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DateEditor.java new file mode 100644 index 00000000..180744c2 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DateEditor.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.xbean.propertyeditor; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.List; +import java.util.ArrayList; + +/** + * A property editor for Date typed properties. + * + * @version $Rev$ $Date$ + */ +public class DateEditor extends AbstractConverter { + + private List formats = new ArrayList(); + + public DateEditor() { + super(Date.class); + + formats.add(DateFormat.getInstance()); + formats.add(DateFormat.getDateInstance()); + formats.add(new SimpleDateFormat("yyyy-MM-dd")); // Atom (ISO 8601))) -- short version; + formats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz")); // Atom (ISO 8601))); + } + + /** + * Convert the text value of the property into a Date object instance. + * + * @return a Date object constructed from the property text value. + * @throws PropertyEditorException + * Unable to parse the string value into a Date. + */ + protected Object toObjectImpl(String text) { + for (DateFormat format : formats) { + try { + return format.parse(text); + } catch (ParseException e) { + } + } + + try { + return complexParse(text); + } catch (ParseException e) { + // any format errors show up as a ParseException, which we turn into + // a PropertyEditorException. + throw new PropertyEditorException(e); + } + } + + private Object complexParse(String text) throws ParseException { + // find out whether the first token is a locale id and style in that + // order + // if there's locale, style is mandatory + Locale locale = Locale.getDefault(); + int style = DateFormat.MEDIUM; + int firstSpaceIndex = text.indexOf(' '); + if (firstSpaceIndex != -1) { + String token = text.substring(0, firstSpaceIndex).intern(); + if (token.startsWith("locale")) { + String localeStr = token.substring(token.indexOf('=') + 1); + int underscoreIndex = localeStr.indexOf('_'); + if (underscoreIndex != -1) { + String language = localeStr.substring(0, underscoreIndex); + String country = localeStr.substring(underscoreIndex + 1); + locale = new Locale(language, country); + } else { + locale = new Locale(localeStr); + } + // locale is followed by mandatory style + int nextSpaceIndex = text.indexOf(' ', firstSpaceIndex + 1); + token = text.substring(firstSpaceIndex + 1, nextSpaceIndex); + String styleStr = token.substring(token.indexOf('=') + 1); + if (styleStr.equalsIgnoreCase("SHORT")) { + style = DateFormat.SHORT; + } else if (styleStr.equalsIgnoreCase("MEDIUM")) { + style = DateFormat.MEDIUM; + } else if (styleStr.equalsIgnoreCase("LONG")) { + style = DateFormat.LONG; + } else if (styleStr.equalsIgnoreCase("FULL")) { + style = DateFormat.FULL; + } else { + // unknown style name + // throw exception or assume default? + style = DateFormat.MEDIUM; + } + text = text.substring(nextSpaceIndex + 1); + } + } + DateFormat formats = DateFormat.getDateInstance(style, locale); + return formats.parse(text); + } + + protected String toStringImpl(Object value) { + Date date = (Date) value; + String text = formats.get(0).format(date); + return text; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DoubleEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DoubleEditor.java new file mode 100644 index 00000000..ea99df7b --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/DoubleEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class DoubleEditor extends AbstractConverter { + public DoubleEditor() { + super(Double.class); + } + + protected Object toObjectImpl(String text) { + try { + return Double.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java new file mode 100644 index 00000000..e4e39e53 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.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.xbean.propertyeditor; + +import java.lang.reflect.Method; + +/** + * @version $Rev$ $Date$ + */ +public class EnumConverter extends AbstractConverter { + + public EnumConverter(Class type) { + super(type); + } + + protected Object toObjectImpl(String text) { + Class type = getType(); + + try { + return Enum.valueOf(type, text); + } catch (Exception cause) { + try { + int index = Integer.parseInt(text); + Method method = type.getMethod("values"); + Object[] values = (Object[]) method.invoke(null); + return values[index]; + } catch (NumberFormatException e) { + } catch (Exception e) { + cause = e; + } + + throw new PropertyEditorException("Value \"" + text + "\" cannot be converted to enum type " + type.getName(), cause); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FileEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FileEditor.java new file mode 100644 index 00000000..2b674881 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FileEditor.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.io.File; + +/** + * A property editor for {@link File}. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class FileEditor extends AbstractConverter { + public FileEditor() { + super(File.class); + } + + /** + * Returns a URL for the input object converted to a string. + * + * @return a URL object + * @throws PropertyEditorException An IOException occured. + */ + protected Object toObjectImpl(String text) { + return new File(text); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FloatEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FloatEditor.java new file mode 100644 index 00000000..bf40ca65 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/FloatEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class FloatEditor extends AbstractConverter { + public FloatEditor() { + super(Float.class); + } + + protected Object toObjectImpl(String text) { + try { + return Float.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java new file mode 100644 index 00000000..ecb4a5cb --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.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.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.util.List; +import java.util.Collection; + +/** + * @version $Rev$ $Date$ + */ +public class GenericCollectionConverter extends AbstractCollectionConverter { + + public GenericCollectionConverter(Class type, PropertyEditor editor) { + super(type, editor); + } + + protected Object createCollection(List list) { + try { + Collection collection = (Collection) getType().newInstance(); + collection.addAll(list); + return collection; + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java new file mode 100644 index 00000000..277ecf7b --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.beans.PropertyEditor; +import java.util.Map; +import java.util.Collection; + +/** + * @version $Rev$ $Date$ + */ +public class GenericMapConverter extends AbstractMapConverter { + public GenericMapConverter(Class type, PropertyEditor keyEditor, PropertyEditor valueEditor) { + super(type, keyEditor, valueEditor); + } + + protected Map createMap(Map data) { + try { + Map map = (Map) getType().newInstance(); + map.putAll(data); + return map; + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashMapEditor.java new file mode 100644 index 00000000..9d4ebed8 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashMapEditor.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.xbean.propertyeditor; + +import java.util.HashMap; +import java.util.Map; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class HashMapEditor extends AbstractMapConverter { + public HashMapEditor() { + super(HashMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new HashMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashtableEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashtableEditor.java new file mode 100644 index 00000000..38ac699a --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/HashtableEditor.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.xbean.propertyeditor; + +import java.util.Hashtable; +import java.util.Map; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class HashtableEditor extends AbstractMapConverter { + public HashtableEditor() { + super(Hashtable.class); + } + + protected Map createMap(Map map) { + Map finalMap = new Hashtable(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IdentityHashMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IdentityHashMapEditor.java new file mode 100644 index 00000000..47eb0541 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IdentityHashMapEditor.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.xbean.propertyeditor; + +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class IdentityHashMapEditor extends AbstractMapConverter { + public IdentityHashMapEditor() { + super(IdentityHashMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new IdentityHashMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet4AddressEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet4AddressEditor.java new file mode 100644 index 00000000..3c8975f8 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet4AddressEditor.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.xbean.propertyeditor; + +import java.net.Inet4Address; +import java.net.UnknownHostException; + +/** + * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ + */ +public class Inet4AddressEditor extends AbstractConverter { + public Inet4AddressEditor() { + super(Inet4Address.class); + } + + protected Object toObjectImpl(String text) { + try { + return Inet4Address.getByName(text); + } catch (UnknownHostException e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet6AddressEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet6AddressEditor.java new file mode 100644 index 00000000..825cac3a --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Inet6AddressEditor.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.xbean.propertyeditor; + +import java.net.Inet6Address; +import java.net.UnknownHostException; + +/** + * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ + */ +public class Inet6AddressEditor extends AbstractConverter { + public Inet6AddressEditor() { + super(Inet6Address.class); + } + + protected Object toObjectImpl(String text) { + try { + return Inet6Address.getByName(text); + } catch (UnknownHostException e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/InetAddressEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/InetAddressEditor.java new file mode 100644 index 00000000..7fd8f7b6 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/InetAddressEditor.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.xbean.propertyeditor; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class InetAddressEditor extends AbstractConverter { + public InetAddressEditor() { + super(InetAddress.class); + } + + protected Object toObjectImpl(String text) { + try { + return InetAddress.getByName(text); + } catch (UnknownHostException e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IntegerEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IntegerEditor.java new file mode 100644 index 00000000..c3bb6cc0 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/IntegerEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class IntegerEditor extends AbstractConverter { + public IntegerEditor() { + super(Integer.class); + } + + protected Object toObjectImpl(String text) { + try { + return Integer.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/JndiConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/JndiConverter.java new file mode 100644 index 00000000..17a45f80 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/JndiConverter.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.xbean.propertyeditor; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.regex.Pattern; + +/** + * @version $Rev$ $Date$ + */ +public class JndiConverter extends AbstractConverter { + public JndiConverter() { + super(Context.class); + } + + protected Object toObjectImpl(String text) { + try { + InitialContext context = new InitialContext(); + return (Context) context.lookup(text); + } catch (NamingException e) { + throw new PropertyEditorException(e); + } + } + +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashMapEditor.java new file mode 100644 index 00000000..d90804b5 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashMapEditor.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.xbean.propertyeditor; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class LinkedHashMapEditor extends AbstractMapConverter { + public LinkedHashMapEditor() { + super(LinkedHashMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new LinkedHashMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashSetEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashSetEditor.java new file mode 100644 index 00000000..c2629548 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedHashSetEditor.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.xbean.propertyeditor; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class LinkedHashSetEditor extends AbstractCollectionConverter { + public LinkedHashSetEditor() { + super(Set.class); + } + + protected Object createCollection(List list) { + return new LinkedHashSet(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedListEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedListEditor.java new file mode 100644 index 00000000..4017e044 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LinkedListEditor.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.xbean.propertyeditor; + +import java.util.List; +import java.util.LinkedList; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class LinkedListEditor extends AbstractCollectionConverter { + public LinkedListEditor() { + super(LinkedList.class); + } + + protected Object createCollection(List list) { + return new LinkedList(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ListEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ListEditor.java new file mode 100644 index 00000000..90e5fbf8 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ListEditor.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.xbean.propertyeditor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class ListEditor extends AbstractCollectionConverter { + public ListEditor() { + super(List.class); + } + + protected Object createCollection(List list) { + return new ArrayList(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Log4jConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Log4jConverter.java new file mode 100644 index 00000000..4cc852c7 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Log4jConverter.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.xbean.propertyeditor; + +import org.apache.log4j.Logger; + +/** + * @version $Rev$ $Date$ + */ +public class Log4jConverter extends AbstractConverter { + public Log4jConverter() { + super(Logger.class); + } + + protected Object toObjectImpl(String text) { + return Logger.getLogger(text); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LoggerConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LoggerConverter.java new file mode 100644 index 00000000..70042ca1 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LoggerConverter.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.xbean.propertyeditor; + +import java.util.logging.Logger; + +/** + * @version $Rev$ $Date$ + */ +public class LoggerConverter extends AbstractConverter { + public LoggerConverter() { + super(Logger.class); + } + + protected Object toObjectImpl(String text) { + return Logger.getLogger(text); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LongEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LongEditor.java new file mode 100644 index 00000000..da8b06e2 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/LongEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class LongEditor extends AbstractConverter { + public LongEditor() { + super(Long.class); + } + + protected Object toObjectImpl(String text) { + try { + return Long.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/MapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/MapEditor.java new file mode 100644 index 00000000..a512d5de --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/MapEditor.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.xbean.propertyeditor; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class MapEditor extends AbstractMapConverter { + public MapEditor() { + super(Map.class); + } + + protected Map createMap(Map map) { + Map finalMap = new LinkedHashMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ObjectNameEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ObjectNameEditor.java new file mode 100644 index 00000000..ea2c4b3f --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ObjectNameEditor.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.xbean.propertyeditor; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * A property editor for {@link javax.management.ObjectName}. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class ObjectNameEditor extends AbstractConverter { + public ObjectNameEditor() { + super(ObjectName.class); + } + + /** + * Returns a ObjectName for the input object converted to a string. + * + * @return a ObjectName object + * @throws PropertyEditorException An MalformedObjectNameException occured. + */ + protected Object toObjectImpl(String text) { + try { + return new ObjectName(text); + } catch (MalformedObjectNameException e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PatternConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PatternConverter.java new file mode 100644 index 00000000..1bf6dc32 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PatternConverter.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.xbean.propertyeditor; + +import java.util.regex.Pattern; +import java.util.logging.Logger; + +/** + * @version $Rev$ $Date$ + */ +public class PatternConverter extends AbstractConverter { + + public PatternConverter() { + super(Pattern.class); + } + + protected Object toObjectImpl(String text) { + try { + return Pattern.compile(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } + +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertiesEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertiesEditor.java new file mode 100644 index 00000000..afcfff77 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertiesEditor.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.xbean.propertyeditor; + +import java.util.Map; +import java.util.Properties; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class PropertiesEditor extends AbstractMapConverter { + public PropertiesEditor() { + super(Properties.class); + } + + protected Map createMap(Map map) { + Properties properties = new Properties(); + properties.putAll(map); + return properties; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorException.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorException.java new file mode 100644 index 00000000..9d9d6dbf --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorException.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.xbean.propertyeditor; + + +/** + * An exception for property editor conversion problems. + * + * @version $Rev: 6680 $ + */ +public class PropertyEditorException extends RuntimeException { + + /** + * Default constructor for a PropertyException. + */ + public PropertyEditorException() { + } + + /** + * PropertyEditorException with a wrappered inner exception. + * + * @param cause Original root cause of the PropertyEditorException. + */ + public PropertyEditorException(Throwable cause) { + super(cause); + } + + /** + * A PropertyEditorException with just an error message. + * + * @param message The text error message describing the condition. + */ + public PropertyEditorException(String message) { + super(message); + } + + /** + * An exception with both a message and an internal exception. + * + * @param message The test error message. + * @param cause The root cause for the exception. + */ + public PropertyEditorException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java new file mode 100644 index 00000000..6eec60d6 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java @@ -0,0 +1,458 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import static org.apache.xbean.recipe.RecipeHelper.getTypeParameters; +import static org.apache.xbean.recipe.RecipeHelper.*; +import org.apache.xbean.recipe.RecipeHelper; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Collection; +import java.util.SortedSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.LinkedHashSet; +import java.util.ArrayList; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.LinkedHashMap; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.lang.reflect.Type; + +/** + * The property editor manager. This orchestrates Geronimo usage of + * property editors, allowing additional search paths to be added and + * specific editors to be registered. + * + * @version $Rev: 6687 $ + */ +public class PropertyEditors { + private static final Map registry = Collections.synchronizedMap(new ReferenceIdentityMap()); + private static final Map PRIMITIVE_TO_WRAPPER; + private static final Map WRAPPER_TO_PRIMITIVE; + private static boolean registerWithVM; + + /** + * Register all of the built in converters + */ + static { + Map map = new HashMap(); + map.put(boolean.class, Boolean.class); + map.put(char.class, Character.class); + map.put(byte.class, Byte.class); + map.put(short.class, Short.class); + map.put(int.class, Integer.class); + map.put(long.class, Long.class); + map.put(float.class, Float.class); + map.put(double.class, Double.class); + PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(map); + + + map = new HashMap(); + map.put(Boolean.class, boolean.class); + map.put(Character.class, char.class); + map.put(Byte.class, byte.class); + map.put(Short.class, short.class); + map.put(Integer.class, int.class); + map.put(Long.class, long.class); + map.put(Float.class, float.class); + map.put(Double.class, double.class); + WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(map); + + // Explicitly register the types + registerConverter(new ArrayListEditor()); + registerConverter(new BigDecimalEditor()); + registerConverter(new BigIntegerEditor()); + registerConverter(new BooleanEditor()); + registerConverter(new ByteEditor()); + registerConverter(new CharacterEditor()); + registerConverter(new ClassEditor()); + registerConverter(new DateEditor()); + registerConverter(new DoubleEditor()); + registerConverter(new FileEditor()); + registerConverter(new FloatEditor()); + registerConverter(new HashMapEditor()); + registerConverter(new HashtableEditor()); + registerConverter(new IdentityHashMapEditor()); + registerConverter(new Inet4AddressEditor()); + registerConverter(new Inet6AddressEditor()); + registerConverter(new InetAddressEditor()); + registerConverter(new IntegerEditor()); + registerConverter(new LinkedHashMapEditor()); + registerConverter(new LinkedHashSetEditor()); + registerConverter(new LinkedListEditor()); + registerConverter(new ListEditor()); + registerConverter(new LongEditor()); + registerConverter(new MapEditor()); + registerConverter(new ObjectNameEditor()); + registerConverter(new PropertiesEditor()); + registerConverter(new SetEditor()); + registerConverter(new ShortEditor()); + registerConverter(new SortedMapEditor()); + registerConverter(new SortedSetEditor()); + registerConverter(new StringEditor()); + registerConverter(new TreeMapEditor()); + registerConverter(new TreeSetEditor()); + registerConverter(new URIEditor()); + registerConverter(new URLEditor()); + registerConverter(new LoggerConverter()); + registerConverter(new PatternConverter()); + registerConverter(new JndiConverter()); + registerConverter(new VectorEditor()); + registerConverter(new WeakHashMapEditor()); + + try { + registerConverter(new Log4jConverter()); + } catch (Throwable e) { + } + + try { + registerConverter(new CommonsLoggingConverter()); + } catch (Throwable e) { + } + } + + /** + * Are converters registered with the VM PropertyEditorManager. By default + * converters are not registered with the VM as this creates problems for + * IDE and Spring because they rely in their specific converters being + * registered to function properly. + */ + public static boolean isRegisterWithVM() { + return registerWithVM; + } + + /** + * Sets if converters registered with the VM PropertyEditorManager. + * If the new value is true, all currently registered converters are + * immediately registered with the VM. + */ + public static void setRegisterWithVM(boolean registerWithVM) { + if (PropertyEditors.registerWithVM != registerWithVM) { + PropertyEditors.registerWithVM = registerWithVM; + + // register all converters with the VM + if (registerWithVM) { + for (Entry entry : registry.entrySet()) { + Class type = entry.getKey(); + Converter converter = entry.getValue(); + PropertyEditorManager.registerEditor(type, converter.getClass()); + } + } + } + } + + public static void registerConverter(Converter converter) { + if (converter == null) throw new NullPointerException("editor is null"); + Class type = converter.getType(); + registry.put(type, converter); + if (registerWithVM) { + PropertyEditorManager.registerEditor(type, converter.getClass()); + } + + if (PRIMITIVE_TO_WRAPPER.containsKey(type)) { + Class wrapperType = PRIMITIVE_TO_WRAPPER.get(type); + registry.put(wrapperType, converter); + if (registerWithVM) { + PropertyEditorManager.registerEditor(wrapperType, converter.getClass()); + } + } else if (WRAPPER_TO_PRIMITIVE.containsKey(type)) { + Class primitiveType = WRAPPER_TO_PRIMITIVE.get(type); + registry.put(primitiveType, converter); + if (registerWithVM) { + PropertyEditorManager.registerEditor(primitiveType, converter.getClass()); + } + } + } + + public static boolean canConvert(String type, ClassLoader classLoader) { + if (type == null) throw new NullPointerException("type is null"); + if (classLoader == null) throw new NullPointerException("classLoader is null"); + + // load using the ClassLoading utility, which also manages arrays and primitive classes. + Class typeClass; + try { + typeClass = Class.forName(type, true, classLoader); + } catch (ClassNotFoundException e) { + throw new PropertyEditorException("Type class could not be found: " + type); + } + + return canConvert(typeClass); + + } + + public static boolean canConvert(Class type) { + PropertyEditor editor = findConverterOrEditor(type); + + return editor != null; + } + + private static PropertyEditor findConverterOrEditor(Type type){ + Converter converter = findConverter(type); + if (converter != null) { + return converter; + } + + // fall back to a property editor + PropertyEditor editor = findEditor(type); + if (editor != null) { + return editor; + } + + converter = findBuiltinConverter(type); + if (converter != null) { + return converter; + } + + return null; + } + + public static String toString(Object value) throws PropertyEditorException { + if (value == null) throw new NullPointerException("value is null"); + + // get an editor for this type + Class type = value.getClass(); + + PropertyEditor editor = findConverterOrEditor(type); + + if (editor instanceof Converter) { + Converter converter = (Converter) editor; + return converter.toString(value); + } + + if (editor == null) { + throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName()); + } + + // create the string value + editor.setValue(value); + String textValue; + try { + textValue = editor.getAsText(); + } catch (Exception e) { + throw new PropertyEditorException("Error while converting a \"" + type.getSimpleName() + "\" to text " + + " using the property editor " + editor.getClass().getSimpleName(), e); + } + return textValue; + } + + public static Object getValue(String type, String value, ClassLoader classLoader) throws PropertyEditorException { + if (type == null) throw new NullPointerException("type is null"); + if (value == null) throw new NullPointerException("value is null"); + if (classLoader == null) throw new NullPointerException("classLoader is null"); + + // load using the ClassLoading utility, which also manages arrays and primitive classes. + Class typeClass; + try { + typeClass = Class.forName(type, true, classLoader); + } catch (ClassNotFoundException e) { + throw new PropertyEditorException("Type class could not be found: " + type); + } + + return getValue(typeClass, value); + + } + + public static Object getValue(Type type, String value) throws PropertyEditorException { + if (type == null) throw new NullPointerException("type is null"); + if (value == null) throw new NullPointerException("value is null"); + + PropertyEditor editor = findConverterOrEditor(type); + + if (editor instanceof Converter) { + Converter converter = (Converter) editor; + return converter.toObject(value); + } + + Class clazz = toClass(type); + + if (editor == null) { + throw new PropertyEditorException("Unable to find PropertyEditor for " + clazz.getSimpleName()); + } + + editor.setAsText(value); + Object objectValue; + try { + objectValue = editor.getValue(); + } catch (Exception e) { + throw new PropertyEditorException("Error while converting \"" + value + "\" to a " + clazz.getSimpleName() + + " using the property editor " + editor.getClass().getSimpleName(), e); + } + return objectValue; + } + + private static Converter findBuiltinConverter(Type type) { + if (type == null) throw new NullPointerException("type is null"); + + Class clazz = toClass(type); + + if (Enum.class.isAssignableFrom(clazz)){ + return new EnumConverter(clazz); + } + + return null; + } + + private static Converter findConverter(Type type) { + if (type == null) throw new NullPointerException("type is null"); + + Class clazz = toClass(type); + + + + // it's possible this was a request for an array class. We might not + // recognize the array type directly, but the component type might be + // resolvable + if (clazz.isArray() && !clazz.getComponentType().isArray()) { + // do a recursive lookup on the base type + PropertyEditor editor = findConverterOrEditor(clazz.getComponentType()); + // if we found a suitable editor for the base component type, + // wrapper this in an array adaptor for real use + if (editor != null) { + return new ArrayConverter(clazz, editor); + } else { + return null; + } + } + + if (Collection.class.isAssignableFrom(clazz)){ + Type[] types = getTypeParameters(Collection.class, type); + + Type componentType = String.class; + if (types != null && types.length == 1 && types[0] instanceof Class) { + componentType = types[0]; + } + + PropertyEditor editor = findConverterOrEditor(componentType); + + if (editor != null){ + if (RecipeHelper.hasDefaultConstructor(clazz)) { + return new GenericCollectionConverter(clazz, editor); + } else if (SortedSet.class.isAssignableFrom(clazz)) { + return new GenericCollectionConverter(TreeSet.class, editor); + } else if (Set.class.isAssignableFrom(clazz)) { + return new GenericCollectionConverter(LinkedHashSet.class, editor); + } else { + return new GenericCollectionConverter(ArrayList.class, editor); + } + } + + return null; + } + + if (Map.class.isAssignableFrom(clazz)){ + Type[] types = getTypeParameters(Map.class, type); + + Type keyType = String.class; + Type valueType = String.class; + if (types != null && types.length == 2 && types[0] instanceof Class && types[1] instanceof Class) { + keyType = types[0]; + valueType = types[1]; + } + + PropertyEditor keyConverter = findConverterOrEditor(keyType); + PropertyEditor valueConverter = findConverterOrEditor(valueType); + + if (keyConverter != null && valueConverter != null){ + if (RecipeHelper.hasDefaultConstructor(clazz)) { + return new GenericMapConverter(clazz, keyConverter, valueConverter); + } else if (SortedMap.class.isAssignableFrom(clazz)) { + return new GenericMapConverter(TreeMap.class, keyConverter, valueConverter); + } else if (ConcurrentMap.class.isAssignableFrom(clazz)) { + return new GenericMapConverter(ConcurrentHashMap.class, keyConverter, valueConverter); + } else { + return new GenericMapConverter(LinkedHashMap.class, keyConverter, valueConverter); + } + } + + return null; + } + + Converter converter = registry.get(clazz); + + // we're outta here if we got one. + if (converter != null) { + return converter; + } + + Class[] declaredClasses = clazz.getDeclaredClasses(); + for (Class declaredClass : declaredClasses) { + if (Converter.class.isAssignableFrom(declaredClass)) { + try { + converter = (Converter) declaredClass.newInstance(); + registerConverter(converter); + + // try to get the converter from the registry... the converter + // created above may have been for another class + converter = registry.get(clazz); + if (converter != null) { + return converter; + } + } catch (Exception e) { + } + + } + } + + // nothing found + return null; + } + + /** + * Locate a property editor for qiven class of object. + * + * @param type The target object class of the property. + * @return The resolved editor, if any. Returns null if a suitable editor + * could not be located. + */ + private static PropertyEditor findEditor(Type type) { + if (type == null) throw new NullPointerException("type is null"); + + Class clazz = toClass(type); + + // try to locate this directly from the editor manager first. + PropertyEditor editor = PropertyEditorManager.findEditor(clazz); + + // we're outta here if we got one. + if (editor != null) { + return editor; + } + + + // it's possible this was a request for an array class. We might not + // recognize the array type directly, but the component type might be + // resolvable + if (clazz.isArray() && !clazz.getComponentType().isArray()) { + // do a recursive lookup on the base type + editor = findEditor(clazz.getComponentType()); + // if we found a suitable editor for the base component type, + // wrapper this in an array adaptor for real use + if (editor != null) { + return new ArrayConverter(clazz, editor); + } + } + + // nothing found + return null; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ReferenceIdentityMap.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ReferenceIdentityMap.java new file mode 100644 index 00000000..3bf4c1bc --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ReferenceIdentityMap.java @@ -0,0 +1,485 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.propertyeditor; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Streamlined version of a WeakIdentityHashMap. Provides Identity semantics with + * Weak References to keys. This allows proxies to be GC'ed when no longer referenced + * by clients. BasicProxymanager.destroyProxy() need not be invoked when a + * proxy is no longer needed. Note that this is not a full Map implementation. + * The iteration and collection capabilities of Map have been discarded to keep the + * implementation lightweight. + *

    + * Much of this code was cribbed from the Commons Collection 3.1 implementation of + * ReferenceIdentityMap and AbstractReferenceMap. + */ +public class ReferenceIdentityMap implements Map { + + /** The default capacity to use. Always use a power of 2!!! */ + private static final int DEFAULT_CAPACITY = 16; + /** The default load factor to use */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** The maximum capacity allowed */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** Load factor, normally 0.75 */ + private float loadFactor; + /** The size of the map */ + private transient int size; + /** Map entries */ + private transient ReferenceEntry[] data; + /** Size at which to rehash */ + private transient int threshold; + + /** + * ReferenceQueue used to eliminate GC'ed entries. + */ + private ReferenceQueue purgeQueue; + + public ReferenceIdentityMap() { + this.loadFactor = DEFAULT_LOAD_FACTOR; + this.data = new ReferenceEntry[DEFAULT_CAPACITY]; + this.threshold = calculateThreshold(DEFAULT_CAPACITY, loadFactor); + this.purgeQueue = new ReferenceQueue(); + } + + /** + * Gets the size of the map. + * + * @return the size + */ + public int size() { + purge(); + return size; + } + + /** + * Checks whether the map is currently empty. + * + * @return true if the map is currently size zero + */ + public boolean isEmpty() { + purge(); + return (size == 0); + } + + /** + * Checks whether the map contains the specified key. + * + * @param key the key to search for + * @return true if the map contains the key + */ + public boolean containsKey(Object key) { + purge(); + ReferenceEntry entry = getEntry(key); + if (entry == null) { + return false; + } + return (entry.getValue() != null); + } + + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + public boolean containsValue(Object value) { + purge(); + if (value == null || size == 0) { + return false; + } + ReferenceEntry [] table = data; + for (int i = 0; i < table.length; i++) { + ReferenceEntry entry = table[i]; + while (entry != null) { + if (value.equals(entry.getValue())) { + return true; + } + entry = entry.next; + } + } + return false; + } + + /** + * Gets the value mapped to the key specified. + * + * @param key the key + * @return the mapped value, null if no match + */ + public Object get(Object key) { + purge(); + ReferenceEntry entry = getEntry(key); + if (entry == null) { + return null; + } + return entry.getValue(); + } + + + /** + * Puts a key-value entry into this map. + * Neither the key nor the value may be null. + * + * @param key the key to add, must not be null + * @param value the value to add, must not be null + * @return the value previously mapped to this key, null if none + */ + public Object put(Object key, Object value) { + assert key != null: "key is null"; + assert value != null: "value is null"; + + purge(); + + int hashCode = hash(key); + int index = hashIndex(hashCode, data.length); + ReferenceEntry entry = data[index]; + while (entry != null) { + if (entry.hashCode == hashCode && key == entry.getKey()) { + return entry.setValue(value); + } + entry = entry.next; + } + + createEntry(index, hashCode, key, value); + return null; + } + + /** + * Removes the specified mapping from this map. + * + * @param key the mapping to remove + * @return the value mapped to the removed key, null if key not in map + */ + public Object remove(Object key) { + if (key == null) { + return null; + } + purge(); + int hashCode = hash(key); + int index = hashIndex(hashCode, data.length); + ReferenceEntry entry = data[index]; + ReferenceEntry previous = null; + while (entry != null) { + if (entry.hashCode == hashCode && (key == entry.getKey())) { + Object oldValue = entry.getValue(); + removeEntry(entry, index, previous); + return oldValue; + } + previous = entry; + entry = entry.next; + } + return null; + } + + /** + * Clears the map, resetting the size to zero and nullifying references + * to avoid garbage collection issues. + */ + public void clear() { + ReferenceEntry[] data = this.data; + for (int i = data.length - 1; i >= 0; i--) { + data[i] = null; + } + size = 0; + while (purgeQueue.poll() != null) {} // drain the queue + } + + public Collection values() { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + public Set entrySet() { + throw new UnsupportedOperationException(); + } + + public Set keySet() { + throw new UnsupportedOperationException(); + } + + // end of public methods + + /** + * Gets the entry mapped to the key specified. + * @param key the key + * @return the entry, null if no match + */ + private ReferenceEntry getEntry(Object key) { + if (key == null) { + return null; + } + int hashCode = hash(key); + ReferenceEntry entry = data[hashIndex(hashCode, data.length)]; + while (entry != null) { + if (entry.hashCode == hashCode && (key == entry.getKey())) { + return entry; + } + entry = entry.next; + } + return null; + } + + /** + * Creates a new ReferenceEntry. + * + * @param index the index into the data map + * @param hashCode the hash code for the new entry + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + private ReferenceEntry createEntry(int index, int hashCode, Object key, Object value) { + ReferenceEntry newEntry = new ReferenceEntry(this, data[index], hashCode, key, value); + data[index] = newEntry; + size++; + checkCapacity(); + return newEntry; + } + + /** + * Removes an entry from the chain stored in a particular index. + *

    + * This implementation removes the entry from the data storage table. + * The size is not updated. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + private void removeEntry(ReferenceEntry entry, int hashIndex, ReferenceEntry previous) { + if (previous == null) { + data[hashIndex] = entry.next; + } else { + previous.next = entry.next; + } + size--; + entry.next = null; + entry.clear(); + entry.value = null; + } + + /** + * Checks the capacity of the map and enlarges it if necessary. + *

    + * This implementation uses the threshold to check if the map needs enlarging + */ + private void checkCapacity() { + if (size >= threshold) { + int newCapacity = data.length * 2; + if (newCapacity <= MAXIMUM_CAPACITY) { + ensureCapacity(newCapacity); + } + } + } + + /** + * Changes the size of the data structure to the capacity proposed. + * + * @param newCapacity the new capacity of the array (a power of two, less or equal to max) + */ + private void ensureCapacity(int newCapacity) { + int oldCapacity = data.length; + if (newCapacity <= oldCapacity) { + return; + } + + ReferenceEntry oldEntries[] = data; + ReferenceEntry newEntries[] = new ReferenceEntry[newCapacity]; + + for (int i = oldCapacity - 1; i >= 0; i--) { + ReferenceEntry entry = oldEntries[i]; + if (entry != null) { + oldEntries[i] = null; // gc + do { + ReferenceEntry next = entry.next; + int index = hashIndex(entry.hashCode, newCapacity); + entry.next = newEntries[index]; + newEntries[index] = entry; + entry = next; + } while (entry != null); + } + } + threshold = calculateThreshold(newCapacity, loadFactor); + data = newEntries; + } + + /** + * Calculates the new threshold of the map, where it will be resized. + * This implementation uses the load factor. + * + * @param newCapacity the new capacity + * @param factor the load factor + * @return the new resize threshold + */ + private int calculateThreshold(int newCapacity, float factor) { + return (int) (newCapacity * factor); + } + + /** + * Gets the hash code for the key specified. + *

    + * This implementation uses the identity hash code. + * + * @param key the key to get a hash code for + * @return the hash code + */ + private int hash(Object key) { + return System.identityHashCode(key); + } + + /** + * Gets the index into the data storage for the hashCode specified. + * This implementation uses the least significant bits of the hashCode. + * + * @param hashCode the hash code to use + * @param dataSize the size of the data to pick a bucket from + * @return the bucket index + */ + private int hashIndex(int hashCode, int dataSize) { + return hashCode & (dataSize - 1); + } + + // Code that handles WeakReference cleanup... Invoked prior to + // any operation accessing the ReferenceEntry array... + + /** + * Purges stale mappings from this map. + *

    + * Note that this method is not synchronized! Special + * care must be taken if, for instance, you want stale + * mappings to be removed on a periodic basis by some + * background thread. + */ + private void purge() { + Reference entryRef = purgeQueue.poll(); + while (entryRef != null) { + purge(entryRef); + entryRef = purgeQueue.poll(); + } + } + + /** + * Purges the specified reference. + * + * @param purgedEntry the reference to purge + */ + private void purge(Reference purgedEntry) { + int hash = ((ReferenceEntry)purgedEntry).hashCode; + int index = hashIndex(hash, data.length); + ReferenceEntry previous = null; + ReferenceEntry currentEntry = data[index]; + while (currentEntry != null) { + if (currentEntry == purgedEntry) { + currentEntry.purged(); + if (previous == null) { + data[index] = currentEntry.next; + } else { + previous.next = currentEntry.next; + } + this.size--; + return; + } + previous = currentEntry; + currentEntry = currentEntry.next; + } + } + + /** + * Each entry in the Map is represented with a ReferenceEntry. + *

    + * If getKey() or getValue() returns null, it means + * the mapping is stale and should be removed. + * + * @since Commons Collections 3.1 + */ + private static class ReferenceEntry extends WeakReference { + /** The next entry in the hash chain */ + private ReferenceEntry next; + /** The hash code of the key */ + private int hashCode; + /** The value */ + private Object value; + + /** + * Creates a new entry object for the ReferenceMap. + * + * @param parent the parent map + * @param next the next entry in the hash bucket + * @param hashCode the hash code of the key + * @param key the key + * @param value the value + */ + private ReferenceEntry(ReferenceIdentityMap parent, ReferenceEntry next, int hashCode, Object key, Object value) { + super(key, parent.purgeQueue); + this.next = next; + this.hashCode = hashCode; + this.value = value; + } + + /** + * Gets the key from the entry. + * This method dereferences weak and soft keys and thus may return null. + * + * @return the key, which may be null if it was garbage collected + */ + private Object getKey() { + return this.get(); + } + + /** + * Gets the value from the entry. + * This method dereferences weak and soft value and thus may return null. + * + * @return the value, which may be null if it was garbage collected + */ + private Object getValue() { + return value; + } + + /** + * Sets the value of the entry. + * + * @param obj the object to store + * @return the previous value + */ + private Object setValue(Object obj) { + Object old = getValue(); + value = obj; + return old; + } + + /** + * Purges this entry. + */ + private void purged() { + this.clear(); + value = null; + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SetEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SetEditor.java new file mode 100644 index 00000000..d29c86e9 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SetEditor.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.xbean.propertyeditor; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class SetEditor extends AbstractCollectionConverter { + public SetEditor() { + super(Set.class); + } + + protected Object createCollection(List list) { + return new LinkedHashSet(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ShortEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ShortEditor.java new file mode 100644 index 00000000..2eabdd17 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ShortEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class ShortEditor extends AbstractConverter { + public ShortEditor() { + super(Short.class); + } + + protected Object toObjectImpl(String text) { + try { + return Short.valueOf(text); + } catch (Exception e) { + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedMapEditor.java new file mode 100644 index 00000000..75432b8c --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedMapEditor.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.xbean.propertyeditor; + +import java.util.Map; +import java.util.TreeMap; +import java.util.SortedMap; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class SortedMapEditor extends AbstractMapConverter { + public SortedMapEditor() { + super(SortedMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new TreeMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedSetEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedSetEditor.java new file mode 100644 index 00000000..87863d7c --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/SortedSetEditor.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.xbean.propertyeditor; + +import java.util.List; +import java.util.TreeSet; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class SortedSetEditor extends AbstractCollectionConverter { + public SortedSetEditor() { + super(TreeSet.class); + } + + protected Object createCollection(List list) { + return new TreeSet(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/StringEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/StringEditor.java new file mode 100644 index 00000000..2882c9f3 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/StringEditor.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.xbean.propertyeditor; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class StringEditor extends AbstractConverter { + public StringEditor() { + super(String.class, false); + } + + protected Object toObjectImpl(String text) { + return text; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeMapEditor.java new file mode 100644 index 00000000..b6c69b73 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeMapEditor.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.xbean.propertyeditor; + +import java.util.Map; +import java.util.TreeMap; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class TreeMapEditor extends AbstractMapConverter { + public TreeMapEditor() { + super(TreeMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new TreeMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeSetEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeSetEditor.java new file mode 100644 index 00000000..4d7497fb --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/TreeSetEditor.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.xbean.propertyeditor; + +import java.util.List; +import java.util.TreeSet; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class TreeSetEditor extends AbstractCollectionConverter { + public TreeSetEditor() { + super(TreeSet.class); + } + + protected Object createCollection(List list) { + return new TreeSet(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URIEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URIEditor.java new file mode 100644 index 00000000..bf1b3005 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URIEditor.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.xbean.propertyeditor; + +import java.net.URI; +import java.net.URISyntaxException; + +/** + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public class URIEditor extends AbstractConverter { + public URIEditor() { + super(URI.class); + } + + protected Object toObjectImpl(String text) { + try { + return new URI(text); + } catch (URISyntaxException e) { + throw new PropertyEditorException(e); + } + } + +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URLEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URLEditor.java new file mode 100644 index 00000000..3813e150 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/URLEditor.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.xbean.propertyeditor; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * A property editor for URL typed properties. + * + * @version $Rev: 6680 $ + */ +public class URLEditor extends AbstractConverter { + public URLEditor() { + super(URL.class); + } + + /** + * Convert the text value of the property into a URL object instance. + * + * @return a URL object constructed from the property text value. + */ + protected Object toObjectImpl(String text) { + try { + // try to create directly from the text property. + URL url = new URL(text); + // this parsed correctly, but if this is a file object, + // we need to make sure this gets converted into the proper + // absolute directory form. + try { + if (url.getProtocol().equals("file")) { + // ok, this is a file URL, so get the file string portion, + // convert that to a file object, then go through the URI()/URL() + // conversion sequence to get a fully valid URL(). + return new File(url.getFile()).toURI().toURL(); + } + } catch (Exception e) { + // any error here is returned as a property editor exception. + throw new PropertyEditorException(e); + } + + return url; + } catch (MalformedURLException e) { + // this is a format error, but it could have been specified as a local + // file name. so try to create a file object and make a URL from that. + } + + try { + // The file class has direct support for returning as a URL, but the Javadoc + // for File.toURL() recommends converting the File object to a URI first + // so that untranslatable characters get handled correctly. + return new File(text).toURI().toURL(); + } catch (MalformedURLException e) { + // any error here is returned as a property editor exception. + throw new PropertyEditorException(e); + } + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/VectorEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/VectorEditor.java new file mode 100644 index 00000000..d6f4c900 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/VectorEditor.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.xbean.propertyeditor; + +import java.util.List; +import java.util.Vector; + +/** + * Adapter for editing array types. + * + * @version $Rev: 6680 $ $Date: 2005-12-24T04:38:27.427468Z $ + */ +public final class VectorEditor extends AbstractCollectionConverter { + public VectorEditor() { + super(Vector.class); + } + + protected Object createCollection(List list) { + return new Vector(list); + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/WeakHashMapEditor.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/WeakHashMapEditor.java new file mode 100644 index 00000000..7a344852 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/WeakHashMapEditor.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.xbean.propertyeditor; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * A property editor for indirect property bundles. This editor + * transforms the text value of the propery into a Property resource bundle. + * + * @version $Rev: 6680 $ + */ +public class WeakHashMapEditor extends AbstractMapConverter { + public WeakHashMapEditor() { + super(WeakHashMap.class); + } + + protected Map createMap(Map map) { + Map finalMap = new WeakHashMap(map); + return finalMap; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/AbstractRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/AbstractRecipe.java new file mode 100644 index 00000000..e79f3add --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/AbstractRecipe.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.xbean.recipe; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.lang.reflect.Type; + +public abstract class AbstractRecipe implements Recipe { + private static final AtomicLong ID = new AtomicLong(1); + private long id; + private String name; + + protected AbstractRecipe() { + id = ID.getAndIncrement(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (name == null) throw new NullPointerException("name is null"); + this.name = name; + } + + public float getPriority() { + return 0; + } + + public Object create() throws ConstructionException { + return create(null); + } + + public final Object create(ClassLoader classLoader) throws ConstructionException { + // if classloader was passed in, set it on the thread + ClassLoader oldClassLoader = null; + if (classLoader != null) { + oldClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader); + } + + try { + return create(Object.class, false); + } finally { + // if we set a thread context class loader, reset it + if (classLoader != null) { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + } + + public final Object create(Type expectedType, boolean lazyRefAllowed) throws ConstructionException { + if (expectedType == null) throw new NullPointerException("expectedType is null"); + + // assure there is a valid thread context class loader + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + if (oldClassLoader == null) { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + + // if there is no execution context, create one + boolean createNewContext = !ExecutionContext.isContextSet(); + if (createNewContext) { + ExecutionContext.setContext(new DefaultExecutionContext()); + } + + try { + ExecutionContext context = ExecutionContext.getContext(); + + // if this recipe has already been executed in this context, return the currently registered value + if (getName() != null && context.containsObject(getName()) && !(context.getObject(getName()) instanceof Recipe)) { + return context.getObject(getName()); + } + + // execute the recipe + context.push(this); + try { + return internalCreate(expectedType, lazyRefAllowed); + } finally { + Recipe popped = context.pop(); + if (popped != this) { + //noinspection ThrowFromFinallyBlock + throw new IllegalStateException("Internal Error: recipe stack is corrupt:" + + " Expected " + this + " to be popped of the stack but " + popped + " was"); + } + } + } finally { + // if we set a new execution context, remove it from the thread + if (createNewContext) { + ExecutionContext context = ExecutionContext.getContext(); + ExecutionContext.setContext(null); + + Map> unresolvedRefs = context.getUnresolvedRefs(); + if (!unresolvedRefs.isEmpty()) { + throw new UnresolvedReferencesException(unresolvedRefs); + } + } + + // if we set a thread context class loader, clear it + if (oldClassLoader == null) { + Thread.currentThread().setContextClassLoader(null); + } + } + } + + protected abstract Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException; + + public List getNestedRecipes() { + return Collections.emptyList(); + } + + public List getConstructorRecipes() { + return Collections.emptyList(); + } + + public String toString() { + if (name != null) { + return name; + } + + String string = getClass().getSimpleName(); + if (string.endsWith("Recipe")) { + string = string.substring(0, string.length() - "Recipe".length()); + } + return string + "@" + id; + } +} diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/AllPropertiesRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/AllPropertiesRecipe.java new file mode 100644 index 00000000..0354ef11 --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/AllPropertiesRecipe.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.xbean.recipe; + +import java.util.Map; +import java.util.Properties; +import java.lang.reflect.Type; + +public class AllPropertiesRecipe extends AbstractRecipe { + public boolean canCreate(Type type) { + return RecipeHelper.isAssignable(type, Properties.class); + } + + protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException { + Recipe outerRecipe = RecipeHelper.getCaller(); + if (!(outerRecipe instanceof ObjectRecipe)) { + throw new ConstructionException("UnsetPropertiesRecipe can only be nested in an ObjectRecipe: outerRecipe=" + outerRecipe); + } + ObjectRecipe objectRecipe = (ObjectRecipe) outerRecipe; + Map allProperties = objectRecipe.getProperties(); + + // copy to a properties object + Properties properties = new Properties(); + for (Map.Entry entry : allProperties.entrySet()) { + properties.put(entry.getKey(), entry.getValue()); + } + + // add to execution context if name is specified + if (getName() != null) { + ExecutionContext.getContext().addObject(getName(), properties); + } + + return properties; + } +} \ No newline at end of file diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java new file mode 100644 index 00000000..8fb216aa --- /dev/null +++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java @@ -0,0 +1,185 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.xbean.recipe; + +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +/** + * @version $Rev$ $Date$ + */ +public class ArrayRecipe extends AbstractRecipe { + private final List list; + private String typeName; + private Class typeClass; + private final EnumSet