Commits

hani  committed 6de93ec

Added ejb3 support
Refactored so third party persistence mechanisms are spat out in their own jars
Moved providers stuff to a new top level dir
Using ejb/hibernate/ofbiz/ojb now requires the propertyset jar AND the persistence impl jar, be warned!

git-svn-id: http://svn.opensymphony.com/svn/propertyset/trunk@169bf3cbcdd-1c1a-0410-9a68-d6f521e3fa7b

  • Participants
  • Parent commits 23df6db

Comments (0)

Files changed (116)

File build.properties

 #build.compiler=jikes
 
 name=propertyset
+fullname=PropertySet
 version=1.3
 # uncomment the next line if this is a release build
 #release=
 <project default="jar" basedir=".">
-  
+
   <property name="src.test" location="src/test"/>
   <property name="src.etc" location="src/etc"/>
-  
+
   <property name="common.build" value="../opensymphony/common/osbuild.xml"/>
+  <property name="providers" location="providers"/>
+
   <import file="${common.build}"/>
+
+  <path id="providers.cp">
+    <path refid="cp" />
+    <fileset dir="${providers}">
+      <include name="*/lib/*.jar"/>
+    </fileset>
+    <pathelement location="${build.java}"/>
+    <pathelement location="${providers}/ejb/build"/>
+    <pathelement location="${providers}/ejb3/build"/>
+    <pathelement location="${providers}/hibernate/build"/>
+    <pathelement location="${providers}/ofbiz/build"/>
+    <pathelement location="${providers}/ojb/build"/>
+  </path>
   
-    <path id="jalopy.classpath">
-        <fileset dir="lib/build/jalopy">
-            <include name="*.jar"/>
+  <path id="jalopy.classpath">
+    <fileset dir="lib/build/jalopy">
+      <include name="*.jar"/>
+    </fileset>
+  </path>
+
+  <taskdef name="jalopy" classname="de.hunsicker.jalopy.plugin.ant.AntPlugin">
+    <classpath refid="jalopy.classpath"/>
+  </taskdef>
+
+  <target name="clean" depends="common.clean">
+    <delete>
+      <fileset dir="${providers}/ejb/src/com/opensymphony/module/propertyset/ejb">
+        <exclude name="**/*EJB*.java"/>
+      </fileset>
+    </delete>
+    <delete includeemptydirs="true">
+      <fileset dir="${providers}">
+        <include name="*/build/**"/>
+      </fileset>
+    </delete>
+  </target>
+
+  <target name="ejbdoclet" depends="init">
+    <taskdef name="ejbdoclet" classname="xdoclet.modules.ejb.EjbDocletTask">
+      <classpath>
+        <path refid="cp" />
+        <fileset dir="${providers}/ejb/lib">
+          <include name="*.jar"/>
         </fileset>
+      </classpath>
+    </taskdef>
+
+    <ejbdoclet ejbspec="2.0" verbose="false" destdir="${providers}/ejb/src">
+      <fileset dir="${providers}/ejb/src">
+        <include name="com/opensymphony/module/propertyset/ejb/**/*EJB.java"/>
+      </fileset>
+      <remoteinterface/>
+      <localinterface/>
+      <homeinterface/>
+      <localhomeinterface/>
+      <!--<valueobject/>-->
+      <!--<entitypk/>-->
+      <entitycmp/>
+      <!--<session/>-->
+      <utilobject pattern="{0}HomeFactory" cacheHomes="true"/>
+      <deploymentdescriptor destdir="${providers}/ejb/build/META-INF" validatexml="true" description="OpenSymphony PropertySet"
+                            displayname="PropertySet Module"/>
+      <jboss version="3.0" destdir="${providers}/ejb/build/META-INF"/>
+      <weblogic version="7.0" destdir="${providers}/ejb/build/META-INF" datasource="defaultDS" createtables="true"/>
+      <jrun version="4.0" destdir="${providers}/ejb/build/META-INF"/>
+      <orion version="2.0" destdir="${providers}/ejb/build/META-INF"/>
+    </ejbdoclet>
+  </target>
+
+  <target name="test-compile" unless="skip.tests">
+    <compile srcdir="${src.test}" destdir="${build.test}" classpathref="providers.cp" />
+
+    <taskdef resource="clovertasks"/>
+    <taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask"/>
+
+    <mkdir dir="${build.clover}"/>
+    <clover-setup initString="${build.clover}/coverage.db">
+      <files>
+        <exclude name="${src.test}/**/*.java"/>
+      </files>
+    </clover-setup>
+
+    <compile srcdir="${src.java}" destdir="${build.java-test}" />
+    <provider-compile provider="ejb3" source="1.5" destdir="${build.java-test}" />
+    <provider-compile provider="ejb" destdir="${build.java-test}" />
+    <provider-compile provider="hibernate" destdir="${build.java-test}" />
+    <provider-compile provider="ofbiz" destdir="${build.java-test}" />
+    <provider-compile provider="ojb" destdir="${build.java-test}" />
+  </target>
+
+  <target name="test" depends="junit-check, clover-check, compile, test-compile" description="run tests"
+          unless="skip.tests">
+    <path id="providers.junit.cp">
+      <path refid="junit.cp" />
+      <path refid="providers.cp" />
     </path>
+    <run-junit classpathref="providers.junit.cp"/>
+  </target>
 
-    <taskdef name="jalopy" classname="de.hunsicker.jalopy.plugin.ant.AntPlugin">
-        <classpath refid="jalopy.classpath"/>
-    </taskdef>
+  <target name="compile" depends="common.compile">
+    <copy file="${src.etc}/propertyset-default.xml" todir="${build.java}/META-INF"/>
+    <provider-compile provider="ejb" />
+    <provider-compile provider="ejb3" source="1.5"/>
+    <provider-compile provider="hibernate" />
+    <provider-compile provider="ofbiz" />
+    <provider-compile provider="ojb" />
+  </target>
+
+  <target name="precompile" depends="ejbdoclet">
+    <jalopy fileformat="unix" convention="src/etc/jalopy.xml" history="file" historymethod="adler32" loglevel="error"
+            threads="2" classpathref="cp">
+      <fileset dir="${src.java}">
+        <include name="**/*.java"/>
+      </fileset>
+      <fileset dir="${src.test}">
+        <include name="**/*.java"/>
+      </fileset>
+      <fileset dir="${providers}">
+        <include name="**/*.java"/>
+        <exclude name="ejb3/**/*Entry*.java"/>
+      </fileset>
+    </jalopy>
+  </target>
 
-    <target name="clean" depends="common.clean">
-        <delete>
-            <fileset dir="${src.java}/com/opensymphony/module/propertyset/ejb">
-                <exclude name="**/*EJB*.java"/>
-            </fileset>
-        </delete>
-    </target>
-
-    <target name="ejbdoclet" depends="init">
-      <taskdef name="ejbdoclet" classname="xdoclet.modules.ejb.EjbDocletTask" classpathref="cp"/>
-
-        <ejbdoclet ejbspec="2.0" verbose="false" destdir="${src.java}">
-            <fileset dir="${src.java}">
-                <include name="com/opensymphony/module/propertyset/ejb/**/*EJB.java"/>
-            </fileset>
-            <remoteinterface/>
-            <localinterface/>
-            <homeinterface/>
-            <localhomeinterface/>
-            <!--<valueobject/>-->
-            <!--<entitypk/>-->
-            <entitycmp/>
-            <!--<session/>-->
-            <utilobject pattern="{0}HomeFactory" cacheHomes="true"/>
-            <deploymentdescriptor destdir="${build.java}/META-INF" validatexml="true" description="OpenSymphony PropertySet" displayname="PropertySet Module"/>
-            <jboss version="3.0" destdir="${build.java}/META-INF"/>
-            <weblogic version="7.0" destdir="${build.java}/META-INF" datasource="defaultDS" createtables="true"/>
-            <jrun version="4.0" destdir="${build.java}/META-INF"/>
-            <orion version="2.0" destdir="${build.java}/META-INF"/>
-        </ejbdoclet>
-    </target>
-
-    <target name="compile" depends="common.compile">
-        <copy file="${src.etc}/propertyset-default.xml" todir="${build.java}/META-INF"/>
-    </target>
-
-    <target name="precompile" depends="ejbdoclet">
-        <jalopy fileformat="unix" convention="src/etc/jalopy.xml" history="file" historymethod="adler32" loglevel="error" threads="2" classpathref="cp">
-            <fileset dir="${src.java}">
-                <include name="**/*.java"/>
-            </fileset>
-            <fileset dir="${src.test}">
-                <include name="**/*.java"/>
-            </fileset>
-        </jalopy>
-    </target>
-
-    <target name="ejb-jar" depends="compile">
-        <mkdir dir="${dist}"/>
-        <jar basedir="${build.java}" jarfile="${dist}/${name}-ejb-${version}.jar" manifest="${src.etc}/manifest-ejb.txt">
-            <exclude name="**/ofbiz/**"/>
-        </jar>
-    </target>
-
-    <target name="ofbiz-jar" depends="compile">
-        <mkdir dir="${dist}"/>
-        <jar basedir="${build.java}" jarfile="${dist}/${name}-ofbiz-${version}.jar">
-            <include name="**/ofbiz/**"/>
-        </jar>
-    </target>
+  <target name="jar" depends="common.jar">
+    <mkdir dir="${dist}"/>
+    <provider-jar provider="ejb" manifest="${src.etc}/manifest-ejb.txt" />
+    <provider-jar provider="ofbiz" />
+    <provider-jar provider="hibernate" />
+    <provider-jar provider="ejb3" />
+    <provider-jar provider="ojb" />
+  </target>
+  
+  <macrodef name="provider-compile">
+    <attribute name="provider" />
+    <attribute name="source" default="${compile.version}" />
+    <attribute name="destdir" default="${providers}/@{provider}/build" />
+    <sequential>
+      <path id="@{provider}.cp">
+        <path refid="cp" />
+        <fileset dir="${providers}/@{provider}/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <pathelement location="${build.java}"/>
+      </path>
+      <compile srcdir="${providers}/@{provider}/src" destdir="@{destdir}" classpathref="@{provider}.cp" source="@{source}" target="@{source}"/>
+    </sequential>
+  </macrodef>
   
+  <macrodef name="provider-jar">
+    <attribute name="provider" />
+    <attribute name="manifest" default="${jar.manifest}"/>
+    <sequential>
+      <jar basedir="${providers}/@{provider}/build" jarfile="${build}/${name}-@{provider}-${version}.jar" manifest="@{manifest}">
+        <manifest>
+          <attribute name="Implementation-Title" value="${fullname}-@{provider}"/>
+          <attribute name="Implementation-Version" value="${version}"/>
+          <attribute name="Implementation-Vendor" value="OpenSymphony"/>
+        </manifest>        
+      </jar>
+    </sequential>
+  </macrodef>
 </project>
 

File lib/build/clover-1.2.3.jar

Binary file removed.

File lib/build/clover.jar

Binary file added.

File lib/build/clover.license

+Product: Clover
+License: Open Source License, 0.x, 1.x
+Issued: Sun Jan 12 2003 16:49:06 CST
+Expiry: Never
+Key: 95021d1a8d297dc7c24a4d55b
+Name: Patrick Lightbody
+Org: OpenSymphony
+Certificate: AAAB02+Ow8B7/zEbxOMqqKwwrdpP+a1COmJGHco7sCNLjHkHnajPF+dQW
+Ct12PMy0uml0s9xuus5wKngJ9OFk5PFeh01dzQF66bhXH1bvegLfvja3Kle6BYtDv4LZgE
+gk3E0aJN4IbgTn+TgUckSevXDR4KzK77NWJfrVzkxV3/JepMgS+gaUIj0h0mnUXI0d9L3d
++Ly/oD4fPPUcM+l7THY/MeGoGxPE34jA+emuAgEC6n8WXzjy9zHEesXaA+2NGYLPMcCL3W
+EOyD9KZPr6k/XEjTzhk+N4BCr2lOgsk4meUx51XqzPkL/ZyOP4owbvVonAQ/L+GDj6aPFk
+WDo0jWpoOc30+HRw+FjC20je0yIpPcSA06h/l9485Kz11TtFSESojB/CyCOKDNpjHd57C2
+D4o9H1xtn89RVjso3ssxJg69Fa2VRYslQsDlN4ydagKqGVbpdNwA4jK0ms7LhkZsgrJdlm
+8btO9j7w8W7Nz88dy0G5/TuCARCra5efv7fg4we6A8/UqAheBU70gw4cow7Ivct1urqTd9
+XM+nAT5mtPq/WUp3rQjR+VmOMGbA/WOM7B+/ohkWsRiEBK83ocO3vs8nIKajzWsul/SDe+
+8/K8n4dJXdGLSygwlLtZhtmxDYQhP+14HrB1hsru9usIilX2v1DGzap25mlAHZxwhxFXPd
+OlbPJEIfwJ+MBSJ4uw9ef3Pt8wtIulNrAraTniIYyShlOc/vgrMuGmZTAObtza2oJAaztu
+87hSZYD0A5Al0hC7kOliEBe871Gl590e9tiSFiNokQ=
+License Agreement: CLOVER VERSION 1 (ONE) SOFTWARE LICENSE AGREEMENT
+
+1. Licenses and Software
+
+Cortex eBusiness Pty Ltd, an Australian Proprietary Limited Company
+("CENQUA") hereby grants to the purchaser (the "LICENSEE") a limited,
+revocable, worldwide, non-exclusive, non-transferable,
+non-sublicensable license to use the Clover version 1 (one) software
+(the "Software"), including any minor upgrades thereof during the Term
+(hereinafter defined) up to, but not including the next major version
+of the Software. The licensee shall not, or knowingly allow others to,
+reverse engineer, decompile, disassemble, modify, adapt, create
+derivative works from or otherwise attempt to derive source code from
+the Software provided. And, in accordance with the terms and
+conditions of this Software License Agreement (the "Agreement"), the
+Software shall be used solely by the licensed users in accordance with
+the following edition specific conditions:
+
+a) Server Edition
+
+A Server Edition license entitles the Licensee to execute one instance
+of Clover Server Edition on one (1) machine for the purposes of
+instrumenting source code and generating reports. There are no
+limitations on the use of the instrumented source code or generated
+reports produced by Server Edition.
+
+b) Workstation Edition
+
+A Workstation Edition license entitles the licensee to use Clover
+Workstation Edition on one (1) machine by one (1) individual end
+user. Workstation Edition does not permit the generation of reports
+for distribution.
+
+c) Team Edition
+
+A Team Edition license entitles the licensee to use Clover Team
+edition on any number of machines solely by the licensed number of
+users. Reports generated by Clover Team Edition are strictly for use
+only by the licensed number of individual end users.
+
+2. License Fee
+
+In exchange for the License(s), the Licensee shall pay to Cenqua a
+one-time, up front, non-refundable license fee. At the sole discretion
+of Cenqua this fee will be waived for non-commercial
+projects. Notwithstanding the Licensee's payment of the License Fee,
+Cenqua reserves the right to terminate the License if Cenqua discovers
+that the Licensee and/or the Licensee's use of the Software is in
+breach of this Agreement.
+
+3. Proprietary Rights
+
+Cenqua will retain all right, title and interest in and to the
+Software, all copies thereof, and Cenqua website(s), software, and
+other intellectual property, including, but not limited to, ownership
+of all copyrights, look and feel, trademark rights, design rights,
+trade secret rights and any and all other intellectual property and
+other proprietary rights therein. The Licensee will not directly or
+indirectly obtain or attempt to obtain at any time, any right, title
+or interest by registration or otherwise in or to the trademarks,
+service marks, copyrights, trade names, symbols, logos or designations
+or other intellectual property rights owned or used by Cenqua. All
+technical manuals or other information provided by Cenqua to the
+Licensee shall be the sole property of Cenqua.
+
+4. Term and Termination
+
+Subject to the other provisions hereof, this Agreement shall commence
+upon the Licensee's opting into this Agreement and continue until the
+Licensee discontinues use of the Software or the Agreement terminates
+automatically upon the Licensee's breach of any term or condition of
+this Agreement (the "Term"). Upon any such termination, the Licensee
+will delete the Software immediately.
+
+5. Copying & Transfer
+
+The Licensee may copy the Software for back-up purposes only. The
+Licensee may not assign or otherwise transfer the Software to any
+third party.
+
+6. Specific Disclaimer of Warranty and Limitation of Liability
+
+THE SOFTWARE IS PROVIDED WITHOUT WARRANTY OF ANY KIND. CENQUA
+DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. CENQUA WILL NOT BE LIABLE FOR ANY DAMAGES
+ASSOCIATED WITH THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ORDINARY,
+INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING
+BUT NOT LIMITED TO DAMAGES RELATING TO LOST DATA OR LOST PROFITS, EVEN
+IF CENQUA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Warranties and Representations
+
+Licensee Indemnification. CENQUA agrees to indemnify, defend and hold
+the Licensee harmless from and against any and all liabilities, 
+damages, losses, claims, costs, and expenses (including reasonable 
+legal fees) arising out of or resulting from the Software or the use 
+thereof infringing upon, misappropriating or violating any patents, 
+copyrights, trademarks, or trade secret rights or other proprietary 
+rights of persons, firms or entities who are not parties to this 
+Agreement.
+
+CENQUA Indemnification. The Licensee warrants and represents that the 
+Licensee's actions with regard to the Software will be in compliance 
+with all applicable  laws; and the Licensee agrees to indemnify, 
+defend, and hold CENQUA harmless  from and against any and all 
+liabilities, damages, losses, claims, costs,  and expenses (including 
+reasonable legal fees) arising out of or resulting  from the 
+Licensee's failure to observe the use restrictions set forth herein.
+
+8. Publicity
+
+The Licensee grants permission for CENQUA to use Licensee's name 
+solely in customer lists. CENQUA shall not, without prior consent in 
+writing, use the Licensee's name, or that of its affiliates, in any 
+form with the specific  exception of customer lists. CENQUA agrees to 
+remove Licensee's name from  any and all materials within 7 days if 
+notified by the Licensee in writing.
+
+9. Governing Law
+
+This Agreement shall be governed by the laws of New South Wales, 
+Australia.
+
+10. Independent Contractors
+
+The parties are independent contractors with respect to each  other, 
+and nothing in this Agreement shall be construed as creating an 
+employer-employee  relationship, a partnership, agency relationship or 
+a joint venture between  the parties.
+
+11. Assignment
+
+This Agreement is not assignable or transferable by the Licensee. 
+CENQUA  in its sole discretion may transfer a license to a third party 
+at the written  request of the Licensee.
+
+12. Entire Agreement
+
+This Agreement constitutes the entire agreement between the parties 
+concerning  the Licensee's use of the Software. This Agreement 
+supersedes any prior verbal understanding between the parties and any 
+Licensee purchase order or other  ordering document, regardless of 
+whether such document is received by CENQUA  before or after execution 
+of this Agreement. This Agreement may be amended  only in writing by 
+CENQUA.

File lib/optional/antlr-2.7.5H3.jar

Binary file added.

File lib/optional/asm-attrs.jar

Binary file added.

File lib/optional/asm.jar

Binary file added.

File lib/optional/cglib-2.1.2.jar

Binary file added.

File lib/optional/cglib2.jar

Binary file removed.

File lib/optional/db-ojb-1.0.rc4.jar

Binary file removed.

File lib/optional/ejb.jar

Binary file removed.

File lib/optional/hibernate-2.1.7c.jar

Binary file removed.

File lib/optional/hibernate-entitymanager.jar

Binary file added.

File lib/optional/hibernate3.jar

Binary file added.

File lib/optional/javassist.jar

Binary file added.

File lib/optional/jboss-archive-browsing.jar

Binary file added.

File lib/optional/ofbcore-entity.jar

Binary file removed.

File lib/optional/ofbcore-share.jar

Binary file removed.

File lib/optional/serializer.jar

Binary file added.

File lib/optional/xalan.jar

Binary file added.

File lib/optional/xerces-serialize.jar

Binary file removed.

File lib/optional/xercesImpl.jar

Binary file added.

File lib/optional/xml-apis.jar

Binary file added.

File providers/ejb/ejb.iml

+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager">
+    <output url="file://$MODULE_DIR$/build" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/ejb.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="propertyset" />
+    <orderEntryProperties />
+  </component>
+</module>
+

File providers/ejb/lib/ejb.jar

Binary file added.

File providers/ejb/src/com/opensymphony/ejb/AbstractEntityAdapter.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.ejb;
+
+import com.opensymphony.module.propertyset.PropertySet;
+import com.opensymphony.module.propertyset.PropertySetManager;
+import com.opensymphony.module.sequence.SequenceGeneratorHome;
+
+import com.opensymphony.util.EJBUtils;
+import com.opensymphony.util.GUID;
+
+import java.rmi.RemoteException;
+
+import java.util.HashMap;
+
+import javax.ejb.CreateException;
+import javax.ejb.EntityContext;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$
+ * @version $Revision$
+ */
+public class AbstractEntityAdapter {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    /**
+     * Reference to EntityContext.
+     */
+    protected EntityContext context;
+
+    /**
+     * Name of the auto-generating sequence.
+     */
+    protected String sequenceName;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Sets the context - should be used only by direct subclasses
+     * Also, attempts to determine the <code>sequenceName</code>.
+     */
+    protected void setContext(EntityContext context) {
+        this.context = context;
+
+        try {
+            sequenceName = (String) EJBUtils.lookup("sequenceName", String.class);
+        } catch (Exception e) {
+            sequenceName = "";
+        }
+    }
+
+    /**
+     * Generate random GUID.
+     *
+     * @see com.opensymphony.util.GUID
+     */
+    protected String generateGUID() {
+        return GUID.generateGUID();
+    }
+
+    /**
+     * Locate PropertySet using PropertyStore for this sequenceName/sequenceId mapping.
+     */
+    protected PropertySet locatePropertySet(long id) throws RemoteException {
+        HashMap args = new HashMap(2);
+        args.put("entityId", new Long(id));
+        args.put("entityName", sequenceName);
+
+        return PropertySetManager.getInstance("ejb", args);
+    }
+
+    /**
+     * @deprecated Use {@link #nextInt()} or {@link #nextLong()} instead.
+     */
+    protected int nextId() throws CreateException, RemoteException {
+        return nextInt();
+    }
+
+    /**
+     * Return int unique id key from a unique instance key generator.
+     */
+    protected int nextInt() throws CreateException, RemoteException {
+        try {
+            return (int) nextLong();
+        } catch (ClassCastException e) {
+            throw new CreateException("Cannot generate id: Sequence cannot be downcasted to long.");
+        } catch (NullPointerException e) {
+            throw new CreateException("Cannot generate id: Sequence returning null.");
+        }
+    }
+
+    /**
+     * Return long unique id key from a unique instance key generator.
+     */
+    protected long nextLong() throws CreateException, RemoteException {
+        try {
+            SequenceGeneratorHome sgHome;
+
+            try {
+                if (sequenceName == null) {
+                    sequenceName = (String) EJBUtils.lookup("sequenceName", String.class);
+                }
+
+                sgHome = (SequenceGeneratorHome) EJBUtils.lookup("ejb/SequenceGenerator", SequenceGeneratorHome.class);
+            } catch (NameNotFoundException e) {
+                sgHome = (SequenceGeneratorHome) EJBUtils.lookup("SequenceGenerator", SequenceGeneratorHome.class);
+            }
+
+            return sgHome.create().getCount(sequenceName);
+        } catch (NamingException e) {
+            throw new CreateException("Cannot generate id: " + e.toString());
+        }
+    }
+}

File providers/ejb/src/com/opensymphony/ejb/EntityAdapter.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.ejb;
+
+import java.rmi.RemoteException;
+
+/* ====================================================================
+ * The OpenSymphony Software License, Version 1.1
+ *
+ * (this license is derived and fully compatible with the Apache Software
+ * License - see http://www.apache.org/LICENSE.txt)
+ *
+ * Copyright (c) 2001 The OpenSymphony Group. 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. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        OpenSymphony Group (http://www.opensymphony.com/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "OpenSymphony" and "The OpenSymphony Group"
+ *    must not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact license@opensymphony.com .
+ *
+ * 5. Products derived from this software may not be called "OpenSymphony"
+ *    or "OSCore", nor may "OpenSymphony" or "OSCore" appear in their
+ *    name, without prior written permission of the OpenSymphony Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
+ * ITS 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.
+ * ====================================================================
+ */
+import javax.ejb.*;
+
+
+/**
+ * <p>
+ *   Abstract base adapter class to be extended by EJB EntityBeans. <br>
+ *   Contains default implementations for all require methods to implement EntityBean.
+ * </p>
+ * <p>
+ *   To create an implementation of an Entity, extend this class and add appropriate ejbCreate(),
+ *   ejbPostCreate() and getter/setter methods.
+ * </p>
+ * <p>
+ *   If an auto-generated primary key (<code>long</code>/<code>int</code>) is to be used,
+ * </p>
+ *
+ * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
+ * @version $Revision$
+ */
+public abstract class EntityAdapter extends AbstractEntityAdapter implements EntityBean {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Required to implement EntityBean. Sets the EntityContext.
+     * Also, attempts to detemine the <code>sequenceName</code>.
+     */
+    public void setEntityContext(EntityContext context) throws EJBException, RemoteException {
+        this.setContext(context);
+    }
+
+    /**
+     * Required to implement EntityBean. Not implemented.
+     */
+    public void ejbActivate() throws EJBException, RemoteException {
+    }
+
+    /**
+     * Required to implement EntityBean. Not implemented.
+     */
+    public void ejbLoad() throws EJBException, RemoteException {
+    }
+
+    /**
+     * Required to implement EntityBean. Not implemented.
+     */
+    public void ejbPassivate() throws EJBException, RemoteException {
+    }
+
+    /**
+     * Required to implement EntityBean. Not implemented.
+     */
+    public void ejbRemove() throws RemoveException, EJBException, RemoteException {
+    }
+
+    /**
+     * Required to implement EntityBean. Not implemented.
+     */
+    public void ejbStore() throws EJBException, RemoteException {
+    }
+
+    /**
+     * Required to implement EntityBean. Sets the EntityContext to null.
+     */
+    public void unsetEntityContext() throws EJBException, RemoteException {
+        context = null;
+    }
+
+    /**
+     * Return EntityContext. To be used by classes extending this.
+     */
+    protected EntityContext getEntityContext() {
+        return context;
+    }
+}

File providers/ejb/src/com/opensymphony/ejb/ExceptionlessEntityAdapter.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.ejb;
+
+import javax.ejb.*;
+
+
+/**
+ * <p>
+ *   Abstract base adapter class to be extended by EJB EntityBeans. <br>
+ *   Contains default implementations for all require methods to implement EntityBean.
+ * </p>
+ * <p>
+ *   To create an implementation of an Entity, extend this class and add appropriate ejbCreate(),
+ *   ejbPostCreate() and getter/setter methods.
+ * </p>
+ * <p>
+ *   If an auto-generated primary key (<code>long</code>/<code>int</code>) is to be used,
+ * </p>
+ * <p>
+ *  <b>This is an identical class in terms of functionality to EntityAdapter, it just does
+ * not throw as many exceptions. This sometimes works much nicer with other tools such as
+ * XDoclet.</b>
+ * </p>
+ *
+ * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
+ * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
+ * @version $Revision$
+ */
+public abstract class ExceptionlessEntityAdapter extends AbstractEntityAdapter implements EntityBean {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+    * Required to implement EntityBean. Sets the EntityContext.
+    * Also, attempts to detemine the <code>sequenceName</code>.
+    */
+    public void setEntityContext(EntityContext context) {
+        this.setContext(context);
+    }
+
+    /**
+    * Required to implement EntityBean. Not implemented.
+    */
+    public void ejbActivate() {
+    }
+
+    /**
+    * Required to implement EntityBean. Not implemented.
+    */
+    public void ejbLoad() {
+    }
+
+    /**
+    * Required to implement EntityBean. Not implemented.
+    */
+    public void ejbPassivate() {
+    }
+
+    /**
+    * Required to implement EntityBean. Not implemented.
+    */
+    public void ejbRemove() throws RemoveException {
+    }
+
+    /**
+    * Required to implement EntityBean. Not implemented.
+    */
+    public void ejbStore() {
+    }
+
+    /**
+    * Required to implement EntityBean. Sets the EntityContext to null.
+    */
+    public void unsetEntityContext() {
+        context = null;
+    }
+
+    /**
+    * Return EntityContext. To be used by classes extending this.
+    */
+    protected EntityContext getEntityContext() {
+        return context;
+    }
+}

File providers/ejb/src/com/opensymphony/ejb/SessionAdapter.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.ejb;
+
+import java.rmi.RemoteException;
+
+/* ====================================================================
+ * The OpenSymphony Software License, Version 1.1
+ *
+ * (this license is derived and fully compatible with the Apache Software
+ * License - see http://www.apache.org/LICENSE.txt)
+ *
+ * Copyright (c) 2001 The OpenSymphony Group. 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. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        OpenSymphony Group (http://www.opensymphony.com/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "OpenSymphony" and "The OpenSymphony Group"
+ *    must not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact license@opensymphony.com .
+ *
+ * 5. Products derived from this software may not be called "OpenSymphony"
+ *    or "OSCore", nor may "OpenSymphony" or "OSCore" appear in their
+ *    name, without prior written permission of the OpenSymphony Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
+ * ITS 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.
+ * ====================================================================
+ */
+import javax.ejb.*;
+
+
+/**
+ * <p>
+ *   Abstract base adapter class to be extended by EJB SessionBeans. <br>
+ *   Contains default implementations for all require methods to implement SessionBean.
+ * </p>
+ * <p>
+ *   To create an implementation of an Session, extend this class and add appropriate ejbCreate()
+ *   and getter/setter methods.
+ * </p>
+ *
+ * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
+ * @version $Revision$
+ */
+public abstract class SessionAdapter implements SessionBean {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    /** Reference to SessionContext. */
+    protected SessionContext context;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /** Required to implement SessionBean. Sets the SessionContext. */
+    public void setSessionContext(SessionContext context) throws RemoteException {
+        this.context = context;
+    }
+
+    /** Required to implement SessionBean. Not implemented. */
+    public void ejbActivate() throws EJBException, RemoteException {
+    }
+
+    /** Required to implement SessionBean. Not implemented. */
+    public void ejbPassivate() throws EJBException, RemoteException {
+    }
+
+    /** Required to implement SessionBean. Not implemented. */
+    public void ejbRemove() throws EJBException, RemoteException {
+    }
+
+    /** Return SessionContext. To be used by classes extending this. */
+    protected SessionContext getSessionContext() throws EJBException, RemoteException {
+        return context;
+    }
+}

File providers/ejb/src/com/opensymphony/module/propertyset/ejb/.cvsignore

+PropertyStore.java
+*Home*.java

File providers/ejb/src/com/opensymphony/module/propertyset/ejb/EJBPropertySet.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.module.propertyset.ejb;
+
+import com.opensymphony.module.propertyset.*;
+
+import com.opensymphony.util.DataUtil;
+import com.opensymphony.util.EJBUtils;
+
+import java.io.Serializable;
+
+import java.rmi.RemoteException;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.ejb.CreateException;
+
+import javax.naming.NamingException;
+
+
+/**
+ * The EJBPropertySet is an implementation of
+ * {@link com.opensymphony.module.propertyset.PropertySet} that
+ * uses Enterprise JavaBeans to store and retrieve Properties.
+ *
+ * <p>This class is a proxy to the
+ * {@link com.opensymphony.module.propertyset.ejb.PropertyStore}
+ * Session Bean that handles the PropertySet and behind the scenes
+ * delegates to various Entity Beans to persist the data in an
+ * efficient way.</p>
+ *
+ * <p>Each method in the proxy will catch any thrown
+ * {@link java.rmi.RemoteException} and rethrow it wrapped in a
+ * {@link com.opensymphony.module.propertyset.PropertyImplementationException} .</p>
+ *
+ * <h3>Usage</h3>
+ *
+ * <p>In order to use an EJBPropertySet, a PropertyStore Session Bean
+ * must first be retrieved that represents the PropertySet data. This
+ * is typically either returned by another EJB, or looked up using
+ * an JNDI location for PropertyStoreHome and an int ID for the actual
+ * PropertySet used.</p>
+ *
+ * <b>Required Args</b>
+ * <ul>
+ *  <li><b>entityId</b> - Long that holds the ID of this entity</li>
+ *  <li><b>entityName</b> - String that holds the name of this entity type</li>
+ * </ul>
+ * <p>
+ *
+ * <b>Optional Configuration</b>
+ * <ul>
+ *  <li><b>storeLocation</b> - the JNDI name for the PropertyStore EJB lookup (defaults to os.PropertyStore)</li>
+ * </ul>
+ *
+ * @author <a href="mailto:joe@wirestation.co.uk">
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision$
+ *
+ * @see com.opensymphony.module.propertyset.PropertySet
+ * @see com.opensymphony.module.propertyset.ejb.PropertyStore
+ * @see com.opensymphony.module.propertyset.ejb.PropertyStoreHome
+ */
+public class EJBPropertySet extends AbstractPropertySet implements Serializable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private PropertyStore store;
+    private String entityName;
+    private long entityId;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+    * Proxy to {@link com.opensymphony.module.propertyset.ejb.PropertyStore#getKeys(java.lang.String,long,java.lang.String,int)}
+    */
+    public Collection getKeys(String prefix, int type) throws PropertyException {
+        try {
+            return store.getKeys(entityName, entityId, prefix, type);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    /**
+    * Proxy to {@link com.opensymphony.module.propertyset.ejb.PropertyStore#getType(java.lang.String,long,java.lang.String)}
+    */
+    public int getType(String key) throws PropertyException {
+        try {
+            return store.getType(entityName, entityId, key);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    /**
+    * Proxy to {@link com.opensymphony.module.propertyset.ejb.PropertyStore#exists(java.lang.String,long,java.lang.String)}
+    */
+    public boolean exists(String key) throws PropertyException {
+        try {
+            return store.exists(entityName, entityId, key);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    public void init(Map config, Map args) {
+        entityId = DataUtil.getLong((Long) args.get("entityId"));
+        entityName = (String) args.get("entityName");
+
+        String storeLocation = (String) config.get("storeLocation");
+
+        if (storeLocation == null) {
+            storeLocation = "PropertyStore";
+        }
+
+        try {
+            PropertyStoreHome home = (PropertyStoreHome) EJBUtils.lookup(storeLocation, PropertyStoreHome.class);
+            store = home.create();
+        } catch (NamingException e) {
+            e.printStackTrace();
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        } catch (CreateException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Proxy to {@link PropertyStore#removeEntry(String, long, String)}
+     */
+    public void remove() throws PropertyException {
+        try {
+            store.removeEntry(entityName, entityId);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    /**
+    * Proxy to {@link PropertyStore#removeEntry(String, long, String)}
+    */
+    public void remove(String key) throws PropertyException {
+        try {
+            store.removeEntry(entityName, entityId, key);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    /**
+    * Proxy to {@link com.opensymphony.module.propertyset.ejb.PropertyStore#set(java.lang.String,long,int,java.lang.String,java.io.Serializable)}
+    */
+    protected void setImpl(int type, String key, Object value) throws PropertyException {
+        try {
+            store.set(entityName, entityId, type, key, (Serializable) value);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+
+    /**
+    * Proxy to {@link com.opensymphony.module.propertyset.ejb.PropertyStore#get(java.lang.String,long,int,java.lang.String)}
+    */
+    protected Object get(int type, String key) throws PropertyException {
+        try {
+            return store.get(entityName, entityId, type, key);
+        } catch (RemoteException re) {
+            throw new PropertyImplementationException(re);
+        }
+    }
+}

File providers/ejb/src/com/opensymphony/module/propertyset/ejb/PropertyStoreEJB.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.module.propertyset.ejb;
+
+import com.opensymphony.module.propertyset.*;
+import com.opensymphony.module.propertyset.ejb.types.PropertyEntryHomeFactory;
+import com.opensymphony.module.propertyset.ejb.types.PropertyEntryLocal;
+import com.opensymphony.module.propertyset.ejb.types.PropertyEntryLocalHome;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+
+import java.util.*;
+
+import javax.ejb.*;
+
+import javax.naming.NamingException;
+
+
+/**
+ * Session bean implementation of PropertyStore.
+ *
+ * <p>Makes use of ValueEntityDelegator to determine which entity beans to use for
+ * appropriate types.</p>
+ *
+ * @ejb.bean
+ *  type="Stateless"
+ *  name="PropertyStore"
+ *  view-type="remote"
+ *  transaction-type="Container"
+ *
+ * @ejb.ejb-ref
+ *  ejb-name="PropertyEntry"
+ *  view-type="local"
+ *
+ * @ejb.permission unchecked="true"
+ * @ejb.transaction type="Supports"
+ *
+ * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
+ * @author <a href="mailto:hani@formicary.net">Hani Suleiman</a>
+ * @version $Revision$
+ *
+ * @see com.opensymphony.module.propertyset.ejb.PropertyStore
+ * @see com.opensymphony.module.propertyset.ejb.PropertyStoreHome
+ */
+public class PropertyStoreEJB implements SessionBean {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    private static final Log logger = LogFactory.getLog(PropertyStoreEJB.class);
+
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private PropertyEntryLocalHome entryHome;
+
+    /*public void ejbPostCreate() throws CreateException {}*/
+    private SessionContext context;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * @ejb.interface-method
+     */
+    public Collection getKeys(String entityName, long entityId, String prefix, int type) {
+        try {
+            if (logger.isDebugEnabled()) {
+                logger.debug("getKeys(" + entityName + "," + entityId + ")");
+            }
+
+            List results = new ArrayList();
+            Iterator entries = entryHome.findByNameAndId(entityName, entityId).iterator();
+
+            while (entries.hasNext()) {
+                PropertyEntryLocal entry = (PropertyEntryLocal) entries.next();
+                String key = entry.getKey();
+
+                if (((prefix == null) || key.startsWith(prefix)) && ((type == 0) || (type == entry.getType()))) {
+                    results.add(key);
+                }
+            }
+
+            Collections.sort(results);
+
+            return results;
+        } catch (FinderException e) {
+            logger.error("Could not find keys.", e);
+            throw new PropertyImplementationException(e);
+        }
+    }
+
+    public void setSessionContext(SessionContext ctx) {
+        try {
+            entryHome = PropertyEntryHomeFactory.getLocalHome();
+        } catch (NamingException e) {
+            logger.fatal("Could not lookup PropertyEntryHome.", e);
+            throw new EJBException(e);
+        }
+
+        this.context = ctx;
+    }
+
+    /**
+     * @ejb.interface-method
+     */
+    public int getType(String entityName, long entityId, String key) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("getType(" + entityName + "," + entityId + ",\"" + key + "\")");
+        }
+
+        try {
+            return entryHome.findByEntity(entityName, entityId, key).getType();
+        } catch (ObjectNotFoundException e) {
+            return 0;
+        } catch (FinderException e) {
+            logger.error("Could not find type.", e);
+            throw new PropertyImplementationException(e);
+        }
+    }
+
+    public void ejbActivate() {
+    }
+
+    /**
+     * @ejb.create-method
+     */
+    public void ejbCreate() throws CreateException {
+    }
+
+    public void ejbPassivate() {
+    }
+
+    public void ejbRemove() {
+    }
+
+    /**
+     * @ejb.interface-method
+     */
+    public boolean exists(String entityName, long entityId, String key) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("exists(" + entityName + "," + entityId + ",\"" + key + "\")");
+        }
+
+        return getType(entityName, entityId, key) != 0;
+    }
+
+    /**
+     * @ejb.interface-method
+     */
+    public Serializable get(String entityName, long entityId, int type, String key) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("get(" + entityName + "," + entityId + "," + type + ",\"" + key + "\")");
+        }
+
+        try {
+            PropertyEntryLocal entry = entryHome.findByEntity(entityName, entityId, key);
+
+            if (type != entry.getType()) {
+                // type does not match
+                if (logger.isDebugEnabled()) {
+                    logger.debug("wrong property type");
+                }
+
+                throw new InvalidPropertyTypeException();
+            }
+
+            return entry.getValue(); // found it
+        } catch (ObjectNotFoundException e) {
+            // Property does not exist.
+            if (logger.isDebugEnabled()) {
+                logger.debug("no property found");
+            }
+
+            return null;
+        } catch (PropertyException e) {
+            throw e;
+        } catch (Exception e) {
+            logger.error("Could not retrieve value.", e);
+            throw new PropertyImplementationException(e);
+        }
+    }
+
+    /**
+     * @ejb.interface-method
+     * @ejb.transaction type="Required"
+     */
+    public void removeEntry(String entityName, long entityId) {
+        try {
+            Collection items = entryHome.findByNameAndId(entityName, entityId);
+            Iterator iter = items.iterator();
+
+            while (iter.hasNext()) {
+                PropertyEntryLocal entry = (PropertyEntryLocal) iter.next();
+                entry.remove();
+            }
+        } catch (FinderException e) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Value did not exist anyway.");
+            }
+        } catch (PropertyException e) {
+            throw e;
+        } catch (Exception e) {
+            logger.error("Could not remove value.", e);
+            throw new PropertyImplementationException("Could not remove value.", e);
+        }
+    }
+
+    /**
+     * @ejb.interface-method
+     */
+    public void removeEntry(String entityName, long entityId, String key) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("remove(" + entityName + "," + entityId + ",\"" + key + "\")");
+        }
+
+        try {
+            entryHome.findByEntity(entityName, entityId, key).remove();
+        } catch (ObjectNotFoundException e) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Value did not exist anyway.");
+            }
+        } catch (PropertyException e) {
+            throw e;
+        } catch (Exception e) {
+            logger.error("Could not remove value.", e);
+            throw new PropertyImplementationException("Could not remove value.", e);
+        }
+    }
+
+    /**
+     * @ejb.interface-method
+     */
+    public void set(String entityName, long entityId, int type, String key, Serializable value) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("set(" + entityName + "," + entityId + "," + type + ",\"" + key + "\", [" + value + "] )");
+        }
+
+        // If null, remove value.
+        if (value == null) {
+            removeEntry(entityName, entityId, key);
+
+            return;
+        }
+
+        PropertyEntryLocal entry;
+
+        try {
+            entry = entryHome.findByEntity(entityName, entityId, key);
+
+            // if we get here, then a property with that key already exists
+            if (entry.getType() != type) { // verify existing property is of same type
+
+                if (logger.isWarnEnabled()) {
+                    logger.warn("property is of different type");
+                }
+
+                throw new DuplicatePropertyKeyException();
+            }
+        } catch (ObjectNotFoundException e) {
+            // property with that key does not yet exist
+            try {
+                entry = entryHome.create(entityName, entityId, type, key);
+            } catch (CreateException ce) {
+                logger.error("Could not create new property.", ce);
+                throw new PropertyImplementationException("Could not create new property.", ce);
+            }
+        } catch (PropertyException e) {
+            throw e;
+        } catch (Exception e) {
+            logger.error("Could not set property.", e);
+            throw new PropertyImplementationException("Could not set property.", e);
+        }
+
+        entry.setValue(value);
+    }
+}

File providers/ejb/src/com/opensymphony/module/propertyset/ejb/types/.cvsignore

+*CMP.java
+*HomeFactory.java
+*Local*.java

File providers/ejb/src/com/opensymphony/module/propertyset/ejb/types/DataEntityEJB.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.module.propertyset.ejb.types;
+
+import com.opensymphony.module.propertyset.*;
+
+import com.opensymphony.util.Data;
+import com.opensymphony.util.XMLUtils;
+
+import org.w3c.dom.Document;
+
+import java.io.*;
+
+import java.util.Properties;
+
+import javax.ejb.*;
+
+
+/**
+ * AbstractValueEntityEJB concrete implementation optimized for storing binary data.
+ * This can be used to store TEXT, OBJECT, DATA, XML, PROPERTIES or anything else
+ * that cannot be stored elsewhere.
+ *
+ * @ejb.bean
+ *  type="CMP"
+ *  name="PropertyData"
+ *  view-type="local"
+ *  reentrant="False"
+ *  primkey-field="id"
+ *
+ * @ejb.pk class="java.lang.Long" extends="java.lang.Object"
+ *
+ * @ejb.persistence table-name="OS_PROPERTYDATA"
+ * @ejb.permission unchecked="true"
+ * @ejb.transaction type="Supports"
+ *
+ * @author <a href="mailto:hani@formicary.net">Hani Suleiman</a>
+ * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
+ * @version $Revision$
+ *
+ */
+public abstract class DataEntityEJB implements EntityBean {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    /**
+     * Flag to signify no value in a property. Some db's can't store nulls or 0 length fields in LOBs properly.
+     */
+    private static final byte[] NULL_DATA = "hello world".getBytes();
+
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private EntityContext context;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public abstract void setBytes(byte[] data);
+
+    /**
+     * @ejb.persistence column-name="value"
+     */
+    public abstract byte[] getBytes();
+
+    public abstract void setId(Long id);
+
+    /**
+     * @ejb.pk-field
+     * @ejb.interface-method
+     * @ejb.persistence column-name="id"
+     */
+    public abstract Long getId();
+
+    public void setEntityContext(EntityContext context) {
+        this.context = context;
+    }
+
+    /**
+     * Set the value. Depending on the type, different casting serializing will occur.
+     *
+     * <p>For TEXT, value must be java.lang.String.<br>
+     * For OBJECT, value must be Serializable object.<br>
+     * For XML, value must be org.w3c.dom.Document.<br>
+     * For DATA, value must be com.opensymphony.util.Data<br>
+     * For PROPERTIES, value must be java.util.Properties<br>
+     *
+     * @ejb.interface-method
+     */
+    public void setValue(int type, Serializable value) {
+        if (value == null) {
+            setBytes(NULL_DATA);
+
+            return;
+        }
+
+        try {
+            switch (type) {
+            case PropertySet.TEXT:
+                setBytes(((String) value).getBytes());
+
+                break;
+
+            case PropertySet.OBJECT:
+
+                if (!(value instanceof Serializable)) {
+                    throw new IllegalPropertyException("Object not serializable.");
+                }
+
+                setBytes(writeObject(value));
+
+                break;
+
+            case PropertySet.XML:
+                setBytes(writeXML((Document) value));
+
+                break;
+
+            case PropertySet.DATA:
+                setBytes(((Data) value).getBytes());
+
+                break;
+
+            case PropertySet.PROPERTIES:
+                setBytes(writeProperties((Properties) value));
+
+                break;
+
+            default:
+
+                // this should never happen.
+                throw new PropertyImplementationException("Cannot store this type of property.");
+            }
+
+            if (getBytes().length == 0) {
+                setBytes(NULL_DATA); // some db's have problems storing nothing
+            }
+        } catch (ClassCastException ce) {
+            throw new IllegalPropertyException("Cannot cast value to appropriate type for persistence.");
+        }
+    }
+
+    /**
+     * Create appropriate wrapper object around value (String, Object, Document, Data, Properties).
+     *
+     * @ejb.interface-method
+     */
+    public Serializable getValue(int type) {
+        byte[] value = getBytes();
+
+        switch (type) {
+        case PropertySet.TEXT:
+            return new String(value);
+
+        case PropertySet.OBJECT:
+            return (Serializable) readObject(value);
+
+        case PropertySet.XML:
+            return (Serializable) readXML(value);
+
+        case PropertySet.DATA:
+            return new Data(value);
+
+        case PropertySet.PROPERTIES:
+            return readProperties(value);
+
+        default:
+
+            // this should never happen.
+            throw new PropertyImplementationException("Cannot retrieve this type of property.");
+        }
+    }
+
+    public void ejbActivate() {
+    }
+
+    /**
+     * @ejb.create-method
+     */
+    public Long ejbCreate(int type, long id) throws CreateException {
+        setId(new Long(id));
+
+        // the concrete setValue() method shall set value to default (not null).
+        setValue(type, null);
+
+        return null;
+    }
+
+    public void ejbLoad() {
+    }
+
+    public void ejbPassivate() {
+    }
+
+    public void ejbPostCreate(int type, long id) throws CreateException {
+    }
+
+    public void ejbRemove() throws RemoveException {
+    }
+
+    public void ejbStore() {
+    }
+
+    public void unsetEntityContext() {
+        context = null;
+    }
+
+    /**
+     * Returns { PropertySet.TEXT, PropertySet.OBJECT, PropertySet.XML, PropertySet.DATA, PropertySet.PROPERTIES }
+     */
+    protected int[] allowedTypes() {
+        return new int[] {
+            PropertySet.TEXT, PropertySet.OBJECT, PropertySet.XML,
+            PropertySet.DATA, PropertySet.PROPERTIES
+        };
+    }
+
+    /**
+     * DeSerialize an Object from byte array.
+     */
+    private Object readObject(byte[] data) {
+        try {
+            ByteArrayInputStream bytes = new ByteArrayInputStream(data);
+            ObjectInputStream stream = new ObjectInputStream(bytes);
+            Object result = stream.readObject();
+            stream.close();
+
+            return result;
+        } catch (IOException e) {
+            throw new PropertyImplementationException("Cannot deserialize Object", e);
+        } catch (ClassNotFoundException e) {
+            throw new PropertyImplementationException("Class not found for Object", e);
+        }
+    }
+
+    /**
+     * Load java.util.Properties from byte array.
+     */
+    private Properties readProperties(byte[] data) {
+        try {
+            ByteArrayInputStream bytes = new ByteArrayInputStream(data);
+            Properties result = new Properties();
+            result.load(bytes);
+
+            return result;
+        } catch (Exception e) {
+            throw new PropertyImplementationException("Cannot load Properties.", e);
+        }
+    }
+
+    /**
+     * Parse XML document from String in byte array.
+     */