You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by fm...@apache.org on 2005/12/21 21:18:36 UTC

svn commit: r358365 [1/4] - in /incubator/jackrabbit/trunk/contrib/extension-framework: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/jackrabbit/ src/main/java/org/apache/jackrabbit/extension/ sr...

Author: fmeschbe
Date: Wed Dec 21 12:17:51 2005
New Revision: 358365

URL: http://svn.apache.org/viewcvs?rev=358365&view=rev
Log:
Adding contributed simple extension framework

Added:
    incubator/jackrabbit/trunk/contrib/extension-framework/   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/project.properties   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/project.xml
    incubator/jackrabbit/trunk/contrib/extension-framework/src/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionType.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/NodeTypeSupport.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/ConfigurationIODelegate.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/ItemConfiguration.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/PropertiesNodeConfiguration.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/RepositoryConfiguration.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/XMLNodeConfiguration.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/configuration/package.html   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/package.html   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/apache/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/apache/jackrabbit/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/apache/jackrabbit/extension/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/apache/jackrabbit/extension/type.cnd
    incubator/jackrabbit/trunk/contrib/extension-framework/src/main/resources/org/apache/jackrabbit/extension/type.cnd.bak
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/classes.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/configuration.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/examples/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/examples.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/examples/deployment.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/examples/instantiating.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/examples/listing.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/index.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/misc.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/navigation.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/site/xdoc/nodetype.xml   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/ExtensionFindTest.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/ExtensionFrameworkTestBase.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/ExtensionNodeTypeTest.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/TestAll.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/configuration/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/java/org/apache/jackrabbit/extension/configuration/ItemConfigurationTest.java   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/resources/
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/resources/DevCoreTest.class.bin   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/resources/log4j.properties   (with props)
    incubator/jackrabbit/trunk/contrib/extension-framework/src/test/resources/repository.xml   (with props)

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Dec 21 12:17:51 2005
@@ -0,0 +1,4 @@
+.classpath
+.project
+target
+velocity.log

Added: incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt Wed Dec 21 12:17:51 2005
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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.
+ */
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/HEADER.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt Wed Dec 21 12:17:51 2005
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/LICENSE.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/extension-framework/project.properties
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/project.properties?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/project.properties (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/project.properties Wed Dec 21 12:17:51 2005
@@ -0,0 +1,108 @@
+#  Copyright 2003-2005 The Apache Software Foundation or its licensors,
+#                      as applicable
+#
+#  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.
+
+######################################################################
+# Apache Central Repository
+######################################################################
+maven.repo.central=www.apache.org
+maven.repo.central.directory=/www/www.apache.org/dist/java-repository
+maven.remote.group=apcvs
+maven.changelog.factory = org.apache.maven.svnlib.SvnChangeLogFactory
+
+######################################################################
+# JUnit Testing
+######################################################################
+maven.test.failure = false
+maven.junit.fork=true
+maven.junit.dir=${maven.build.dir}
+#maven.junit.sysproperties=org.xml.sax.driver java.security.auth.login.config
+maven.junit.sysproperties=org.xml.sax.driver
+org.xml.sax.driver=org.apache.xerces.parsers.SAXParser
+#java.security.auth.login.config=applications/test/jaas.config
+
+
+#If you wish to skip tests when doing builds, uncomment
+#maven.test.skip = true
+
+######################################################################
+# Checkstyle
+######################################################################
+maven.checkstyle.properties= checkstyle.xml
+maven.linkcheck.enable=false
+
+######################################################################
+# JavaDoc
+#
+# javadoc urls can be added here, multiple urls are appended using a comma
+#
+# maven.javadoc.links = http://foo/bar/api,\
+#                       http://flim/flam/api/
+######################################################################
+maven.javadoc.links=http://java.sun.com/j2se/1.4.2/docs/api/,http://www.day.com/maven/jsr170/javadocs/jcr-0.16.4.1/
+maven.javadoc.author=false
+maven.javadoc.version=false
+
+######################################################################
+# Other opts
+######################################################################
+# uncomment the next line to work in offline mode (no jar download & no linkcheck)
+#maven.mode.online=
+
+maven.compile.debug=on
+maven.compile.deprecation=off
+maven.compile.optimize=off
+maven.compile.source=1.4
+maven.compile.target=1.4
+
+maven.jarResources.basedir=src/java
+maven.jar.excludes=**/package.html
+
+# Location of the generated query language parsers. Needed for
+# the Maven Eclipse plugin to automatically locate the generated
+# source files. Note that this value matches the hardcoded path
+# in the Maven JavaCC plugin. Therefore, do not change this value!
+maven.gen.src=${maven.build.dir}/generated-src/main
+
+# specifying additional remote repository for downloading dependencies
+# not available at www.ibiblio.org/maven/
+maven.repo.remote = http://www.ibiblio.org/maven/,http://www.day.com/maven/
+
+######################################################################
+# Site L&F
+######################################################################
+# maven.xdoc.jsl=
+maven.xdoc.date=
+maven.xdoc.poweredby.image=maven-feather.png
+maven.xdoc.version=${pom.currentVersion}
+maven.xdoc.developmentProcessUrl=http://incubator.apache.org/projects/jackrabbit.html
+maven.docs.src=${basedir}/src/site/xdoc
+maven.changelog.range=60
+maven.changelog.factory=org.apache.maven.svnlib.SvnChangeLogFactory
+maven.multiproject.overviewPage.title=Jackrabbit components
+
+# ------------------------------------------------------------------------
+# M A V E N  J A R  O V E R R I D E
+# ------------------------------------------------------------------------
+#maven.jar.override = on
+#maven.jar.jcr = ${basedir}/lib/jcr.jar
+
+######################################################################
+# Site Deploy (into ../jackrabbit-site for checkout on incubator.apache.org)
+######################################################################
+maven.site.deploy.method=fs
+
+# IDE settings
+maven.eclipse.resources.addtoclasspath=true
+

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/project.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/extension-framework/project.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/project.xml?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/project.xml (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/project.xml Wed Dec 21 12:17:51 2005
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2004-2005 The Apache Software Foundation or its licensors,
+                       as applicable.
+
+   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.
+  -->
+<project>
+    <pomVersion>1</pomVersion>
+    <groupId>org.apache.jackrabbit</groupId>
+    <currentVersion>1.0-SNAPSHOT</currentVersion>
+    <organization>
+        <name>The Apache Software Foundation</name>
+        <url>http://incubator.apache.org/projects/jackrabbit.html</url>
+        <logo>http://incubator.apache.org/images/apache-incubator-logo.png</logo>
+    </organization>
+    <inceptionYear>2005</inceptionYear>
+    <package>org.apache.jackrabbit.extension</package>
+    <description>The Jackrabbit Extension Framework is a simple framework providing functionality to easily extend applications&#xD;
+in a plug-in style.</description>
+    <shortDescription>Extension Framework for JCR Repositories</shortDescription>
+    <repository />
+    <developers>
+        <developer>
+            <name>Felix Meschberger</name>
+            <id>1</id>
+            <email>fmeschbe at apache dot org</email>
+            <organization>Day Software</organization>
+            <timezone>+1</timezone>
+        </developer>
+    </developers>
+    <licenses>
+        <license>
+            <name>The Apache Software License, Version 2.0</name>
+            <url>/LICENSE.txt</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    <reports>
+        <report>maven-javadoc-plugin</report>
+        <report>maven-jdepend-plugin</report>
+        <report>maven-license-plugin</report>
+    </reports>
+    <artifactId>extension-framework</artifactId>
+    <name>Jackrabbit Extension Framework</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>classloader</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>nt-ns-util</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>jsr170</groupId>
+            <artifactId>jcr</artifactId>
+            <version>1.0</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.0</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>1.1</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.1</version>
+            <type>jar</type>
+        </dependency>
+        <!-- Unit Testing -->
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.8</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.1</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>concurrent</groupId>
+            <artifactId>concurrent</artifactId>
+            <version>1.3.4</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>lucene</groupId>
+            <artifactId>lucene</artifactId>
+            <version>1.4.3</version>
+            <type>jar</type>
+        </dependency>
+    </dependencies>
+    <build>
+        <sourceDirectory>src/main/java</sourceDirectory>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>false</filtering>
+            </resource>
+        </resources>
+        <unitTestSourceDirectory>src/test/java</unitTestSourceDirectory>
+        <unitTest>
+            <includes>
+                <include>**/*TestAll.java</include>
+            </includes>
+            <resources>
+                <resource>
+                    <directory>src/test/resources</directory>
+                    <filtering>false</filtering>
+                </resource>
+            </resources>
+        </unitTest>
+    </build>
+</project>
+

Added: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java Wed Dec 21 12:17:51 2005
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.extension;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.extension.configuration.ItemConfiguration;
+import org.apache.jackrabbit.extension.configuration.RepositoryConfiguration;
+
+/**
+ * The <code>ExtensionDescriptor</code> class implements a descriptor for an
+ * extension defined in a repository node with mixin node type
+ * <code>rep:extension</code>.
+ * <p>
+ * Two instances of this class are considered equal if they are the same
+ * instance or if they are of the same extension type and if their extension
+ * names are equal.
+ * <p>
+ * This class implements the <code>Comparable</code> interface defining an order
+ * amongst two instances of this class according to the extension type
+ * identification and the extension name. See {@link #compareTo(Object)}.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date$
+ *
+ * @see org.apache.jackrabbit.extension.ExtensionType
+ * @see org.apache.jackrabbit.extension.ExtensionManager
+ */
+public class ExtensionDescriptor implements Comparable {
+
+    /** default log */
+    private static final Log log = LogFactory.getLog(ExtensionDescriptor.class);
+
+    /**
+     * The name of the property containing the extension type identification
+     * (value is "rep:id").
+     * This is a mandatory property of an extension node.
+     */
+    public static final String PROP_REP_ID = "rep:id";
+
+    /**
+     * The name of the property containing the extension name (value is
+     * "rep:name").
+     * This is a mandatory property of an extension node.
+     */
+    public static final String PROP_REP_NAME = "rep:name";
+
+    /**
+     * The name of the property containing the fully qualified name of a class
+     * implementing the extension (value is "rep:class").
+     * This is an optional property of the extension node.
+     */
+    public static final String PROP_REP_CLASS = "rep:class";
+
+    /**
+     * The name of the multivalue property containing the class path providing
+     * the extension class(es) (value is "rep:classpath").
+     * This is an optional property of the extension node.
+     */
+    public static final String PROP_REP_CLASSPATH = "rep:classpath";
+
+    /**
+     * The name of the property containing the fully qualified name of a class
+     * implementing the <code>org.apache.commons.configuration.Configuration</code>
+     * interface (value is "rep:configurationClass").
+     * This is an optional property of the extension node.
+     */
+    public static final String PROP_REP_CONFIGURATION_CLASS =
+        "rep:configurationClass";
+
+    /**
+     * The name of the child node containing the configuration for this
+     * extension (value is "rep:configuration").
+     * This is an optional child node of the extension node.
+     */
+    public static final String NODE_REP_CONFIGURATION = "rep:configuration";
+
+    /**
+     * The {@link ExtensionType} to which this extension belongs.
+     * @see #getExtensionType
+     */
+    private final ExtensionType type;
+
+    /**
+     * The <code>Node</code> from which this descriptor has been loaded.
+     * @see #getNode()
+     */
+    private final Node node;
+
+    /**
+     * The extension type identification read from the {@link #PROP_REP_ID}
+     * property of the node describing the extension.
+     * @see #getId()
+     */
+    private final String id;
+
+    /**
+     * The extension name read from the {@link #PROP_REP_NAME} property of the
+     * node describing the extension.
+     * @see #getName()()
+     */
+    private final String name;
+
+    /**
+     * The fully qualified name of the class implementing the extension or
+     * <code>null</code> if none is defined. The value of this field is read
+     * from the {@link #PROP_REP_CLASS} property of the node describing the
+     * extension.
+     * @see #getClassName()
+     */
+    private final String className;
+
+    /**
+     * The classpath to configure on the extension type's class loader to load
+     * and use this extension or <code>null</code> if none is defined. The value
+     * of this field is read from the {@link #PROP_REP_CLASSPATH} property of
+     * the node describing the extension.
+     * @see #getClassPath()
+     */
+    private final String[] classPath;
+
+    /**
+     * The fully qualified name of the class implementing the Apache Jakarta
+     * Commons <code>Configuration</code> interface or <code>null</code> if
+     * none is defined. The value of this field is read from the
+     * {@link #PROP_REP_CONFIGURATION_CLASS} property of the node describing the
+     * extension.
+     * @see #getConfigurationClassName()
+     * @see #getConfiguration()
+     * @see #getConfigurationNode()
+     */
+    private final String configurationClassName;
+
+    /**
+     * The absolute path of the {@link #node} from which this descriptor has
+     * been loaded.
+     * @see #getNodePath();
+     */
+    private String nodePath;
+
+    /**
+     * The extension instance created for this descriptor by the
+     * {@link #getExtension()} method or <code>null</code> if none has been
+     * created yet.
+     * @see #getExtension()
+     */
+    private Object extension;
+
+    /**
+     * The configuration object created for this descriptor by the
+     * {@link #getConfiguration()} method or <code>null</code> if none has been
+     * created yet.
+     * @see #getConfiguration()
+     */
+    private Configuration configuration;
+
+    /**
+     * Creates an instance of this class loading the definition from the given
+     * <code>extensionNode</code>.
+     * <p>
+     * This method does not check whether the node is of the correct type but
+     * merely accesses the properties required to exist and tries to access
+     * optional properties. If an error occurrs accessing the properties,
+     * an <code>ExtensionException</code> is thrown with the cause set.
+     *
+     * @param type The {@link ExtensionType} having loaded this extension
+     *      object.
+     * @param extensionNode The <code>Node</code> containing the extension
+     *      description.
+     *
+     * @throws ExtensionException If an error occurrs reading the extension
+     *      description from the node.
+     */
+    /* package */ ExtensionDescriptor(ExtensionType type, Node extensionNode)
+            throws ExtensionException {
+
+        this.type = type;
+        node = extensionNode;
+
+        try {
+            // required data
+            id = getPropertyOrNull(extensionNode, PROP_REP_ID);
+            name = getPropertyOrNull(extensionNode, PROP_REP_NAME);
+            if (id == null || name == null) {
+                throw new ExtensionException("Missing id or name property");
+            }
+
+            // optional class, classpath and configuration class
+            className = getPropertyOrNull(extensionNode, PROP_REP_CLASS);
+            classPath = getPropertiesOrNull(extensionNode, PROP_REP_CLASSPATH);
+            configurationClassName =
+               getPropertyOrNull(extensionNode, PROP_REP_CONFIGURATION_CLASS);
+        } catch (RepositoryException re) {
+            throw new ExtensionException("Cannot load extension", re);
+        }
+    }
+
+    /**
+     * Returns the {@link ExtensionType} which has loaded this extension.
+     */
+    private ExtensionType getExtensionType() {
+        return type;
+    }
+
+    /**
+     * Returns the <code>Node</code> from which this extension has been loaded.
+     * Any modification to the node returned will only be active the next
+     * time an instance of this class is created from the node.
+     */
+    public final Node getNode() {
+        return node;
+    }
+
+    /**
+     * Returns the absolute path of the <code>Node</code> from which this
+     * extension has been loaded.
+     */
+    public final String getNodePath() {
+        if (nodePath == null) {
+            try {
+                nodePath = getNode().getPath();
+            } catch (RepositoryException re) {
+                log.warn("Cannot get the path of the extension node", re);
+                nodePath = getNode().toString();
+            }
+        }
+
+        return nodePath;
+    }
+
+    /**
+     * Returns the identification of the extension type implemented by this
+     * extension.
+     */
+    public final String getId() {
+        return id;
+    }
+
+    /**
+     * Returns the name of this extension.
+     */
+    public final String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the fully qualified name of the class implementing this extension
+     * or <code>null</code> if none is configured in the extension descriptor
+     * node.
+     */
+    public final String getClassName() {
+        return className;
+    }
+
+    /**
+     * Returns the extension class path or <code>null</code> if none has been
+     * configured in the extension descriptor. Note that an empty array is
+     * never returned by this method.
+     */
+    public final String[] getClassPath() {
+        return classPath;
+    }
+
+    /**
+     * Returns the fully qualified name of the extensions configuration class
+     * or <code>null</code> if none is configured in the extension's node.
+     * @see #getConfiguration()
+     * @see #getConfigurationNode()
+     */
+    public final String getConfigurationClassName() {
+        return configurationClassName;
+    }
+
+    //---------- Instantiation support ----------------------------------------
+
+    /**
+     * Returns the class loader to be used to load the extension object and the
+     * configuration for the extension described by this descriptor.
+     */
+    public ClassLoader getExtensionLoader() {
+        return getExtensionType().getClassLoader(this);
+    }
+
+    /**
+     * Creates an instance of the extension class defined by this descriptor.
+     * <p>
+     * If the descriptor contains a classpath specification, the class loader of
+     * the extension type to which the extension belongs, is configured with the
+     * additional classpath.
+     * <p>
+     * The extension class must provide either of two constructors for it to be
+     * instantiated by this method:
+     * <ol>
+     * <li>If a public constructor taking an instance of
+     * {@link ExtensionDescriptor} is available, that constructor is used to
+     * create the extension instance.</il>
+     * <li>Otherwise if a public default constructor taking no paramaters at
+     * all is available, that constructor is used to create the extension
+     * instance. In this case it is the responsibility of the application to
+     * provide the extension instance with more information if required.</li>
+     * </ol>
+     * <p>
+     * If neither constructor is available in the class, this method fails with
+     * an {@link ExtensionException}.
+     * <p>
+     * If the class provides a public method taking a single parameter of
+     * type <code>ExtensionDescriptor</code>, that method is called with this
+     * instance as the parameter value. This allows for parameterless default
+     * constructors in the extension classes while still getting the extension
+     * descriptor.
+     * <p>
+     * If no class has been defined for this extension, an
+     * <code>IllegalArgumentException</code> is thrown.
+     *
+     * @return The instance created for this extension.
+     *
+     * @throws IllegalArgumentException if no extension class specification is
+     *      available in this extension descriptor.
+     * @throws ExtensionException if the extension class has no suitable
+     *      constructor or if an error occurrs loading or instantiating the
+     *      class.
+     */
+    public Object getExtension() throws ExtensionException {
+
+        // immediately return the extension, if it is already defined
+        if (extension != null) {
+            return extension;
+        }
+        // otherwise, we have to instantiate
+
+        // fail if there is no class name in the descriptor
+        if (getClassName() == null) {
+            throw new IllegalArgumentException("Descriptor has no class definition");
+        }
+
+        try {
+            log.debug("Loading class " + getClassName());
+            Class clazz = getExtensionLoader().loadClass(getClassName());
+            Object extension = instantiate(clazz);
+            setDescriptor(extension);
+            return extension;
+
+        } catch (Exception e) {
+            throw new ExtensionException("Cannot instantiate extension " +
+                getClassName(), e);
+        }
+    }
+
+    /**
+     * Returns the node containing the configuration of this extension. If the
+     * extension's node has a child node <code>rep:configuration</code>, that
+     * child node is returned, otherwise the extension's node is returned.
+     *
+     * @return The configuration node of this extension.
+     */
+    public Node getConfigurationNode() {
+        Node node = getNode();
+
+        try {
+            if (node.hasNode(NODE_REP_CONFIGURATION)) {
+                return node.getNode(NODE_REP_CONFIGURATION);
+            }
+        } catch (RepositoryException re) {
+            log.warn("Cannot check or access configuration node " +
+                NODE_REP_CONFIGURATION + ". Using extension node", re);
+        }
+
+        return node;
+    }
+
+    /**
+     * Returns the <code>Configuration</code> object used to configure this
+     * extension.
+     * <p>
+     * If the extension descriptor does not contain the fully qualified name of
+     * a configuration class, this method returns an instance of the
+     * {@link ItemConfiguration} class loaded from the extension's node.
+     * <p>
+     * Otherwise the named class is loaded through the extensions class loader
+     * (see {@link #getExtensionLoader()}) and instantiated. A class to be used
+     * like this must implement the <code>Configuration</code> interface and
+     * provide a public default constructor. If any of the requirements is not
+     * met by the configured class, this method throws an exception.
+     * <p>
+     * If the configured class implements the {@link RepositoryConfiguration}
+     * interface, the configuration is configured with the extension's node
+     * and loaded.
+     * <p>
+     * The main use of this method is for the extension class itself to
+     * configure itself. Another use may be for an administrative application
+     * to update configuration and optionally store it back.
+     *
+     * @return The <code>Configuration</code> object used to configured this
+     *      extension.
+     *
+     * @throws ExtensionException If the configuration class has no public
+     *      default constructor or if the configuration class is not an
+     *      implementation of the <code>Configuration</code> interface or if an
+     *      error occurrs loading or instantiating the configuration class.
+     */
+    public Configuration getConfiguration() throws ExtensionException {
+        // immediately return the configuration, if it is already defined
+        if (configuration != null) {
+            return configuration;
+        }
+        // otherwise, we have to instantiate
+
+        // use a default configuration if no specific class defined
+        if (getConfigurationClassName() == null) {
+            log.debug("No configurationClass setting, using ItemConfiguration");
+            try {
+                return new ItemConfiguration(getConfigurationNode());
+            } catch (ConfigurationException ce) {
+                throw new ExtensionException(
+                    "Cannot load ItemConfiguration from " + getNodePath());
+            }
+        }
+
+        try {
+            log.debug("Loading class " + getConfigurationClassName());
+            Class clazz =
+                getExtensionLoader().loadClass(getConfigurationClassName());
+
+            // create an instance using the one taking an extension descriptor
+            // if available otherwise use the default constructor
+            log.debug("Creating configuration object instance");
+            Object configObject = clazz.newInstance();
+            if (!(configObject instanceof Configuration)) {
+                throw new ExtensionException("Configuration class " +
+                    getClassName() +
+                    " does not implement Configuration interface");
+            }
+
+            // load the repository configuration from the extension node
+            if (configObject instanceof RepositoryConfiguration) {
+                RepositoryConfiguration repoConfig =
+                    (RepositoryConfiguration) configObject;
+                repoConfig.setNode(getConfigurationNode());
+                repoConfig.load();
+            }
+
+            configuration = (Configuration) configObject;
+            return configuration;
+
+        } catch (Exception e) {
+            throw new ExtensionException("Cannot instantiate extension " +
+                getClassName(), e);
+        }
+    }
+
+    //---------- Comparable interface -----------------------------------------
+
+    /**
+     * Compares this object with the specified object for order.  Returns a
+     * negative integer, zero, or a positive integer as this object is less
+     * than, equal to, or greater than the specified object.
+
+     * @param obj the Object to be compared, which must be an instance of this
+     *      class.
+     *
+     * @return a negative integer, zero, or a positive integer as this
+     *      descriptor is less than, equal to, or greater than the specified
+     *      descriptor.
+     *
+     * @throws NullPointerException if <code>obj</code> is <code>null</code>.
+     * @throws ClassCastException if <code>obj</code> is not an
+     *      <code>ExtensionDescriptor</code>.
+     */
+    public int compareTo(Object obj) {
+        // throws documented ClassCastException
+        ExtensionDescriptor other = (ExtensionDescriptor) obj;
+
+        // check the order amongst the id and return if not equal
+        int idOrder = id.compareTo(other.id);
+        if (idOrder != 0) {
+            return idOrder;
+        }
+
+        // id's are the same, so return order amongst names
+        return name.compareTo(other.name);
+    }
+
+    //---------- Object overwrite ---------------------------------------------
+
+    /**
+     * Returns a combined hash code of the {@link #getId() type identification}
+     * and the {@link #getName() name} of this extension as this extension's
+     * hash code.
+     */
+    public int hashCode() {
+        return id.hashCode() + 17 * name.hashCode();
+    }
+
+    /**
+     * Returns <code>true</code> if <code>obj</code> is the same as this or
+     * if it is a <code>ExtensionDescriptor</code> whose type identification
+     * and name equals the type identification and name of this extension.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof ExtensionDescriptor) {
+            ExtensionDescriptor other = (ExtensionDescriptor) obj;
+            return id.equals(other.id) && name.equals(other.name);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns a string representation of this extension descriptor which
+     * contains the extension type identification and the extension name.
+     */
+    public String toString() {
+        return "Extension " + id + ":" + name;
+    }
+
+    //---------- innternal ----------------------------------------------------
+
+    /**
+     * Returns the value of the {@link #PROP_REP_NAME} property of the
+     * <code>extensionNode</code> or <code>null</code> if no such property
+     * exists.
+     *
+     * @param extensionNode The <code>Node</code> whose extension name property
+     *      value is to bereturned.
+     *
+     * @throws RepositoryException if an error occurrs accessing the extension
+     *      name property.
+     */
+    /* package */ static String getExtensionName(Node extensionNode)
+            throws RepositoryException {
+        return getPropertyOrNull(extensionNode, PROP_REP_NAME);
+    }
+
+    /**
+     * Returns the string value of the named (single-value) property of the
+     * node or <code>null</code> if the the property does not exists or its
+     * value is empty.
+     *
+     * @param node The <code>Node</code> containing the named property.
+     * @param property The name of the property to reutrn.
+     *
+     * @return The property's string value or <code>null</code> if the property
+     *      does not exist or is empty.
+     *
+     * @throws RepositoryException If an error occurrs accesing the node or
+     *      property.
+     */
+    private static String getPropertyOrNull(Node node, String property)
+            throws RepositoryException {
+        if (node.hasProperty(property)) {
+            String value = node.getProperty(property).getString();
+            return (value == null || value.length() == 0) ? null : value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the string values of the named (multi-valued) property of the
+     * node or <code>null</code> if the the property does not exists or its
+     * value is empty.
+     *
+     * @param node The <code>Node</code> containing the named property.
+     * @param property The name of the property to reutrn.
+     *
+     * @return A string array containing the string representations of the
+     *      property's values or <code>null</code> if the property does not
+     *      exist or is empty.
+     *
+     * @throws RepositoryException If an error occurrs accesing the node or
+     *      property.
+     */
+    private static String[] getPropertiesOrNull(Node node, String property)
+            throws RepositoryException {
+
+        if (node.hasProperty(property)) {
+            Value[] clsPath = node.getProperty(property).getValues();
+            if (clsPath != null && clsPath.length >= 0) {
+                List pathList = new ArrayList();
+                for (int i=0; i < clsPath.length; i++) {
+                    String pathEntry = clsPath[i].getString().trim();
+
+                    // ignore empty or existing path entry
+                    if (pathEntry.length() == 0 ||
+                            pathList.contains(pathEntry)) {
+                        continue;
+                    }
+
+                    // new class path entry, add
+                    pathList.add(pathEntry);
+                }
+
+                if (pathList.size() > 0) {
+                    return (String[]) pathList.toArray(new String[pathList.size()]);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates an instance of the given <code>clazz</code>. If the class has
+     * a public constructor taking a single parameter of type
+     * <code>ExtensionDescriptor</code> that constructor is used to create the
+     * instance. Otherwise the public default constructor is used if available.
+     * If none of both is available or if an error occurrs creating the instance
+     * a <code>ExtensionException</code> is thrown.
+     *
+     * @param clazz The <code>Class</code> to instantiate.
+     *
+     * @return The instance created.
+     *
+     * @throws ExtensionException If an error occurrs instantiating the class.
+     *      If instantiation failed due to an exception while calling the
+     *      constructor, the causing exception is available as the cause of
+     *      the exception.
+     */
+    private Object instantiate(Class clazz) throws ExtensionException {
+        // find constructors (taking descriptor and default)
+        Constructor defaultConstr = null;
+        Constructor descrConstr = null;
+        Constructor[] constructors = clazz.getConstructors();
+        for (int i=0; i < constructors.length; i++) {
+            Class parms[] = constructors[i].getParameterTypes();
+            if (parms.length == 0) {
+                defaultConstr = constructors[i];
+            } else if (parms.length == 1 && parms[i].equals(getClass())) {
+                descrConstr = constructors[i];
+            }
+        }
+
+        try {
+            // create an instance using the one taking an extension descriptor
+            // if available otherwise use the default constructor
+            if (descrConstr != null) {
+                log.debug("Creating instance with descriptor " + this);
+                return descrConstr.newInstance(new Object[]{ this });
+            } else if (defaultConstr != null) {
+                log.debug("Creating default instance without descriptor");
+                return defaultConstr.newInstance(null);
+            } else {
+                throw new ExtensionException("No suitable constructor found " +
+                        "to instantiate " +  getClassName());
+            }
+        } catch (InstantiationException ie) {
+            throw new ExtensionException(
+                "Cannot instantiate " + getClassName(), ie);
+        } catch (IllegalAccessException iae) {
+            throw new ExtensionException("Cannot access constructor of "
+                + getClassName(), iae);
+        } catch (InvocationTargetException ite) {
+            throw new ExtensionException("Error while instantiating "
+                + getClassName(), ite);
+        }
+    }
+
+    /**
+     * Calls a method taking a single parameter of type
+     * <code>ExtensionDescriptor</code> to provide the extension descriptor to
+     * the extension loaded.
+     * <p>
+     * If an error occurrs calling a method found, an WARN message is logged and
+     * other methods according to the required signature are looked for. If no
+     * suitable method can be found, an INFO method is logged and the extension
+     * could not be provided with the extension descriptor.
+     *
+     * @param extension The extension to provide witch the extension descriptor.
+     */
+    private void setDescriptor(Object extension) {
+        Method[] methods = extension.getClass().getMethods();
+        for (int i=0; i < methods.length; i++) {
+            Class[] parTypes = methods[i].getParameterTypes();
+            if (parTypes.length == 1 && parTypes[0].equals(getClass())) {
+                try {
+                    methods[i].invoke(extension, new Object[]{ this });
+                    return;
+                } catch (Exception ite) {
+                    log.warn("setDescriptor: Calling " +
+                        extension.getClass().getName() + "." +
+                        methods[i].getName() + " failed", ite);
+                }
+            }
+        }
+
+        log.info("setDescriptor: No setter method for ExtensionDescriptor " +
+                "found in class " + extension.getClass().getName() +
+                " of extension " + getId() + ":" + getName());
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionDescriptor.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java Wed Dec 21 12:17:51 2005
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.extension;
+
+/**
+ * The <code>ExtensionException</code> class defines an exception which may be
+ * thrown in the Jackrabbit Extension Framework.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date$
+ */
+public class ExtensionException extends Exception {
+
+    /**
+     * serialization identification
+     */
+    private static final long serialVersionUID = 535080559025771531L;
+
+    /**
+     * Creates an instance of this exception with a message.
+     *
+     * @param message The message describing the problem.
+     */
+    public ExtensionException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an instance of this exception with causing <code>Throwable</code>.
+     *
+     * @param cause The <code>Throwable</code> causing the problem.
+     */
+    public ExtensionException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates an instance of this exception with a message and a causing
+     * <code>Throwable</code>.
+     *
+     * @param message The message describing the problem.
+     * @param cause The <code>Throwable</code> causing the problem.
+     */
+    public ExtensionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionException.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java Wed Dec 21 12:17:51 2005
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.extension;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The <code>ExtensionIterator</code> class implements the iterator
+ * over instances of the {@link ExtensionDescriptor}s.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date$
+ */
+public class ExtensionIterator implements Iterator {
+
+    /** Default log */
+    private static final Log log = LogFactory.getLog(ExtensionIterator.class);
+
+    /**
+     * The type of the extension descriptors returned.
+     */
+    private final ExtensionType type;
+
+    /**
+     * The underlying iterator of nodes containing the extension descriptors.
+     */
+    private final NodeIterator nodes;
+
+    /**
+     * The preloaded next <code>EventDescriptor</code>. If <code>null</code>
+     * no more descriptors are available in this iterator.
+     */
+    private ExtensionDescriptor next;
+
+    /**
+     * Creates an instance for the given underlying iterator of nodes.
+     *
+     * @param nodes The underlying <code>NodeIterator</code>.
+     */
+    /* package */ ExtensionIterator(ExtensionType type, NodeIterator nodes) {
+        this.type = type;
+        this.nodes = nodes;
+        seek();
+    }
+
+    /**
+     * Returns <code>true</code> if there is at least one more extension
+     * descriptor available in this iterator.
+     */
+    public boolean hasNext() {
+        return next != null;
+    }
+
+    /**
+     * Returns the next available extension descriptor.
+     *
+     * @throws NoSuchElementException If no more extension descriptors are
+     *      available.
+     */
+    public Object next() {
+        return nextExtension();
+    }
+
+    /**
+     * Returns the next available extension descriptor.
+     *
+     * @throws NoSuchElementException If no more extension descriptors are
+     *      available.
+     */
+    public ExtensionDescriptor nextExtension() {
+        if (next == null) {
+            throw new NoSuchElementException("No more Descriptors");
+        }
+
+        ExtensionDescriptor toReturn = next;
+        seek();
+        return toReturn;
+    }
+
+    /**
+     * Throws <code>UnsupportedOpertationException</code> because this
+     * method is not supported by this implementation.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("remove");
+    }
+
+    /**
+     * Preload the next <code>ExtensionDescriptor</code> from the next node
+     * in the underlying node iterator.
+     * <p>
+     * If an error occurrs instantiating an extension descriptor for any
+     * node in the iterator, the node is ignored and the next node is
+     * used. This is repeated until either no more nodes are available in
+     * the underlying iterator or an extension descriptor can sucessfully
+     * be created.
+     */
+    private void seek() {
+        while (nodes.hasNext()) {
+            try {
+                Node extNode = nodes.nextNode();
+                String name = ExtensionDescriptor.getExtensionName(extNode);
+                next = type.getOrCreateExtension(name, extNode);
+                return;
+            } catch (RepositoryException re) {
+                log.warn("Cannot get the extension name", re);
+            } catch (ExtensionException ee) {
+                log.warn("Cannot create extensions descriptor", ee);
+            }
+        }
+
+        // fallback if no more nodes
+        next = null;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionIterator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java?rev=358365&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java (added)
+++ incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java Wed Dec 21 12:17:51 2005
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.extension;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.classloader.RepositoryClassLoader;
+
+/**
+ * The <code>ExtensionManager</code> class provides the core functionality
+ * of the Jackrabbit Extension Framework by methods for finding extensions.
+ * <p>
+ * Instances of this class are created with a <code>Session</code> to the
+ * repository. Consequently all access to the repository is confined to the
+ * workspace to which the session is attached. That is, only extensions located
+ * in the session's workspace are found.
+ * <p>
+ * Additionally the class provides functionality to define the extension node
+ * types on demand.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date$
+ */
+public final class ExtensionManager {
+
+    /** default logger */
+    private static final Log log = LogFactory.getLog(ExtensionManager.class);
+
+    /**
+     * The name of the repository node type defining the properties making up
+     * an extension description (value is "rep:extension").
+     */
+    public static final String NODE_EXTENSION_TYPE = "rep:extension";
+
+    /**
+     * The session providing access to the repository for loading extensions.
+     */
+    private final Session session;
+
+    /**
+     * The application's class loader as provided to the constructor.
+     */
+    private final ClassLoader applicationLoader;
+
+    /**
+     * The map of extension types by the extension type identifiers.
+     */
+    private Map extensionTypes;
+
+    /**
+     * Creates an instance of this manager accessing the repository through the
+     * given <code>session</code>.
+     * <p>
+     * This also confines the extensions available to this manager to the
+     * extensions available through the workspace accessed by the
+     * <code>session</code>.
+     * <p>
+     * If the <code>applicationLoader</code> parameter is <code>null</code>
+     * either the current thread's context class loader is used or if that one
+     * is <code>null</code>, too, the class loader of this class is used.
+     * It is recommended, that the caller of the constructor provides a
+     * non-<code>null</code> class loader to prevent unexpected class loading
+     * issues.
+     * <p>
+     * To make sure extensions may actually be correctly handled, the
+     * {@link #checkNodeType(Session)} method is called.
+     *
+     * @param session The <code>Session</code> to search for and load extensions.
+     * @param applicationLoader The <code>ClassLoader</code> used as the parent
+     *      for the repository class loaders.
+     *
+     * @throws NullPointerException If <code>session</code> is <code>null</code>.
+     */
+    public ExtensionManager(Session session, ClassLoader applicationLoader)
+            throws ExtensionException {
+
+        // check session
+        if (session == null) {
+            throw new NullPointerException("session");
+        }
+
+        // make sure the extension system node type is available
+        checkNodeType(session);
+
+        // make sure the application class loader is non-null
+        // - first try current thread context loader
+        // - otherwise use this class's class loader
+        if (applicationLoader == null) {
+            applicationLoader = Thread.currentThread().getContextClassLoader();
+        }
+        if (applicationLoader == null) {
+            applicationLoader = getClass().getClassLoader();
+        }
+
+        // assign fields
+        this.session = session;
+        this.applicationLoader = applicationLoader;
+    }
+
+    /**
+     * Searches in the workspace of this instance's <code>Session</code> for
+     * extensions of type <code>id</code> returning an <code>Iterator</code>
+     * of {@link ExtensionDescriptor} instances. If <code>root</code> is
+     * non-<code>null</code> the search for extensions only takes place in the
+     * indicated subtree.
+     * <p>
+     * <b>NOTE</B>: This method may return more than one extension with the same
+     * name for a given <code>id</code>. This is the only place in the Jackrabbit
+     * Extension Framework which handles duplicate extension names. The rest
+     * relies on extensions to have unique <code>id</code>/name pairs.
+     * <p>
+     * Calling this method multiple times with the same <code>id</code> will
+     * return the same {@link ExtensionDescriptor} instances. Previously
+     * available instances will not returned though if their extension node
+     * has been removed in the meantime. Such instances will still be available
+     * through {@link #getExtension(String, String, String)} but will not be
+     * available on next system restart.
+     *
+     * @param id The extension type identification describing the extensions to
+     *      be found.
+     * @param root The root node below which the extensions are looked for. This
+     *      path is taken as an absolute path regardless of whether it begins
+     *      with a slash or not. If <code>null</code> or empty, the search
+     *      takes place in the complete workspace.
+     *
+     * @return An <code>Iterator</code> of {@link ExtensionDescriptor} instances
+     *      describing the extensions found.
+     *
+     * @throws IllegalArgumentException If <code>id</code> is empty or
+     *      <code>null</code>.
+     * @throws ExtensionException If an error occurrs looking for extensions.
+     */
+    public Iterator getExtensions(String id, String root)
+            throws ExtensionException {
+
+        // delegate finding/loading to the extension type
+        return getExtensionType(id).getExtensions(root);
+    }
+
+    /**
+     * Searches in the workspace of this instance's <code>Session</code> for
+     * an extension with the given <code>name</code> of type <code>id</code>.
+     * If <code>root</code> is non-<code>null</code> the search for extensions
+     * only takes place in the indicated subtree.
+     * <p>
+     * This method fails with an exception if more than one extension with the
+     * same name of the same type is found in the workspace. Not finding the
+     * requested extension also yields an exception.
+     * <p>
+     * Two consecutive calls to this method with the same arguments, namely
+     * the same <code>id</code> and <code>name</code> will return the same
+     * {@link ExtensionDescriptor} instance.
+     *
+     * @param id The extension type identification describing the extensions to
+     *      be found.
+     * @param name The name of the extension of the indicated type to be found.
+     * @param root The root node below which the extensions are looked for. This
+     *      path is taken as an absolute path regardless of whether it begins
+     *      with a slash or not. If <code>null</code> or empty, the search
+     *      takes place in the complete workspace.
+     *
+     * @return The named {@link ExtensionDescriptor} instances.
+     *
+     * @throws IllegalArgumentException If <code>id</code> or <code>name</code>
+     *      is empty or <code>null</code>.
+     * @throws ExtensionException If no or more than one extensions with the
+     *      same name and type can be found or if another error occurrs looking
+     *      for extensions.
+     */
+    public ExtensionDescriptor getExtension(String id, String name, String root)
+            throws ExtensionException {
+
+        // delegate finding/loading to the extension type
+        return getExtensionType(id).getExtension(name, root);
+    }
+
+    //---------- Extension type helper methods --------------------------------
+
+    /**
+     * Creates a new instance of the <code>RepositoryClassLoader</code> class
+     * with an empty path accessing the repository through the session of this
+     * manager instance.
+     * <p>
+     * Note, that each call to this method returns a new RepositoryClassLoader
+     * instance.
+     */
+    /* package */ RepositoryClassLoader createClassLoader() {
+        return new RepositoryClassLoader(session, new String[0],
+            applicationLoader);
+    }
+
+    /**
+     * Executes the given XPath query on this mnanager's session and returns
+     * a <code>NodeIterator</code> over the nodes matching the query.
+     *
+     * @param xpath The XPath query to execute
+     *
+     * @return The <code>NodeIterator</code> on the nodes matching the query.
+     *
+     * @throws ExtensionException If an error occurrs executing the query.
+     *      The underlying exception is available as the cause of the exception.
+     */
+    /* package */ NodeIterator findNodes(String xpath) throws ExtensionException {
+        try {
+            // look for the extension nodes
+            QueryManager qm = session.getWorkspace().getQueryManager();
+            Query query = qm.createQuery(xpath, Query.XPATH);
+            QueryResult res = query.execute();
+
+            // check whether we found at least one node
+            return res.getNodes();
+        } catch (RepositoryException re) {
+            throw new ExtensionException("Problem executing query '" +
+                xpath + "'", re);
+        }
+    }
+
+    //---------- internal helper ----------------------------------------------
+
+    /**
+     * Returns an {@link ExtensionType} instance for the given name.
+     *
+     * @throws IllegalArgumentException if <code>id</code> is <code>null</code>
+     *      or an empty string.
+     */
+    /* package */ ExtensionType getExtensionType(String id) {
+        if (id == null || id.length() == 0) {
+            throw new IllegalArgumentException("Extension type identifier " +
+                    "must not be null or empty string");
+        }
+
+        if (extensionTypes == null) {
+            extensionTypes = new TreeMap();
+        }
+
+        ExtensionType type = (ExtensionType) extensionTypes.get(id);
+        if (type == null) {
+            type = new ExtensionType(this, id);
+            extensionTypes.put(id, type);
+        }
+
+        return type;
+    }
+
+    /**
+     * Makes sure the <code>rep:extension</code> node type is registered with
+     * the <code>session</code>'s repository.
+     * <p>
+     * If the required extension descriptor node type is not defined in the
+     * repository yet, it is tried to be registered. If an error occurrs
+     * registering the node type a message is logged and an
+     * <code>ExtensionException</code> is thrown.
+     *
+     * @param session The <code>Session</code> used to access the repository
+     *      to test and optionally register the node.
+     *
+     * @throws ExtensionException If an error occurrs checking ro defining the
+     *      node type.
+     */
+    public static void checkNodeType(Session session) throws ExtensionException {
+        // quick check for the node type, succeed if defined
+        try {
+            session.getWorkspace().getNodeTypeManager().getNodeType(NODE_EXTENSION_TYPE);
+            log.debug("Required node type exists, can expand archives");
+            return;
+        } catch (NoSuchNodeTypeException nst) {
+            log.debug("Required node types does not exist, try to define");
+        } catch (RepositoryException re) {
+            log.debug("Cannot check for required node type, cannot expand " +
+                    "archives", re);
+            throw new ExtensionException("Cannot check for required node type", re);
+        }
+
+        try {
+            Workspace workspace = session.getWorkspace();
+            if (!NodeTypeSupport.registerNodeType(workspace)) {
+                throw new ExtensionException("Registering required node type failed");
+            }
+        } catch (ExtensionException ee) {
+            throw ee;
+        } catch (Throwable t) {
+            // Prevent anything from hapening if node type registration fails
+            // due to missing libraries or other errors
+            log.debug("Error registering node type", t);
+            throw new ExtensionException("Cannot register required node type", t);
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/extension-framework/src/main/java/org/apache/jackrabbit/extension/ExtensionManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url