Before migrating to use weaving hooks

git-svn-id: file:///svnroot/rt/org.eclipse.gemini.jpa/tags/Pre-WeavingHooks@112 738bc060-e27f-0410-be59-ab60bd4d2b7a
diff --git a/LICENSE-2.0 b/LICENSE-2.0
new file mode 100644
index 0000000..75b5248
--- /dev/null
+++ b/LICENSE-2.0
@@ -0,0 +1,202 @@
+

+                                 Apache License

+                           Version 2.0, January 2004

+                        http://www.apache.org/licenses/

+

+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+

+   1. Definitions.

+

+      "License" shall mean the terms and conditions for use, reproduction,

+      and distribution as defined by Sections 1 through 9 of this document.

+

+      "Licensor" shall mean the copyright owner or entity authorized by

+      the copyright owner that is granting the License.

+

+      "Legal Entity" shall mean the union of the acting entity and all

+      other entities that control, are controlled by, or are under common

+      control with that entity. For the purposes of this definition,

+      "control" means (i) the power, direct or indirect, to cause the

+      direction or management of such entity, whether by contract or

+      otherwise, or (ii) ownership of fifty percent (50%) or more of the

+      outstanding shares, or (iii) beneficial ownership of such entity.

+

+      "You" (or "Your") shall mean an individual or Legal Entity

+      exercising permissions granted by this License.

+

+      "Source" form shall mean the preferred form for making modifications,

+      including but not limited to software source code, documentation

+      source, and configuration files.

+

+      "Object" form shall mean any form resulting from mechanical

+      transformation or translation of a Source form, including but

+      not limited to compiled object code, generated documentation,

+      and conversions to other media types.

+

+      "Work" shall mean the work of authorship, whether in Source or

+      Object form, made available under the License, as indicated by a

+      copyright notice that is included in or attached to the work

+      (an example is provided in the Appendix below).

+

+      "Derivative Works" shall mean any work, whether in Source or Object

+      form, that is based on (or derived from) the Work and for which the

+      editorial revisions, annotations, elaborations, or other modifications

+      represent, as a whole, an original work of authorship. For the purposes

+      of this License, Derivative Works shall not include works that remain

+      separable from, or merely link (or bind by name) to the interfaces of,

+      the Work and Derivative Works thereof.

+

+      "Contribution" shall mean any work of authorship, including

+      the original version of the Work and any modifications or additions

+      to that Work or Derivative Works thereof, that is intentionally

+      submitted to Licensor for inclusion in the Work by the copyright owner

+      or by an individual or Legal Entity authorized to submit on behalf of

+      the copyright owner. For the purposes of this definition, "submitted"

+      means any form of electronic, verbal, or written communication sent

+      to the Licensor or its representatives, including but not limited to

+      communication on electronic mailing lists, source code control systems,

+      and issue tracking systems that are managed by, or on behalf of, the

+      Licensor for the purpose of discussing and improving the Work, but

+      excluding communication that is conspicuously marked or otherwise

+      designated in writing by the copyright owner as "Not a Contribution."

+

+      "Contributor" shall mean Licensor and any individual or Legal Entity

+      on behalf of whom a Contribution has been received by Licensor and

+      subsequently incorporated within the Work.

+

+   2. Grant of Copyright License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      copyright license to reproduce, prepare Derivative Works of,

+      publicly display, publicly perform, sublicense, and distribute the

+      Work and such Derivative Works in Source or Object form.

+

+   3. Grant of Patent License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      (except as stated in this section) patent license to make, have made,

+      use, offer to sell, sell, import, and otherwise transfer the Work,

+      where such license applies only to those patent claims licensable

+      by such Contributor that are necessarily infringed by their

+      Contribution(s) alone or by combination of their Contribution(s)

+      with the Work to which such Contribution(s) was submitted. If You

+      institute patent litigation against any entity (including a

+      cross-claim or counterclaim in a lawsuit) alleging that the Work

+      or a Contribution incorporated within the Work constitutes direct

+      or contributory patent infringement, then any patent licenses

+      granted to You under this License for that Work shall terminate

+      as of the date such litigation is filed.

+

+   4. Redistribution. You may reproduce and distribute copies of the

+      Work or Derivative Works thereof in any medium, with or without

+      modifications, and in Source or Object form, provided that You

+      meet the following conditions:

+

+      (a) You must give any other recipients of the Work or

+          Derivative Works a copy of this License; and

+

+      (b) You must cause any modified files to carry prominent notices

+          stating that You changed the files; and

+

+      (c) You must retain, in the Source form of any Derivative Works

+          that You distribute, all copyright, patent, trademark, and

+          attribution notices from the Source form of the Work,

+          excluding those notices that do not pertain to any part of

+          the Derivative Works; and

+

+      (d) If the Work includes a "NOTICE" text file as part of its

+          distribution, then any Derivative Works that You distribute must

+          include a readable copy of the attribution notices contained

+          within such NOTICE file, excluding those notices that do not

+          pertain to any part of the Derivative Works, in at least one

+          of the following places: within a NOTICE text file distributed

+          as part of the Derivative Works; within the Source form or

+          documentation, if provided along with the Derivative Works; or,

+          within a display generated by the Derivative Works, if and

+          wherever such third-party notices normally appear. The contents

+          of the NOTICE file are for informational purposes only and

+          do not modify the License. You may add Your own attribution

+          notices within Derivative Works that You distribute, alongside

+          or as an addendum to the NOTICE text from the Work, provided

+          that such additional attribution notices cannot be construed

+          as modifying the License.

+

+      You may add Your own copyright statement to Your modifications and

+      may provide additional or different license terms and conditions

+      for use, reproduction, or distribution of Your modifications, or

+      for any such Derivative Works as a whole, provided Your use,

+      reproduction, and distribution of the Work otherwise complies with

+      the conditions stated in this License.

+

+   5. Submission of Contributions. Unless You explicitly state otherwise,

+      any Contribution intentionally submitted for inclusion in the Work

+      by You to the Licensor shall be under the terms and conditions of

+      this License, without any additional terms or conditions.

+      Notwithstanding the above, nothing herein shall supersede or modify

+      the terms of any separate license agreement you may have executed

+      with Licensor regarding such Contributions.

+

+   6. Trademarks. This License does not grant permission to use the trade

+      names, trademarks, service marks, or product names of the Licensor,

+      except as required for reasonable and customary use in describing the

+      origin of the Work and reproducing the content of the NOTICE file.

+

+   7. Disclaimer of Warranty. Unless required by applicable law or

+      agreed to in writing, Licensor provides the Work (and each

+      Contributor provides its Contributions) on an "AS IS" BASIS,

+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

+      implied, including, without limitation, any warranties or conditions

+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A

+      PARTICULAR PURPOSE. You are solely responsible for determining the

+      appropriateness of using or redistributing the Work and assume any

+      risks associated with Your exercise of permissions under this License.

+

+   8. Limitation of Liability. In no event and under no legal theory,

+      whether in tort (including negligence), contract, or otherwise,

+      unless required by applicable law (such as deliberate and grossly

+      negligent acts) or agreed to in writing, shall any Contributor be

+      liable to You for damages, including any direct, indirect, special,

+      incidental, or consequential damages of any character arising as a

+      result of this License or out of the use or inability to use the

+      Work (including but not limited to damages for loss of goodwill,

+      work stoppage, computer failure or malfunction, or any and all

+      other commercial damages or losses), even if such Contributor

+      has been advised of the possibility of such damages.

+

+   9. Accepting Warranty or Additional Liability. While redistributing

+      the Work or Derivative Works thereof, You may choose to offer,

+      and charge a fee for, acceptance of support, warranty, indemnity,

+      or other liability obligations and/or rights consistent with this

+      License. However, in accepting such obligations, You may act only

+      on Your own behalf and on Your sole responsibility, not on behalf

+      of any other Contributor, and only if You agree to indemnify,

+      defend, and hold each Contributor harmless for any liability

+      incurred by, or claims asserted against, such Contributor by reason

+      of your accepting any such warranty or additional liability.

+

+   END OF TERMS AND CONDITIONS

+

+   APPENDIX: How to apply the Apache License to your work.

+

+      To apply the Apache License to your work, attach the following

+      boilerplate notice, with the fields enclosed by brackets "[]"

+      replaced with your own identifying information. (Don't include

+      the brackets!)  The text should be enclosed in the appropriate

+      comment syntax for the file format. We also recommend that a

+      file or class name and description of purpose be included on the

+      same "printed page" as the copyright notice for easier

+      identification within third-party archives.

+

+   Copyright [yyyy] [name of copyright owner]

+

+   Licensed under the Apache License, Version 2.0 (the "License");

+   you may not use this file except in compliance with the License.

+   You may obtain a copy of the License at

+

+       http://www.apache.org/licenses/LICENSE-2.0

+

+   Unless required by applicable law or agreed to in writing, software

+   distributed under the License is distributed on an "AS IS" BASIS,

+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+   See the License for the specific language governing permissions and

+   limitations under the License.

diff --git a/about.html b/about.html
new file mode 100644
index 0000000..2e352c6
--- /dev/null
+++ b/about.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 

+<html xmlns="http://www.w3.org/1999/xhtml"> 

+<head> 

+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> 

+<title>Gemini JPA 1.0.0</title> 

+</head>

+

+<body lang="EN-US"> 

+<h2>About</h2> 

+<p>April 20, 2010</p> 

+

+<p>

+The Gemini JPA project provides the supporting code for a JPA provider to be compliant with the OSGi JPA specification. It uses EclipseLink JPA as the provider.

+</p>

+

+<h3>License </h3>

+<p>

+The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 (&quot;EPL&quot;) and Apache Software License Version 2.0 (&quot;ASL&quot;). A copy of the EPL is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> and a copy of the ASL is available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>. For purposes of the EPL, &quot;Program&quot; will mean the Content.

+</p>

+<p>

+If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may apply to your use of any object code in the Content. Check the Redistributor&#8217;s license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise indicated below, the terms and conditions of the EPL and ASL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.

+</p>

+

+<h3>Third Party Content</h3>

+

+<p>

+The Content includes items that have been developed by third parties as set out below. If you did not receive this Content directly from the Eclipse Foundation, the following is provided for informational purposes only, and you should look to the Redistributor&#8217;s license for terms and conditions of use.

+</p>

+

+<h3><a name="JPA2" id="JPA2"></a>Java Persistence API (JPA) 2.0.  </h3>

+<p>

+The Java Persistence API (JPA) v2.0 was created in the JCP in <a href="http://jcp.org/en/jsr/detail?id=317">JSR 317</a>  

+and defined the APIs that are used by Gemini JPA and by EclipseLink. This copy of the specification APIs is licensed

+under the <a href="http://www.eclipse.org/legal/epl-v10.html">EPL</a> and 

+<a href="http://www.apache.org/licenses/LICENSE-2.0">ASL</a> licenses.

+</p>

+

+</body>

+</html>

+

diff --git a/doc/GettingStarted.txt b/doc/GettingStarted.txt
new file mode 100644
index 0000000..cd5f90b
--- /dev/null
+++ b/doc/GettingStarted.txt
@@ -0,0 +1,62 @@
+Getting Started with Gemini JPA 

+--------------------------------

+

+Gemini JPA implements the OSGi JPA specification. It uses EclipseLink as the JPA provider.

+

+Required Bundles

+----------------

+

+To run Gemini JPA in OSGi you will need to have the following:

+

+1. OSGi Framework

+

+You may use Plugin Development Environment (PDE) in Eclipse, which will give you access to the Equinox 

+OSGi framework, or you may execute outside of PDE and use any OSGi framework you choose, such as Felix. 

+Weaving of entity classes is only supported on Equinox, but weaving is strictly an optimization and is 

+not needed for JPA operation. 

+

+2. OSGi Enterprise API bundle

+

+Gemini JPA implements some of the OSGi Enterprise APIs so the osgi.enterprise bundle must be resident. 

+The bundle includes both the source and the class files so it can be used for both execution and debugging.

+The Gemini DBAccess download includes this bundle as part of its download, or it can be downloaded from

+the OSGi web site: http://www.osgi.org/Download/Release4V42

+

+3. Gemini DBAccess (Optional)

+

+While you don't strictly require Gemini DBAccess to run Gemini JPA it renders your application much more

+modular and easier to configure. The actual bundles you need depend upon the database you are using. 

+If DBAccess is not present you will need to make the JDBC driver accessible to the EclipseLink bundles.

+

+4. EclipseLink bundles

+

+The following EclipseLink bundles are required:

+

+- javax.persistence

+- org.eclipse.persistence.asm

+- org.eclipse.persistence.antlr

+- org.eclipse.persistence.core

+- org.eclipse.persistence.jpa

+

+These bundles must be version 2.2 or greater. 

+

+Note: You do *NOT* need the org.eclipse.persistence.jpa.osgi bundle shipped with EclipseLink.

+

+5. Gemini JPA bundles

+

+There are two bundles shipped with Gemini JPA:

+

+- org.elipse.gemini.jpa - the Gemini supporting classes

+- org.elipse.gemini.jpa.weaving - weaving support for Gemini JPA

+

+Installation

+------------

+

+If running in PDE then unzip the files into a directory and import the various projects into your workspace.

+If you are using the framework directly then unzip the distribution files and follow the documentation of

+the framework for installing the bundles in the order specified above.

+

+Configuration

+-------------

+

+Gemini JPA does not require any special environment configuration.

diff --git a/doc/Readme.txt b/doc/Readme.txt
new file mode 100644
index 0000000..76380d0
--- /dev/null
+++ b/doc/Readme.txt
@@ -0,0 +1,9 @@
+

+Gemini JPA 1.0.0 RC1 - Mar 25, 2011

+

+This milestone provides you with access to the EclipseLink JPA 2.0 provider. The 

+EclipseLink 2.2 or later bundles must be used.

+

+For an example of how to access an EntityManager from an OSGi program run the sample program.

+To do this, install and start the bundles described in "Getting Started.txt" included in this 

+distribution. Additionally, install the org.eclipse.gemini.jpa.samples bundle.

diff --git a/epl-v10.html b/epl-v10.html
new file mode 100644
index 0000000..c36d29b
--- /dev/null
+++ b/epl-v10.html
@@ -0,0 +1,260 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

+<html xmlns="http://www.w3.org/1999/xhtml"> 

+ 

+<head> 

+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> 

+<title>Eclipse Public License - Version 1.0</title> 

+<style type="text/css"> 

+  body {

+    size: 8.5in 11.0in;

+    margin: 0.25in 0.5in 0.25in 0.5in;

+    tab-interval: 0.5in;

+    }

+  p {  	

+    margin-left: auto;

+    margin-top:  0.5em;

+    margin-bottom: 0.5em;

+    }

+  p.list {

+  	margin-left: 0.5in;

+    margin-top:  0.05em;

+    margin-bottom: 0.05em;

+    }

+  </style> 

+ 

+</head> 

+ 

+<body lang="EN-US"> 

+ 

+<h2>Eclipse Public License - v 1.0</h2> 

+ 

+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE

+PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR

+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS

+AGREEMENT.</p> 

+ 

+<p><b>1. DEFINITIONS</b></p> 

+ 

+<p>&quot;Contribution&quot; means:</p> 

+ 

+<p class="list">a) in the case of the initial Contributor, the initial

+code and documentation distributed under this Agreement, and</p> 

+<p class="list">b) in the case of each subsequent Contributor:</p> 

+<p class="list">i) changes to the Program, and</p> 

+<p class="list">ii) additions to the Program;</p> 

+<p class="list">where such changes and/or additions to the Program

+originate from and are distributed by that particular Contributor. A

+Contribution 'originates' from a Contributor if it was added to the

+Program by such Contributor itself or anyone acting on such

+Contributor's behalf. Contributions do not include additions to the

+Program which: (i) are separate modules of software distributed in

+conjunction with the Program under their own license agreement, and (ii)

+are not derivative works of the Program.</p> 

+ 

+<p>&quot;Contributor&quot; means any person or entity that distributes

+the Program.</p> 

+ 

+<p>&quot;Licensed Patents&quot; mean patent claims licensable by a

+Contributor which are necessarily infringed by the use or sale of its

+Contribution alone or when combined with the Program.</p> 

+ 

+<p>&quot;Program&quot; means the Contributions distributed in accordance

+with this Agreement.</p> 

+ 

+<p>&quot;Recipient&quot; means anyone who receives the Program under

+this Agreement, including all Contributors.</p> 

+ 

+<p><b>2. GRANT OF RIGHTS</b></p> 

+ 

+<p class="list">a) Subject to the terms of this Agreement, each

+Contributor hereby grants Recipient a non-exclusive, worldwide,

+royalty-free copyright license to reproduce, prepare derivative works

+of, publicly display, publicly perform, distribute and sublicense the

+Contribution of such Contributor, if any, and such derivative works, in

+source code and object code form.</p> 

+ 

+<p class="list">b) Subject to the terms of this Agreement, each

+Contributor hereby grants Recipient a non-exclusive, worldwide,

+royalty-free patent license under Licensed Patents to make, use, sell,

+offer to sell, import and otherwise transfer the Contribution of such

+Contributor, if any, in source code and object code form. This patent

+license shall apply to the combination of the Contribution and the

+Program if, at the time the Contribution is added by the Contributor,

+such addition of the Contribution causes such combination to be covered

+by the Licensed Patents. The patent license shall not apply to any other

+combinations which include the Contribution. No hardware per se is

+licensed hereunder.</p> 

+ 

+<p class="list">c) Recipient understands that although each Contributor

+grants the licenses to its Contributions set forth herein, no assurances

+are provided by any Contributor that the Program does not infringe the

+patent or other intellectual property rights of any other entity. Each

+Contributor disclaims any liability to Recipient for claims brought by

+any other entity based on infringement of intellectual property rights

+or otherwise. As a condition to exercising the rights and licenses

+granted hereunder, each Recipient hereby assumes sole responsibility to

+secure any other intellectual property rights needed, if any. For

+example, if a third party patent license is required to allow Recipient

+to distribute the Program, it is Recipient's responsibility to acquire

+that license before distributing the Program.</p> 

+ 

+<p class="list">d) Each Contributor represents that to its knowledge it

+has sufficient copyright rights in its Contribution, if any, to grant

+the copyright license set forth in this Agreement.</p> 

+ 

+<p><b>3. REQUIREMENTS</b></p> 

+ 

+<p>A Contributor may choose to distribute the Program in object code

+form under its own license agreement, provided that:</p> 

+ 

+<p class="list">a) it complies with the terms and conditions of this

+Agreement; and</p> 

+ 

+<p class="list">b) its license agreement:</p> 

+ 

+<p class="list">i) effectively disclaims on behalf of all Contributors

+all warranties and conditions, express and implied, including warranties

+or conditions of title and non-infringement, and implied warranties or

+conditions of merchantability and fitness for a particular purpose;</p> 

+ 

+<p class="list">ii) effectively excludes on behalf of all Contributors

+all liability for damages, including direct, indirect, special,

+incidental and consequential damages, such as lost profits;</p> 

+ 

+<p class="list">iii) states that any provisions which differ from this

+Agreement are offered by that Contributor alone and not by any other

+party; and</p> 

+ 

+<p class="list">iv) states that source code for the Program is available

+from such Contributor, and informs licensees how to obtain it in a

+reasonable manner on or through a medium customarily used for software

+exchange.</p> 

+ 

+<p>When the Program is made available in source code form:</p> 

+ 

+<p class="list">a) it must be made available under this Agreement; and</p> 

+ 

+<p class="list">b) a copy of this Agreement must be included with each

+copy of the Program.</p> 

+ 

+<p>Contributors may not remove or alter any copyright notices contained

+within the Program.</p> 

+ 

+<p>Each Contributor must identify itself as the originator of its

+Contribution, if any, in a manner that reasonably allows subsequent

+Recipients to identify the originator of the Contribution.</p> 

+ 

+<p><b>4. COMMERCIAL DISTRIBUTION</b></p> 

+ 

+<p>Commercial distributors of software may accept certain

+responsibilities with respect to end users, business partners and the

+like. While this license is intended to facilitate the commercial use of

+the Program, the Contributor who includes the Program in a commercial

+product offering should do so in a manner which does not create

+potential liability for other Contributors. Therefore, if a Contributor

+includes the Program in a commercial product offering, such Contributor

+(&quot;Commercial Contributor&quot;) hereby agrees to defend and

+indemnify every other Contributor (&quot;Indemnified Contributor&quot;)

+against any losses, damages and costs (collectively &quot;Losses&quot;)

+arising from claims, lawsuits and other legal actions brought by a third

+party against the Indemnified Contributor to the extent caused by the

+acts or omissions of such Commercial Contributor in connection with its

+distribution of the Program in a commercial product offering. The

+obligations in this section do not apply to any claims or Losses

+relating to any actual or alleged intellectual property infringement. In

+order to qualify, an Indemnified Contributor must: a) promptly notify

+the Commercial Contributor in writing of such claim, and b) allow the

+Commercial Contributor to control, and cooperate with the Commercial

+Contributor in, the defense and any related settlement negotiations. The

+Indemnified Contributor may participate in any such claim at its own

+expense.</p> 

+ 

+<p>For example, a Contributor might include the Program in a commercial

+product offering, Product X. That Contributor is then a Commercial

+Contributor. If that Commercial Contributor then makes performance

+claims, or offers warranties related to Product X, those performance

+claims and warranties are such Commercial Contributor's responsibility

+alone. Under this section, the Commercial Contributor would have to

+defend claims against the other Contributors related to those

+performance claims and warranties, and if a court requires any other

+Contributor to pay any damages as a result, the Commercial Contributor

+must pay those damages.</p> 

+ 

+<p><b>5. NO WARRANTY</b></p> 

+ 

+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS

+PROVIDED ON AN &quot;AS IS&quot; 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. Each Recipient is solely

+responsible for determining the appropriateness of using and

+distributing the Program and assumes all risks associated with its

+exercise of rights under this Agreement , including but not limited to

+the risks and costs of program errors, compliance with applicable laws,

+damage to or loss of data, programs or equipment, and unavailability or

+interruption of operations.</p> 

+ 

+<p><b>6. DISCLAIMER OF LIABILITY</b></p> 

+ 

+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT

+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,

+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING

+WITHOUT LIMITATION LOST PROFITS), 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 OR

+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED

+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p> 

+ 

+<p><b>7. GENERAL</b></p> 

+ 

+<p>If any provision of this Agreement is invalid or unenforceable under

+applicable law, it shall not affect the validity or enforceability of

+the remainder of the terms of this Agreement, and without further action

+by the parties hereto, such provision shall be reformed to the minimum

+extent necessary to make such provision valid and enforceable.</p> 

+ 

+<p>If Recipient institutes patent litigation against any entity

+(including a cross-claim or counterclaim in a lawsuit) alleging that the

+Program itself (excluding combinations of the Program with other

+software or hardware) infringes such Recipient's patent(s), then such

+Recipient's rights granted under Section 2(b) shall terminate as of the

+date such litigation is filed.</p> 

+ 

+<p>All Recipient's rights under this Agreement shall terminate if it

+fails to comply with any of the material terms or conditions of this

+Agreement and does not cure such failure in a reasonable period of time

+after becoming aware of such noncompliance. If all Recipient's rights

+under this Agreement terminate, Recipient agrees to cease use and

+distribution of the Program as soon as reasonably practicable. However,

+Recipient's obligations under this Agreement and any licenses granted by

+Recipient relating to the Program shall continue and survive.</p> 

+ 

+<p>Everyone is permitted to copy and distribute copies of this

+Agreement, but in order to avoid inconsistency the Agreement is

+copyrighted and may only be modified in the following manner. The

+Agreement Steward reserves the right to publish new versions (including

+revisions) of this Agreement from time to time. No one other than the

+Agreement Steward has the right to modify this Agreement. The Eclipse

+Foundation is the initial Agreement Steward. The Eclipse Foundation may

+assign the responsibility to serve as the Agreement Steward to a

+suitable separate entity. Each new version of the Agreement will be

+given a distinguishing version number. The Program (including

+Contributions) may always be distributed subject to the version of the

+Agreement under which it was received. In addition, after a new version

+of the Agreement is published, Contributor may elect to distribute the

+Program (including its Contributions) under the new version. Except as

+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no

+rights or licenses to the intellectual property of any Contributor under

+this Agreement, whether expressly, by implication, estoppel or

+otherwise. All rights in the Program not expressly granted under this

+Agreement are reserved.</p> 

+ 

+<p>This Agreement is governed by the laws of the State of New York and

+the intellectual property laws of the United States of America. No party

+to this Agreement will bring a legal action under this Agreement more

+than one year after the cause of action arose. Each party waives its

+rights to a jury trial in any resulting litigation.</p> 

+ 

+</body> 

+ 

+</html>
\ No newline at end of file
diff --git a/notice.html b/notice.html
new file mode 100644
index 0000000..fe1ce99
--- /dev/null
+++ b/notice.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 

+<html xmlns="http://www.w3.org/1999/xhtml"> 

+<head> 

+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> 

+<title>Eclipse Foundation Software User Agreement</title> 

+</head> 

+ 

+<body lang="EN-US"> 

+<h2>Eclipse Foundation Software User Agreement</h2> 

+<p>April 14, 2010</p> 

+ 

+<h3>Usage Of Content</h3> 

+ 

+<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS

+   (COLLECTIVELY &quot;CONTENT&quot;).  USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND

+   CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW.  BY USING THE CONTENT, YOU AGREE THAT YOUR USE

+   OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR

+   NOTICES INDICATED OR REFERENCED BELOW.  IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND

+   CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p> 

+ 

+<h3>Applicable Licenses</h3> 

+ 

+<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 (&quot;EPL&quot;) and Apache Software License Version 2.0 (&quot;ASL&quot;). A copy of the EPL is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> and a copy of the ASL is available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>. For purposes of the EPL, &quot;Program&quot; will mean the Content.</p> 

+ 

+<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code repository (&quot;Repository&quot;) in software modules (&quot;Modules&quot;) and made available as downloadable archives (&quot;Downloads&quot;).</p> 

+ 

+<ul> 

+       <li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content.  Typical modules may include plug-ins (&quot;Plug-ins&quot;), plug-in fragments (&quot;Fragments&quot;), and features (&quot;Features&quot;).</li> 

+       <li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java&trade; ARchive) in a directory named &quot;plugins&quot;.</li> 

+       <li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.  Each Feature may be packaged as a sub-directory in a directory named &quot;features&quot;.  Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of the Plug-ins

+      and/or Fragments associated with that Feature.</li> 

+       <li>Features may also include other Features (&quot;Included Features&quot;). Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of Included Features.</li> 

+</ul> 

+ 

+<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named &quot;about.html&quot; (&quot;Abouts&quot;). The terms and conditions governing Features and

+Included Features should be contained in files named &quot;license.html&quot; (&quot;Feature Licenses&quot;).  Abouts and Feature Licenses may be located in any directory of a Download or Module

+including, but not limited to the following locations:</p> 

+ 

+<ul> 

+       <li>The top-level (root) directory</li> 

+       <li>Plug-in and Fragment directories</li> 

+       <li>Inside Plug-ins and Fragments packaged as JARs</li> 

+       <li>Sub-directories of the directory named &quot;src&quot; of certain Plug-ins</li> 

+       <li>Feature directories</li> 

+</ul> 

+ 

+<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license (&quot;Feature Update License&quot;) during the

+installation process.  If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or

+inform you where you can locate them.  Feature Update Licenses may be found in the &quot;license&quot; property of files named &quot;feature.properties&quot; found within a Feature.

+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in

+that directory.</p> 

+ 

+<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.  SOME OF THESE

+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p> 

+ 

+<ul> 

+       <li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li> 

+       <li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li> 

+       <li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li> 

+       <li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li> 

+       <li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li> 

+</ul> 

+ 

+<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT.  If no About, Feature License, or Feature Update License is provided, please

+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p> 

+ 

+ 

+<h3>Use of Provisioning Technology</h3> 

+ 

+<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse

+   Update Manager (&quot;Provisioning Technology&quot;) for the purpose of allowing users to install software, documentation, information and/or

+   other materials (collectively &quot;Installable Software&quot;). This capability is provided with the intent of allowing such users to

+   install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a 

+       href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a> 

+   (&quot;Specification&quot;).</p> 

+ 

+<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the

+   applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology

+   in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the

+   Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p> 

+ 

+<ol> 

+       <li>A series of actions may occur (&quot;Provisioning Process&quot;) in which a user may execute the Provisioning Technology

+       on a machine (&quot;Target Machine&quot;) with the intent of installing, extending or updating the functionality of an Eclipse-based

+       product.</li> 

+       <li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be

+       accessed and copied to the Target Machine.</li> 

+       <li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable

+       Software (&quot;Installable Software Agreement&quot;) and such Installable Software Agreement shall be accessed from the Target

+       Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern

+       the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such

+       indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li> 

+</ol> 

+ 

+<h3>Cryptography</h3> 

+ 

+<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to

+   another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,

+   possession, or use, and re-export of encryption software, to see if this is permitted.</p> 

+ 

+<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p> 

+</body> 

+</html> 
\ No newline at end of file
diff --git a/org.eclipse.gemini.javax.persistence/.classpath b/org.eclipse.gemini.javax.persistence/.classpath
new file mode 100644
index 0000000..da97c9e
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry exported="true" kind="lib" path="lib/javax.persistence_2.0.0.v201005141517.jar"/>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.javax.persistence/.project b/org.eclipse.gemini.javax.persistence/.project
new file mode 100644
index 0000000..3d7cdab
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.javax.persistence</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cc5f5ea
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri May 28 16:51:52 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..3f17d8c
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu May 13 20:28:15 EDT 2010

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.javax.persistence/META-INF/MANIFEST.MF b/org.eclipse.gemini.javax.persistence/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..0dffc6a
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Gemini javax.persistence (Incubation)
+Bundle-SymbolicName: org.eclipse.gemini.javax.persistence
+Bundle-Version: 2.0.0.M2-incubation
+Bundle-ClassPath: .,lib/javax.persistence_2.0.0.v201005141517.jar
+Bundle-Activator: org.eclipse.gemini.jpa.spec.Activator
+Bundle-Vendor: Oracle
+Export-Package: javax.persistence;uses="javax.persistence.spi,javax.persistence.criteria,javax.persistence.metamodel";version="1.1.0";jpa="2.0";spec="2.0",
+ javax.persistence.criteria;uses="javax.persistence";version="1.1.0";jpa="2.0";spec="2.0",
+ javax.persistence.metamodel;uses="javax.persistence";version="1.1.0";jpa="2.0";spec="2.0",
+ javax.persistence.spi;uses="javax.persistence";version="1.1.0";jpa="2.0";spec="2.0",
+ org.osgi.service.jpa;version="1.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.osgi.framework;version="1.3.0"
diff --git a/org.eclipse.gemini.javax.persistence/build.properties b/org.eclipse.gemini.javax.persistence/build.properties
new file mode 100644
index 0000000..21e1f54
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/build.properties
@@ -0,0 +1,5 @@
+source.. = src/

+output.. = classes/

+bin.includes = META-INF/,\

+               .,\

+               lib/javax.persistence_2.0.0.*.jar

diff --git a/org.eclipse.gemini.javax.persistence/lib/javax.persistence.source_2.0.0.v201005141517.jar b/org.eclipse.gemini.javax.persistence/lib/javax.persistence.source_2.0.0.v201005141517.jar
new file mode 100644
index 0000000..fe5337c
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/lib/javax.persistence.source_2.0.0.v201005141517.jar
Binary files differ
diff --git a/org.eclipse.gemini.javax.persistence/lib/javax.persistence_2.0.0.v201005141517.jar b/org.eclipse.gemini.javax.persistence/lib/javax.persistence_2.0.0.v201005141517.jar
new file mode 100644
index 0000000..dc968bf
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/lib/javax.persistence_2.0.0.v201005141517.jar
Binary files differ
diff --git a/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/Activator.java b/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/Activator.java
new file mode 100644
index 0000000..8b1bdf2
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/Activator.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright (C) 2010 Oracle Corporation

+ *

+ * 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.eclipse.gemini.jpa.spec;

+

+import javax.persistence.spi.PersistenceProviderResolverHolder;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Activator class for OSGi integration

+ * 

+ * @author mkeith, tware

+ */

+public class Activator implements BundleActivator {

+

+    public void start(BundleContext context) throws Exception {

+        PersistenceProviderResolverHolder.setPersistenceProviderResolver(new OSGiProviderResolver(context));

+        System.out.println("Gemini javax.persistence active");

+    }

+

+    public void stop(BundleContext context) throws Exception {

+        PersistenceProviderResolverHolder.setPersistenceProviderResolver(null);

+    }

+}

diff --git a/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/OSGiProviderResolver.java b/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/OSGiProviderResolver.java
new file mode 100644
index 0000000..8e38e85
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/src/org/eclipse/gemini/jpa/spec/OSGiProviderResolver.java
@@ -0,0 +1,218 @@
+/*

+ * Copyright (C) 2010 Oracle Corporation

+ *

+ * 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.eclipse.gemini.jpa.spec;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.PersistenceException;

+

+import javax.persistence.spi.LoadState;

+import javax.persistence.spi.PersistenceProvider;

+import javax.persistence.spi.PersistenceProviderResolver;

+import javax.persistence.spi.PersistenceUnitInfo;

+import javax.persistence.spi.ProviderUtil;

+

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+/**

+ * This class acts as the ProviderResolver as well as the 

+ * PersistenceProvider to the Persistence class.

+ * 

+ * @author mkeith

+ */

+@SuppressWarnings("unchecked")

+public class OSGiProviderResolver implements PersistenceProviderResolver, 

+                                             PersistenceProvider {

+    BundleContext ctx;

+    

+    public OSGiProviderResolver(BundleContext ctx) {

+        this.ctx = ctx;

+    }

+    

+    /*=====================================*/

+    /* PersistenceProviderResolver methods */

+    /*=====================================*/

+

+    /**

+     * ProviderResolver API method to get all of the providers. We will use 

+     * this to hook ourselves in through the Persistence class so we can intercept 

+     * all of the provider calls and redirect them to use OSGi services.

+     */

+    public List<PersistenceProvider> getPersistenceProviders() {

+

+        List<PersistenceProvider> listOfOurself = new ArrayList<PersistenceProvider>();

+        listOfOurself.add(this);

+        return listOfOurself;

+    }

+

+    // Do nothing since we aren't caching anything

+    public void clearCachedProviders () {}

+    

+    /*=============================*/

+    /* PersistenceProvider methods */

+    /*=============================*/

+    

+    public EntityManagerFactory createEntityManagerFactory(String unitName, Map props) {

+        return (((props == null) || (props.isEmpty())))

+            ? lookupEMF(unitName)

+            : lookupEMFBuilder(unitName, props);

+    }

+

+    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map props) {

+        // Not supported through the Persistence class

+        return null;

+    }

+    

+    public ProviderUtil getProviderUtil() {

+        // Now it gets interesting, because we need to return something that looks and smells like 

+        // a ProviderUtil, but that does the job of the Persistence class cycling through the providers

+        return new ForwardingProviderUtil();

+    }

+

+    /*==========================*/

+    /* ProviderUtil inner class */

+    /*==========================*/

+    

+    public class ForwardingProviderUtil implements ProviderUtil {

+

+        public LoadState isLoadedWithReference(Object entity, String attributeName) {

+            Collection<PersistenceProvider> providers = lookupProviders();

+            for (PersistenceProvider provider : providers) {

+                LoadState loadstate = provider.getProviderUtil().isLoadedWithReference(entity, attributeName);

+                if (loadstate != LoadState.UNKNOWN) {

+                    return loadstate;

+                } // else keep looking

+            }

+            // None of the providers knew, so the load state is unknown

+            return LoadState.UNKNOWN;

+        }

+

+        public LoadState isLoadedWithoutReference(Object entity, String attributeName) {

+            Collection<PersistenceProvider> providers = lookupProviders();

+            for (PersistenceProvider provider : providers) {

+                LoadState loadstate = provider.getProviderUtil().isLoadedWithoutReference(entity, attributeName);

+                if (loadstate != LoadState.UNKNOWN) {

+                    return loadstate;

+                } // else keep looking

+            }

+            // None of the providers knew, so the load state is unknown

+            return LoadState.UNKNOWN;

+        }

+

+        public LoadState isLoaded(Object entity) {

+            Collection<PersistenceProvider> providers = lookupProviders();

+            for (PersistenceProvider provider : providers) {

+                LoadState loadstate = provider.getProviderUtil().isLoaded(entity);

+                if (loadstate != LoadState.UNKNOWN) {

+                    return loadstate;

+                } // else keep looking

+            }

+            // None of the providers knew, so the load state is unknown

+            return LoadState.UNKNOWN;

+        }

+    }

+

+    /*================*/

+    /* Helper Methods */

+    /*================*/

+    

+    /**

+     * Look up the Entity Manager Factory service based on the unit name.

+     */

+    public EntityManagerFactory lookupEMF(String unitName) {

+

+        debug("Persistence class - lookupEMF, punit=", unitName);

+        String filter = null;

+        ServiceReference[] refs = null;        

+        try {

+            filter = "(osgi.unit.name="+ unitName +")";

+            refs = ctx.getServiceReferences(EntityManagerFactory.class.getName(), filter);

+        } catch (InvalidSyntaxException isEx) {

+            new PersistenceException("Implementation error - incorrect filter specified while looking up EMF", isEx);

+        }

+        if ((refs != null) && (refs.length != 0)) {

+            debug("Persistence class - lookupEMF, found service ", unitName, " in registry");

+            // Take the first one

+            return (EntityManagerFactory)ctx.getService(refs[0]);

+        }

+        // Didn't find any EMF service under the given name

+        debug("Persistence class - lookupEMF, *** EMF service ", unitName, " not found in registry ***");

+        return null; 

+    }

+

+    /**

+     * Look up the Entity Manager Factory Builder service based on the unit name. 

+     *

+     * @return The EntityManagerFactory object that was looked up (or created from the 

+     *         EntityManagerFactoryBuilder)

+     */

+    public EntityManagerFactory lookupEMFBuilder(String unitName, Map<?,?> props) {

+

+        debug("Persistence class - lookupEMFBuilder, punit=", unitName);

+        String filter = null;

+        ServiceReference[] refs = null;        

+        try {

+            filter = "(osgi.unit.name="+ unitName +")";

+            refs = ctx.getServiceReferences(EntityManagerFactoryBuilder.class.getName(), filter);

+        } catch (InvalidSyntaxException isEx) {

+            new PersistenceException("Implementation error - incorrect filter specified while looking up EMF", isEx);

+        }

+        if ((refs != null) && (refs.length != 0)) {

+            debug("Persistence class - lookupEMFBuilder, found service ", unitName, " in registry");

+            // Take the first one and create an EMF from it

+            EntityManagerFactoryBuilder builder = (EntityManagerFactoryBuilder)ctx.getService(refs[0]);

+            return builder.createEntityManagerFactory(props);

+        }

+        // Didn't find anything under the given name

+        debug("Persistence class - lookupEMFBuilder, *** EMFBuilder service ", unitName, 

+              " not found in registry ***");

+        return null; 

+    }

+    

+    public Collection<PersistenceProvider> lookupProviders() {

+

+        debug("Persistence class - lookupProviders");

+        Collection<PersistenceProvider> providers = new HashSet<PersistenceProvider>();

+        

+        ServiceReference[] refs = null;

+        try { refs = ctx.getServiceReferences(PersistenceProvider.class.getName(), null); }

+        catch (InvalidSyntaxException invEx) {} // Can't happen since filter is null

+        

+        if (refs != null) {

+            for (ServiceReference ref : refs) {

+                providers.add((PersistenceProvider)ctx.getService(ref));

+            }

+        }

+        return providers;

+    }

+

+    protected void debug(String... msgs) {

+        if (System.getProperty("JPA_DEBUG") != null) {

+            StringBuilder sb = new StringBuilder();

+            for (String msg : msgs) sb.append(msg);

+            System.out.println(sb.toString()); 

+        }

+    }

+}

diff --git a/org.eclipse.gemini.javax.persistence/src/org/osgi/service/jpa/EntityManagerFactoryBuilder.java b/org.eclipse.gemini.javax.persistence/src/org/osgi/service/jpa/EntityManagerFactoryBuilder.java
new file mode 100644
index 0000000..028f2a8
--- /dev/null
+++ b/org.eclipse.gemini.javax.persistence/src/org/osgi/service/jpa/EntityManagerFactoryBuilder.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright (c) OSGi Alliance (2009). All Rights Reserved.

+ * 

+ * 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.osgi.service.jpa;

+

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+

+/**

+ * This service interface offers JPA clients the ability to create instances of

+ * EntityManagerFactory for a given named persistence unit. A service instance  

+ * will be created for each named persistence unit and can be filtered on by 

+ * comparing the value of the osgi.unit.name property containing the persistence 

+ * unit name. 

+ * 

+ * This service is used specifically when the caller wants to pass in factory-scoped

+ * properties as arguments. If no properties are being used in the creation of the 

+ * EntityManagerFactory then the basic EntityManagerFactory service should be used.

+ */

+public interface EntityManagerFactoryBuilder {

+

+    /**

+     * The name of the persistence unit.

+     */

+    public static final String JPA_UNIT_NAME = "osgi.unit.name";

+

+    /**

+     * The version of the persistence unit bundle.

+     */

+    public static final String JPA_UNIT_VERSION = "osgi.unit.version";

+

+    /**

+     * The class name of the provider that registered the service and implements 

+     * the JPA javax.persistence.PersistenceProvider interface.

+     */

+    public static final String JPA_UNIT_PROVIDER = "osgi.unit.provider";

+

+	/**

+	 * Return an EntityManagerFactory instance configured according to the properties

+     * defined in the corresponding persistence descriptor, as well as the properties 

+     * passed into the method.

+	 * 

+	 * @param props Properties to be used, in addition to those in the persistence descriptor,

+     *              for configuring the EntityManagerFactory for the persistence unit.

+     *

+	 * @return An EntityManagerFactory for the persistence unit associated with this service.

+	 */

+    @SuppressWarnings("unchecked")

+    EntityManagerFactory createEntityManagerFactory(Map props);

+}

diff --git a/org.eclipse.gemini.jpa.samples/.classpath b/org.eclipse.gemini.jpa.samples/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.samples/.project b/org.eclipse.gemini.jpa.samples/.project
new file mode 100644
index 0000000..8e03944
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.samples</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a22af20
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:15:57 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.samples/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.samples/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..0c5f478
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Meta-Persistence: META-INF/persistence.xml
+Bundle-Name: Gemini JPA Sample Persistence Unit
+Bundle-SymbolicName: org.eclipse.gemini.jpa.samples
+Bundle-Version: 1.0.0.RC2
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.persistence;version="1.1.0";jpa="2.0",
+ javax.persistence.criteria;version="1.1.0";jpa="2.0",
+ javax.persistence.metamodel;version="1.1.0";jpa="2.0",
+ org.osgi.framework;version="1.4.0",
+ org.osgi.util.tracker,
+ org.osgi.service.jpa
+Export-Package: model.account;version="1.0.0"
+Bundle-Activator: sample.Activator
diff --git a/org.eclipse.gemini.jpa.samples/META-INF/persistence.xml b/org.eclipse.gemini.jpa.samples/META-INF/persistence.xml
new file mode 100644
index 0000000..b778dd3
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/META-INF/persistence.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="Accounts" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+

+        <class>model.account.Account</class>

+        <class>model.account.Customer</class>

+        <class>model.account.Transaction</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINE"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.samples/build.properties b/org.eclipse.gemini.jpa.samples/build.properties
new file mode 100644
index 0000000..53510db
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/build.properties
@@ -0,0 +1,5 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/

+src.includes = META-INF/persistence.xml

diff --git a/org.eclipse.gemini.jpa.samples/src/model/account/Account.java b/org.eclipse.gemini.jpa.samples/src/model/account/Account.java
new file mode 100644
index 0000000..d69187f
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/model/account/Account.java
@@ -0,0 +1,74 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA Sample 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+

+import java.util.Date;

+import java.util.List;

+import java.util.ArrayList;

+

+/**

+ * Sample JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class Account {

+

+    @Id @GeneratedValue

+    int id;

+    

+    double balance;

+    

+    @OneToOne(mappedBy="account")

+    Customer customer;

+

+    @OneToMany(mappedBy="account")

+    @OrderBy("txTime")

+    List<Transaction> txns;

+    

+    @Temporal(TemporalType.DATE)

+    public Date dateCreated;

+

+    /* Constructors */

+    public Account() { 

+        dateCreated = new Date(System.currentTimeMillis()); 

+    }

+    public Account(Customer customer) {

+        this();

+        this.balance = 0;

+        this.customer = customer;

+        customer.setAccount(this);

+        this.txns = new ArrayList<Transaction>();

+    }

+    

+    /* Getters and setters */

+    public int getId() { return id; }

+    public void setId(int id) { this.id = id; }

+    

+    public double getBalance() { return balance; }

+    public void setBalance(double balance) { this.balance = balance; }

+

+    public Customer getCustomer() { return customer; }

+    public void setCustomer(Customer customer) { this.customer = customer; }

+    

+    public List<Transaction> getTxns() { return txns; }

+    public void setTxns(List<Transaction> txns) { this.txns = txns; }

+    

+    public String toString() {

+        return "Account(" + id + ", " + ((customer!=null)?customer.getLastName():"null") + ", Balance: $" + balance + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.samples/src/model/account/Customer.java b/org.eclipse.gemini.jpa.samples/src/model/account/Customer.java
new file mode 100644
index 0000000..76b179b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/model/account/Customer.java
@@ -0,0 +1,68 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA Sample 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+

+/**

+ * Gemini JPA Sample class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class Customer {

+    @Id @GeneratedValue

+    int id;

+    

+    @Column(name="LNAME")

+    String lastName;

+    

+    @Column(name="FNAME")

+    String firstName;

+

+    @Column(name="ADDR")

+    String address;

+

+    @OneToOne

+    Account account;

+

+    /* Constructors */

+    public Customer() { super(); }

+    public Customer(String lastName, String firstName, String address) {

+        super();

+        this.lastName = lastName;

+        this.firstName = firstName;

+        this.address = address;

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public String getLastName() { return lastName; }

+    public void setLastName(String lastName) { this.lastName = lastName; }

+

+    public String getFirstName() { return firstName; }

+    public void setFirstName(String firstName) { this.firstName = firstName; }

+

+    public String getAddress() { return address; }

+    public void setAddress(String address) { this.address = address; }

+

+    public Account getAccount() { return account; }

+    public void setAccount(Account account) { this.account = account; }

+    

+    public String toString() {

+        return "Customer(" + firstName + " " + lastName + ", " + address + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.samples/src/model/account/Transaction.java b/org.eclipse.gemini.jpa.samples/src/model/account/Transaction.java
new file mode 100644
index 0000000..72d8553
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/model/account/Transaction.java
@@ -0,0 +1,76 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA Sample 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+import java.util.Date;

+import java.util.Calendar;

+

+

+/**

+ * Gemini JPA Sample class

+ * 

+ * @author mkeith

+ */

+@Entity

+@Table(name="ACCT_TXN")

+@NamedQuery(name="Transaction.findAllSince", 

+    query="SELECT t FROM Transaction t WHERE t.account = :account AND t.txTime >= :dateArg")

+public class Transaction {

+

+    @Id @GeneratedValue

+    int id;

+

+    @ManyToOne

+    Account account;

+

+    @Column(name="OP")

+    TxOperation operation;

+

+    double amount;

+    

+    @Temporal(TemporalType.TIME)

+    Date txTime;

+

+    /* Constructors */

+    public Transaction() { super(); }

+    public Transaction(Account account, TxOperation operation, double amount) {

+        super();

+        this.account = account;

+        account.getTxns().add(this);

+        this.operation = operation;

+        this.amount = amount;

+        this.txTime = Calendar.getInstance().getTime();

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public Account getAccount() { return account; }

+    public void setAccount(Account account) { this.account = account; }

+    

+    public TxOperation getOperation() { return operation; }

+    public void setOperation(TxOperation operation) { this.operation = operation; }

+    

+    public Date getTxTime() { return txTime; }

+    public void setTxTime(Date txTime) { this.txTime = txTime; }

+    

+    public double getAmount() { return amount; }

+    public void setAmount(double amount) { this.amount = amount; }

+    

+    public String toString() {

+        return "("+ txTime + " - " + "Acct#: " + account.getId() + " " + operation.toString() + ": " + amount + ")"; 

+    }

+}

diff --git a/org.eclipse.gemini.jpa.samples/src/model/account/TxOperation.java b/org.eclipse.gemini.jpa.samples/src/model/account/TxOperation.java
new file mode 100644
index 0000000..f305e6b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/model/account/TxOperation.java
@@ -0,0 +1,26 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA Sample 

+ ******************************************************************************/

+package model.account;

+

+/**

+ * Gemini JPA Sample class

+ * 

+ * @author mkeith

+ */

+public enum TxOperation {

+    

+    DEPOSIT,

+    WITHDRAW

+}

diff --git a/org.eclipse.gemini.jpa.samples/src/sample/Activator.java b/org.eclipse.gemini.jpa.samples/src/sample/Activator.java
new file mode 100644
index 0000000..5d44795
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/sample/Activator.java
@@ -0,0 +1,73 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA sample 

+ ******************************************************************************/

+package sample;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+import org.osgi.util.tracker.ServiceTracker;

+import org.osgi.util.tracker.ServiceTrackerCustomizer;

+

+/**

+ * Gemini JPA sample activator class

+ * 

+ * @author mkeith

+ */

+public class Activator implements BundleActivator, ServiceTrackerCustomizer {

+

+    BundleContext ctx;

+    ServiceTracker emfTracker;

+    Client client;

+

+    public void start(BundleContext context) throws Exception {

+        ctx = context;

+        client = new Client();

+        System.out.println("Gemini JPA Sample started");

+

+        /* We are in the same bundle as the persistence unit so the services should be 

+         * available when we start up (if nothing bad happened) and the tracker is really 

+         * just saving us the lookup, but this is the idea of how you would listen for a 

+         * persistence unit coming from another bundle.

+         */

+        emfTracker = new ServiceTracker(ctx, EntityManagerFactory.class.getName(), this);

+        emfTracker.open();

+    }

+

+    public void stop(BundleContext context) throws Exception {

+        emfTracker.close();

+        client = null;

+        System.out.println("Gemini JPA Sample stopped");

+    }

+    

+    /*========================*/

+    /* ServiceTracker methods */

+    /*========================*/

+

+    public Object addingService(ServiceReference ref) {

+        Bundle b = ref.getBundle();

+        Object service = b.getBundleContext().getService(ref);

+        String unitName = (String)ref.getProperty(EntityManagerFactoryBuilder.JPA_UNIT_NAME);

+        if (unitName.equals("Accounts")) {

+            client.run((EntityManagerFactory)service);

+        }

+        return service;

+    }

+    public void modifiedService(ServiceReference ref, Object service) {}

+    public void removedService(ServiceReference ref, Object service) {}    

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.samples/src/sample/Client.java b/org.eclipse.gemini.jpa.samples/src/sample/Client.java
new file mode 100644
index 0000000..58d255f
--- /dev/null
+++ b/org.eclipse.gemini.jpa.samples/src/sample/Client.java
@@ -0,0 +1,53 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA sample 

+ ******************************************************************************/

+package sample;

+

+import java.util.List;

+

+import javax.persistence.EntityManager;

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.TypedQuery;

+

+import model.account.Account;

+import model.account.Customer;

+

+/**

+ * Gemini JPA sample client class

+ * 

+ * @author mkeith

+ */

+public class Client {

+    

+    public void run(EntityManagerFactory emf) {

+        EntityManager em = emf.createEntityManager();

+        em.getTransaction().begin();

+        

+        Customer c = new Customer("Chan", "Jackie", "1034 KingFu Lane, Los Angeles, CA");

+        em.persist(c);

+        Account a = new Account(c);

+        a.setBalance(100.0);

+        em.persist(a);

+

+        em.getTransaction().commit();

+

+        TypedQuery<Account> q = em.createQuery("SELECT a FROM Account a", Account.class);

+        List<Account> results = q.getResultList();

+        System.out.println("\n*** Account Report ***");

+        for (Account acct : results) {

+            System.out.println("Account: " + acct);

+        }

+        em.close();

+    }

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/.classpath b/org.eclipse.gemini.jpa.test.embeddedpunit/.classpath
new file mode 100644
index 0000000..88921a5
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry exported="true" kind="lib" path="jar/embeddedPunit.jar"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/.project b/org.eclipse.gemini.jpa.test.embeddedpunit/.project
new file mode 100644
index 0000000..bc4f085
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.test.embeddedpunit</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..491a8ab
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:16:50 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d668c0f
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Meta-Persistence: jar/embeddedPunit.jar!/META-INF/embeddedJpa.xml
+Bundle-Name: Gemini JPA Test Embedded Persistence Unit
+Bundle-SymbolicName: org.eclipse.gemini.jpa.test.embeddedpunit
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.persistence;version="1.1.0";jpa="2.0",
+ javax.persistence.criteria;version="1.1.0";jpa="2.0",
+ javax.persistence.metamodel;version="1.1.0";jpa="2.0",
+ org.osgi.framework;version="1.4.0"
+Export-Package: model.embeddedaccount;version="1.0.0"
+Bundle-Activator: punit.Activator
+Bundle-ClassPath: .,
+ jar/embeddedPunit.jar
diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/embeddedJpa.xml b/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/embeddedJpa.xml
new file mode 100644
index 0000000..a4d9608
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/META-INF/embeddedJpa.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="EmbeddedAccounts" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+

+        <class>model.embeddedaccount.EmbAccount</class>

+        <class>model.embeddedaccount.EmbCustomer</class>

+        <class>model.embeddedaccount.EmbTransaction</class>

+        <class>model.embeddedaccount.EmbTxOperation</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/build.properties b/org.eclipse.gemini.jpa.test.embeddedpunit/build.properties
new file mode 100644
index 0000000..c8fb95b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/build.properties
@@ -0,0 +1,9 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/,\

+               jpa1.xml,\

+               jar/embeddedPunit.jar

+src.includes = jar/embeddedPunit.jar,\

+               META-INF/jpa2.xml,\

+               jpa1.xml

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/jar/META-INF/embeddedJpa.xml b/org.eclipse.gemini.jpa.test.embeddedpunit/jar/META-INF/embeddedJpa.xml
new file mode 100644
index 0000000..5115018
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/jar/META-INF/embeddedJpa.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="EmbeddedAccounts" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+

+        <class>model.embeddedaccount.EmbAccount</class>

+        <class>model.embeddedaccount.EmbCustomer</class>

+        <class>model.embeddedaccount.EmbTransaction</class>

+        <class>model.embeddedaccount.EmbTxOperation</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/jar/embeddedPunit.jar b/org.eclipse.gemini.jpa.test.embeddedpunit/jar/embeddedPunit.jar
new file mode 100644
index 0000000..95599b1
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/jar/embeddedPunit.jar
Binary files differ
diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbAccount.java b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbAccount.java
new file mode 100644
index 0000000..fc8179f
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbAccount.java
@@ -0,0 +1,74 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.embeddedaccount;

+

+import javax.persistence.*;

+

+import java.util.Date;

+import java.util.List;

+import java.util.ArrayList;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class EmbAccount {

+

+    @Id @GeneratedValue

+    int id;

+    

+    double balance;

+    

+    @OneToOne(mappedBy="account")

+    EmbCustomer customer;

+

+    @OneToMany(mappedBy="account")

+    @OrderBy("txTime")

+    List<EmbTransaction> txns;

+    

+    @Temporal(TemporalType.DATE)

+    public Date dateCreated;

+

+    /* Constructors */

+    public EmbAccount() { 

+        dateCreated = new Date(System.currentTimeMillis()); 

+    }

+    public EmbAccount(EmbCustomer customer) {

+        this();

+        this.balance = 0;

+        this.customer = customer;

+        customer.setAccount(this);

+        this.txns = new ArrayList<EmbTransaction>();

+    }

+    

+    /* Getters and setters */

+    public int getId() { return id; }

+    public void setId(int id) { this.id = id; }

+    

+    public double getBalance() { return balance; }

+    public void setBalance(double balance) { this.balance = balance; }

+

+    public EmbCustomer getCustomer() { return customer; }

+    public void setCustomer(EmbCustomer customer) { this.customer = customer; }

+    

+    public List<EmbTransaction> getTxns() { return txns; }

+    public void setTxns(List<EmbTransaction> txns) { this.txns = txns; }

+    

+    public String toString() {

+        return "EmbAccount(" + id + ", " + ((customer!=null)?customer.getLastName():"null") + ", Balance: $" + balance + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbCustomer.java b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbCustomer.java
new file mode 100644
index 0000000..b983b68
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbCustomer.java
@@ -0,0 +1,68 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.embeddedaccount;

+

+import javax.persistence.*;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class EmbCustomer {

+    @Id @GeneratedValue

+    int id;

+    

+    @Column(name="LNAME")

+    String lastName;

+    

+    @Column(name="FNAME")

+    String firstName;

+

+    @Column(name="ADDR")

+    String address;

+

+    @OneToOne

+    EmbAccount account;

+

+    /* Constructors */

+    public EmbCustomer() { super(); }

+    public EmbCustomer(String lastName, String firstName, String address) {

+        super();

+        this.lastName = lastName;

+        this.firstName = firstName;

+        this.address = address;

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public String getLastName() { return lastName; }

+    public void setLastName(String lastName) { this.lastName = lastName; }

+

+    public String getFirstName() { return firstName; }

+    public void setFirstName(String firstName) { this.firstName = firstName; }

+

+    public String getAddress() { return address; }

+    public void setAddress(String address) { this.address = address; }

+

+    public EmbAccount getAccount() { return account; }

+    public void setAccount(EmbAccount account) { this.account = account; }

+    

+    public String toString() {

+        return "EmbCustomer(" + firstName + " " + lastName + ", " + address + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTransaction.java b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTransaction.java
new file mode 100644
index 0000000..483c0dc
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTransaction.java
@@ -0,0 +1,76 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.embeddedaccount;

+

+import javax.persistence.*;

+import java.util.Date;

+import java.util.Calendar;

+

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+@Table(name="ACCT_TXN")

+@NamedQuery(name="EmbTransaction.findAllSince", 

+    query="SELECT t FROM EmbTransaction t WHERE t.account = :account AND t.txTime >= :dateArg")

+public class EmbTransaction {

+

+    @Id @GeneratedValue

+    int id;

+

+    @ManyToOne

+    EmbAccount account;

+

+    @Column(name="OP")

+    EmbTxOperation operation;

+

+    double amount;

+    

+    @Temporal(TemporalType.TIME)

+    Date txTime;

+

+    /* Constructors */

+    public EmbTransaction() { super(); }

+    public EmbTransaction(EmbAccount account, EmbTxOperation operation, double amount) {

+        super();

+        this.account = account;

+        account.getTxns().add(this);

+        this.operation = operation;

+        this.amount = amount;

+        this.txTime = Calendar.getInstance().getTime();

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public EmbAccount getAccount() { return account; }

+    public void setAccount(EmbAccount account) { this.account = account; }

+    

+    public EmbTxOperation getOperation() { return operation; }

+    public void setOperation(EmbTxOperation operation) { this.operation = operation; }

+    

+    public Date getTxTime() { return txTime; }

+    public void setTxTime(Date txTime) { this.txTime = txTime; }

+    

+    public double getAmount() { return amount; }

+    public void setAmount(double amount) { this.amount = amount; }

+    

+    public String toString() {

+        return "("+ txTime + " - " + "Acct#: " + account.getId() + " " + operation.toString() + ": " + amount + ")"; 

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTxOperation.java b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTxOperation.java
new file mode 100644
index 0000000..c2ea16f
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/src/model/embeddedaccount/EmbTxOperation.java
@@ -0,0 +1,26 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.embeddedaccount;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+public enum EmbTxOperation {

+    

+    DEPOSIT,

+    WITHDRAW

+}

diff --git a/org.eclipse.gemini.jpa.test.embeddedpunit/src/punit/Activator.java b/org.eclipse.gemini.jpa.test.embeddedpunit/src/punit/Activator.java
new file mode 100644
index 0000000..70fc5fd
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.embeddedpunit/src/punit/Activator.java
@@ -0,0 +1,32 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package punit;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Embedded Test JPA persistence unit activator class

+ * 

+ * @author mkeith

+ */

+public class Activator implements BundleActivator {

+

+    public void start(BundleContext context) throws Exception {

+        System.out.println("Embedded test persistence unit active");

+    }

+

+    public void stop(BundleContext context) throws Exception {}

+}

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/.classpath b/org.eclipse.gemini.jpa.test.emptypunit/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/.project b/org.eclipse.gemini.jpa.test.emptypunit/.project
new file mode 100644
index 0000000..0a56413
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.test.emptypunit</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a22af20
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:15:57 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.test.emptypunit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b7c011c
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Meta-Persistence: META-INF/emptyJpa.xml
+Bundle-Name: Gemini JPA Test Empty Persistence Unit
+Bundle-SymbolicName: org.eclipse.gemini.jpa.test.emptypunit
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.osgi.framework;version="1.4.0"
+Bundle-Activator: punit.Activator
diff --git a/org.eclipse.gemini.jpa.test.emptypunit/META-INF/emptyJpa.xml b/org.eclipse.gemini.jpa.test.emptypunit/META-INF/emptyJpa.xml
new file mode 100644
index 0000000..ac68f75
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/META-INF/emptyJpa.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="Empty1" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+        <properties>

+            <property name="testEmptyProperty" value=""/>

+			<property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+        </properties>

+	</persistence-unit>

+    <persistence-unit name="Empty2" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/build.properties b/org.eclipse.gemini.jpa.test.emptypunit/build.properties
new file mode 100644
index 0000000..7d98739
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/build.properties
@@ -0,0 +1,5 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/

+src.includes = META-INF/emptyJpa.xml

diff --git a/org.eclipse.gemini.jpa.test.emptypunit/src/punit/Activator.java b/org.eclipse.gemini.jpa.test.emptypunit/src/punit/Activator.java
new file mode 100644
index 0000000..f319769
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.emptypunit/src/punit/Activator.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright (C) 2010 Oracle Corporation

+ *

+ * 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 punit;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Test JPA persistence unit activator class

+ * 

+ * @author mkeith

+ */

+public class Activator implements BundleActivator {

+

+    public void start(BundleContext context) throws Exception {

+        System.out.println("Test empty persistence unit active");

+    }

+

+    public void stop(BundleContext context) throws Exception {}

+}

diff --git a/org.eclipse.gemini.jpa.test.punit/.classpath b/org.eclipse.gemini.jpa.test.punit/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.test.punit/.project b/org.eclipse.gemini.jpa.test.punit/.project
new file mode 100644
index 0000000..7bcbfd3
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.test.punit</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a22af20
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:15:57 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.test.punit/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.test.punit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d8bbbb0
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Meta-Persistence: jpa1.xml, META-INF/jpa2.xml
+Bundle-Name: Gemini JPA Test Persistence Unit
+Bundle-SymbolicName: org.eclipse.gemini.jpa.test.punit
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.persistence;version="1.1.0";jpa="2.0",
+ javax.persistence.criteria;version="1.1.0";jpa="2.0",
+ javax.persistence.metamodel;version="1.1.0";jpa="2.0",
+ org.osgi.framework;version="1.4.0"
+Export-Package: model.account;version="1.0.0"
+Bundle-Activator: punit.Activator
diff --git a/org.eclipse.gemini.jpa.test.punit/META-INF/jpa2.xml b/org.eclipse.gemini.jpa.test.punit/META-INF/jpa2.xml
new file mode 100644
index 0000000..b00a2ab
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/META-INF/jpa2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="AccountsNoDataSource" transaction-type="RESOURCE_LOCAL">

+        <provider>

+            org.eclipse.persistence.jpa.PersistenceProvider

+        </provider>

+

+        <class>model.account.Account</class>

+        <class>model.account.Customer</class>

+        <class>model.account.Transaction</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.punit/build.properties b/org.eclipse.gemini.jpa.test.punit/build.properties
new file mode 100644
index 0000000..50bf0ce
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/build.properties
@@ -0,0 +1,7 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/,\

+               jpa1.xml

+src.includes = META-INF/jpa2.xml,\

+               jpa1.xml

diff --git a/org.eclipse.gemini.jpa.test.punit/jpa1.xml b/org.eclipse.gemini.jpa.test.punit/jpa1.xml
new file mode 100644
index 0000000..32c925d
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/jpa1.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="Accounts" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+

+        <class>model.account.Account</class>

+        <class>model.account.Customer</class>

+        <class>model.account.Transaction</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>            

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.punit/src/model/account/Account.java b/org.eclipse.gemini.jpa.test.punit/src/model/account/Account.java
new file mode 100644
index 0000000..2a1948d
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/src/model/account/Account.java
@@ -0,0 +1,74 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+

+import java.util.Date;

+import java.util.List;

+import java.util.ArrayList;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class Account {

+

+    @Id @GeneratedValue

+    int id;

+    

+    double balance;

+    

+    @OneToOne(mappedBy="account")

+    Customer customer;

+

+    @OneToMany(mappedBy="account")

+    @OrderBy("txTime")

+    List<Transaction> txns;

+    

+    @Temporal(TemporalType.DATE)

+    public Date dateCreated;

+

+    /* Constructors */

+    public Account() { 

+        dateCreated = new Date(System.currentTimeMillis()); 

+    }

+    public Account(Customer customer) {

+        this();

+        this.balance = 0;

+        this.customer = customer;

+        customer.setAccount(this);

+        this.txns = new ArrayList<Transaction>();

+    }

+    

+    /* Getters and setters */

+    public int getId() { return id; }

+    public void setId(int id) { this.id = id; }

+    

+    public double getBalance() { return balance; }

+    public void setBalance(double balance) { this.balance = balance; }

+

+    public Customer getCustomer() { return customer; }

+    public void setCustomer(Customer customer) { this.customer = customer; }

+    

+    public List<Transaction> getTxns() { return txns; }

+    public void setTxns(List<Transaction> txns) { this.txns = txns; }

+    

+    public String toString() {

+        return "Account(" + id + ", " + ((customer!=null)?customer.getLastName():"null") + ", Balance: $" + balance + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.punit/src/model/account/Customer.java b/org.eclipse.gemini.jpa.test.punit/src/model/account/Customer.java
new file mode 100644
index 0000000..db65167
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/src/model/account/Customer.java
@@ -0,0 +1,68 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+public class Customer {

+    @Id @GeneratedValue

+    int id;

+    

+    @Column(name="LNAME")

+    String lastName;

+    

+    @Column(name="FNAME")

+    String firstName;

+

+    @Column(name="ADDR")

+    String address;

+

+    @OneToOne

+    Account account;

+

+    /* Constructors */

+    public Customer() { super(); }

+    public Customer(String lastName, String firstName, String address) {

+        super();

+        this.lastName = lastName;

+        this.firstName = firstName;

+        this.address = address;

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public String getLastName() { return lastName; }

+    public void setLastName(String lastName) { this.lastName = lastName; }

+

+    public String getFirstName() { return firstName; }

+    public void setFirstName(String firstName) { this.firstName = firstName; }

+

+    public String getAddress() { return address; }

+    public void setAddress(String address) { this.address = address; }

+

+    public Account getAccount() { return account; }

+    public void setAccount(Account account) { this.account = account; }

+    

+    public String toString() {

+        return "Customer(" + firstName + " " + lastName + ", " + address + ")";

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.punit/src/model/account/Transaction.java b/org.eclipse.gemini.jpa.test.punit/src/model/account/Transaction.java
new file mode 100644
index 0000000..0bd8deb
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/src/model/account/Transaction.java
@@ -0,0 +1,76 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.account;

+

+import javax.persistence.*;

+import java.util.Date;

+import java.util.Calendar;

+

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+@Entity

+@Table(name="ACCT_TXN")

+@NamedQuery(name="Transaction.findAllSince", 

+    query="SELECT t FROM Transaction t WHERE t.account = :account AND t.txTime >= :dateArg")

+public class Transaction {

+

+    @Id @GeneratedValue

+    int id;

+

+    @ManyToOne

+    Account account;

+

+    @Column(name="OP")

+    TxOperation operation;

+

+    double amount;

+    

+    @Temporal(TemporalType.TIME)

+    Date txTime;

+

+    /* Constructors */

+    public Transaction() { super(); }

+    public Transaction(Account account, TxOperation operation, double amount) {

+        super();

+        this.account = account;

+        account.getTxns().add(this);

+        this.operation = operation;

+        this.amount = amount;

+        this.txTime = Calendar.getInstance().getTime();

+    }

+

+    /* Getters and setters */

+    public int getId() { return id; }

+    

+    public Account getAccount() { return account; }

+    public void setAccount(Account account) { this.account = account; }

+    

+    public TxOperation getOperation() { return operation; }

+    public void setOperation(TxOperation operation) { this.operation = operation; }

+    

+    public Date getTxTime() { return txTime; }

+    public void setTxTime(Date txTime) { this.txTime = txTime; }

+    

+    public double getAmount() { return amount; }

+    public void setAmount(double amount) { this.amount = amount; }

+    

+    public String toString() {

+        return "("+ txTime + " - " + "Acct#: " + account.getId() + " " + operation.toString() + ": " + amount + ")"; 

+    }

+}

diff --git a/org.eclipse.gemini.jpa.test.punit/src/model/account/TxOperation.java b/org.eclipse.gemini.jpa.test.punit/src/model/account/TxOperation.java
new file mode 100644
index 0000000..eaec007
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/src/model/account/TxOperation.java
@@ -0,0 +1,26 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.account;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+public enum TxOperation {

+    

+    DEPOSIT,

+    WITHDRAW

+}

diff --git a/org.eclipse.gemini.jpa.test.punit/src/punit/Activator.java b/org.eclipse.gemini.jpa.test.punit/src/punit/Activator.java
new file mode 100644
index 0000000..b616af8
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.punit/src/punit/Activator.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright (C) 2010 Oracle Corporation

+ *

+ * 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 punit;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Test JPA persistence unit activator class

+ * 

+ * @author mkeith

+ */

+public class Activator implements BundleActivator {

+

+    public void start(BundleContext context) throws Exception {

+        System.out.println("Test persistence unit active");

+    }

+

+    public void stop(BundleContext context) throws Exception {}

+}

diff --git a/org.eclipse.gemini.jpa.test.state/.classpath b/org.eclipse.gemini.jpa.test.state/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.test.state/.project b/org.eclipse.gemini.jpa.test.state/.project
new file mode 100644
index 0000000..b093234
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.test.state</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a22af20
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:15:57 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.test.state/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.test.state/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2b9a069
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Gemini Test State
+Bundle-SymbolicName: org.eclipse.gemini.jpa.test.state
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.osgi.framework;version="1.4.0", 
+ org.junit.runner;version="4.3.1"
+Export-Package: test;version="1.0.0"
+Bundle-Activator: test.TestState
diff --git a/org.eclipse.gemini.jpa.test.state/build.properties b/org.eclipse.gemini.jpa.test.state/build.properties
new file mode 100644
index 0000000..72e0578
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/build.properties
@@ -0,0 +1,4 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/

diff --git a/org.eclipse.gemini.jpa.test.state/src/test/TestState.java b/org.eclipse.gemini.jpa.test.state/src/test/TestState.java
new file mode 100644
index 0000000..bfc873b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.state/src/test/TestState.java
@@ -0,0 +1,89 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package test;

+

+import java.util.HashSet;

+import java.util.Set;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import org.junit.runner.Result;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Test state class used to keep a memory of the tests that get executed

+ * in the face of the test cases continually being refreshed

+ * 

+ * @author mkeith

+ */

+public class TestState implements BundleActivator {

+

+    static Set<String> incompletedTests = new HashSet<String>();

+    static Map<String,Result> completedTests = new HashMap<String,Result>();

+    

+    static void initTests() {

+        incompletedTests = new HashSet<String>();

+        completedTests = new HashMap<String,Result>();

+

+        // Tests to run - Comment out tests to disable them.

+        

+        incompletedTests.add("TestStaticPersistence");

+        incompletedTests.add("TestEMFService");

+        incompletedTests.add("TestEMFBuilderService");

+        incompletedTests.add("TestEMFBuilderServiceProperties");

+        incompletedTests.add("TestEMFBuilderExternalDataSource");

+        incompletedTests.add("TestEmbeddedPUnit");

+        incompletedTests.add("TestOrmMappingFile");

+        incompletedTests.add("TestMappingFileElement");

+        incompletedTests.add("TestEmptyPersistence");

+        incompletedTests.add("TestEmptyPersistenceWithProps");

+    }

+    

+    public static void resetTests() { 

+        initTests(); 

+    }

+

+    public static void startTest(String s) { 

+        incompletedTests.remove(s);

+    }

+    

+    public static void completedTest(String s, Result r) { 

+        completedTests.put(s, r);

+    }

+

+    public static Set<String> getIncompletedTests() { 

+        return incompletedTests;

+    }

+

+    public static boolean isTested(String s) { 

+        return !incompletedTests.contains(s); 

+    }

+

+    public static Map<String,Result> getAllTestResults() { 

+        return completedTests; 

+    }

+

+    public void start(BundleContext context) throws Exception {

+        initTests();

+        System.out.println("TestState active");

+        System.out.println("Tests in run list: ");

+        System.out.println(""+incompletedTests);

+    }

+

+    public void stop(BundleContext context) throws Exception {}

+}

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/.classpath b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/.project b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.project
new file mode 100644
index 0000000..afb8059
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.test.xmlmappedpunit</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a22af20
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:15:57 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..25eae0b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Thu Aug 13 23:45:24 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..bc817ca
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Meta-Persistence: META-INF/xmlMappedJpa.xml
+Bundle-Name: Gemini JPA Test XML Persistence Unit
+Bundle-SymbolicName: org.eclipse.gemini.jpa.test.xmlmappedpunit
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.osgi.framework;version="1.4.0"
+Export-Package: model.xmlmapped;version="1.0.0"
+Bundle-Activator: punit.Activator
diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm.xml b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm.xml
new file mode 100644
index 0000000..80b1e47
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"

+    version="1.0">

+

+    <package>model.xmlmapped</package>

+

+    <entity class="SimpleEntity">

+        <attributes>

+            <id name="id">

+                <column name="ID1"/>

+            </id>

+            <basic name="simpleString"/>

+        </attributes>

+    </entity>

+

+</entity-mappings>
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm2.xml b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm2.xml
new file mode 100644
index 0000000..62b368e
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/orm2.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"

+    version="1.0">

+

+    <package>model.xmlmapped</package>

+

+    <entity class="SimpleEntity2">

+        <attributes>

+            <id name="id">

+                <column name="ID2"/>

+            </id>

+            <basic name="simpleInt"/>

+        </attributes>

+    </entity>

+

+</entity-mappings>
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/xmlMappedJpa.xml b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/xmlMappedJpa.xml
new file mode 100644
index 0000000..8277ab5
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/META-INF/xmlMappedJpa.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<persistence version="1.0" 

+    xmlns="http://java.sun.com/xml/ns/persistence"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

+

+    <persistence-unit name="XmlMapped" transaction-type="RESOURCE_LOCAL">

+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

+        <mapping-file>META-INF/orm2.xml</mapping-file>

+        <class>model.xmlmapped.SimpleEntity</class>

+        <class>model.xmlmapped.SimpleEntity2</class>

+

+        <exclude-unlisted-classes>true</exclude-unlisted-classes>

+

+        <properties>

+            <property name="eclipselink.target-database" value="Derby"/>

+            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>

+            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/accountDB;create=true"/>

+            <property name="javax.persistence.jdbc.user" value="app"/>

+            <property name="javax.persistence.jdbc.password" value="app"/>

+

+            <property name="eclipselink.logging.level" value="FINEST"/>

+            <property name="eclipselink.logging.timestamp" value="false"/>

+            <property name="eclipselink.logging.thread" value="false"/>

+            <property name="eclipselink.logging.exceptions" value="true"/>

+            <property name="eclipselink.orm.throw.exceptions" value="true"/>

+            <property name="eclipselink.jdbc.read-connections.min" value="1"/>

+            <property name="eclipselink.jdbc.write-connections.min" value="1"/>                        

+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

+            <property name="eclipselink.weaving" value="false"/>

+

+        </properties>

+    </persistence-unit>

+</persistence>

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/build.properties b/org.eclipse.gemini.jpa.test.xmlmappedpunit/build.properties
new file mode 100644
index 0000000..de1e423
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/build.properties
@@ -0,0 +1,7 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/

+src.includes = META-INF/xmlMappedJpa.xml,\

+               META-INF/orm.xml,\

+               META-INF/orm2.xml

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity.java b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity.java
new file mode 100644
index 0000000..e6693eb
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity.java
@@ -0,0 +1,33 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.xmlmapped;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+public class SimpleEntity {

+

+    int id;

+    String simpleString;

+

+    public int getId() { return id; }

+    public void setId(int id) { this.id = id; }

+ 

+    public String getSimpleString() { return simpleString; }

+    public void setSimpleString(String simpleString) { this.simpleString = simpleString; }

+

+}

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity2.java b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity2.java
new file mode 100644
index 0000000..eeeaa83
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/model/xmlmapped/SimpleEntity2.java
@@ -0,0 +1,33 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package model.xmlmapped;

+

+/**

+ * Test JPA model class

+ * 

+ * @author mkeith

+ */

+public class SimpleEntity2 {

+

+    int id;

+    int simpleInt;

+ 

+    public int getId() { return id; }

+    public void setId(int id) { this.id = id; }

+ 

+    public int getSimpleInt() { return simpleInt; }

+    public void setSimpleInt(int simpleInt) { this.simpleInt = simpleInt; }

+

+}

diff --git a/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/punit/Activator.java b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/punit/Activator.java
new file mode 100644
index 0000000..0e3b949
--- /dev/null
+++ b/org.eclipse.gemini.jpa.test.xmlmappedpunit/src/punit/Activator.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright (C) 2010 Oracle Corporation

+ *

+ * 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 punit;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Test JPA persistence unit activator class

+ * 

+ * @author mkeith

+ */

+public class Activator implements BundleActivator {

+

+    public void start(BundleContext context) throws Exception {

+        System.out.println("Test XML mapped persistence unit active");

+    }

+

+    public void stop(BundleContext context) throws Exception {}

+}

diff --git a/org.eclipse.gemini.jpa.tests/.classpath b/org.eclipse.gemini.jpa.tests/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.tests/.project b/org.eclipse.gemini.jpa.tests/.project
new file mode 100644
index 0000000..adca819
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.tests</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..491a8ab
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jul 06 10:16:50 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..5b13dda
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Wed Aug 12 10:01:56 EDT 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa.tests/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.tests/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ec080b3
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Gemini JPA Unit Tests
+Bundle-SymbolicName: org.eclipse.gemini.jpa.tests
+Bundle-Version: 1.0.0
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.persistence;jpa="2.0";version="1.1.0",
+ javax.persistence.metamodel;jpa="2.0";version="1.1.0",
+ org.junit;version="4.8.1",
+ org.junit.runner;version="4.8.1",
+ org.junit.runner.notification;version="4.5.0",
+ org.osgi.framework;version="1.3.0",
+ org.osgi.service.jpa;version="1.0.0",
+ org.osgi.util.tracker;version="1.3.1",
+ org.osgi.service.jdbc;version="[1.0,2.0)",
+ test;version="1.0.0"
+Require-Bundle: org.eclipse.gemini.jpa.test.punit,
+ org.eclipse.gemini.jpa.test.embeddedpunit,
+ org.eclipse.gemini.jpa.test.xmlmappedpunit,
+ org.eclipse.gemini.jpa.test.emptypunit
+Bundle-Activator: org.eclipse.gemini.jpa.tests.Activator
diff --git a/org.eclipse.gemini.jpa.tests/build.properties b/org.eclipse.gemini.jpa.tests/build.properties
new file mode 100644
index 0000000..41eb6ad
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/build.properties
@@ -0,0 +1,4 @@
+source.. = src/

+output.. = bin/

+bin.includes = META-INF/,\

+               .

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/Activator.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/Activator.java
new file mode 100644
index 0000000..ef4343b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/Activator.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution. 
+ * The Eclipse Public License is available at
+ *     http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at 
+ *     http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ *     mkeith - Gemini JPA tests 
+ ******************************************************************************/
+package org.eclipse.gemini.jpa.tests;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import test.TestState;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.osgi.service.jpa.EntityManagerFactoryBuilder;
+
+/**
+ * Activator to start tests when relevant service becomes available
+ * 
+ * @author mkeith
+ */
+public class Activator implements BundleActivator, ServiceTrackerCustomizer {
+
+    BundleContext ctx;
+    
+    ServiceTracker emfTracker;
+    ServiceTracker emfbTracker;
+    
+    
+
+    public void start(BundleContext context) throws Exception {
+        log("Tests active");
+
+        ctx = context;
+        JpaTest.context = context;
+                
+        emfTracker = new ServiceTracker(ctx, EntityManagerFactory.class.getName(), this);
+        emfbTracker = new ServiceTracker(ctx, EntityManagerFactoryBuilder.class.getName(), this);
+
+        emfTracker.open();
+        emfbTracker.open();
+        
+        // Run tests from tracker when service is online
+    }
+
+    public void stop(BundleContext context) throws Exception {
+        log("Tests stopped");
+        emfbTracker.close();
+        emfTracker.close();
+    }
+
+    boolean shouldRun(Class<? extends JpaTest> testClass, String unitName, boolean isEMFService) {
+        JpaTest test = null;
+        try { test = testClass.newInstance(); 
+        } catch (Exception ex) { throw new RuntimeException(ex); }
+        return !TestState.isTested(testClass.getSimpleName()) 
+               && test.getTestPersistenceUnitName().equals(unitName)
+               && (!(test.needsEmfService() ^ isEMFService));
+    }
+    
+    void runTest(Class<? extends JpaTest> testClass) {
+        String testName = testClass.getSimpleName();
+        TestState.startTest(testName);
+        log("Running " + testName + ": ");
+        Result r = JUnitCore.runClasses(testClass);
+
+        log(testName + " results: ");
+        logResultStats(r);
+        TestState.completedTest(testName,r);
+            
+        log("Done " + testName);
+
+        Set<String> incompleteTests = TestState.getIncompletedTests();
+        if (!incompleteTests.isEmpty()) {
+            System.out.println("------------------- Tests not run yet: " + incompleteTests);
+        } else {
+            // If no more tests to run, print out a summary
+            System.out.println("-----------------------------------------------------"); 
+            System.out.println("------------------- Test Summary --------------------"); 
+            System.out.println("-----------------------------------------------------"); 
+            Set<Map.Entry<String,Result>> results = TestState.getAllTestResults().entrySet();
+            for (Map.Entry<String,Result> entry : results) {
+                System.out.println("Test: " + entry.getKey()); 
+                logResultStats(entry.getValue()); 
+            }
+        }
+    }
+
+    /* ServiceTracker methods */
+
+    public Object addingService(ServiceReference ref) {
+        Bundle b = ref.getBundle();
+        Object service = b.getBundleContext().getService(ref);
+        
+        String unitName = (String)ref.getProperty(EntityManagerFactoryBuilder.JPA_UNIT_NAME);
+
+        if (unitName != null) {
+            // We have a JPA service. Is it an EMF or an EMFBuilder?
+            boolean isEmfService = EntityManagerFactory.class.isInstance(service);
+            
+            log("Service added **** name=" + unitName + " EMF=" + isEmfService);
+            
+            // Now ask each test if it should run based on the punit name and whether 
+            // the service is an EMF or an EMFBuilder. Note that more than one test 
+            // may run on the same EMF/B service.
+            
+            if (shouldRun(TestStaticPersistence.class, unitName, isEmfService))
+                runTest(TestStaticPersistence.class);
+            if (shouldRun(TestEMFService.class, unitName, isEmfService))
+                runTest(TestEMFService.class);
+            if (shouldRun(TestEMFBuilderService.class, unitName, isEmfService))
+                runTest(TestEMFBuilderService.class);
+            if (shouldRun(TestEMFBuilderServiceProperties.class, unitName, isEmfService))
+                runTest(TestEMFBuilderServiceProperties.class);
+            if (shouldRun(TestEMFBuilderExternalDataSource.class, unitName, isEmfService))
+                runTest(TestEMFBuilderExternalDataSource.class);
+            if (shouldRun(TestEmbeddedPUnit.class, unitName, isEmfService))
+                runTest(TestEmbeddedPUnit.class);
+            if (shouldRun(TestOrmMappingFile.class, unitName, isEmfService))
+                runTest(TestOrmMappingFile.class);
+            if (shouldRun(TestMappingFileElement.class, unitName, isEmfService))
+                runTest(TestMappingFileElement.class);
+            if (shouldRun(TestEmptyPersistence.class, unitName, isEmfService))
+                runTest(TestEmptyPersistence.class);
+            if (shouldRun(TestEmptyPersistenceWithProps.class, unitName, isEmfService))
+                runTest(TestEmptyPersistenceWithProps.class);
+        }
+        return service;
+    }
+
+    public void modifiedService(ServiceReference ref, Object service) {}
+
+    public void removedService(ServiceReference ref, Object service) {}
+
+    void logResultStats(Result r) {
+        log("Result: " + 
+                " runs=" + r.getRunCount() + 
+                " failures=" + r.getFailureCount() +
+                " ignore=" + r.getIgnoreCount());        
+        log("Failures: " + r.getFailures());
+        for (Failure f : r.getFailures())
+            log("--- Failure: \n" + f.getTrace());
+    }
+    
+    static void log(String msg) {
+        System.out.println("===== " + msg);
+    }    
+}
diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/JpaTest.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/JpaTest.java
new file mode 100644
index 0000000..e7c1a7c
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/JpaTest.java
@@ -0,0 +1,180 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+

+import javax.persistence.*;

+import javax.persistence.metamodel.EntityType;

+

+import org.junit.*;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+import model.account.*;

+

+/**

+ * Test class to test EMF Service from a client

+ * 

+ * @author mkeith

+ */

+public abstract class JpaTest {

+    

+    public static BundleContext context;

+

+    // Define the JDBC access details. At some point we should probably externalize this...

+    public static final String JDBC_TEST_DRIVER = "org.apache.derby.jdbc.ClientDriver";

+    public static final String JDBC_TEST_URL = "jdbc:derby://localhost:1527/accountDB;create=true";

+    public static final String JDBC_TEST_USER = "app";

+    public static final String JDBC_TEST_PASSWORD = "app";

+

+    // Put the JDBC access config in the default props

+    public static Map<String,Object> defaultProps() {

+        Map<String,Object> props = new HashMap<String,Object>();        

+        props.put("javax.persistence.jdbc.driver", JDBC_TEST_DRIVER);

+        props.put("javax.persistence.jdbc.url", JDBC_TEST_URL);

+        props.put("javax.persistence.jdbc.user", JDBC_TEST_USER);

+        props.put("javax.persistence.jdbc.password", JDBC_TEST_URL);

+        return props;

+    }

+

+    /* === Methods that *must* be subclassed === */

+

+    public abstract EntityManagerFactory getEmf();

+

+    public abstract String getTestPersistenceUnitName();

+    

+    /* === Methods that *may* be subclassed === */

+

+    public boolean needsEmfService() { return true; }

+

+    public String testName() {

+        return this.getClass().getSimpleName();

+    }

+

+    public Object newObject() {

+        Account a = new Account();

+        a.setBalance(100.0);

+        return a;

+    }

+    

+    public Object findObject() {

+        EntityManager em = getEmf().createEntityManager();

+        Object obj = em.find(Account.class, 1);

+        em.close();

+        return obj;

+    }

+

+    public Object queryObjects() {

+        EntityManager em = getEmf().createEntityManager();

+        List<?> result = em.createQuery("SELECT a FROM Account a").getResultList();

+        em.close();

+        return result;

+    }

+

+    /* === Test Methods === */

+    

+    @Test

+    public void testGettingEntityManager() {

+        log("testGettingEntityManager");

+        EntityManager em = getEmf().createEntityManager();

+        log("Got EM - " + em);

+    }

+

+    @Test

+    public void testPersisting() {

+        log("testPersisting");

+        EntityManager em = getEmf().createEntityManager();

+        Object obj = newObject();

+        em.getTransaction().begin();

+        log("testPersisting - tx begun");

+        try {

+            em.persist(obj);

+        } catch (Exception e) {

+            log("Error calling persist(): ");

+            e.printStackTrace(System.out);

+        }

+        log("testPersisting - tx committing");

+        em.getTransaction().commit();

+        em.close();

+        log("Persisted " + obj);

+    }

+    

+    @Test

+    public void testFinding() {

+        log("testFinding");

+        log("Find returned - " + findObject());

+    }

+

+    @Test

+    public void testQuerying() {

+        log("testQuerying");

+        log("Query returned - " + queryObjects());

+    }

+

+    @Test

+    public void testGettingMetamodel() {

+        log("testGettingMetamodel");

+        EntityManager em = getEmf().createEntityManager();

+        Set<EntityType<?>> s = em.getMetamodel().getEntities();

+        for (EntityType<?> et : s) {

+            log("Managed Entity name: " + et.getName());

+            log("Managed Entity class: " + et.getJavaType());

+            log("Classloader: " + et.getJavaType().getClassLoader());

+        }

+    }

+    

+    /* === Helper methods === */

+

+    public static EntityManagerFactory lookupEntityManagerFactory(String testName, String puName) {

+        String filter = "(osgi.unit.name="+puName+")";

+        ServiceReference[] refs = null;

+        try {

+            refs = context.getServiceReferences(EntityManagerFactory.class.getName(), filter);

+        } catch (InvalidSyntaxException isEx) {

+            new RuntimeException("Bad filter", isEx);

+        }

+        slog(testName, "EMF Service refs looked up from registry: " + refs);

+        return (refs == null)

+            ? null

+            : (EntityManagerFactory) context.getService(refs[0]);

+    }

+    

+    public static EntityManagerFactoryBuilder lookupEntityManagerFactoryBuilder(String testName, String puName) {

+        String filter = "(osgi.unit.name="+puName+")";

+        ServiceReference[] refs = null;

+        try {

+            refs = context.getServiceReferences(EntityManagerFactoryBuilder.class.getName(), filter);

+        } catch (InvalidSyntaxException isEx) {

+            new RuntimeException("Bad filter", isEx);

+        }

+        slog(testName, "EMF Builder Service refs looked up from registry: " + refs);

+        return (refs == null)

+            ? null

+            : (EntityManagerFactoryBuilder) context.getService(refs[0]);

+    }

+

+    public static void slog(String testName, String msg) {

+        System.out.println("***** " + testName + " - " + msg);

+    }    

+    public void log(String msg) {

+        System.out.println("***** " + this.getClass().getSimpleName() + " - " + msg);

+    }    

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderExternalDataSource.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderExternalDataSource.java
new file mode 100644
index 0000000..e24a8f0
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderExternalDataSource.java
@@ -0,0 +1,100 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.sql.SQLException;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Properties;

+

+import javax.persistence.EntityManagerFactory;

+import javax.sql.DataSource;

+

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.jdbc.DataSourceFactory;

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+import org.junit.*;

+

+/**

+ * Test class to test looking up EMF Builder Service from a client

+ * for a punit that does not a have data source props specified

+ * 

+ * @author mkeith

+ */

+public class TestEMFBuilderExternalDataSource extends JpaTest {

+

+    public static final String TEST_NAME = "TestEMFBuilderExternalDataSource";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "AccountsNoDataSource";

+

+    public static EntityManagerFactory emf;

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        EntityManagerFactoryBuilder emfb = lookupEntityManagerFactoryBuilder(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+

+        DataSource ds = null;

+        try {

+            ServiceReference[] refs = context.getServiceReferences(

+                    DataSourceFactory.class.getName(), "(osgi.jdbc.driver.class=" + JDBC_TEST_DRIVER + ")");

+

+            if (refs != null) {

+            	DataSourceFactory dsf = (DataSourceFactory)context.getService(refs[0]);

+            	

+            	if ( dsf != null ) {

+	                Properties props = new Properties();        

+

+	                props.put(DataSourceFactory.JDBC_URL, JDBC_TEST_URL);

+	                props.put(DataSourceFactory.JDBC_USER, JDBC_TEST_USER);

+	                props.put(DataSourceFactory.JDBC_PASSWORD, JDBC_TEST_PASSWORD);

+	                

+	                ds = dsf.createDataSource(props);

+	                

+	                context.ungetService(refs[0]);

+            	}

+            }

+        } catch (InvalidSyntaxException e) {

+            // TODO Auto-generated catch block

+            e.printStackTrace();

+        } catch (SQLException e) {

+			// TODO Auto-generated catch block

+			e.printStackTrace();

+		}

+

+        Map<String,Object> props = new HashMap<String,Object>();        

+        props.put("javax.persistence.nonJtaDataSource", ds);

+        

+        emf = emfb.createEntityManagerFactory(props);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public boolean needsEmfService() { return false; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderService.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderService.java
new file mode 100644
index 0000000..12add83
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderService.java
@@ -0,0 +1,64 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+import org.junit.*;

+

+/**

+ * Test class to test looking up EMF Builder Service from a client

+ * 

+ * @author mkeith

+ */

+public class TestEMFBuilderService extends JpaTest {

+    

+    public static final String TEST_NAME = "TestEMFBuilderService";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "Accounts";

+

+    protected static EntityManagerFactory emf;

+

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        EntityManagerFactoryBuilder emfb = lookupEntityManagerFactoryBuilder(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        Map<String,Object> props = new HashMap<String,Object>();        

+        emf = emfb.createEntityManagerFactory(props);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public boolean needsEmfService() { return false; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderServiceProperties.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderServiceProperties.java
new file mode 100644
index 0000000..3419a4e
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFBuilderServiceProperties.java
@@ -0,0 +1,59 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+import org.junit.*;

+

+/**

+ * Test class to test looking up EMF Builder Service from a client

+ * for a punit that does not have data source props specified

+ * 

+ * @author mkeith

+ */

+public class TestEMFBuilderServiceProperties extends JpaTest {

+

+    public static final String TEST_NAME = "TestEMFBuilderServiceProperties";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "AccountsNoDataSource";

+

+    public static EntityManagerFactory emf;

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        EntityManagerFactoryBuilder emfb = lookupEntityManagerFactoryBuilder(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        emf = emfb.createEntityManagerFactory(JpaTest.defaultProps());

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public boolean needsEmfService() { return false; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFService.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFService.java
new file mode 100644
index 0000000..0a889f0
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEMFService.java
@@ -0,0 +1,55 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.junit.*;

+

+/**

+ * Test class to test looking up EMF Service from a client

+ * 

+ * @author mkeith

+ */

+public class TestEMFService extends JpaTest {

+        

+    public static final String TEST_NAME = "TestEMFService";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "Accounts";

+

+    protected static EntityManagerFactory emf;

+

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        emf = lookupEntityManagerFactory(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+    

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmbeddedPUnit.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmbeddedPUnit.java
new file mode 100644
index 0000000..86d61b1
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmbeddedPUnit.java
@@ -0,0 +1,80 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.List;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.EntityManager;

+

+import model.embeddedaccount.EmbAccount;

+

+import org.junit.*;

+

+/**

+ * Test class to test looking up EMF Service of an embedded persistence unit

+ * 

+ * @author mkeith

+ */

+public class TestEmbeddedPUnit extends JpaTest {

+        

+    public static final String TEST_NAME = "TestEmbeddedPUnit";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "EmbeddedAccounts";

+

+    protected static EntityManagerFactory emf;

+    

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        emf = lookupEntityManagerFactory(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public Object newObject() {

+        EmbAccount a = new EmbAccount();

+        a.setBalance(100.0);

+        return a;

+    }

+    

+    public Object findObject() {

+        EntityManager em = emf.createEntityManager();

+        Object obj = em.find(EmbAccount.class, 1);

+        em.close();

+        return obj;

+    }

+

+    public Object queryObjects() {

+        EntityManager em = emf.createEntityManager();

+        List<?> result = em.createQuery("SELECT a FROM EmbAccount a").getResultList();

+        em.close();

+        return result;

+    }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistence.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistence.java
new file mode 100644
index 0000000..f3c0e66
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistence.java
@@ -0,0 +1,71 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.junit.*;

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+/**

+ * Test class to test empty persistence unit using OSGi JPA services

+ * 

+ * @author mkeith

+ */

+public class TestEmptyPersistence extends JpaTest {

+    

+    public static final String TEST_NAME = "TestEmptyPersistenceUnit";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "Empty1";

+

+    public static EntityManagerFactory emf;

+

+    public static boolean shouldRun(String unitName, boolean isEMF) {

+        return PERSISTENCE_UNIT_UNDER_TEST.equals(unitName) && !isEMF;

+    }

+    

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        EntityManagerFactoryBuilder emfb = lookupEntityManagerFactoryBuilder(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        emf = emfb.createEntityManagerFactory(JpaTest.defaultProps());

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    public void testPersisting() {

+        log("overridden testPersisting");

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public boolean needsEmfService() { return false; }

+

+    public Object newObject() { return null; }

+    public Object findObject() { return null; }

+    public Object queryObjects() { return null; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistenceWithProps.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistenceWithProps.java
new file mode 100644
index 0000000..4e0f32e
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestEmptyPersistenceWithProps.java
@@ -0,0 +1,71 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.junit.*;

+import org.osgi.service.jpa.EntityManagerFactoryBuilder;

+

+/**

+ * Test class to test empty persistence unit using OSGi JPA services

+ * 

+ * @author mkeith

+ */

+public class TestEmptyPersistenceWithProps extends JpaTest {

+    

+    public static final String TEST_NAME = "TestEmptyPersistenceUnitWithProps";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "Empty2";

+

+    public static EntityManagerFactory emf;

+    

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        EntityManagerFactoryBuilder emfb = lookupEntityManagerFactoryBuilder(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        Map<String,Object> props = new HashMap<String,Object>();        

+        emf = emfb.createEntityManagerFactory(props);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    public void testPersisting() {

+        log("overridden testPersisting");

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public boolean needsEmfService() { return false; }

+

+    public Object newObject() { return null; }

+    public Object findObject() { return null; }

+    public Object queryObjects() { return null; }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestMappingFileElement.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestMappingFileElement.java
new file mode 100644
index 0000000..d8d06f5
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestMappingFileElement.java
@@ -0,0 +1,82 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.List;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.EntityManager;

+

+import model.xmlmapped.SimpleEntity2;

+

+import org.junit.*;

+

+/**

+ * Test class for an entity mapped in orm.xml

+ * 

+ * @author mkeith

+ */

+public class TestMappingFileElement extends JpaTest {

+        

+    public static final String TEST_NAME = "TestMappingFileElement";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "XmlMapped";

+

+    protected static EntityManagerFactory emf;

+    

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        emf = lookupEntityManagerFactory(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public Object newObject() {

+        SimpleEntity2 a = new SimpleEntity2();

+        a.setId(20);

+        a.setSimpleInt(21);

+        return a;

+    }

+    

+    public Object findObject() {

+        EntityManager em = emf.createEntityManager();

+        Object obj = em.find(SimpleEntity2.class, 1);

+        em.close();

+        return obj;

+    }

+

+    public Object queryObjects() {

+        EntityManager em = emf.createEntityManager();

+        List<?> result = em.createQuery("SELECT a FROM SimpleEntity2 a").getResultList();

+        assert(result.size() == 1);

+        em.close();

+        return result;

+    }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestOrmMappingFile.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestOrmMappingFile.java
new file mode 100644
index 0000000..659ee00
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestOrmMappingFile.java
@@ -0,0 +1,82 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import java.util.List;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.EntityManager;

+

+import model.xmlmapped.SimpleEntity;

+

+import org.junit.*;

+

+/**

+ * Test class for an entity mapped in orm.xml

+ * 

+ * @author mkeith

+ */

+public class TestOrmMappingFile extends JpaTest {

+        

+    public static final String TEST_NAME = "TestOrmMappingFile";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "XmlMapped";

+

+    protected static EntityManagerFactory emf;

+    

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        emf = lookupEntityManagerFactory(TEST_NAME, PERSISTENCE_UNIT_UNDER_TEST);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+

+    public Object newObject() {

+        SimpleEntity a = new SimpleEntity();

+        a.setId(10);

+        a.setSimpleString("11");

+        return a;

+    }

+    

+    public Object findObject() {

+        EntityManager em = emf.createEntityManager();

+        Object obj = em.find(SimpleEntity.class, 1);

+        em.close();

+        return obj;

+    }

+

+    public Object queryObjects() {

+        EntityManager em = emf.createEntityManager();

+        List<?> result = em.createQuery("SELECT a FROM SimpleEntity a").getResultList();

+        assert(result.size() == 1);

+        em.close();

+        return result;

+    }

+}

diff --git a/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestStaticPersistence.java b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestStaticPersistence.java
new file mode 100644
index 0000000..5e2ae2d
--- /dev/null
+++ b/org.eclipse.gemini.jpa.tests/src/org/eclipse/gemini/jpa/tests/TestStaticPersistence.java
@@ -0,0 +1,56 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA tests 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.tests;

+

+import javax.persistence.Persistence;

+import javax.persistence.EntityManagerFactory;

+

+import org.junit.*;

+

+/**

+ * Test class to test static Persistence class using OSGi JPA services

+ * 

+ * @author mkeith

+ */

+public class TestStaticPersistence extends JpaTest {

+    

+    public static final String TEST_NAME = "TestStaticPersistence";

+    public static final String PERSISTENCE_UNIT_UNDER_TEST = "Accounts";

+

+    public static EntityManagerFactory emf;

+

+    /* === Test Methods === */

+

+    @BeforeClass

+    public static void classSetUp() {

+        slog(TEST_NAME, "In setup");

+        emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_UNDER_TEST);

+        slog(TEST_NAME, "Got EMF - " + emf);

+    }

+

+    @AfterClass

+    public static void classCleanUp() throws Exception {

+        if (emf != null) {

+            emf.close();

+            emf = null;

+        }

+    }

+

+    /* === Subclassed methods === */

+

+    public EntityManagerFactory getEmf() { return emf; }

+

+    public String getTestPersistenceUnitName() { return PERSISTENCE_UNIT_UNDER_TEST; }

+}

diff --git a/org.eclipse.gemini.jpa.weaving/.classpath b/org.eclipse.gemini.jpa.weaving/.classpath
new file mode 100644
index 0000000..e179e61
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/**" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa.weaving/.options b/org.eclipse.gemini.jpa.weaving/.options
new file mode 100644
index 0000000..aea826c
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/.options
@@ -0,0 +1 @@
+org.eclipse.gemini.jpa.weaving/debug=false
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.weaving/.project b/org.eclipse.gemini.jpa.weaving/.project
new file mode 100644
index 0000000..3374366
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa.weaving</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa.weaving/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa.weaving/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..983f2d7
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Wed Jul 14 15:50:45 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa.weaving/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa.weaving/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..eccb0d5
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0

+Bundle-ManifestVersion: 2

+Bundle-Name: Gemini JPA Dynamic Weaving Fragment

+Bundle-SymbolicName: org.eclipse.gemini.jpa.weaving

+Bundle-Version: 1.0.0.RC2

+Bundle-Vendor: Oracle Corp.

+Bundle-RequiredExecutionEnvironment: J2SE-1.5

+Export-Package: org.eclipse.gemini.jpa.weaving,

+ org.eclipse.gemini.jpa.weaving.equinox

+Fragment-Host: system.bundle;extension:=framework

+

diff --git a/org.eclipse.gemini.jpa.weaving/build.properties b/org.eclipse.gemini.jpa.weaving/build.properties
new file mode 100644
index 0000000..41eb6ad
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/build.properties
@@ -0,0 +1,4 @@
+source.. = src/

+output.. = bin/

+bin.includes = META-INF/,\

+               .

diff --git a/org.eclipse.gemini.jpa.weaving/hookconfigurators.properties b/org.eclipse.gemini.jpa.weaving/hookconfigurators.properties
new file mode 100644
index 0000000..74d100b
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/hookconfigurators.properties
@@ -0,0 +1 @@
+hook.configurators=org.eclipse.gemini.jpa.weaving.equinox.WeavingConfigurator
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/IWeaver.java b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/IWeaver.java
new file mode 100644
index 0000000..304500c
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/IWeaver.java
@@ -0,0 +1,30 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith, tware - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.weaving;

+

+import org.osgi.framework.Version;

+

+/** 

+ * An interface used to register a byte code weaving service that

+ * would typically wrap a ClassTransformer.

+ * 

+ * @see javax.persistence.spi.ClassTransormer

+ * 

+ * @author shsmith

+ */

+public interface IWeaver {

+	byte[] transform(String className, String bundleId, Version bundleVersion, byte[] classfileBuffer);

+}

+

diff --git a/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeaverRegistry.java b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeaverRegistry.java
new file mode 100644
index 0000000..7a16734
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeaverRegistry.java
@@ -0,0 +1,122 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith, tware - EclipseLink integration

+ ******************************************************************************/

+ package org.eclipse.gemini.jpa.weaving.equinox;

+

+import java.security.ProtectionDomain;

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+

+import org.eclipse.gemini.jpa.weaving.IWeaver;

+import org.eclipse.osgi.baseadaptor.BaseData;

+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;

+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;

+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;

+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;

+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;

+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;

+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.ServiceReference;

+import org.osgi.util.tracker.ServiceTracker;

+import org.osgi.util.tracker.ServiceTrackerCustomizer;

+

+public class WeaverRegistry implements ClassLoadingHook, ServiceTrackerCustomizer {

+	private static WeaverRegistry instance = new WeaverRegistry();

+	private List<ServiceReference> weaverServices = new ArrayList<ServiceReference>();

+	private BundleContext ctx;

+	private ServiceTracker serviceTracker;

+	

+	private WeaverRegistry() {}

+	

+	public static WeaverRegistry getInstance() {

+		return instance;

+	}

+

+    public boolean addClassPathEntry(@SuppressWarnings("rawtypes") ArrayList cpEntries, String cp,

+			ClasspathManager hostmanager, BaseData sourcedata,

+			ProtectionDomain sourcedomain) {

+		return false;

+	}

+

+	public BaseClassLoader createClassLoader(ClassLoader parent,

+			ClassLoaderDelegate delegate, BundleProtectionDomain domain,

+			BaseData data, String[] bundleclasspath) {

+		return null;

+	}

+

+	public String findLibrary(BaseData data, String libName) {

+		return null;

+	}

+

+	public ClassLoader getBundleClassLoaderParent() {

+		return null;

+	}

+

+	public void initializedClassLoader(BaseClassLoader baseClassLoader,

+			BaseData data) {		

+	}

+

+	public byte[] processClass(String name, byte[] classbytes,

+			ClasspathEntry classpathEntry, BundleEntry entry,

+			ClasspathManager manager) {

+		if (this.weaverServices.isEmpty()) {

+			return null;

+		}

+		for (Iterator<ServiceReference> iterator = this.weaverServices.iterator(); iterator.hasNext();) {

+			ServiceReference reference = iterator.next();

+			IWeaver weaver = (IWeaver)ctx.getService(reference);

+			if (weaver != null) {

+				BaseData baseData = manager.getBaseData();

+				byte[] transformedBytes = weaver.transform(name, baseData.getSymbolicName(), baseData.getVersion(), classbytes);

+				if (transformedBytes != null) {

+					return transformedBytes;

+				}

+			}

+		}

+		return null;

+	}

+

+	public void start(BundleContext context) {

+		this.ctx = context;

+		serviceTracker = new ServiceTracker(context, IWeaver.class.getName(), this);

+		serviceTracker.open();

+	}

+

+	public void stop(BundleContext context) {

+		// Close the service tracker

+		serviceTracker.close();

+		serviceTracker = null;

+		weaverServices = new ArrayList<ServiceReference>();

+	}

+	

+	public Object addingService(ServiceReference reference) {

+        if (System.getProperty("GEMINI_DEBUG_XML") != null) {

+        	System.out.println("Registering Service " + reference);

+        }

+		this.weaverServices.add(reference);

+		return reference;

+	}

+

+	public void modifiedService(ServiceReference reference, Object service) {

+		// Rogue provider -- we don't support modifying provider services

+		removedService(reference, service);

+	}

+

+	public void removedService(ServiceReference reference, Object service) {

+		this.weaverServices.remove(reference);				

+	}

+

+}

diff --git a/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingAdaptor.java b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingAdaptor.java
new file mode 100644
index 0000000..28fd2a3
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingAdaptor.java
@@ -0,0 +1,62 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith, tware - EclipseLink integration

+ ******************************************************************************/

+ package org.eclipse.gemini.jpa.weaving.equinox;

+

+import java.io.IOException;

+import java.net.URLConnection;

+import java.util.Properties;

+

+import org.eclipse.osgi.baseadaptor.BaseAdaptor;

+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;

+import org.eclipse.osgi.framework.log.FrameworkLog;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.BundleException;

+

+public class WeavingAdaptor implements AdaptorHook {

+

+	public void frameworkStart(BundleContext context) throws BundleException {

+		WeaverRegistry.getInstance().start(context);

+	}

+

+	public void frameworkStopping(BundleContext context) {

+		WeaverRegistry.getInstance().stop(context);

+

+	}

+

+	public void addProperties(Properties properties) {

+	}

+

+	public FrameworkLog createFrameworkLog() {

+		return null;

+	}

+

+	public void frameworkStop(BundleContext context) throws BundleException {

+	}

+

+	public void handleRuntimeError(Throwable error) {

+	}

+

+	public void initialize(BaseAdaptor adaptor) {

+	}

+

+	public URLConnection mapLocationToURLConnection(String location)

+			throws IOException {

+		return null;

+	}

+

+	public boolean matchDNChain(String pattern, String[] dnChain) {

+		return false;

+	}

+}

diff --git a/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingConfigurator.java b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingConfigurator.java
new file mode 100644
index 0000000..19fd6b8
--- /dev/null
+++ b/org.eclipse.gemini.jpa.weaving/src/org/eclipse/gemini/jpa/weaving/equinox/WeavingConfigurator.java
@@ -0,0 +1,39 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith, tware - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.weaving.equinox;

+

+import org.eclipse.osgi.baseadaptor.HookConfigurator;

+import org.eclipse.osgi.baseadaptor.HookRegistry;

+import org.eclipse.osgi.framework.debug.Debug;

+import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;

+

+public class WeavingConfigurator implements HookConfigurator {

+

+    private static final String OPTION_DEBUG = "org.eclipse.gemini.jpa.weaving/debug";

+

+    public WeavingConfigurator() {

+        super();

+    }

+

+    public void addHooks(HookRegistry hookRegistry) {

+        FrameworkDebugOptions dbgOptions = FrameworkDebugOptions.getDefault();

+        boolean debug = dbgOptions.getBooleanOption(OPTION_DEBUG, false);

+        if (debug) {

+            Debug.println("Gemini JPA: Adding WeaverRegistry Class Loading Hook"); //$NON-NLS-1$

+        }

+        hookRegistry.addClassLoadingHook(WeaverRegistry.getInstance());

+        hookRegistry.addAdaptorHook(new WeavingAdaptor());

+    }

+}

diff --git a/org.eclipse.gemini.jpa/.classpath b/org.eclipse.gemini.jpa/.classpath
new file mode 100644
index 0000000..335ba65
--- /dev/null
+++ b/org.eclipse.gemini.jpa/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry excluding="**/.svn/*" kind="src" path="src"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.gemini.jpa/.project b/org.eclipse.gemini.jpa/.project
new file mode 100644
index 0000000..35a7e04
--- /dev/null
+++ b/org.eclipse.gemini.jpa/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.gemini.jpa</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.gemini.jpa/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.gemini.jpa/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..97cc081
--- /dev/null
+++ b/org.eclipse.gemini.jpa/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Thu May 27 18:21:32 EDT 2010

+eclipse.preferences.version=1

+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled

+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5

+org.eclipse.jdt.core.compiler.compliance=1.5

+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error

+org.eclipse.jdt.core.compiler.source=1.5

diff --git a/org.eclipse.gemini.jpa/.settings/org.eclipse.pde.core.prefs b/org.eclipse.gemini.jpa/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..adff9e5
--- /dev/null
+++ b/org.eclipse.gemini.jpa/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Wed Nov 25 13:45:02 EST 2009

+eclipse.preferences.version=1

+pluginProject.equinox=false

+pluginProject.extensions=false

+resolve.requirebundle=false

diff --git a/org.eclipse.gemini.jpa/META-INF/MANIFEST.MF b/org.eclipse.gemini.jpa/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c69d98a
--- /dev/null
+++ b/org.eclipse.gemini.jpa/META-INF/MANIFEST.MF
@@ -0,0 +1,29 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Gemini JPA
+Bundle-SymbolicName: org.eclipse.gemini.jpa
+Bundle-Version: 1.0.0.RC2
+Bundle-Activator: org.eclipse.gemini.jpa.provider.EclipseLinkOSGiProvider
+Bundle-Vendor: Oracle Corporation
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.naming,
+ javax.persistence;jpa="2.0";version="1.1.0",
+ javax.persistence.criteria;jpa="2.0";version="1.1.0",
+ javax.persistence.metamodel;jpa="2.0";version="1.1.0",
+ javax.persistence.spi;jpa="2.0";version="1.1.0",
+ javax.sql,
+ javax.xml.parsers,
+ org.eclipse.gemini.jpa.weaving,
+ org.osgi.framework;version="1.5.0",
+ org.osgi.service.jdbc,
+ org.osgi.service.jpa,
+ org.osgi.service.packageadmin;version="1.2.0",
+ org.osgi.util.tracker,
+ org.xml.sax,
+ org.xml.sax.helpers
+Bundle-ClassPath: .
+Require-Bundle: org.eclipse.persistence.asm;bundle-version="2.3",
+ org.eclipse.persistence.antlr;bundle-version="2.3",
+ org.eclipse.persistence.core;bundle-version="2.3",
+ org.eclipse.persistence.jpa;bundle-version="2.3"
+Export-Package: org.eclipse.gemini.jpa.provider
diff --git a/org.eclipse.gemini.jpa/build.properties b/org.eclipse.gemini.jpa/build.properties
new file mode 100644
index 0000000..4acf9bd
--- /dev/null
+++ b/org.eclipse.gemini.jpa/build.properties
@@ -0,0 +1,5 @@
+source.. = src/

+output.. = bin/

+bin.includes = .,\

+               META-INF/

+jars.compile.order = .

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/DataSourceTracker.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/DataSourceTracker.java
new file mode 100644
index 0000000..d890ae9
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/DataSourceTracker.java
@@ -0,0 +1,52 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import org.osgi.framework.ServiceReference;

+import org.osgi.util.tracker.ServiceTrackerCustomizer;

+

+/**

+ *  Service Tracker that tracks the DataSourceFactory service

+ *  for a given EMF service.

+ */

+public class DataSourceTracker implements ServiceTrackerCustomizer {

+

+    // Look for data source factory for this persistence unit

+    private PUnitInfo pUnitInfo;

+

+    // Tell this guy when the service disappears

+    GeminiServicesUtil servicesUtil;

+        

+    public DataSourceTracker(PUnitInfo pUnitInfo,

+                             GeminiServicesUtil servicesUtil) {

+        this.pUnitInfo = pUnitInfo;

+        this.servicesUtil = servicesUtil;

+    }

+    

+    public Object addingService(ServiceReference ref) {

+        return pUnitInfo.getAssignedProvider()

+                        .getBundleContext()

+                        .getService(ref);

+        // TODO We would like to be calling 

+        //       servicesUtil.dataSourceFactoryAdded(pUnitInfo)

+        // but that would involve doing all kinds of EMF service registration

+    }

+

+    public void modifiedService(ServiceReference ref, Object service) {}

+

+    public void removedService(ServiceReference ref, Object service) {

+        servicesUtil.dataSourceFactoryRemoved(pUnitInfo);

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiServicesUtil.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiServicesUtil.java
new file mode 100644
index 0000000..290f081
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiServicesUtil.java
@@ -0,0 +1,559 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.bundleVersion;

+import static org.eclipse.gemini.jpa.GeminiUtil.debug;

+import static org.eclipse.gemini.jpa.GeminiUtil.fatalError;

+import static org.eclipse.gemini.jpa.GeminiUtil.warning;

+

+import java.lang.reflect.Proxy;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Dictionary;

+import java.util.Hashtable;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+

+import org.eclipse.gemini.jpa.classloader.BundleProxyClassLoader;

+import org.eclipse.gemini.jpa.classloader.CompositeClassLoader;

+import org.eclipse.gemini.jpa.provider.OSGiJpaProvider;

+import org.eclipse.gemini.jpa.proxy.EMFBuilderServiceProxyHandler;

+import org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+import org.osgi.framework.ServiceRegistration;

+import org.osgi.service.jdbc.DataSourceFactory;

+import org.osgi.util.tracker.ServiceTracker;

+

+/**

+ * This class provides functionality to handle service registration of 

+ * persistence units and providers, etc. One instance per provider.

+ */

+public class GeminiServicesUtil {

+

+    // The provider using this instance

+    OSGiJpaProvider osgiJpaProvider;

+    

+    // Keep this for logging convenience

+    String providerClassName;

+   

+    // The anchor util to use to get anchor class info from

+    AnchorClassUtil anchorUtil;

+    

+    // PersistenceProvider service

+    ServiceRegistration providerService;

+    

+    

+    public GeminiServicesUtil(OSGiJpaProvider provider, AnchorClassUtil anchorUtil) {

+        this.osgiJpaProvider = provider;

+        this.providerClassName = provider.getProviderClassName();

+        this.anchorUtil= anchorUtil;

+    }

+    

+    /*==================*/

+    /* Services methods */

+    /*==================*/

+    

+    /**

+     * Register the provider as a persistence provider service.

+     * The service registration will be stored locally.

+     */

+    public void registerProviderService() {

+        

+        debug("GeminiServicesUtil registering provider service for ", providerClassName);

+

+        // Service strings

+        String[] serviceNames = { javax.persistence.spi.PersistenceProvider.class.getName() };

+        // Get a provider JPA SPI instance 

+        javax.persistence.spi.PersistenceProvider persistenceProvider = osgiJpaProvider.getProviderInstance();

+

+        // Store the version of the provider as a service property

+        String version = bundleVersion(osgiJpaProvider.getBundle());

+        Dictionary<String,String> props = new Hashtable<String,String>();

+        props.put("osgi.jpa.provider.version", version);

+        props.put("javax.persistence.provider", providerClassName);

+        

+        // Register the provider service

+        providerService = osgiJpaProvider.getBundleContext().registerService(

+                serviceNames, persistenceProvider, props);

+        debug("GeminiServicesUtil successfully registered provider service for ", providerClassName);

+    }    

+

+    /**

+     * Unregister the provider service. 

+     */

+    public void unregisterProviderService() {

+

+        debug("GeminiServicesUtil unregistering provider service for ", providerClassName);

+        providerService.unregister();

+        providerService = null;

+        debug("GeminiServicesUtil successfully unregistered provider service for ", providerClassName);

+    }

+

+    /**

+     * Register the EMF and EMFBuilder services.

+     */

+    public void registerEMFServices(PUnitInfo pUnitInfo) {

+

+        debug("GeminiServicesUtil registerEMFServices for ", pUnitInfo.getUnitName());

+

+        // Map of generated anchor classes keyed by class name

+        Map<String, Class<?>> anchorClasses; 

+        

+        // Will be empty if anchor classes not generated

+        anchorClasses = anchorUtil.loadAnchorClasses(pUnitInfo);

+

+        // Create the properties used for both services

+        Dictionary<String,String> props = buildServiceProperties(pUnitInfo);

+        

+        // Try to register the EMF service (it will only occur if data source is available)

+        tryToRegisterEMFService(pUnitInfo, anchorClasses, props);                

+

+        // Create a builder service in any case

+        registerEMFBuilderService(pUnitInfo, anchorClasses, props);

+    }

+

+    /**

+     * Unregister the EMF service if there was an EMF service registered.

+     * 

+     * @param pUnitInfo

+     * 

+     * @return EntityManagerFactory the factory backing the service, or null if one didn't exist

+     */

+    public EntityManagerFactory unregisterEMFService(PUnitInfo pUnitInfo) {

+

+        debug("GeminiServicesUtil unregisterEMFService for ", pUnitInfo.getUnitName());

+

+        // If the tracking service is going, stop it

+        // TODO Take this out when we handle the DSF coming back?

+        stopTrackingDataSourceFactory(pUnitInfo);

+

+        // If an EMF service is registered then unregister it

+        ServiceRegistration emfService = pUnitInfo.getEmfService();

+        if (emfService != null) {

+            debug("GeminiServicesUtil unregistering EMF service for ", pUnitInfo.getUnitName());

+            try { 

+                emfService.unregister(); 

+            } catch (Exception e) {

+                warning("Error unregistering EMF service: ", e);

+            }

+            debug("GeminiServicesUtil unregistered EMF service for ", pUnitInfo.getUnitName());

+            pUnitInfo.setEmfService(null);

+        }

+

+        EMFServiceProxyHandler emfHandler = pUnitInfo.getEmfHandler();

+        EntityManagerFactory emf = null;

+        if (emfHandler != null) {

+            emf = emfHandler.getEMF();

+            debug("GeminiServicesUtil EMF service had emf: ", emf);

+            emfHandler.setEMF(null);

+            pUnitInfo.setEmfHandler(null);

+        }

+        return emf;

+    }

+

+    /**

+     * Unregister the EMFBuilder service.

+     * 

+     * @param pUnitInfo

+     * 

+     * @return EntityManagerFactory the factory backing the service, or null if one didn't exist yet

+     */

+    public EntityManagerFactory unregisterEMFBuilderService(PUnitInfo pUnitInfo) {

+

+        debug("GeminiServicesUtil unregisterEMFBuilderService for ", pUnitInfo.getUnitName());

+

+        // Unregister the service

+        ServiceRegistration emfBuilderService = pUnitInfo.getEmfBuilderService();

+        if (emfBuilderService != null) {

+            debug("GeminiServicesUtil unregistering EMFBuilder service for ", pUnitInfo.getUnitName());

+            try {

+                emfBuilderService.unregister();

+            } catch (Exception e) {

+                warning("Error unregistering EMFBuilder service: ", e);

+            }

+            debug("GeminiServicesUtil unregistered EMFBuilder service for ", pUnitInfo.getUnitName());

+            pUnitInfo.setEmfBuilderService(null);

+        }

+

+        // Save the EMF if it exists and clear out the handler

+        EMFBuilderServiceProxyHandler emfBuilderHandler = pUnitInfo.getEmfBuilderHandler();

+        EntityManagerFactory emf = null;

+        if (emfBuilderHandler != null) {

+            emf = emfBuilderHandler.getEMF();

+            debug("GeminiServicesUtil EMFBuilder service had emf: ", emf);

+            emfBuilderHandler.setEMF(null);

+            pUnitInfo.setEmfBuilderHandler(null);

+        }

+        return emf;

+    }    

+    

+    /*================*/

+    /* Helper methods */

+    /*================*/

+        

+    /**

+     * Get or create a loader to load classes from the punit.

+     * A sequence of conditions provides a pattern for obtaining it.

+     */

+    ClassLoader extractPUnitLoader(PUnitInfo pUnitInfo, 

+                                   Map<String, Class<?>> anchorClasses) {

+    

+        ClassLoader pUnitLoader = null;

+        

+        // 1. If there are any anchor classes then load one and get its loader

+        if (!anchorClasses.isEmpty()) {

+            pUnitLoader = anchorClasses.values().iterator().next().getClassLoader();

+

+        // 2. Otherwise, if there are managed JPA classes listed, use one to get the loader

+        } else if (!pUnitInfo.getClasses().isEmpty()) {

+            try { 

+                pUnitLoader = pUnitInfo.getBundle().loadClass((String)(pUnitInfo.getClasses().toArray()[0])).getClassLoader();

+            } catch (ClassNotFoundException cnfEx) {

+                fatalError("Could not load domain class in p-unit", cnfEx);

+            }

+            

+        // 3. If all else fails just use a proxy loader

+        } else {

+            pUnitLoader = new BundleProxyClassLoader(pUnitInfo.getBundle());

+        }

+        debug("GeminiServicesUtil pUnit loader ", pUnitLoader);

+        return pUnitLoader;

+    }

+

+    /**

+     * Get or create a loader to use to create a proxy class.

+     */

+    ClassLoader proxyLoader(PUnitInfo pUnitInfo,

+                            Map<String, Class<?>> anchorClasses, 

+                            Class<?> jpaClass) {

+        

+        ClassLoader cl = null;

+

+        // If there are no managed JPA classes listed, return loader used to load the class passed in

+        if (pUnitInfo.getClasses().isEmpty()) {

+            cl = jpaClass.getClassLoader();

+        } else if (!anchorClasses.isEmpty()) {

+            // If anchor classes exist then get a loader from one of them

+            cl = anchorClasses.values().iterator().next().getClassLoader();

+        } else {

+            try {

+                // We have domain classes, but no anchor classes were generated.

+                // Load a domain class and get a loader from it. Combine it with the provider loader.

+                ClassLoader pUnitLoader = 

+                    pUnitInfo.getBundle().loadClass((String)(pUnitInfo.getClasses().toArray()[0])).getClassLoader();

+                ClassLoader jpaClassLoader = jpaClass.getClassLoader();

+                cl = (pUnitLoader == jpaClassLoader) 

+                    ? jpaClassLoader 

+                    : new CompositeClassLoader(pUnitLoader, jpaClassLoader);

+            } catch (ClassNotFoundException cnfEx) {

+                fatalError("Could not load domain class in p-unit", cnfEx);

+            }

+        }

+        debug("GeminiServicesUtil proxy loader ", cl);

+        return cl;

+    }

+

+

+    /** 

+     * Load the EMF class from the specified bundle.

+     * Throw a fatal exception if not found. 

+     */

+    Class<?> loadEMFClass(Bundle b) {

+

+        debug("GeminiServicesUtil loading EMF class");

+        try {

+            return b.loadClass("javax.persistence.EntityManagerFactory");

+        } catch (ClassNotFoundException cnfEx) {

+            fatalError("Could not load EntityManagerFactory from bundle " + b, cnfEx);

+        }

+        return null;

+    }

+    

+    /** 

+     * Load the EMFBuilder class from the specified bundle.

+     * Throw a fatal exception if not found. 

+     */

+    Class<?> loadEMFBuilderClass(Bundle b) {

+

+        debug("GeminiServicesUtil loading EMFBuilder class");

+        try {

+            return b.loadClass("org.osgi.service.jpa.EntityManagerFactoryBuilder");

+        } catch (ClassNotFoundException cnfEx) {

+            fatalError("Could not load EntityManagerFactoryBuilder from bundle " + b, cnfEx);

+        }

+        return null;

+    }

+

+    /** 

+     * Create and return a proxy for the EMF (and specified list of classes

+     * which must include the EMF class).

+     */

+    Object createEMFProxy(PUnitInfo pUnitInfo, ClassLoader loader, Class<?>[] clsArray) {

+

+        EMFServiceProxyHandler emfProxyHandler = new EMFServiceProxyHandler(pUnitInfo);

+        Object result = null;

+        try {

+            result = Proxy.newProxyInstance(loader, clsArray, emfProxyHandler);

+            debug("GeminiServicesUtil created EMF proxy ");

+        } catch (Exception e) { 

+            fatalError("GeminiServicesUtil - Failed to create proxy for EMF service: ", e); 

+        }

+        pUnitInfo.setEmfHandler(emfProxyHandler);

+        return result;

+    }

+    

+    /** 

+     * Create and return a proxy for the EMFBuilder (and specified list of classes

+     * which must include the EMFBuilder class).

+     */

+    Object createEMFBuilderProxy(PUnitInfo pUnitInfo, 

+                                        ClassLoader loader, 

+                                        Class<?>[] clsArray) {

+        

+        // Assume that EMF proxy handler has been created and is stored in pUnitInfo

+        EMFBuilderServiceProxyHandler emfBuilderProxyHandler = 

+            new EMFBuilderServiceProxyHandler(pUnitInfo, pUnitInfo.getEmfHandler());

+        Object result = null;

+        try {

+            result = Proxy.newProxyInstance(loader, clsArray, emfBuilderProxyHandler);

+            debug("GeminiServicesUtil created EMFBuilder proxy ");

+        } catch (Exception e) { 

+            fatalError("GeminiServicesUtil - Failed to create proxy for EMFBuilder service: ", e); 

+        }

+        pUnitInfo.setEmfBuilderHandler(emfBuilderProxyHandler);

+        return result;

+    }

+    

+    /** 

+     * build the list of service properties for the service.

+     */

+    Dictionary<String,String> buildServiceProperties(PUnitInfo pUnitInfo) {

+

+        Bundle pUnitBundle = pUnitInfo.getBundle();

+        // Assemble the properties

+        Dictionary<String,String> props = new Hashtable<String,String>();

+        props.put("osgi.unit.name", pUnitInfo.getUnitName());

+        props.put("osgi.unit.version", bundleVersion(pUnitInfo.getBundle()));

+        props.put("osgi.unit.provider", providerClassName);

+        // For now, only support punits composed of one bundle

+        String bundleId = pUnitBundle.getSymbolicName() + "_" + bundleVersion(pUnitBundle);

+        props.put("osgi.managed.bundles", bundleId);

+        debug("GeminiServicesUtil EMF[Builder] services props: ", props);

+        return props;

+    }

+

+    /** 

+     * Register the EMF service.

+     */

+    void tryToRegisterEMFService(PUnitInfo pUnitInfo,

+                                        Map<String,Class<?>> anchorClasses,

+                                        Dictionary<String,String> props) {

+

+        debug("GeminiServicesUtil register EMF service");

+        // Array of classes being proxied by EMF proxy

+        Collection<Class<?>> proxiedClasses = new ArrayList<Class<?>>();

+

+        // Load the EMF class. TODO Make this the pUnit loader when fragment in place

+        Class<?> emfClass = loadEMFClass(osgiJpaProvider.getBundle());

+        

+        // Add EMF class and anchor classes to the proxied class collection for EMF proxy

+        proxiedClasses.addAll(anchorClasses.values());

+        proxiedClasses.add(emfClass);

+        Class<?>[] classArray = proxiedClasses.toArray(new Class[0]);

+        debug("GeminiServicesUtil EMF proxy class array: ", classArray);

+        

+        // Get a loader to load the proxy classes

+        ClassLoader loader = proxyLoader(pUnitInfo, anchorClasses, emfClass);

+

+        // Create proxy impl object for EMF service

+        Object emfServiceProxy = createEMFProxy(pUnitInfo, loader, classArray);

+

+        // Do we create an EMF service?

+        if (pUnitInfo.getDriverClassName() == null) {

+            debug("GeminiServicesUtil No driver class specified so no factory service created");            

+        } else {

+            if (!trackDataSourceFactory(pUnitInfo)) {

+                warning("DataSourceFactory for " + pUnitInfo.getDriverClassName(), " not registered.");

+            } else {

+                // Convert array of classes to class name strings

+                String[] classNameArray = new String[classArray.length];

+                for (int i=0; i<classArray.length; i++)

+                    classNameArray[i] = classArray[i].getName();

+

+                // Register the EMF service (using p-unit context) and set registration in PUnitInfo

+                ServiceRegistration emfService = null;

+                try {

+                    emfService = pUnitInfo.getBundle().getBundleContext()

+                                   .registerService(classNameArray, emfServiceProxy, props);

+                    debug("GeminiServicesUtil EMF service: ", emfService);

+                } catch (Exception e) {

+                    fatalError("GeminiServicesUtil could not register EMF service for " + pUnitInfo.getUnitName(), e);

+                }

+                pUnitInfo.setEmfService(emfService);

+                debug("GeminiServicesUtil registered EMF service for ", pUnitInfo.getUnitName());

+            }

+        }

+    }

+    

+    

+    /** 

+     * Register the EMFBuilder service.

+     */

+    void registerEMFBuilderService(PUnitInfo pUnitInfo,

+                                          Map<String,Class<?>> anchorClasses,

+                                          Dictionary<String,String> props) {

+    

+        debug("GeminiServicesUtil register EMFBuilder service");

+        // Array of classes being proxied by EMFBuilder proxy

+        Collection<Class<?>> proxiedClasses = new ArrayList<Class<?>>();

+

+        // Load the EMFB class. TODO Make this the pUnit loader when fragment in place

+        Class<?> emfBuilderClass = loadEMFBuilderClass(osgiJpaProvider.getBundle());

+

+        // Add EMF class and anchor classes to the proxied class collection for EMF proxy

+        proxiedClasses.addAll(anchorClasses.values());

+        proxiedClasses.add(emfBuilderClass);

+        debug("GeminiServicesUtil EMFBuilder proxied classes: ", proxiedClasses);

+        Class<?>[] classArray = proxiedClasses.toArray(new Class[0]);

+        

+        // Get a loader to load the proxy classes

+        ClassLoader loader = proxyLoader(pUnitInfo, anchorClasses, emfBuilderClass);

+

+        // Create proxy impl object for EMF service

+        Object emfBuilderServiceProxy = createEMFBuilderProxy(pUnitInfo, loader, classArray);

+

+        // Convert array of classes to class name strings

+        String[] classNameArray = new String[classArray.length];

+        for (int i=0; i<classArray.length; i++)

+            classNameArray[i] = classArray[i].getName();

+    

+        //Register the EMFBuilder service and set it in the PUnitInfo

+        ServiceRegistration emfBuilderService = null;

+        try {

+            // TODO Should be registered by p-unit context, not provider context

+            // emfBuilderService = pUnitInfo.getBundle().getBundleContext()

+            emfBuilderService = osgiJpaProvider.getBundleContext()

+                    .registerService(classNameArray, emfBuilderServiceProxy, props);

+        } catch (Exception e) {

+            fatalError("GeminiServicesUtil could not register EMFBuilder service for " + pUnitInfo.getUnitName(), e);

+        }

+        pUnitInfo.setEmfBuilderService(emfBuilderService);

+        debug("GeminiServicesUtil registered EMFBuilder service for ", pUnitInfo.getUnitName());

+

+    }    

+

+    /*================================*/

+    /* Data source management methods */

+    /*================================*/

+

+    /** 

+     * Look up the data source factory service for the specified

+     * persistence unit and cause it to be tracked, so that if it gets stopped 

+     * then we can be told and remove the dependent EMF service.

+     * Return true if the data source factory was registered, false if it wasn't

+     */

+    public boolean trackDataSourceFactory(PUnitInfo pUnitInfo) {

+

+        debug("GeminiServicesUtil trackDataSourceFactory for p-unit ", pUnitInfo.getUnitName());

+        ServiceReference[] dsfRefs = null;

+

+        // See if the data source factory service for the specified driver is registered

+        String filter = "(" + DataSourceFactory.OSGI_JDBC_DRIVER_CLASS + "=" + 

+                        pUnitInfo.getDriverClassName() + ")";

+        try {

+            dsfRefs = pUnitInfo.getBundle().getBundleContext().getServiceReferences(

+                DataSourceFactory.class.getName(), filter);

+        } catch (InvalidSyntaxException isEx) {

+            fatalError("Bad filter syntax (likely because of missing driver class name)", isEx);

+        } 

+        if (dsfRefs == null)

+            return false;

+        

+        // We found at least one -- track the first one (assuming it will be used)

+        // TODO Race condition where service could disappear before being tracked

+        debug("GeminiServicesUtil starting tracker for DataSourceFactory ", 

+                pUnitInfo.getDriverClassName());

+        ServiceTracker tracker = new ServiceTracker(osgiJpaProvider.getBundleContext(), 

+                                                    dsfRefs[0],

+                                                    new DataSourceTracker(pUnitInfo, this));

+        pUnitInfo.setTracker(tracker);

+        tracker.open();

+        return true;

+    }

+

+    /** 

+     * Stop tracking the data source factory for the given p-unit

+     */

+    public void stopTrackingDataSourceFactory(PUnitInfo pUnitInfo) {

+        // Clean up the tracker

+        debug("GeminiServicesUtil stopTrackingDataSourceFactory ", 

+                pUnitInfo.getDriverClassName(), " for p-unit: ", pUnitInfo.getUnitName());

+        if (pUnitInfo.getTracker() != null) {

+            debug("GeminiServicesUtil stopping tracker for DataSourceFactory ", 

+                    pUnitInfo.getDriverClassName());

+            pUnitInfo.getTracker().close();

+            pUnitInfo.setTracker(null);

+        }

+    }

+

+    /** 

+     * This method will be invoked by the data source factory tracker

+     * when the data source factory service comes online.

+     */

+    public void dataSourceFactoryAdded(PUnitInfo pUnitInfo) {

+        debug("GeminiServicesUtil dataSourceFactoryAdded, ", pUnitInfo.getDriverClassName());

+        if ((pUnitInfo.getEmfBuilderHandler() != null) && 

+                (pUnitInfo.getEmfBuilderHandler().getEMF() != null)) {

+            // A factory already exists in the builder so we can't register an EMF service

+        } else {

+            // The builder has not been used. Just unregister it and go through the entire 

+            // registration process again assuming the data source service is present

+            debug("GeminiServicesUtil dataSourceFactoryAdded, unregistering builder and reregistering ", pUnitInfo.getDriverClassName());

+            unregisterEMFBuilderService(pUnitInfo);

+            registerEMFServices(pUnitInfo);

+        }

+    }

+

+    /** 

+     * This method will be invoked by the data source factory tracker

+     * when the data source factory service that we rely on disappears.

+     */

+    public void dataSourceFactoryRemoved(PUnitInfo pUnitInfo) {

+        // TODO async handling of data source removal

+

+        debug("GeminiServicesUtil dataSourceFactoryRemoved, ", pUnitInfo.getDriverClassName());

+        if ((pUnitInfo.getEmfBuilderHandler() != null) && 

+                (pUnitInfo.getEmfBuilderHandler().getEMF() != null)) {

+            debug("GeminiServicesUtil builder had an EMF, ", 

+                    "unregistering both services for p-unit ", 

+                    pUnitInfo.getDriverClassName());

+            // Call into the provider to do the unregistration

+            Collection<PUnitInfo> pUnits = new ArrayList<PUnitInfo>();

+            pUnits.add(pUnitInfo);

+            osgiJpaProvider.unregisterPersistenceUnits(pUnits);

+        } else {

+            // Only unregister the EMF service, and leave the Builder

+            debug("GeminiServicesUtil only unregistering EMF service, ", 

+                    "leaving Builder service");

+            EntityManagerFactory emf = unregisterEMFService(pUnitInfo);           

+            if (emf != null) 

+                emf.close();

+        }

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiUtil.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiUtil.java
new file mode 100644
index 0000000..c0ffff9
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/GeminiUtil.java
@@ -0,0 +1,197 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import java.io.Closeable;

+import java.lang.reflect.Array;

+

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.BundleEvent;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.packageadmin.PackageAdmin;

+

+/**

+ * Utility class containing functions that are generally useful during 

+ * development and at runtime.

+ */

+public class GeminiUtil {

+    

+    /*==================*/

+    /* Static constants */

+    /*==================*/

+    

+    public static String JPA_JDBC_DRIVER_PROPERTY = "javax.persistence.jdbc.driver";

+    public static String JPA_JDBC_URL_PROPERTY = "javax.persistence.jdbc.url";

+    public static String JPA_JDBC_USER_PROPERTY = "javax.persistence.jdbc.user";

+    public static String JPA_JDBC_PASSWORD_PROPERTY = "javax.persistence.jdbc.password";

+    

+    /*============================*/

+    /* Helper and Utility methods */

+    /*============================*/

+

+    // Function to obtain the version from a bundle

+    public static String bundleVersion(Bundle b) {

+        return b.getVersion().toString();

+    }

+    

+    // Function to return a package String formatted with "." instead of "/"

+    public static String formattedPackageString(String s, char beingReplaced, char replacer) {

+        String formatted = s;

+        // Replace all instances of character

+        if (formatted.indexOf(beingReplaced) >= 0) 

+            formatted = formatted.replace(beingReplaced, replacer);

+        // Tack on trailing character if needed

+        if (formatted.charAt(formatted.length()-1) != replacer) 

+            formatted = formatted + replacer;

+        return formatted;

+    }

+    

+    // Function to close a closeable (as much as it can be closed)

+    public static void close(Closeable c) {

+        try { c.close(); } catch (Throwable ex){}

+    }

+    

+    // Obtain and return the PackageAdmin    

+    public static PackageAdmin getPackageAdmin(BundleContext ctx) {

+        ServiceReference ref = ctx.getServiceReference(PackageAdmin.class.getName());

+        return (ref != null) 

+            ? (PackageAdmin) ctx.getService(ref)

+            : null;

+    }

+    

+    // Strip off preceding slash, if present, and return the resulting string

+    public static String stripPrecedingSlash(String s) {

+        if (s == null || s.length()==0 || !s.startsWith("/")) 

+            return s;

+        return (s.length() == 1) 

+            ? "" 

+            : s.substring(1, s.length());

+    }

+    /*================================*/

+    /* Status and debugging functions */

+    /*================================*/

+    

+    // Function to throw a runtime exception (throws exception)

+    public static void fatalError(String s, Throwable t) { 

+        System.out.println("*** FATAL ERROR *** " + s);

+        if (t != null) 

+            t.printStackTrace(System.out);

+        throw new RuntimeException(s,t); 

+    }

+

+    // Function to indicate a warning condition (non-terminating)

+    public static void warning(String msg) {

+        warning(msg, "");

+    }

+

+    // Function to indicate a warning condition (non-terminating)

+    public static void warning(String msg, Throwable t) {

+        String msg2 = (t != null ? (" Exception: " + t) : "");

+        warning(msg, msg2);

+    }

+

+    // Function to indicate a warning condition (non-terminating)

+    public static void warning(String msg, String msg2) {

+        String outputMsg = "WARNING: " + msg + msg2;  

+        System.out.println(outputMsg);

+    }

+

+    // Function to print out debug strings for XML parsing purposes

+    public static void debugXml(String... msgs) { 

+        if (GeminiProperties.debugXml()) {

+            debug(msgs);

+        }

+    }

+    

+    // Function to print out debug strings for classloading purposes

+    public static void debugClassLoader(String... msgs) { 

+        if (GeminiProperties.debugClassloader()) {

+            debug(msgs);

+        }

+    }

+    

+    // Function to print out series of debug strings

+    public static void debug(String... msgs) { 

+        if (GeminiProperties.debug()) {

+            StringBuilder sb = new StringBuilder();

+            for (String msg : msgs) sb.append(msg);

+            System.out.println(sb.toString()); 

+        }

+    }

+

+    // Function to print out a string and an object.

+    // Handles some objects specially and prints out more info

+    public static void debug(String msg, Object obj) { 

+        if (GeminiProperties.debug()) {

+            if (obj == null) {

+                System.out.println(msg + String.valueOf(obj));

+            } else if (ClassLoader.class.isAssignableFrom(obj.getClass())) {

+                System.out.println(msg + obj);

+                ClassLoader p = (ClassLoader)obj;

+                while (p.getParent() != null) {

+                    System.out.println("  Parent loader: " + p.getParent());

+                    p = p.getParent();

+                }

+            } else if (Bundle.class.isAssignableFrom(obj.getClass())) {

+                    Bundle b = (Bundle) obj;

+                    System.out.println(msg + " bundle=" + b.getSymbolicName() + 

+                                             " id=" + b.getBundleId()+ 

+                                             " state=" + stringBundleStateFromInt(b.getState()));

+            } else if (BundleEvent.class.isAssignableFrom(obj.getClass())) {

+                    BundleEvent event = (BundleEvent) obj;

+                    System.out.println(msg + " bundle=" + event.getBundle().getSymbolicName() + 

+                            ", event=" + stringBundleEventFromInt(event.getType())); 

+            } else if (obj.getClass().isArray()) {

+                System.out.println(msg);

+                int len = ((Object[])obj).length;

+                for (int i=0; i<len; i++) {

+                    System.out.print("  ");

+                    System.out.println(String.valueOf(Array.get(obj, i)));                    

+                }

+            } else {

+                System.out.println(msg + String.valueOf(obj));

+            }

+        }

+    }

+    

+    public static String stringBundleStateFromInt(int bundleState) {

+        switch (bundleState) {

+            case 1: return "UNINSTALLED";

+            case 2: return "INSTALLED";

+            case 4: return "RESOLVED";

+            case 8: return "STARTING";

+            case 16: return "STOPPING";

+            case 32: return "ACTIVE";

+            default: return "UNDEFINED_STATE";

+        }

+    }

+    

+    public static String stringBundleEventFromInt(int eventType) {

+        switch (eventType) {

+            case 1: return "INSTALLED";

+            case 2: return "STARTED";

+            case 4: return "STOPPED";

+            case 8: return "UPDATED";

+            case 16: return "UNINSTALLED";

+            case 32: return "RESOLVED";

+            case 64: return "UNRESOLVED";

+            case 128: return "STARTING";

+            case 256: return "STOPPING";

+            case 512: return "LAZY_ACTIVATION";

+            default: return "UNDEFINED_EVENT";

+        }

+    }

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PUnitInfo.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PUnitInfo.java
new file mode 100644
index 0000000..0b17719
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PUnitInfo.java
@@ -0,0 +1,186 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

+import org.osgi.framework.Bundle;

+import org.osgi.framework.ServiceRegistration;

+import org.osgi.util.tracker.ServiceTracker;

+

+

+import org.eclipse.gemini.jpa.provider.OSGiJpaProvider;

+import org.eclipse.gemini.jpa.proxy.EMFBuilderServiceProxyHandler;

+import org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler;

+

+public class PUnitInfo {

+    

+    /*===============*/

+    /* Runtime state */

+    /*===============*/

+    

+    /** 

+     * Persistence unit bundle - set by extender

+     * @see PersistenceBundleExtender 

+     */

+    Bundle bundle;

+

+    /** 

+     * The provider servicing this p-unit - set by extender

+     * @see PersistenceBundleExtender 

+     */

+    OSGiJpaProvider assignedProvider;

+    

+    /** 

+     * Info about the persistence descriptor this data came from - set by bundleUtil

+     * @see PersistenceUnitBundleUtil

+     */

+    PersistenceDescriptorInfo descriptorInfo;

+

+    /** 

+     * Package names of all managed classes in the p-unit - set by bundleUtil

+     * @see PersistenceUnitBundleUtil

+     */

+    List<String> uniquePackageNames;

+    

+    /** 

+     * EMF Service state - set by servicesUtil

+     * @see GeminiServicesUtil

+     */

+    EMFServiceProxyHandler emfHandler;

+    ServiceRegistration emfService;

+

+    /** 

+     * EMF Builder Service state - set by servicesUtil

+     * @see GeminiServicesUtil

+     */

+    EMFBuilderServiceProxyHandler emfBuilderHandler;

+    ServiceRegistration emfBuilderService;

+

+    /** 

+     * For tracking the data source factory - set by servicesUtil

+     * @see GeminiServicesUtil 

+     */

+    ServiceTracker tracker;

+

+    /*==============================*/

+    /* Persistence descriptor state */

+    /*==============================*/

+

+    /**

+     * All of following state is set by the XML parser 

+     * @see PersistenceDescriptorHandler 

+     */

+    String unitName;

+    String provider;

+    Set<String> classes = new HashSet<String>();

+    String driverClassName;

+    String driverUrl;

+    String driverUser;

+    String driverPassword;

+

+    /*=============================*/

+    /* Accessors for runtime state */

+    /*=============================*/

+    

+    public Bundle getBundle() { return bundle; }

+    public void setBundle(Bundle b) { this.bundle = b; }

+    

+    public OSGiJpaProvider getAssignedProvider() { return assignedProvider; }

+    public void setAssignedProvider(OSGiJpaProvider p) { this.assignedProvider = p; }

+

+    public PersistenceDescriptorInfo getDescriptorInfo() { return descriptorInfo; }

+    public void setDescriptorInfo(PersistenceDescriptorInfo info) { this.descriptorInfo = info; }

+

+    public List<String> getUniquePackageNames() { return uniquePackageNames; }

+    public void setUniquePackageNames(List<String> names) { this.uniquePackageNames = names; }

+

+    public EMFServiceProxyHandler getEmfHandler() { return emfHandler; }

+    public void setEmfHandler(EMFServiceProxyHandler emfHandler) { this.emfHandler = emfHandler; }

+

+    public ServiceRegistration getEmfService() { return emfService; }

+    public void setEmfService(ServiceRegistration emfService) { this.emfService = emfService; }

+

+    public EMFBuilderServiceProxyHandler getEmfBuilderHandler() { return emfBuilderHandler; }

+    public void setEmfBuilderHandler(EMFBuilderServiceProxyHandler emfBuilderHandler) { this.emfBuilderHandler = emfBuilderHandler; }

+

+    public ServiceRegistration getEmfBuilderService() { return emfBuilderService; }

+    public void setEmfBuilderService(ServiceRegistration emfBuilderService) { this.emfBuilderService = emfBuilderService; }

+

+    public ServiceTracker getTracker() { return tracker; }

+    public void setTracker(ServiceTracker tracker) { this.tracker = tracker; }

+    

+    /*============================================*/

+    /* Accessors for Persistence descriptor state */

+    /*============================================*/

+

+    public String getUnitName() { return unitName; }

+    public void setUnitName(String s) { this.unitName = s ; }

+

+    public String getProvider() { return provider; }

+    public void setProvider(String s) { this.provider = s; }

+

+    public Set<String> getClasses() { return classes; }

+    public void addClass(String s) { this.classes.add(s); }

+

+    public String getDriverClassName() { return driverClassName; }

+    public void setDriverClassName(String s) { driverClassName = s; }

+

+    public String getDriverUrl() { return driverUrl; }

+    public void setDriverUrl(String driverUrl) { this.driverUrl = driverUrl; }

+

+    public String getDriverUser() { return driverUser; }

+    public void setDriverUser(String driverUser) { this.driverUser = driverUser; }

+

+    public String getDriverPassword() { return driverPassword; }

+    public void setDriverPassword(String driverPassword) { this.driverPassword = driverPassword; }

+    

+    /*=========*/

+    /* Methods */

+    /*=========*/

+

+    @Override

+    public int hashCode() { return getUnitName().hashCode(); }

+        

+    @Override

+    public boolean equals(Object o) { 

+        return (o instanceof PUnitInfo)

+            && (this.getUnitName() != null)

+            && (this.getUnitName().equals(((PUnitInfo)o).getUnitName())); 

+    }

+    

+    public String toString() {

+        StringBuilder sb = new StringBuilder();

+        sb.append("\nPUnit: ").append(getUnitName())

+          .append("\n  --- XML Data ---")

+          .append("\n  provider: ").append(getProvider())

+          .append("\n  classes: ").append(getClasses())

+          .append("\n  driverClassName: ").append(getDriverClassName())

+          .append("\n  driverUrl: ").append(getDriverUrl())

+          .append("\n  driverUser: ").append(getDriverUser())

+          .append("\n  driverPassword: ").append(getDriverPassword())

+          .append("\n  --- Runtime Data ---")

+          .append("\n  bundle: ").append(getBundle() == null ? "null" : getBundle().getSymbolicName())

+          .append("\n  assignedProvider: ").append(getAssignedProvider())

+          .append("\n  descriptorInfo: ").append(getDescriptorInfo())

+          .append("\n  uniquePackageNames: ").append(getUniquePackageNames())

+          .append("\n  emfHandler: ").append(getEmfHandler())

+          .append("\n  emfBuilderHandler: ").append(getEmfBuilderHandler())

+          .append("\n  DSF tracker: ").append(getTracker());

+       return sb.toString();

+    }

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceBundleExtender.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceBundleExtender.java
new file mode 100644
index 0000000..785dfc7
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceBundleExtender.java
@@ -0,0 +1,378 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.BundleEvent;

+import org.osgi.framework.Constants;

+import org.osgi.framework.SynchronousBundleListener;

+import org.osgi.service.packageadmin.PackageAdmin;

+

+import org.eclipse.gemini.jpa.provider.OSGiJpaProvider;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.*;

+

+/**

+ * This extender is used by the provider to listen for persistence unit 

+ * bundles and assign them to the provider if the unit is able to be assigned.

+ */

+public class PersistenceBundleExtender implements SynchronousBundleListener  {

+

+    /*================*/

+    /* Instance state */

+    /*================*/

+    

+    // The provider associated with this extender

+    OSGiJpaProvider osgiJpaProvider;

+    

+    // Utility classes

+    PersistenceUnitBundleUtil bundleUtil;

+    

+    // Persistence units by bundle 

+    Map<Bundle, List<PUnitInfo>> unitsByBundle = 

+        Collections.synchronizedMap(new HashMap<Bundle, List<PUnitInfo>>());

+    

+    // Just keep the bundle ids to prevent hard references to the bundles

+    Set<Long> lazyBundles = new HashSet<Long>();

+    Set<Long> refreshingBundles = new HashSet<Long>();

+    

+    /*==============*/

+    /* Constructors */

+    /*==============*/

+    

+    public PersistenceBundleExtender() {}

+    public PersistenceBundleExtender(OSGiJpaProvider provider) { 

+        this.osgiJpaProvider = provider;

+        this.bundleUtil = new PersistenceUnitBundleUtil();

+        

+    }

+

+    /*================================*/

+    /* API methods called by provider */

+    /*================================*/

+

+    /**

+     * Start listening for bundle events to indicate the presence of 

+     * persistence unit bundles. 

+     */

+    public void startListening() {

+        debug("GeminiExtender listening");

+        osgiJpaProvider.getBundleContext().addBundleListener(this);

+    }

+

+    /**

+     * Stop listening to bundle events. 

+     */

+    public void stopListening() {

+        debug("GeminiExtender no longer listening");

+        osgiJpaProvider.getBundleContext().removeBundleListener(this);

+    }

+

+    /**

+     * Look for persistence unit bundles that are already installed. 

+     */

+    public void lookForExistingBundles() {

+        

+        // Look at the bundles that are already installed

+        Bundle[] installedBundles = osgiJpaProvider.getBundleContext().getBundles();

+        debug("GeminiExtender looking at existing bundles: ", installedBundles);

+        

+        // Check if any are p-unit bundles

+        for (Bundle b : installedBundles) {

+            if (isPersistenceUnitBundle(b)) {

+                // We found a persistence unit bundle.

+                if (GeminiProperties.refreshPersistenceBundles()) {

+                    // Refresh it so it will go through resolving again and we can assign it a provider, etc.

+                    if ((b.getState() != Bundle.INSTALLED) && (b.getState() != Bundle.UNINSTALLED)) {

+                        refreshBundle(b);

+                    }

+                } else {

+                    // Refreshing is disabled - go through assigning and registering process w/o events

+                    if (b.getState() != Bundle.UNINSTALLED) {

+                        // Assign the p-unit

+                        // NOTE: With no refresh, assigning may be happening after the bundle has been resolved

+                        tryAssigningPersistenceUnitsInBundle(b);

+                        // Now if bundle is starting or active then register the p-units in it

+                        if ((b.getState() == Bundle.STARTING) || (b.getState() == Bundle.ACTIVE)) {

+                            registerPersistenceUnitsInBundle(b);

+                        } // Otherwise just let future events take their course 

+                    }

+                }

+            }

+        }

+    }

+

+    public Map<Bundle, List<PUnitInfo>> clearAllPUnitInfos() {

+        Map<Bundle, List<PUnitInfo>> pUnitInfos = unitsByBundle;

+        unitsByBundle = null;

+        lazyBundles = null;

+        refreshingBundles = null;

+        return pUnitInfos;

+    }

+

+    /*============================*/

+    /* Additional Support Methods */

+    /*============================*/

+    

+    /**

+     * Go through the p-units in a given bundle and assign the ones that do 

+     * not have a provider, or have a provider specified as this one.

+     * 

+     * @param b the bundle to look for p-units in

+     */

+    public void tryAssigningPersistenceUnitsInBundle(Bundle b) {

+        

+        debug("GeminiExtender tryAssigningPersistenceUnitsInBundle: ", b);

+        // If we have already assigned it then bail

+        if (isAssigned(b)) {

+            warning("Attempted to assign a bundle that was already assigned: ", b.toString());

+            return;

+        }

+

+        // Look for all of the persistence descriptor files in the bundle

+        List<PersistenceDescriptorInfo> descriptorInfos = bundleUtil.persistenceDescriptorInfos(b);

+

+        // Do a partial parse of the descriptors

+        Set<PUnitInfo> pUnitInfos = bundleUtil.persistenceUnitInfoFromXmlFiles(descriptorInfos);

+

+        // Cycle through each p-unit info and see if a provider was specified

+        for (PUnitInfo info : pUnitInfos) {

+            if ((info.getProvider() == null) || (osgiJpaProvider.getProviderClassName().equals(info.getProvider()))) {

+                // We can be the provider; claim the p-unit and add it to our list

+                info.setBundle(b);

+                info.setAssignedProvider(osgiJpaProvider);

+                addToBundleUnits(unitsByBundle, b, info);

+            }

+        }

+        // If we found any that were for us then let the provider know

+        List<PUnitInfo> unitsFound = unitsByBundle.get(b);

+        if ((unitsFound != null) && (unitsFound.size() != 0)) {

+            osgiJpaProvider.assignPersistenceUnitsInBundle(b, unitsByBundle.get(b));

+        }

+    }

+    

+    /**

+     * Unassign all of the p-units in a given bundle.

+     * 

+     * @param b the bundle the p-units are in

+     */

+    public void unassignPersistenceUnitsInBundle(Bundle b) { 

+        

+        debug("GeminiExtender unassignPersistenceUnitsInBundle: ", b);

+        List<PUnitInfo> infos = unitsByBundle.get(b);

+        unitsByBundle.remove(b);

+        removeFromLazyBundles(b);   

+        osgiJpaProvider.unassignPersistenceUnitsInBundle(b, infos);

+        // Uninitialize the state of the p-unit

+        for (PUnitInfo info : infos) {

+            info.setAssignedProvider(null);

+            info.setBundle(null);

+        }

+    }

+

+    /**

+     * Register the p-units of a given bundle.

+     * 

+     * @param b the bundle the p-units are in

+     */

+    public void registerPersistenceUnitsInBundle(Bundle b) {

+        

+        debug("GeminiExtender registerPersistenceUnitsInBundle: ", b);

+        if (!isAssigned(b)) {

+            warning("Register called on bundle " + b.getSymbolicName(), " but bundle was not assigned");

+            return;

+        }

+        if (areCompatibleBundles(b, osgiJpaProvider.getBundle())) { 

+            debug("GeminiExtender provider compatible with bundle: ", b);            

+            osgiJpaProvider.registerPersistenceUnits(unitsByBundle.get(b));

+        } else {

+            warning("Cannot support bundle " + b.getSymbolicName() +  

+                    " because it is not JPA-compatible with the assigned provider " + 

+                    osgiJpaProvider.getProviderClassName() + ". This is because the " +

+                    "persistence unit bundle has resolved to a different javax.persistence " +

+                    "than the provider. \nTo fix this, uninstall one of the javax.persistence " +

+                    "bundles so that both the persistence unit bundle and the provider resolve " +

+                    "to the same javax.persistence package.");

+            unassignPersistenceUnitsInBundle(b);

+            // No point in updating or refreshing. 

+            // It would likely just re-resolve to the same JPA interface package.

+        }

+    }

+    

+    /**

+     * Unregister the p-units of a given bundle.

+     * 

+     * @param b the bundle the p-units are in

+     */

+    public void unregisterPersistenceUnitsInBundle(Bundle b) {

+        

+        debug("GeminiExtender unregisterPersistenceUnitsInBundle: ", b);

+        if (!isAssigned(b)) {

+            warning("Unregister called on bundle " + b.getSymbolicName(), " but bundle was not assigned");

+            return;

+        }

+        osgiJpaProvider.unregisterPersistenceUnits(unitsByBundle.get(b));

+    }    

+    

+    /**

+     * Refresh the persistence bundle.

+     * 

+     * @param b the bundle the p-units are in

+     */    

+    public void refreshBundle(Bundle b) {

+        // Add the list of currently refreshing bundles. 

+        // (It will be removed when the UNRESOLVED event is fired on it)

+        addToRefreshingBundles(b);

+        // Call refresh on all of the packages

+        PackageAdmin admin = getPackageAdmin(osgiJpaProvider.getBundleContext());

+        debug("GeminiExtender refreshing packages of bundle ", b);

+        admin.refreshPackages(new Bundle[] { b }); 

+    }

+    

+    /*========================*/

+    /* BundleListener methods */

+    /*========================*/

+

+    public void bundleChanged(BundleEvent event) {

+

+        // Only continue if it is a persistence unit bundle

+        Bundle b = event.getBundle();

+        debug("Extender - bundle event, ", event);

+        if (!isPersistenceUnitBundle(b)) return;

+

+        // Process each event

+        int eventType = event.getType();

+

+        if (eventType == BundleEvent.INSTALLED) {

+            tryAssigningPersistenceUnitsInBundle(b);

+

+        } else if (eventType == BundleEvent.LAZY_ACTIVATION) {

+            if (isAssigned(b)) {

+                lazyBundles.add(b.getBundleId()); 

+                registerPersistenceUnitsInBundle(b);

+            }

+        } else if (eventType == BundleEvent.STARTING) {

+            if (isAssigned(b)) {

+                if (!isLazy(b)) {

+                    registerPersistenceUnitsInBundle(b);

+                }

+            }

+        } else if (eventType == BundleEvent.STOPPING) {

+            if (isAssigned(b)) {

+                // Fix for bug #342996

+                if (isLazy(b)) {

+                    removeFromLazyBundles(b);

+                }

+                unregisterPersistenceUnitsInBundle(b);

+            }

+        } else if (eventType == BundleEvent.UNINSTALLED) {

+            if (isAssigned(b)) {

+                unassignPersistenceUnitsInBundle(b);

+            }

+        } else if (eventType == BundleEvent.UPDATED) {

+            if (isAssigned(b)) {

+                unassignPersistenceUnitsInBundle(b);

+            }

+            tryAssigningPersistenceUnitsInBundle(b);

+

+        } else if (eventType == BundleEvent.UNRESOLVED) {

+            if (isRefreshing(b)) {  // assign refreshing bundles

+                tryAssigningPersistenceUnitsInBundle(b);

+                removeFromRefreshingBundles(b);

+            }

+        } else {  // RESOLVED, STARTED, STOPPED

+            // Do nothing.

+        }

+    }

+

+    /*================*/

+    /* Helper methods */

+    /*================*/

+    

+    protected boolean isAssigned(Bundle b) {

+        return unitsByBundle.containsKey(b);

+    }

+

+    protected boolean isLazy(Bundle b) {

+        return lazyBundles.contains(b.getBundleId());

+    }

+    protected boolean addToLazyBundles(Bundle b) {

+        return lazyBundles.add(b.getBundleId());

+    }

+    protected boolean removeFromLazyBundles(Bundle b) {

+        return lazyBundles.remove(b.getBundleId());

+    }

+

+    protected boolean isRefreshing(Bundle b) {

+        return refreshingBundles.contains(b.getBundleId());

+    }

+    protected void addToRefreshingBundles(Bundle b) {

+        refreshingBundles.add(b.getBundleId());

+    }

+    protected void removeFromRefreshingBundles(Bundle b) {

+        refreshingBundles.remove(b.getBundleId());

+    }

+        

+    protected void addToBundleUnits(Map<Bundle,List<PUnitInfo>> map, 

+                                    Bundle b, 

+                                    PUnitInfo info) {

+        synchronized (map) {

+            if (!map.containsKey(b))

+                map.put(b, new ArrayList<PUnitInfo>());

+            List<PUnitInfo> infos = map.get(b);

+            if (!infos.contains(info)) 

+                infos.add(info);

+        }

+    }

+

+    public boolean isPersistenceUnitBundle(Bundle b) {

+        return b.getHeaders().get("Meta-Persistence") != null;

+    }

+

+    public boolean isLazyActivatedBundle(Bundle b) {

+        String policy = (String) b.getHeaders().get(Constants.BUNDLE_ACTIVATIONPOLICY);

+        return (policy != null) && (policy.equals(Constants.ACTIVATION_LAZY));        

+    }

+        

+    /**

+     * Return whether or not the persistence unit bundle

+     * has a consistent JPA interface class space with the provider bundle. 

+     * This method must be called after both bundles have been resolved.

+     */

+    public boolean areCompatibleBundles(Bundle pUnitBundle, Bundle providerBundle) {

+        try {

+            debug("GeminiExtender checking bundle compatibility of: ", pUnitBundle);

+            Class<?> pUnitClass = pUnitBundle.loadClass("javax.persistence.Entity");

+            Class<?> providerClass = providerBundle.loadClass("javax.persistence.Entity");

+            return pUnitClass.getClassLoader() == providerClass.getClassLoader();

+        } catch (ClassNotFoundException cnfEx) {

+            // If one of the bundles does not have the class in its class space 

+            // then by definition the two are consistent w.r.t. that package

+            return true;

+        } 

+    }

+    public void stop(BundleContext context) throws Exception {

+    }

+    public void start(BundleContext context) throws Exception {

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceDescriptorInfo.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceDescriptorInfo.java
new file mode 100644
index 0000000..dcf5922
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceDescriptorInfo.java
@@ -0,0 +1,124 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.debug;

+import static org.eclipse.gemini.jpa.GeminiUtil.warning;

+

+import java.io.BufferedInputStream;

+import java.io.IOException;

+import java.io.InputStream;

+import java.net.URL;

+import java.util.jar.JarEntry;

+import java.util.jar.JarInputStream;

+

+public class PersistenceDescriptorInfo {

+

+    // Url in the bundle of the descriptor.

+    // If embedded in a JAR then this is the url of the JAR.

+    URL url;

+    

+    // If embedded in a JAR then this the path of the JAR

+    // Should be null if not an embedded descriptor

+    String jarPath;

+    

+    // Path to the descriptor 

+    // If embedded in a JAR then this is only the path from within the JAR

+    String descriptorPath;

+    

+    public PersistenceDescriptorInfo() {}

+    public PersistenceDescriptorInfo(URL url, String descPath) {

+        this();

+        this.url = url;

+        this.descriptorPath = descPath;

+    }

+    public PersistenceDescriptorInfo(URL url, String descPath, String jarPath) {

+        this(url, descPath);

+        this.jarPath = jarPath;

+    }

+

+    public URL getUrl() { return url; }

+    public void setUrl(URL url) { this.url = url; }

+

+    public String getJarPath() { return jarPath; }

+    public void setJarPath(String jarPath) { this.jarPath = jarPath; }

+

+    public String getDescriptorPath() { return descriptorPath; }

+    public void setDescriptorPath(String descriptorPath) { this.descriptorPath = descriptorPath; }

+

+    public boolean isEmbedded() { return jarPath != null; }

+

+    /**

+     * Compose the complete descriptor path once again

+     * 

+     * @return a complete string path including JAR prefix when embedded in a JAR

+     */

+    public String fullDescriptorPath() {

+        return isEmbedded() 

+            ? jarPath + "!/" + descriptorPath

+            : descriptorPath;

+    }

+    

+    /**

+     * Return a stream over the descriptor. THe caller should close the stream when done.

+     * 

+     * @return An input stream to the descriptor file. Return null if

+     * the descriptor could not be found.

+     */

+    public InputStream getDescriptorStream() {

+        debug("getDescStream - Looking for descriptor in : ", jarPath);

+        InputStream inStream = null;

+        try { inStream = url.openStream(); }

+        catch (IOException ioEx) {} // inStream will be null

+

+        // If not embedded then return the stream

+        // If it failed, the stream will be null so return it anyway

+        if (!isEmbedded()  || inStream == null)

+            return inStream;

+

+        // At this point we know it is an embedded descriptor and we have 

+        // a stream on the JAR file. Use a jar stream to rifle through the JAR

+        JarInputStream jarStream = null;

+        JarEntry jarEntry = null;

+        try {

+            jarStream = new JarInputStream(new BufferedInputStream(inStream));

+            debug("getDescStream - Looking in jar for embedded descriptor: ", descriptorPath);

+            do {

+                jarEntry = jarStream.getNextJarEntry();

+                if (jarEntry != null) {

+                    if (jarEntry.getName().equalsIgnoreCase(descriptorPath)) {

+                        debug("getDescStream - Found jar entry: ", jarEntry.getName());

+                        break;

+                    } 

+                }

+            } while (jarEntry != null);

+        } catch (IOException ioEx) {

+            warning("getDescStream - Received exception looking for embedded descriptor ", ioEx);

+            try { jarStream.close(); } catch (Throwable t) {}

+            return null;

+        }

+        return jarStream;

+    }

+

+    public String toString() {

+        StringBuilder sb = new StringBuilder();

+        sb.append("Descriptor: ").append(getDescriptorPath());

+        if (isEmbedded()) {

+            sb.append(" embedded in ");

+            sb.append(jarPath);

+        }

+       return sb.toString();

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceUnitBundleUtil.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceUnitBundleUtil.java
new file mode 100644
index 0000000..dd3a64e
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PersistenceUnitBundleUtil.java
@@ -0,0 +1,164 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import java.io.InputStream;

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+import javax.xml.parsers.SAXParser;

+import javax.xml.parsers.SAXParserFactory;

+

+import org.osgi.framework.Bundle;

+import org.eclipse.gemini.jpa.xml.PersistenceDescriptorHandler;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.*;

+

+/**

+ * Utility class that implements functionality for JPA providers 

+ * to be able to support the OSGi JPA specification.

+ * 

+ * This class provides some of the functionality for processing

+ * a persistence unit bundle.

+ */

+public class PersistenceUnitBundleUtil {

+    

+    public static final String JPA_MANIFEST_HEADER = "Meta-Persistence"; 

+    public static final String EMBEDDED_JAR_SEPARATOR = "!/"; 

+    public static final String DEFAULT_DESCRIPTOR_PATH = "META-INF/persistence.xml"; 

+

+    /**

+     * Return a List of PersistenceDescriptorInfo, each of which contains info about 

+     * a persistence descriptor in the bundle. An entry to the default file will be 

+     * included if it exists in the bundle.

+     * 

+     * @param b the persistence unit bundle 

+     * 

+     * @return a List of PersistenceDescriptorInfo

+     */

+    public List<PersistenceDescriptorInfo> persistenceDescriptorInfos(Bundle pUnitBundle) {

+        List<PersistenceDescriptorInfo> descInfos = new ArrayList<PersistenceDescriptorInfo>();

+        debug("Looking for persistence descriptors in bundle ", pUnitBundle.getSymbolicName());

+        

+        // Add default META-INF/persistence.xml if it exists in the bundle

+        URL defaultUrl = pUnitBundle.getEntry(DEFAULT_DESCRIPTOR_PATH);

+        if (defaultUrl != null) {

+            descInfos.add(new PersistenceDescriptorInfo(defaultUrl, DEFAULT_DESCRIPTOR_PATH));

+        }

+

+        Object headerEntry = pUnitBundle.getHeaders().get(JPA_MANIFEST_HEADER);

+        

+        // If no entries were specified then we're done

+        if (headerEntry == null) return descInfos;

+

+        // Iterate through all of the specified Meta-Persistence entries

+        for (String paddedPath : headerEntry.toString().split(",")) {

+            String path = stripPrecedingSlash(paddedPath.trim());

+            

+            // If standard path was specified ignore it since we already added it above

+            if ((path.length() == 0) || path.equals(DEFAULT_DESCRIPTOR_PATH)) {

+                continue;

+            }

+            // Check if it is an embedded JAR path

+            int splitPosition = path.indexOf(EMBEDDED_JAR_SEPARATOR);

+            URL url = null;

+            if (splitPosition == -1) {

+                // Not an embedded JAR path, just get URL from bundle call

+                url = pUnitBundle.getEntry(path);

+                if (url != null)

+                    descInfos.add(new PersistenceDescriptorInfo(url, path));

+                else

+                    warning("Could not find JPA descriptor: ", path);

+            } else {

+                // It's an embedded JAR path, we need to do some work to get the info

+                String jarPrefixPath = path.substring(0, splitPosition);

+                String descPath = path.substring(splitPosition+2);

+                descPath = stripPrecedingSlash(descPath);

+                debug("Descriptor JAR prefix: ", jarPrefixPath);

+                debug("Embedded descriptor suffix: ", descPath);

+                URL prefixUrl = pUnitBundle.getEntry(jarPrefixPath);

+                debug("Embedded JAR url: ", prefixUrl);

+                if (prefixUrl != null) {

+                    descInfos.add(new PersistenceDescriptorInfo(prefixUrl, descPath, jarPrefixPath));

+                } else {

+                    warning("Could not find nested JAR: ", jarPrefixPath);                    

+                    continue;

+                }

+            }

+        }

+        debug("Found persistence descriptors: ", descInfos);

+        return descInfos;

+    }

+

+    /**

+     * Process each of the descriptor infos passed in and return a Set of 

+     * PartialPersistenceUnitInfo objects, one for each persistence unit.

+     * The descriptor infos are expected to be in the same bundle.

+     * 

+     * @param descInfos a List of PersistenceDescriptorInfo with each 

+     *                  referring to a different persistence descriptor file

+     * 

+     * @return a Set of < partial persistence unit information >

+     */

+    public Set<PUnitInfo> persistenceUnitInfoFromXmlFiles(List<PersistenceDescriptorInfo> descriptorInfos) {

+

+        // Set of p-unit info

+        Set<PUnitInfo> pUnits = new HashSet<PUnitInfo>();

+        // Set of p-unit names to ensure no duplicates

+        Set<String> pUnitNames = new HashSet<String>();

+        

+        // Read each of the persistence descriptor files

+        InputStream in = null;

+        PersistenceDescriptorHandler handler = null;

+        for (PersistenceDescriptorInfo info : descriptorInfos) {

+            try { 

+                debug("Parsing persistence descriptor ", info.getDescriptorPath());

+                // Open a stream on the descriptor

+                in = info.getDescriptorStream();

+                // Create a parser

+                SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

+                // Parse the file

+                handler = new PersistenceDescriptorHandler();

+                parser.parse(in, handler);

+                debug("Finished parsing persistence descriptor ", info.getUrl());

+            } catch(Throwable ex) {

+                fatalError("Could not parse XML descriptor ", ex);

+            } finally {

+                close(in);

+            }

+            // Get the p-units found in the current descriptor file

+            Set<PUnitInfo> newPUnits = handler.getPersistenceUnitInfo();

+            debug("Found " + newPUnits.size() + " persistence units");

+

+            // Cycle through the newly found p-units ensuring there are no duplicates

+            // and setting the URL

+            for (PUnitInfo unitInfo : newPUnits) {

+                String unitName = unitInfo.getUnitName();

+                if (pUnitNames.contains(unitName)) {

+                    fatalError("Persistence unit " + unitName + " already defined ", null);

+                }

+                pUnitNames.add(unitName);

+                // Set the URL 

+                unitInfo.setDescriptorInfo(info);

+            }

+            // Add the new ones to the list of processed p-units

+            pUnits.addAll(newPUnits);

+        }

+        debug("Parsed persistence descriptors: ", pUnits);

+        return pUnits;

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PlainDriverDataSource.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PlainDriverDataSource.java
new file mode 100644
index 0000000..d14c7cd
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/PlainDriverDataSource.java
@@ -0,0 +1,101 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa;

+

+import java.io.PrintWriter;

+import java.sql.Connection;

+import java.sql.Driver;

+import java.sql.DriverManager;

+import java.sql.SQLException;

+import java.util.Properties;

+import javax.sql.DataSource;

+

+import static org.osgi.service.jdbc.DataSourceFactory.*;

+

+/** 

+ * An abbreviated/simplified DataSource impl that takes a URL from the client

+ * and just returns a thin data source wrapper around the basic JDBC driver.

+ */

+public class PlainDriverDataSource implements DataSource {

+

+    Driver driver = null;

+    Properties properties = null;

+    String url = null;

+    

+    public PlainDriverDataSource(Driver driver, Properties properties) {

+        this.driver = driver;

+        this.properties = (Properties) properties.clone();

+        this.url = properties.getProperty(JDBC_URL);

+    }

+

+    public Connection getConnection() throws java.sql.SQLException {

+        if (url == null) missingUrlException();

+        return driver.connect(url, properties);

+    }

+

+    public Connection getConnection(String user, String password) throws java.sql.SQLException {

+        if (url == null) missingUrlException();

+        Properties localProps = (Properties) properties.clone();

+        localProps.put(JDBC_USER, user);

+        localProps.put(JDBC_PASSWORD, password);

+        return driver.connect(url, localProps);

+    }

+ 

+    public boolean isWrapperFor(Class<?> cls) throws SQLException { 

+        // Fix for bug #342942

+        try {

+            // If driver class or subclass passed in then true

+            if (driver.getClass().isAssignableFrom(cls)) 

+                return true;

+            // If driver implements interface passed in then true

+            Class<?>[] interfaces = driver.getClass().getInterfaces();

+            for (Class<?> i : interfaces)

+                if (i == cls) return true;

+            // Otherwise we don't

+            return false;

+        } catch (Exception ex) {

+            throw new SQLException(ex);

+        }

+    }

+    

+    public <T> T unwrap(Class<T> cls) throws SQLException {

+        try {

+            return cls.cast(driver);

+        } catch (ClassCastException ex) {

+            throw new SQLException(ex);

+        }

+    }

+

+    public PrintWriter getLogWriter() throws SQLException { return DriverManager.getLogWriter(); }

+

+    public int getLoginTimeout() throws SQLException { return DriverManager.getLoginTimeout(); }

+

+    // Don't support setting log writer or timeout 

+

+    public void setLogWriter(PrintWriter writer) throws SQLException {

+        throw new SQLException("Can't set Log Writer on URL data source");

+    }

+    

+    public void setLoginTimeout(int timeout) throws SQLException {

+        throw new SQLException("Can't set Login Timeout on URL data source");

+    }

+

+    // Helper method

+    void missingUrlException() throws SQLException {

+        throw new SQLException("URL was not specified");            

+    }

+ 

+

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/BundleProxyClassLoader.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/BundleProxyClassLoader.java
new file mode 100644
index 0000000..df5172b
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/BundleProxyClassLoader.java
@@ -0,0 +1,146 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith - inspired by http://wiki.eclipse.org/index.php/BundleProxyClassLoader_recipe

+ ******************************************************************************/  

+package org.eclipse.gemini.jpa.classloader;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.debugClassLoader;

+

+import java.io.File;

+import java.io.IOException;

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.Iterator;

+import java.util.List;

+

+import org.osgi.framework.Bundle;

+

+public class BundleProxyClassLoader extends ClassLoader {

+

+    private Bundle bundle;

+    private EclipseDotClasspathHelper classpathHelper = new EclipseDotClasspathHelper();

+        

+    public BundleProxyClassLoader(Bundle bundle) {

+        this.bundle = bundle;

+    }

+    

+    @Override

+    public Enumeration<URL> findResources(String name) throws IOException {

+        try {

+            List<URL> resourceURLs = new ArrayList<URL>(1);

+            URL entry = getEntry(name);

+            if (entry != null){

+                resourceURLs.add(entry);

+            }

+            return new ListEnumeration(resourceURLs);

+        } catch (Exception e) {

+            e.printStackTrace();

+            throw new RuntimeException(e);

+        }

+    }

+

+    @Override

+    public URL findResource(String name) {

+         return getEntry(name);

+    }

+

+    @Override

+    public URL getResource(String name) {

+        try {

+            if ((bundle.getState() == Bundle.INSTALLED) ||

+                (bundle.getState() == Bundle.UNINSTALLED)){

+                // bundle has no classloader yet so resort to getEntry

+                debugClassLoader("Bundle has no classloader so getResource(", name ,

+                    ") calling findResource");

+                return findResource(name);

+            } else {

+                // bundle has classloader so forward getResource

+                return bundle.getResource(name);

+            }

+        } catch (Exception e) {

+            throw new RuntimeException(e);

+        }

+    }

+

+    @Override

+    public Enumeration<URL> getResources(String name) throws IOException {

+        try {

+            if ((bundle.getState() == Bundle.INSTALLED) ||

+                (bundle.getState() == Bundle.UNINSTALLED)){

+               // bundle has no classloader

+                debugClassLoader("Bundle has no classloader so getResources(", name ,

+                    ") calling findResources");

+                return findResources(name);

+            } else {

+                return bundle.getResources(name);

+            }

+        } catch (Exception e) {

+            throw new RuntimeException(e);

+        }

+    }

+

+    @Override

+    public java.lang.Class<?> loadClass(String name) throws ClassNotFoundException {

+        if ((bundle.getState() == Bundle.INSTALLED) ||

+            (bundle.getState() == Bundle.UNINSTALLED)){

+            // Bundle has no classloader and bundle.loadClass

+            // may result in attempt to resolve bundle which we

+            // don't want as a side effect.

+            debugClassLoader("Bundle has no classloader so loadClass(", name,

+                ") is returning null");

+            return null;

+        } else {

+            return bundle.loadClass(name);

+        }

+    }

+    

+    protected URL getEntry(String name) {

+        URL entry = bundle.getEntry(name);

+        if (entry == null) {

+            entry = getEclipseProjectEntry(name, entry);

+        }

+        return entry;

+    }

+

+    protected URL getEclipseProjectEntry(String name, URL entry) {

+        String binPath = classpathHelper.getBinPath(bundle);

+        if (binPath != null) {

+            entry = bundle.getEntry(binPath + File.separator + name);

+        }

+        return entry;

+    }

+

+    private final class ListEnumeration implements Enumeration {

+        private Iterator iterator;

+

+        public ListEnumeration(List<?> list) {

+            this.iterator = list.iterator();

+        }

+

+        public boolean hasMoreElements() {

+            return iterator.hasNext();

+        }

+

+        public Object nextElement() {

+            return iterator.next();

+        }

+    }

+

+    @Override

+    public String toString() {

+    	return super.toString() + "(" + this.bundle.getSymbolicName() + ")";

+    }

+

+}

+

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeClassLoader.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeClassLoader.java
new file mode 100644
index 0000000..e261d7a
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeClassLoader.java
@@ -0,0 +1,197 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors: 

+ *    ssmith - A ClassLoader that aggregates multiple ClassLoaders

+ ******************************************************************************/  

+package org.eclipse.gemini.jpa.classloader;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.debugClassLoader;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.List;

+

+public class CompositeClassLoader extends ClassLoader {

+    private List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();

+    

+    /**

+     * Create a CompositeClassLoader with two class loaders.

+     * 

+     * @param loader1

+     * @param loader2

+     */

+    public CompositeClassLoader(ClassLoader loader1, ClassLoader loader2) {

+        classLoaders.add(loader1);

+        classLoaders.add(loader2);

+    }

+

+    /**

+     * Create a CompositeClassLoader from a list of class loaders.

+     * 

+     * @param loaders

+     */

+    public CompositeClassLoader(List<ClassLoader> loaders) {

+        classLoaders.addAll(loaders);

+    }

+

+    /**

+     * Get the contained class loaders.

+     * 

+     * @return the list of the contained class loaders

+     */

+    public List<ClassLoader> getClassLoaders() {

+        return classLoaders;

+    }

+

+    /**

+     * Sets the default assertion status for this class loader to

+     * <tt>false</tt> and discards any package defaults or class assertion

+     * on all contained class loaders.

+     * 

+     * @see  ClassLoader#clearAssertionStatus()

+     */

+    @Override

+    public synchronized void clearAssertionStatus() {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            classLoader.clearAssertionStatus();

+        }

+    }

+

+    /**

+     * Finds the resource with the given name.  Contained class 

+     * loaders are queried until one returns the requested

+     * resource or <tt>null</tt> if not found. 

+     * 

+     * @see  ClassLoader#getResource(String)

+     */

+    @Override

+    public URL getResource(String name) {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            debugClassLoader("Attempting getResource(", name,") on ", classLoader.toString());

+            URL resource = classLoader.getResource(name);

+            if (resource != null) {

+                return resource;

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Returns an input stream for reading the specified resource.

+     * Contained class loaders are queried until one returns the 

+     * requested resource stream or <tt>null</tt> if not found.

+     * 

+     * @see  ClassLoader#getResourceAsStream(String)

+     */ 

+    @Override

+    public InputStream getResourceAsStream(String name) {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            debugClassLoader("Attempting getResourceAsStream(", name,") on ", classLoader.toString());

+            InputStream stream = classLoader.getResourceAsStream(name);

+            if (stream != null) {

+                return stream;

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Finds all the resources with the given name. Contained class 

+     * loaders are queried and the results aggregated into a single

+     * Enumeration.

+     * 

+     * @throws  IOException

+     *          If I/O errors occur

+     *          

+     * @see  ClassLoader#getResources(String)

+     */

+    @Override

+    public Enumeration<URL> getResources(String name) throws IOException {

+        List<Enumeration<URL>> enumerations = new ArrayList<Enumeration<URL>>(getClassLoaders().size());

+        for (ClassLoader classLoader : getClassLoaders()) {

+            debugClassLoader("Attempting getResources(", name,") on ", classLoader.toString());

+            Enumeration<URL> resources = classLoader.getResources(name);

+            if (resources != null) {

+                enumerations.add(resources);

+            }

+        }

+        return new CompositeEnumeration<URL>(enumerations); 

+    }

+

+   /**

+     * Loads the class with the specified <a href="#name">binary name</a>.

+     * Contained class loaders are queried until one returns the 

+     * requested class.

+     * 

+     * @see  ClassLoader#loadClass(String)

+     * 

+     * @throws  ClassNotFoundException

+     *          If the class was not found

+     */

+    @Override

+    public Class<?> loadClass(String name) throws ClassNotFoundException {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            debugClassLoader("Attempting loadClass(", name,") on ", classLoader.toString());

+            try {

+                Class<?> aClass = classLoader.loadClass(name);

+                return aClass;

+            } catch (ClassNotFoundException e) {

+                debugClassLoader("ClassNotFound '", name,"' in ", classLoader.toString());                

+            }            

+        }

+        throw new ClassNotFoundException(name);

+    }

+

+    /** 

+     * Sets the desired assertion status for the named top-level class.

+     * 

+     * @see  ClassLoader#setClassAssertionStatus(String, boolean)

+     */

+    @Override

+    public synchronized void setClassAssertionStatus(String className,

+            boolean enabled) {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            classLoader.setClassAssertionStatus(className, enabled);

+        }

+    }

+

+    /**

+     * Sets the default assertion status for this class loader. 

+     * 

+     * @see  ClassLoader#setDefaultAssertionStatus(boolean)

+     */

+    @Override

+    public synchronized void setDefaultAssertionStatus(boolean enabled) {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            classLoader.setDefaultAssertionStatus(enabled);

+        }

+    }

+

+    /**

+     * Sets the package default assertion status for the named package.

+     * 

+     * @see  ClassLoader#setPackageAssertionStatus(String,boolean)

+     */

+    @Override

+    public synchronized void setPackageAssertionStatus(String packageName,

+            boolean enabled) {

+        for (ClassLoader classLoader : getClassLoaders()) {

+            classLoader.setPackageAssertionStatus(packageName, enabled);

+        }

+    }

+    

+    

+    

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeEnumeration.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeEnumeration.java
new file mode 100644
index 0000000..f50316e
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/CompositeEnumeration.java
@@ -0,0 +1,77 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith

+ ******************************************************************************/  

+package org.eclipse.gemini.jpa.classloader;

+

+import java.util.Enumeration;

+import java.util.Iterator;

+import java.util.List;

+import java.util.NoSuchElementException;

+

+/**

+ * CompositeEnumeration is, as the name implies, a Composite of Enumerations.

+ * It provides a way to iterate across a set of Enumerations as if they are

+ * a single Enumeration.  The order of the elements returned reflects the order

+ * of the Enumerations in the Vector passed to the constructor.

+ * 

+ * @author ssmith

+ *

+ * @see org.eclipse.persistence.internal.jpa.deployment.osgi.CompositeClassLoader

+ * 

+ * @param <T>

+ */

+public class CompositeEnumeration<T> implements Enumeration<T> {

+    

+    private Enumeration<T> currentEnumeration;

+    private Iterator<Enumeration<T>> enumerationIterator;

+    

+    public CompositeEnumeration(List<Enumeration<T>> enumerations) {

+        this.enumerationIterator = enumerations.iterator();

+        if (this.enumerationIterator.hasNext()) {

+            this.currentEnumeration = this.enumerationIterator.next();

+        } else {

+            this.currentEnumeration = new NullObjectEnumeration();

+        }

+    }

+

+    public boolean hasMoreElements() {

+        boolean hasMoreElements = this.currentEnumeration.hasMoreElements();

+        if (hasMoreElements) {

+            return true;

+        } else {

+            if (this.enumerationIterator.hasNext()) {

+                this.currentEnumeration = this.enumerationIterator.next();

+                return this.hasMoreElements();

+            } else {

+                return false;

+            }

+        }

+    }

+

+    public T nextElement() {

+        return this.currentEnumeration.nextElement();

+    }

+

+    private final class NullObjectEnumeration implements Enumeration<T> {

+        public boolean hasMoreElements() {

+            return false;

+        }

+

+        public T nextElement() {

+            throw new NoSuchElementException();

+        }

+    }

+

+

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/EclipseDotClasspathHelper.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/EclipseDotClasspathHelper.java
new file mode 100644
index 0000000..dad63bc
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/classloader/EclipseDotClasspathHelper.java
@@ -0,0 +1,70 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.classloader;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.close;

+import static org.eclipse.gemini.jpa.GeminiUtil.debug;

+import static org.eclipse.gemini.jpa.GeminiUtil.fatalError;

+

+import java.io.InputStream;

+import java.net.URL;

+

+import javax.xml.parsers.SAXParser;

+import javax.xml.parsers.SAXParserFactory;

+

+import org.eclipse.gemini.jpa.xml.EclipseDotClasspathHandler;

+import org.osgi.framework.Bundle;

+

+public class EclipseDotClasspathHelper {

+

+    private EclipseDotClasspathHandler handler;

+

+    /** 

+     * Answer the output path contained in an Eclipse project's

+     * .classpath file.  Will only open and parse the file

+     * once, if it exists.

+     * 

+     * @return String

+     */

+    public String getBinPath(Bundle bundle) {

+        if (handler == null) {

+            handler = new EclipseDotClasspathHandler();

+        } else {

+            // if handler is != null then we have already

+            // attempted to locate and parse the .classpath

+            return handler.getBinPath();

+        }

+        URL resource = bundle.getEntry(".classpath");

+        if (resource == null) {

+            return null;

+        }

+        InputStream in = null;

+        try { 

+            in = resource.openStream();

+            debug("Parsing Eclipse .classpath");

+            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

+            parser.parse(in, handler);

+            String binPath = handler.getBinPath();

+            debug("Finished parsing Eclipse .classpath: ", binPath);

+            return binPath;

+        } catch(Throwable ex) {

+            fatalError("Could not parse .classpath ", ex);

+        } finally {

+            close(in);

+        }

+        return null;

+    }

+

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/BundleArchive.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/BundleArchive.java
new file mode 100644
index 0000000..1b58466
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/BundleArchive.java
@@ -0,0 +1,120 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     tware - initial implementation

+ *     ssmith - support for user specified Eclipse project bin path

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.net.MalformedURLException;

+import java.net.URI;

+import java.net.URISyntaxException;

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.Iterator;

+import java.util.Map;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+import org.eclipse.gemini.jpa.classloader.EclipseDotClasspathHelper;

+import org.eclipse.persistence.internal.jpa.deployment.ArchiveBase;

+import org.eclipse.persistence.jpa.Archive;

+import org.osgi.framework.Bundle;

+

+/**

+ * A bundle archive subclasses from EclipseLink's Bundle framework in order 

+ * to allow use of the Bundle API to look inside persistence units.

+ * @author tware

+ *

+ */

+public class BundleArchive extends ArchiveBase implements Archive {

+

+    protected Bundle bundle = null;

+    protected EclipseDotClasspathHelper pdeClasspathHelper = new EclipseDotClasspathHelper();

+    

+    @SuppressWarnings("unused")

+    private Logger logger;

+    

+    /** 

+     * This is used for Eclipse PDE support.  PDE does not always store entries

+     * at the root of the bundle.  We store the size of the path prefix in the bundle when

+     * we go looking for resources.  This is used to trim entry sizes in order to build class 

+     * names from bundle entries

+     */

+    protected Integer pathPrefixSize = null;

+

+    @SuppressWarnings("deprecation")

+    public BundleArchive(URL rootUrl, Map properties, String descriptorLocation) throws MalformedURLException {

+        this(rootUrl, properties, descriptorLocation, Logger.global);

+    }

+

+    public BundleArchive(URL rootUrl, Map properties, String descriptorLocation, Logger logger)

+            throws MalformedURLException {

+        super(rootUrl, descriptorLocation);

+        this.bundle = (Bundle)properties.get("org.eclipse.gemini.jpa.bundle");

+        logger.entering("BundleArchive", "BundleArchive " + rootUrl);

+        this.logger = logger;

+

+        this.descriptorLocation = descriptorLocation;

+        logger.logp(Level.FINER, "BundleArchive", "BundleArchive", // NOI18N

+                "rootURL = {0}", rootURL); // NOI18N

+    }

+

+    public Iterator<String> getEntries() {

+        

+        Enumeration<URL> entries = null;

+        String binPath = pdeClasspathHelper.getBinPath(bundle);

+        if (binPath != null) { // In Eclipse PDE

+            entries = bundle.findEntries(binPath,"*.class", true);

+            pathPrefixSize = binPath.length() + 2; // leading and trailing separator chars

+        } else {

+            entries = bundle.findEntries(".","*.class", true);

+            pathPrefixSize = 3;

+        }

+        ArrayList<String> result = new ArrayList<String>();

+        while (entries.hasMoreElements()) {

+            URL bundleEntry = entries.nextElement();

+            try{

+                URI bundleUri = bundleEntry.toURI();

+                result.add(trimClassName(bundleUri.getPath()));

+            } catch (URISyntaxException e){

+                e.printStackTrace();

+            }

+        }

+        return result.iterator();

+    }

+

+    

+    protected String trimClassName(String path){

+        return path.substring(pathPrefixSize);

+    }

+    

+    @Override

+    public InputStream getEntry(String entryPath) throws IOException {

+        InputStream is = null;

+        final URL entry = bundle.getEntry(entryPath);

+        if (entry != null) {

+            is = entry.openStream();

+        }

+        return is;

+    }

+

+    public URL getEntryAsURL(String entryPath) throws IOException {

+        return bundle.getEntry(entryPath);

+    }

+

+    public void close() {     

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/EclipseLinkOSGiProvider.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/EclipseLinkOSGiProvider.java
new file mode 100644
index 0000000..42c181f
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/EclipseLinkOSGiProvider.java
@@ -0,0 +1,453 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ *     ssmith - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.debug;

+import static org.eclipse.gemini.jpa.GeminiUtil.fatalError;

+import static org.eclipse.gemini.jpa.GeminiUtil.warning;

+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_PASSWORD;

+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_URL;

+import static org.osgi.service.jdbc.DataSourceFactory.JDBC_USER;

+import static org.osgi.service.jdbc.DataSourceFactory.OSGI_JDBC_DRIVER_CLASS;

+

+import java.io.FileWriter;

+import java.io.IOException;

+import java.sql.Driver;

+import java.sql.SQLException;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Properties;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.spi.PersistenceProvider;

+import javax.persistence.spi.PersistenceUnitInfo;

+import javax.persistence.spi.ProviderUtil;

+import javax.sql.DataSource;

+

+import org.eclipse.gemini.jpa.AnchorClassUtil;

+import org.eclipse.gemini.jpa.FragmentUtil;

+import org.eclipse.gemini.jpa.GeminiProperties;

+import org.eclipse.gemini.jpa.GeminiServicesUtil;

+import org.eclipse.gemini.jpa.GeminiUtil;

+import org.eclipse.gemini.jpa.PUnitInfo;

+import org.eclipse.gemini.jpa.PersistenceBundleExtender;

+import org.eclipse.gemini.jpa.PersistenceUnitBundleUtil;

+import org.eclipse.gemini.jpa.PlainDriverDataSource;

+import org.eclipse.gemini.jpa.classloader.BundleProxyClassLoader;

+import org.eclipse.gemini.jpa.classloader.CompositeClassLoader;

+import org.eclipse.persistence.config.PersistenceUnitProperties;

+import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor;

+import org.eclipse.persistence.logging.AbstractSessionLog;

+import org.eclipse.persistence.logging.DefaultSessionLog;

+import org.eclipse.persistence.logging.SessionLog;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.jdbc.DataSourceFactory;

+

+//TODO Add substitutability of provider

+

+public class EclipseLinkOSGiProvider implements BundleActivator, 

+                                                OSGiJpaProvider,

+                                                PersistenceProvider {

+

+    /*==================*/

+    /* Static constants */

+    /*==================*/

+    public static final String PROVIDER_CLASS_NAME = "org.eclipse.persistence.jpa.PersistenceProvider";

+    

+    public static final int MAX_EVENT_COLLISION_TRIES = 3;

+    

+    /*================*/

+    /* Provider state */

+    /*================*/

+    

+    /** Provider bundle context */

+    BundleContext ctx;

+

+    /** Extender code to find and process persistence unit bundles */

+    PersistenceBundleExtender extender;

+

+    /** Services utility code */

+    GeminiServicesUtil servicesUtil;

+    

+    /** Anchor class gen utility */

+    AnchorClassUtil anchorUtil;    

+    

+    /** An SPI instance of this provider */

+    PersistenceProvider eclipseLinkProvider;

+    

+    /** Map of p-units we have registered */

+    Map<String, PUnitInfo> pUnitsByName;

+    

+    private FileWriter eclipseLinkLog;

+

+    /*=====================*/

+    /* Activator functions */

+    /*=====================*/

+    

+    public void start(BundleContext context) throws Exception {

+        

+        debug("EclipseLinkProvider starting...");

+

+        // Initialize our state

+        ctx = context;

+        pUnitsByName = Collections.synchronizedMap(new HashMap<String, PUnitInfo>());

+        extender = new PersistenceBundleExtender(this);

+        anchorUtil = new AnchorClassUtil(GeminiProperties.generateAnchorClasses());

+        servicesUtil = new GeminiServicesUtil(this, anchorUtil);

+        openEclipseLinkLoggingFile();

+        eclipseLinkProvider = new org.eclipse.gemini.jpa.provider.PersistenceProvider();

+        PersistenceUnitProcessor.setArchiveFactory(new OSGiArchiveFactoryImpl());

+        

+        // Register as a provider 

+        servicesUtil.registerProviderService();

+

+        // Kick the extender to go looking for persistence bundles

+        extender.startListening();

+        extender.lookForExistingBundles();

+        debug("EclipseLinkProvider started");

+    }

+    

+    public void stop(BundleContext context) throws Exception {

+

+        debug("EclipseLinkProvider stopping...");

+

+        // Take the extender offline and unregister ourselves as a provider

+        extender.stopListening();

+        servicesUtil.unregisterProviderService();

+        

+        // Unregister all of the persistence units that we have registered

+        List<PUnitInfo> pUnits = new ArrayList<PUnitInfo>(); // Need a new copy

+        pUnits.addAll(pUnitsByName.values());

+        unregisterPersistenceUnits(pUnits);

+        pUnitsByName = null;

+        

+        // Now unassign all of the persistence units that have been assigned to us

+        Map<Bundle,List<PUnitInfo>> pUnitInfos = extender.clearAllPUnitInfos();

+        for (Map.Entry<Bundle,List<PUnitInfo>> entry : pUnitInfos.entrySet()) {

+            unassignPersistenceUnitsInBundle(entry.getKey(), entry.getValue());

+        }

+        closeEclipseLinkLoggingFile();

+        debug("EclipseLinkProvider stopped");

+    }

+    

+    /*==============================*/

+    /* OSGiJpaProvider impl methods */

+    /*==============================*/

+

+    // Used to compare against the <provider> element in persistence descriptors

+    public String getProviderClassName() { return PROVIDER_CLASS_NAME; }

+

+    // Used to invoke regular JPA createEntityManagerFactory() methods

+    public javax.persistence.spi.PersistenceProvider getProviderInstance() { 

+        return this;

+    }

+

+    public Bundle getBundle() { return ctx.getBundle(); }

+    

+    public BundleContext getBundleContext() { return ctx; }

+    

+    /**

+     * Assignment happens before resolution. This callback offers the provider a chance to do 

+     * anything that must be done before the bundle is resolved.

+     * 

+     * @param b

+     * @param pUnits

+     */

+    public void assignPersistenceUnitsInBundle(Bundle b, Collection<PUnitInfo> pUnits) {

+        debug("EclipseLinkProvider assignPersistenceUnitsInBundle: ", b.getSymbolicName());

+

+        // Run Initializer to process PU and register transformers

+        BundleContext bundleContext = getBundleContext();

+

+        //TODO Check state of bundle in assign call

+

+        // Generate a fragment for the p-units

+        if (GeminiProperties.generateFragments()) {

+            new FragmentUtil(ctx.getBundle())

+                    .generateAndInstallFragment(b, pUnits, anchorUtil);

+        }

+

+        ClassLoader compositeLoader = compositeLoader(bundleContext,b);

+        GeminiOSGiInitializer initializer = new GeminiOSGiInitializer(compositeLoader);

+        

+        initializer.registerBundle(bundleContext, b, compositeLoader, pUnits);

+        

+    }

+

+    /**

+     * The persistence bundle is resolved. In this callback the provider 

+     * must register the persistence unit services in the registry.

+     * 

+     * @param pUnits Usually, but not always, all in the same bundle

+     */

+    public void registerPersistenceUnits(Collection<PUnitInfo> pUnits) {

+        

+        debug("EclipseLinkProvider registerPersistenceUnits: ", pUnits);

+

+        if (pUnits == null) return;

+

+        for (PUnitInfo info : pUnits) {

+            String pUnitName = info.getUnitName();

+            int attempts = 0;

+            while (pUnitsByName.containsKey(pUnitName) && (attempts < MAX_EVENT_COLLISION_TRIES)) {

+                // Shouldn't be in the map. We might have a race condition due to event ordering 

+                // where the previous entry just hasn't been removed yet. Take a short break 

+                // and give a chance for the unregister to occur.

+                try { Thread.sleep(1000); } catch (InterruptedException iEx) {}

+                attempts++;

+            } 

+            if (pUnitsByName.containsKey(pUnitName)) {

+                // It's still there. Take matters into our own hands and force the unregister

+                warning("EclipseLinkProvider forcing unregister of persistence unit: " + info.getUnitName());

+                Collection<PUnitInfo> units = new ArrayList<PUnitInfo>();

+                units.add(info);

+                unregisterPersistenceUnits(units);

+            }

+            // Keep a local copy of all of the p-units we are registering

+            pUnitsByName.put(pUnitName, info); 

+            // Do the registering

+            servicesUtil.registerEMFServices(info);

+        }

+    }

+

+    /**

+     * In this callback the provider must unregister the persistence unit services 

+     * from the registry and clean up any resources.

+     * 

+     * @param pUnits Usually, but not always, all in the same bundle

+     */

+    public void unregisterPersistenceUnits(Collection<PUnitInfo> pUnits) {

+

+        debug("EclipseLinkProvider unregisterPersistenceUnits: ", pUnits);

+

+        EntityManagerFactory emf1 = null, 

+                             emf2 = null;

+

+        if (pUnits == null) return;

+        

+        for (PUnitInfo info : pUnits) {

+            // TODO re-org tracker to be from provider

+            servicesUtil.stopTrackingDataSourceFactory(info);

+            emf1 = servicesUtil.unregisterEMFService(info);

+            emf2 = servicesUtil.unregisterEMFBuilderService(info);

+            if (emf1 != null) {

+                emf1.close();

+            } else if (emf2 != null) {

+                emf2.close();

+            }

+            // Remove from our local pUnit copy 

+            pUnitsByName.remove(info.getUnitName());

+        }

+    }

+

+    public void unassignPersistenceUnitsInBundle(Bundle b, Collection<PUnitInfo> pUnits) {

+        debug("EclipseLinkProvider unassignPersistenceUnitsInBundle: ", b.getSymbolicName());

+    }

+    

+    /*=============================*/

+    /* PersistenceProvider methods */

+    /*=============================*/

+    

+    /**

+     * Intercept calls to the OSGi EclipseLink JPA provider so we can insert 

+     * classloader, data source and descriptor properties that can be used by

+     * EclipseLink to sort things out.

+     */

+    public EntityManagerFactory createEntityManagerFactory(String emName, Map properties) {

+        

+        debug("EclipseLinkProvider createEMF invoked for p-unit: ", emName);

+        debug("Properties map: ", properties);

+

+        PUnitInfo pUnitInfo = pUnitsByName.get(emName);

+        if (pUnitInfo == null)

+            fatalError("createEntityManagerFactory() called on provider, but provider has not registered the p-unit " + emName, null);

+        Map<String,Object> props = new HashMap<String,Object>();

+        props.putAll(properties);

+        props.put(PersistenceUnitProperties.CLASSLOADER, compositeLoader(pUnitInfo));

+        props.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, acquireDataSource(pUnitInfo, properties));

+        props.put(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, fullDescriptorPath(pUnitInfo));

+        props.put(GeminiOSGiInitializer.OSGI_BUNDLE, pUnitInfo.getBundle());

+        

+        EntityManagerFactory emf = eclipseLinkProvider.createEntityManagerFactory(emName, props);

+        return emf;

+    }

+

+    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {

+

+        String pUnitName = info.getPersistenceUnitName();

+        warning("Container JPA not currently supported for p-unit ", pUnitName);

+        

+        // Can't hurt to go ahead and try, though...

+        PUnitInfo pUnitInfo = pUnitsByName.get(pUnitName);

+        if (pUnitInfo == null)

+            fatalError("createContainerEntityManagerFactory() called on provider, but provider has not registered the p-unit " + pUnitName, null);

+        Map<String,Object> props = new HashMap<String,Object>();

+        props.putAll(properties);

+        props.put(PersistenceUnitProperties.CLASSLOADER, compositeLoader(pUnitInfo));

+        props.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, acquireDataSource(pUnitInfo, properties));

+        props.put(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, fullDescriptorPath(pUnitInfo));

+        props.put(GeminiOSGiInitializer.OSGI_BUNDLE, pUnitInfo.getBundle());

+

+        return eclipseLinkProvider.createContainerEntityManagerFactory(info, props);

+    }

+

+    public ProviderUtil getProviderUtil() { 

+        debug("EclipseLinkProvider getProviderUtil invoked");

+        return eclipseLinkProvider.getProviderUtil(); 

+    }

+

+    /*================*/

+    /* Helper methods */

+    /*================*/

+

+    protected ClassLoader compositeLoader(PUnitInfo pUnitInfo) {

+        return compositeLoader(getBundleContext(), pUnitInfo.getBundle());

+    }

+

+    protected ClassLoader compositeLoader(BundleContext context,

+            Bundle bundle) {

+        ClassLoader pUnitLoader = new BundleProxyClassLoader(bundle);

+        debug("PUnit bundle proxy loader created: ", pUnitLoader);

+        ClassLoader providerLoader = new BundleProxyClassLoader(context.getBundle());

+        debug("Provider bundle proxy loader created: ", providerLoader);

+        List<ClassLoader> loaders = new ArrayList<ClassLoader>();

+        loaders.add(pUnitLoader);

+        loaders.add(providerLoader);

+        ClassLoader compositeLoader = new CompositeClassLoader(loaders);

+        debug("Composite loader created: ", compositeLoader);

+        return compositeLoader;

+    }

+

+    protected DataSource acquireDataSource(PUnitInfo pUnitInfo, Map<?,?> properties) {

+

+        // If an actual data source object was passed in then just return it and 

+        // let it be re-added to the properties map by the caller  

+        // ### Enhancement for bug 335983 - Contributed by Eduard Bartsch ###

+        Object ds = properties.get(PersistenceUnitProperties.NON_JTA_DATASOURCE);

+        if (ds instanceof DataSource) {

+            return (DataSource) ds;

+        }

+        

+        // Otherwise we create a data source based on the properties

+        ServiceReference[] dsfRefs = null;

+        

+        // Get the driver name from either the pUnitInfo or the runtime properties

+        String driverName = (String)properties.get(GeminiUtil.JPA_JDBC_DRIVER_PROPERTY);

+        if (driverName == null)

+            driverName = pUnitInfo.getDriverClassName();

+

+        // We at least need a driver name. If we don't have one we are basically hosed

+        if (driverName == null)

+            fatalError("No driver was specified", null);

+

+        Properties props = getJdbcProperties(pUnitInfo, properties);

+        

+        String filterString = "(" + OSGI_JDBC_DRIVER_CLASS + "=" + driverName + ")";

+        debug("EclipseLinkProvider acquireDataSource - pUnit = ", 

+                pUnitInfo.getUnitName(), " filter = ", filterString);

+        try {

+            dsfRefs = getBundleContext().getServiceReferences(

+                                DataSourceFactory.class.getName(), filterString);

+        } catch (InvalidSyntaxException isEx) {} // dev time error

+        if (dsfRefs == null)

+            fatalError("Could not find data source factory in registry: " + driverName, null);

+

+        DataSourceFactory dsf = (DataSourceFactory) getBundleContext().getService(dsfRefs[0]);

+        Driver driver = null;

+        try {

+            // There is no standard way of getting JDBC properties from JPA

+            // (apart from the url/user/pw that are passed into the data source below)

+            driver = dsf.createDriver(null);

+        } catch (SQLException sqlEx) {

+            fatalError("Could not create data source for " + driverName, sqlEx);

+        }

+        return new PlainDriverDataSource(driver, props);

+    }

+    

+    protected String fullDescriptorPath(PUnitInfo pUnitInfo) {

+        return pUnitInfo.getDescriptorInfo().fullDescriptorPath();

+    }

+

+    /*

+     * Return the current JDBC url, user and password properties. The props set in the

+     * XML file (from pUnitInfo) will be overridden by any properties of the same name

+     * in the runtime properties Map passed in. The resulting properties will be passed

+     * to a JDBC driver.

+     */

+    protected Properties getJdbcProperties(PUnitInfo pUnitInfo, Map<?,?> properties) {

+

+        Properties props = new Properties();

+        

+        // Get the 3 driver properties, if they exist (url, user, password)

+        debug("EclipseLinkProvider - getJDBCProperties");

+        debug("  fromMap: ", properties);

+        debug("  fromDescriptor: ", pUnitInfo);

+

+        String url = (String)properties.get(GeminiUtil.JPA_JDBC_URL_PROPERTY);

+        if (url == null)

+            url = pUnitInfo.getDriverUrl();        

+        if (url != null) 

+            props.put(JDBC_URL, url);

+        

+        String user = (String)properties.get(GeminiUtil.JPA_JDBC_USER_PROPERTY);

+        if (user == null)

+            user = pUnitInfo.getDriverUser();        

+        if (user != null) 

+            props.put(JDBC_USER, user);

+

+        String pw = (String)properties.get(GeminiUtil.JPA_JDBC_PASSWORD_PROPERTY);

+        if (pw == null)

+            pw = pUnitInfo.getDriverPassword();        

+        if (pw != null) 

+            props.put(JDBC_PASSWORD, pw);

+

+        debug("EclipseLinkProvider - getJDBCProperties - returning: ", props);

+        return props;

+    }

+

+    public void openEclipseLinkLoggingFile() {

+        String loggingFile = System.getProperty(PersistenceUnitProperties.LOGGING_FILE);

+        try {

+            if (loggingFile != null) {

+                eclipseLinkLog = new FileWriter(loggingFile);

+                AbstractSessionLog.getLog().setWriter(eclipseLinkLog);

+            }

+        } catch (IOException e) {

+            AbstractSessionLog.getLog().log(SessionLog.WARNING, "cmp_init_default_logging_file_is_invalid",loggingFile,e);

+        }

+    }

+    public void closeEclipseLinkLoggingFile() {

+        // Reset to default

+        AbstractSessionLog.setLog(new DefaultSessionLog());

+        try {

+            if (eclipseLinkLog != null) {

+                eclipseLinkLog.close();

+            }

+        } catch (IOException e) {

+        }

+    }

+

+

+

+}
\ No newline at end of file
diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/GeminiOSGiInitializer.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/GeminiOSGiInitializer.java
new file mode 100644
index 0000000..9b6cd34
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/GeminiOSGiInitializer.java
@@ -0,0 +1,161 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.Hashtable;

+import java.util.List;

+import java.util.Map;

+

+import javax.persistence.spi.ClassTransformer;

+import javax.persistence.spi.PersistenceUnitInfo;

+

+import org.eclipse.gemini.jpa.PUnitInfo;

+import org.eclipse.gemini.jpa.weaving.IWeaver;

+import org.eclipse.persistence.internal.jpa.deployment.JPAInitializer;

+import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor;

+import org.eclipse.persistence.jpa.Archive;

+import org.eclipse.persistence.logging.AbstractSessionLog;

+import org.eclipse.persistence.logging.SessionLog;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+

+public class GeminiOSGiInitializer extends JPAInitializer {

+    public static final String OSGI_BUNDLE = "org.eclipse.gemini.jpa.bundle";

+    private static final String OSGI_CONTEXT = "org.eclipse.gemini.jpa.context";

+

+    private boolean weavingSupported = true; // TODO: need to determine if Equinox 

+   

+    /**

+     * Constructor used when registering bundles.

+     */

+    public GeminiOSGiInitializer() {

+    }

+

+    /** 

+     * Constructor used by PersistenceProvider$PersistenceInitializationHelper

+     * @param loader

+     */

+    GeminiOSGiInitializer(ClassLoader loader) {

+        this.initializationClassloader = loader;

+    }

+

+    /**

+     * Indicates whether puName uniquely defines the persistence unit.

+     */

+    @Override

+    public boolean isPersistenceUnitUniquelyDefinedByName() {

+        // TODO isPersistenceUnitUniquelyDefinedByName should be false

+        // but PU creation fails for embedded PUs

+        return true;

+    }

+    

+    /***

+     * A bundle is being stopped or becoming in some way unavailable.

+     * Undeploy the bundle's persistence units and remove all references

+     * to it.

+     * @param bundle

+     */

+    public void unregisterBundle(final Bundle bundle, Collection<PUnitInfo> pUnits) {

+        //TODO: unregisterBundle(final Bundle bundle, Collection<PUnitInfo> pUnits)

+    }

+    

+    /***

+     * registerBundle will 

+     * @param context

+     * @param bundle

+     * @param pUnits

+     */

+    public void registerBundle(final BundleContext context, final Bundle bundle, ClassLoader bundleLoader, Collection<PUnitInfo> pUnits) {

+        this.initializationClassloader = bundleLoader;

+        

+        List<Archive> pars = new ArrayList<Archive>();

+        Map<String, String> storedArchives = new HashMap<String, String>();

+        for (PUnitInfo pUnitInfo : pUnits) {

+            if (!storedArchives.containsKey(pUnitInfo.getDescriptorInfo().fullDescriptorPath())){

+                pars.addAll(PersistenceUnitProcessor.findPersistenceArchives(bundleLoader, pUnitInfo.getDescriptorInfo().fullDescriptorPath()));

+                storedArchives.put(pUnitInfo.getDescriptorInfo().fullDescriptorPath(), null);

+            }

+        }

+        // Create a properties map with the bundle and context so they

+        // are available when defining a transformer.

+        Map<String, Object> properties = new HashMap<String, Object>();

+        properties.put(OSGI_BUNDLE, bundle);

+        properties.put(OSGI_CONTEXT, context);

+        for (Archive archive: pars) {

+            AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_initialize", archive);

+            initPersistenceUnits(archive, properties);

+        }

+    }

+

+    protected ClassLoader createTempLoader(Collection col, boolean shouldOverrideLoadClassForCollectionMembers) {

+        return Thread.currentThread().getContextClassLoader();

+    }

+

+    public ClassLoader getBundleClassLoader(){

+        return initializationClassloader;

+    }

+    

+    /**

+     * Check whether weaving is possible and update the properties and variable as appropriate

+     * @param properties The list of properties to check for weaving and update if weaving is not needed

+     */

+    @Override

+    public void checkWeaving(Map properties){

+    }

+    

+    /***

+	 * In OSGi we don't need a temp loader so use the loader built

+	 * for the bundle.

+	 */

+	@SuppressWarnings("rawtypes")

+    @Override

+	protected ClassLoader createTempLoader(Collection col) {

+	    return this.initializationClassloader;

+	}

+     

+    public void initialize(Map m) {

+    }

+    

+    @SuppressWarnings("rawtypes")

+    @Override

+    public void registerTransformer(ClassTransformer transformer, PersistenceUnitInfo persistenceUnitInfo, Map properties) {

+        if (weavingSupported) {

+            Bundle bundle = (Bundle) properties.get(OSGI_BUNDLE);

+            if (bundle == null){

+                AbstractSessionLog.getLog().log(SessionLog.FINER, "Bundle null, not registering Weaving Service");

+                return;

+            }

+            BundleContext context = (BundleContext) properties.get(OSGI_CONTEXT);

+            if (context == null){

+                AbstractSessionLog.getLog().log(SessionLog.FINER, "Bundle Context null, not registering Weaving Service");

+                return;

+            }

+            if (transformer != null) {

+                AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_register_transformer", persistenceUnitInfo.getPersistenceUnitName());

+                IWeaver weavingService = new OSGiWeaver(transformer, bundle.getSymbolicName(), bundle.getVersion());

+                context.registerService(IWeaver.class.getName(), weavingService, new Hashtable());

+                AbstractSessionLog.getLog().log(SessionLog.FINER, "Registering Weaving Service");

+            } else if (transformer == null) {

+                AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_transformer_is_null");

+            }

+        } else {

+            throw new RuntimeException("Attempt to create a transformer when weaving not supported!");

+        }

+    }

+    

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiArchiveFactoryImpl.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiArchiveFactoryImpl.java
new file mode 100644
index 0000000..ded32b4
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiArchiveFactoryImpl.java
@@ -0,0 +1,51 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     tware - initial implementation

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.io.IOException;

+import java.net.URISyntaxException;

+import java.net.URL;

+import java.util.Map;

+import java.util.logging.Level;

+

+import org.eclipse.persistence.internal.jpa.deployment.ArchiveFactoryImpl;

+import org.eclipse.persistence.internal.jpa.deployment.JarInputStreamURLArchive;

+import org.eclipse.persistence.jpa.Archive;

+

+/**

+ * Subclass of EclipseLink's ArchiveFactoryImpl

+ * This subclass allows construction of a BundleArchive which can use the Bundle API

+ * to extract information out of a persistence unit

+ * @author tware

+ *

+ */

+public class OSGiArchiveFactoryImpl extends ArchiveFactoryImpl{

+

+    @Override

+    public Archive createArchive(URL rootUrl, String descriptorLocation, Map properties) throws URISyntaxException, IOException {

+        logger.entering("ArchiveFactoryImpl", "createArchive", new Object[]{rootUrl, descriptorLocation});

+        String protocol = rootUrl.getProtocol();

+        logger.logp(Level.FINER, "ArchiveFactoryImpl", "createArchive", "protocol = {0}", protocol);

+        

+        if (properties != null && properties.get("org.eclipse.gemini.jpa.bundle") != null){

+            if (isJarInputStream(rootUrl)){

+                return new JarInputStreamURLArchive(rootUrl, descriptorLocation);

+            } else {

+                return new BundleArchive(rootUrl,properties, descriptorLocation, logger);

+            }

+        }

+        return super.createArchive(rootUrl, descriptorLocation, properties);

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiJpaProvider.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiJpaProvider.java
new file mode 100644
index 0000000..501b4c7
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiJpaProvider.java
@@ -0,0 +1,92 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ *     ssmith - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.util.Collection;

+

+import javax.persistence.spi.PersistenceProvider;

+

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+

+import org.eclipse.gemini.jpa.PUnitInfo;

+

+/**

+ * This is the interface that a provider must implement in order to

+ * be able to use the Gemini classes. The extender and servicesUtil 

+ * hold a reference to the impl of this interface and call it to

+ * obtain any required information about the provider in order to 

+ * perform work on its behalf.

+ */

+public interface OSGiJpaProvider {

+

+    /** 

+     * The public string that represents this provider; the one that users

+     * are expected to put in the <provider> element in the persistence descriptor.

+     */

+    String getProviderClassName();

+    

+    /**

+     * The JPA PersistenceProvider implementation instance that is to be invoked

+     * when the EntityManagerFactory services are used.

+     */

+    PersistenceProvider getProviderInstance();

+    

+    /**

+     * The bundle that contains this provider.

+     */

+    Bundle getBundle();

+    

+    /**

+     * The bundle context for this provider.

+     */

+    BundleContext getBundleContext();

+

+    /**

+     * Called by the Gemini persistence unit extender when a persistence

+     * bundle has been detected and is being assigned to this provider.

+     * Fragments typically need to be created. 

+     * 

+     * @see PersistenceUnitExtender#generateAndInstallFragment(Bundle, Collection<PUnitInfo>)

+     */

+    void assignPersistenceUnitsInBundle(Bundle b, Collection<PUnitInfo> pUnits);

+

+    /**

+     * Called by the Gemini persistence unit extender when a persistence

+     * bundle has been resolved and is now ready to have its EMF[Builder]

+     * services registered. 

+     * 

+     * @see PeristenceServicesUtil.registerEMFServices(PUnitInfo)

+     */

+    void registerPersistenceUnits(Collection<PUnitInfo> pUnits);

+

+    /**

+     * Called by the Gemini persistence unit extender when a persistence

+     * bundle is leaving the active state. It may be called whenever

+     * the services for the persistence unit should be removed.

+     * 

+     * @see PeristenceServicesUtil.unregisterEMFService(PUnitInfo)

+     * @see PeristenceServicesUtil.unregisterEMFBuilderService(PUnitInfo)

+     */

+    void unregisterPersistenceUnits(Collection<PUnitInfo> pUnits);

+    

+    /**

+     * Called by the Gemini persistence unit extender when a persistence

+     * bundle is being uninstalled or updated. It may be called whenever

+     * the persistence unit is longer being assigned to this provider.

+     */

+    void unassignPersistenceUnitsInBundle(Bundle b, Collection<PUnitInfo> pUnits);

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiWeaver.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiWeaver.java
new file mode 100644
index 0000000..bd9ffdd
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/OSGiWeaver.java
@@ -0,0 +1,63 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.lang.instrument.IllegalClassFormatException;

+

+import javax.persistence.spi.ClassTransformer;

+

+import org.eclipse.gemini.jpa.weaving.IWeaver;

+import org.eclipse.persistence.logging.AbstractSessionLog;

+import org.eclipse.persistence.logging.SessionLog;

+import org.osgi.framework.Version;

+

+

+/**

+ * Provides a weaving wrapper for JPA on OSGi

+ * 

+ * @author ssmith

+ * @author tware

+ *

+ */

+public class OSGiWeaver implements IWeaver {

+    private final ClassTransformer transformer;

+    private String bundleName;

+    private Version bundleVersion;

+

+    OSGiWeaver(final ClassTransformer transformer, final String bundleName, final Version bundleVersion) {

+        this.transformer = transformer;

+        this.bundleName = bundleName;

+        this.bundleVersion = bundleVersion;

+    }

+

+    public byte[] transform(String className, String bundleName, Version bundleVersion, byte[] classfileBuffer ) {

+        // Only attempt to weave if the class originates in the bundle and version

+        // that this weaver was built for.

+        if (!(this.bundleName.equals(bundleName) && this.bundleVersion.equals(bundleVersion))) {

+            return null;

+        }

+        try {

+            byte[] transformedBytes = transformer.transform(null, className, null, null, classfileBuffer);

+            if (transformedBytes != null) {

+                AbstractSessionLog.getLog().log(SessionLog.FINER, className + " woven successfully");  // TODO NON-NLS 

+            }

+            return transformedBytes;

+        } catch (IllegalClassFormatException e) {

+            // TODO log appropriate warning

+            e.printStackTrace();

+            return null;

+        }

+    }

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/PersistenceProvider.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/PersistenceProvider.java
new file mode 100644
index 0000000..6961db8
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/provider/PersistenceProvider.java
@@ -0,0 +1,39 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith - EclipseLink integration

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.provider;

+

+import java.util.Map;

+

+import org.eclipse.persistence.internal.jpa.deployment.JPAInitializer;

+

+public class PersistenceProvider extends org.eclipse.persistence.jpa.PersistenceProvider {

+    

+    @SuppressWarnings("rawtypes")

+    public JPAInitializer createInitializer(final ClassLoader classLoader, Map m) {

+        return new GeminiOSGiInitializer(classLoader);

+     }

+    

+    /**

+     * Return JPAInitializer corresponding to the passed classLoader.

+     * @param classLoader

+     * @param m

+     * @return

+     */

+    public JPAInitializer getInitializer(String emName, Map m){

+        ClassLoader classLoader = getClassLoader(emName, m);

+        return createInitializer(classLoader, m);

+    }

+}

+

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFBuilderServiceProxyHandler.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFBuilderServiceProxyHandler.java
new file mode 100644
index 0000000..464ac54
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFBuilderServiceProxyHandler.java
@@ -0,0 +1,144 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.proxy;

+

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+import java.util.HashMap;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+

+

+import org.eclipse.gemini.jpa.PUnitInfo;

+import org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.*;

+

+/**

+ * Dynamic proxy class to proxy the EMFBuilder service

+ */

+public class EMFBuilderServiceProxyHandler extends EMFServiceProxyHandler

+                                           implements InvocationHandler {

+            

+    // We inherit an emf field and use that in the case when there is no EMF Service.

+    // When there is an EMF Service then we use the emf field in the EMF service proxy stored below.

+    

+    // Hold onto the EMF service if it exists

+    EMFServiceProxyHandler emfService;

+    

+    // Keep around a copy of the props used to create an EMF through the EMF builder

+    Map<String,Object> emfProps = new HashMap<String,Object>();

+            

+    public EMFBuilderServiceProxyHandler(PUnitInfo pUnitInfo,

+                                         EMFServiceProxyHandler emfService) {

+        super(pUnitInfo);

+        this.emfService = emfService;

+    }

+

+    /*=========================*/

+    /* InvocationProxy methods */

+    /*=========================*/

+

+    // Will only get calls for the method on the EntityManagerFactoryBuilder interface

+    @Override

+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

+

+        debug("EMFBuilderProxy invocation on method ", method.getName());

+

+        if (method.getName().equals("hashCode"))

+            return this.hashCode();

+

+        // Must be a createEntityManagerFactory(String, Map) call

+

+        // If we have an EMF and it has already been closed, discard it

+        synchronized (this) {

+            if ((emf != null) && (!emf.isOpen()))

+                emf = null;

+    

+            // If we have a local factory, return it

+            if (emf != null) 

+                return emf;

+        }

+        // The first arg must be the properties Map

+        Map<String,Object> props = (Map<String,Object>)args[0];

+

+        // If an EMF service is registered the EMF must be stored there

+        if (emfService != null) {

+

+            // Verify the JDBC properties match the ones in the descriptor.

+            verifyJDBCProperties(pUnitInfo.getDriverClassName(), 

+                                 pUnitInfo.getDriverUrl(), 

+                                 props);

+            

+            // Verify the JDBC properties match the ones previously passed in.

+            verifyJDBCProperties((String) emfProps.get(JPA_JDBC_DRIVER_PROPERTY), 

+                                 (String) emfProps.get(JPA_JDBC_URL_PROPERTY), 

+                                 props);

+

+            // Synchronize to ensure we share the same factory

+            synchronized(emfService) {

+                

+                // If EMF service has one that is closed then discard it

+                if ((emfService.getEMF() != null) && (!emfService.getEMF().isOpen())) {

+                    emfService.setEMF(null);

+                    emfProps.clear();

+                }

+                // If it doesn't have one, then assign it one

+                if (emfService.getEMF() == null) {

+                    emfService.setEMF(createEMF(props));

+                }      

+                // Create a proxy to the EMF in the EMFService

+                return Proxy.newProxyInstance(this.getClass().getClassLoader(),

+                                              new Class[] { EntityManagerFactory.class },

+                                              emfService);

+            }

+        } else {

+            // No EMF service (data source was not active). Create our own EMF since we don't have one

+            synchronized (this) {

+                if (emf == null)

+                    emf = createEMF(props);

+            }

+            return emf;

+        }

+    }

+

+    /*================*/

+    /* Helper methods */

+    /*================*/

+    

+    protected EntityManagerFactory createEMF(Map<String,Object> props) {

+        emfProps = props;

+        return super.createEMF(props);

+    }

+

+    // Local method to compare properties passed in Map to ones in persistence descriptor or in previously set props

+    protected void verifyJDBCProperties(String driver, String driverUrl, Map<String,Object> props) {

+

+        if (driver != null) {

+            String propDriver = (String) props.get(JPA_JDBC_DRIVER_PROPERTY);

+            if ((propDriver != null) && !driver.equals(propDriver)) {

+                throw new IllegalArgumentException();

+            }

+        }

+        if (driverUrl != null) {

+            String propUrl = (String) props.get(JPA_JDBC_URL_PROPERTY);

+            if ((propUrl != null) && !driverUrl.equals(propUrl)) {

+                throw new IllegalArgumentException();

+            }

+        }

+    }

+}        

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFServiceProxyHandler.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFServiceProxyHandler.java
new file mode 100644
index 0000000..634ccc9
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/proxy/EMFServiceProxyHandler.java
@@ -0,0 +1,125 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.proxy;

+

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.Method;

+import java.util.HashMap;

+import java.util.Map;

+

+import javax.persistence.EntityManagerFactory;

+import javax.persistence.spi.PersistenceProvider;

+

+import org.osgi.framework.Bundle;

+import org.osgi.framework.ServiceFactory;

+import org.osgi.framework.ServiceRegistration;

+

+import org.eclipse.gemini.jpa.PUnitInfo;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.*;

+

+/**

+ * Dynamic proxy class to proxy the EMF service

+ */

+public class EMFServiceProxyHandler implements InvocationHandler, ServiceFactory {

+    

+    PUnitInfo pUnitInfo;

+    EntityManagerFactory emf;

+    

+    public EMFServiceProxyHandler(PUnitInfo pUnitInfo) { this.pUnitInfo = pUnitInfo; }

+

+    // May be called by the EMFBuilder service

+    public EntityManagerFactory getEMF() { return emf; }

+    public void setEMF(EntityManagerFactory factory) { emf = factory; }

+    

+    /*=========================*/

+    /* InvocationProxy methods */

+    /*=========================*/

+    

+    // Will only get calls for the methods on the EntityManagerFactory interface

+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

+

+        debug("EMFProxy invocation on method ", method.getName());

+

+        /* Allow close() to pass through in order for users to close and reopen the EMF. */

+        /* NOTE: This means that any user that closes an EMF will cause it to be closed  */

+        /* for all other service references of the EMF service. */

+        // If close() invoked then just ignore it

+        /* if (method.getName().equals("close"))

+            return null;

+         */

+        

+        // Invoke these methods on the actual proxy (not the object it's proxying)

+        if (method.getName().equals("hashCode"))

+            return this.hashCode();

+        if (method.getName().equals("toString"))

+            return this.toString();

+        

+        

+        /*===========================================================================*/

+        /* ** NOTE: What if the provider supports multiple EMFs for the same punit?  */

+        /*          Should we ignore the cache and just call the provider each time? */

+        /*          Not right now.                                                   */

+        /*===========================================================================*/

+

+        if (emf == null) {

+            synchronized(this) {

+                if (emf == null) {

+                    emf = createEMF(new HashMap<String,Object>());

+                }

+            } 

+        }

+        // Invoke the EMF method that was called

+        Object result = method.invoke(emf,args);

+        

+        // If the operation was to close the EMF then remove our ref to it

+        synchronized(this) {

+            if (!emf.isOpen()) 

+                emf = null;

+        }

+        return result;

+    }

+

+    /*========================*/

+    /* ServiceFactory methods */

+    /*========================*/

+

+    public Object getService(Bundle b, ServiceRegistration serviceReg) {

+        // TODO Track client bundles that use this service and clean up if they leave

+        return this;

+    }

+    

+    public void ungetService(Bundle b, ServiceRegistration serviceReg, Object obj) {

+        // EMF is shared, leave as is until the p-unit or the provider goes away

+        // and the service is unregistered

+    }

+    

+    /*================*/

+    /* Helper methods */

+    /*================*/

+

+    // Use info from the cached pUnitInfo and create a new EMF to store locally

+    protected EntityManagerFactory createEMF(Map<String,Object> props) {

+        

+        String unitName =  pUnitInfo.getUnitName();

+        PersistenceProvider provider = pUnitInfo.getAssignedProvider().getProviderInstance();

+        EntityManagerFactory result = provider.createEntityManagerFactory(unitName, props);

+        if (result == null)

+            fatalError("Proxy could not create EMF " + unitName + " from provider " + provider, null);

+        return result;

+    }

+

+}        

+

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/EclipseDotClasspathHandler.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/EclipseDotClasspathHandler.java
new file mode 100644
index 0000000..ff9b9c4
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/EclipseDotClasspathHandler.java
@@ -0,0 +1,73 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     ssmith, mkeith 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.xml;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.debugXml;

+

+import org.xml.sax.Attributes;

+import org.xml.sax.SAXException;

+import org.xml.sax.helpers.DefaultHandler;

+

+

+/**

+ * Parser handler for parsing the Eclipse .classpath file.

+ * This class does not parse the entire file.  We're only

+ * interested in the binary "output" directory that contains

+ * classes and resources.

+ */

+public class EclipseDotClasspathHandler extends DefaultHandler {

+    

+    // The current element

+    private String binPath;

+    

+    /*========================*/

+    /* DefaultHandler methods */

+    /*========================*/

+

+    protected void setBinPath(String path) {

+        this.binPath = path;

+    }

+

+    public String getBinPath() {

+        return binPath;

+    }

+

+    @Override

+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

+        debugXml("<<< startElement localName: ", localName, " qName: ", qName);

+

+        if ("classpathentry".equals(qName)) {

+            String kind = attributes.getValue("kind");

+            debugXml("kind: ", kind);

+            if ("output".equals(kind)) { 

+                String path = attributes.getValue("path");

+                debugXml("path: ", kind);

+                setBinPath(path);

+            }            

+        }

+    }

+

+    @Override

+    public void endElement(String uri, String localName, String qName) throws SAXException {

+        debugXml(">>> EndElement,  localName: ", localName, "  qName: ", qName);

+    }

+

+    /*================*/

+    /* Helper methods */

+    /*================*/

+    

+    protected boolean nullOrEmpty(String s) { return s == null || s.length() == 0; }

+

+}

diff --git a/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/PersistenceDescriptorHandler.java b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/PersistenceDescriptorHandler.java
new file mode 100644
index 0000000..3ed8b43
--- /dev/null
+++ b/org.eclipse.gemini.jpa/src/org/eclipse/gemini/jpa/xml/PersistenceDescriptorHandler.java
@@ -0,0 +1,133 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Oracle.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * and Apache License v2.0 which accompanies this distribution. 

+ * The Eclipse Public License is available at

+ *     http://www.eclipse.org/legal/epl-v10.html

+ * and the Apache License v2.0 is available at 

+ *     http://www.opensource.org/licenses/apache2.0.php.

+ * You may elect to redistribute this code under either of these licenses.

+ *

+ * Contributors:

+ *     mkeith - Gemini JPA work 

+ ******************************************************************************/

+package org.eclipse.gemini.jpa.xml;

+

+import java.util.HashSet;

+import java.util.Set;

+

+import org.xml.sax.Attributes;

+import org.xml.sax.SAXException;

+import org.xml.sax.helpers.DefaultHandler;

+

+

+import org.eclipse.gemini.jpa.GeminiUtil;

+import org.eclipse.gemini.jpa.PUnitInfo;

+

+import static org.eclipse.gemini.jpa.GeminiUtil.*;

+

+

+/**

+ * Parser handler for parsing the persistence descriptors.

+ * This class does not parse the descriptors in their entirety,

+ * but saves only a portion of the content in order for the OSGi

+ * integration to know whether a given persistence unit can be

+ * assigned to a particular provider.

+ */

+public class PersistenceDescriptorHandler extends DefaultHandler {

+    

+    // The current element

+    String currentElement;

+    

+    // The current persistence unit being processed

+    PUnitInfo currentPUnit;

+        

+    // Set of persistence unit info in the persistence descriptor

+    Set<PUnitInfo> pUnits = new HashSet<PUnitInfo>();

+    

+    public Set<PUnitInfo> getPersistenceUnitInfo() { return pUnits; }

+

+    /*========================*/

+    /* DefaultHandler methods */

+    /*========================*/

+

+    @Override

+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

+        debugXml("<<< startElement localName: ", localName, " qName: ", qName);

+

+        currentElement = localName;

+        if (nullOrEmpty(currentElement)) {

+            if (nullOrEmpty(qName))

+                GeminiUtil.fatalError("No element returned", null);

+            String[] comps = qName.split(":");

+            currentElement = comps[comps.length - 1];

+        }

+        debugXml("currentElement: ", currentElement);

+        

+        // Beginning of p-unit

+        if (currentElement.equals("persistence-unit")) {

+            String unitName = attributes.getValue("name");

+            debugXml("unit name: ", unitName);

+            if (nullOrEmpty(unitName)) 

+                GeminiUtil.fatalError("No unit name found" , null);

+            

+            currentPUnit = new PUnitInfo();

+            currentPUnit.setUnitName(unitName);

+            pUnits.add(currentPUnit);

+            

+        // Look for Driver properties

+        } else if (currentElement.equals("property")) {

+            String propName = attributes.getValue("name");

+            debugXml("prop name: ", propName);

+            if (nullOrEmpty(propName))

+                GeminiUtil.fatalError("Invalid 'name' for persistence descriptor <property>" , null);

+            String propValue = attributes.getValue("value");

+            debugXml("prop value: ", propValue);

+            // If no value then set to empty string

+            if (propValue == null)

+                propValue = "";

+

+            if (propName.equals(GeminiUtil.JPA_JDBC_DRIVER_PROPERTY))

+                currentPUnit.setDriverClassName(propValue);

+            else if (propName.equals(GeminiUtil.JPA_JDBC_URL_PROPERTY))

+                currentPUnit.setDriverUrl(propValue);

+            else if (propName.equals(GeminiUtil.JPA_JDBC_USER_PROPERTY))

+                currentPUnit.setDriverUser(propValue);

+            else if (propName.equals(GeminiUtil.JPA_JDBC_PASSWORD_PROPERTY))

+                currentPUnit.setDriverPassword(propValue);

+        }

+    }

+

+    @Override

+    public void endElement(String uri, String localName, String qName) throws SAXException {

+        currentElement = null;

+        debugXml(">>> EndElement,  localName: ", localName, "  qName: ", qName);

+    }

+

+    @Override

+    public void characters(char[] ch, int start, int length) throws SAXException {

+        if (currentElement == null) return;

+        if (currentElement.equals("provider")) {

+            currentPUnit.setProvider(contentFromChars(ch, start, length));

+            debugXml("provider: ", currentPUnit.getProvider());

+        } else if (currentElement.equals("class")) {

+            String classString = contentFromChars(ch, start, length);

+            currentPUnit.addClass(classString);

+            debugXml("class: ", classString);

+        }

+    }

+

+    /*================*/

+    /* Helper methods */

+    /*================*/

+    

+    protected boolean nullOrEmpty(String s) { return s == null || s.length() == 0; }

+

+    protected String contentFromChars(char[] chars, int start, int length) {

+        StringBuilder s = new StringBuilder(length);

+        for (int idx=start; idx<(start+length); idx++) s.append(chars[idx]);

+        // Bug 327908 - Trim to remove whitespace

+        return s.toString().trim();

+    }

+}