001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.persistence;
019    
020    import java.io.File;
021    import java.net.MalformedURLException;
022    import java.net.URI;
023    import java.net.URISyntaxException;
024    import java.net.URL;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Properties;
030    
031    import javax.persistence.EntityManager;
032    import javax.persistence.EntityManagerFactory;
033    import javax.persistence.PersistenceException;
034    import javax.persistence.spi.ClassTransformer;
035    import javax.persistence.spi.PersistenceProvider;
036    import javax.persistence.spi.PersistenceUnitInfo;
037    import javax.persistence.spi.PersistenceUnitTransactionType;
038    import javax.sql.DataSource;
039    import javax.resource.ResourceException;
040    
041    import org.apache.geronimo.connector.outbound.ConnectionFactorySource;
042    import org.apache.geronimo.gbean.GBeanInfo;
043    import org.apache.geronimo.gbean.GBeanInfoBuilder;
044    import org.apache.geronimo.gbean.GBeanLifecycle;
045    import org.apache.geronimo.gbean.SingleElementCollection;
046    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
047    import org.apache.geronimo.kernel.classloader.TemporaryClassLoader;
048    import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
049    import org.apache.geronimo.transformer.TransformerAgent;
050    
051    /**
052     * @version $Rev: 545396 $ $Date: 2007-06-07 23:49:40 -0400 (Thu, 07 Jun 2007) $
053     */
054    public class PersistenceUnitGBean implements GBeanLifecycle {
055        private final String persistenceUnitRoot;
056        private final PersistenceUnitInfoImpl persistenceUnitInfo;
057        private final EntityManagerFactory entityManagerFactory;
058        private final TransactionManagerImpl transactionManager;
059        private final SingleElementCollection<ExtendedEntityManagerRegistry> entityManagerRegistry;
060    
061    
062        public PersistenceUnitGBean() {
063            persistenceUnitRoot = null;
064            persistenceUnitInfo = null;
065            entityManagerFactory = null;
066            transactionManager = null;
067            entityManagerRegistry = null;
068        }
069    
070        public PersistenceUnitGBean(String persistenceUnitName,
071                String persistenceProviderClassName,
072                String persistenceUnitTransactionTypeString,
073                ConnectionFactorySource jtaDataSourceWrapper,
074                ConnectionFactorySource nonJtaDataSourceWrapper,
075                List<String> mappingFileNamesUntyped,
076                List<String> jarFileUrlsUntyped,
077                String persistenceUnitRoot,
078                List<String> managedClassNames,
079                boolean excludeUnlistedClassesValue,
080                Properties properties,
081                TransactionManagerImpl transactionManager,
082                Collection<ExtendedEntityManagerRegistry > entityManagerRegistry,
083                URL configurationBaseURL,
084                ClassLoader classLoader) throws URISyntaxException, MalformedURLException, ResourceException {
085            List<String> mappingFileNames = mappingFileNamesUntyped == null? new ArrayList<String>(): new ArrayList<String>(mappingFileNamesUntyped);
086            this.persistenceUnitRoot = persistenceUnitRoot;
087            URI configurationBaseURI = new File(configurationBaseURL.getFile()).toURI();
088            URL rootURL = null;
089            List<URL> jarFileUrls = null;
090            if (!excludeUnlistedClassesValue) {
091                rootURL = configurationBaseURI.resolve(persistenceUnitRoot).normalize().toURL();
092                jarFileUrls = new ArrayList<URL>();
093                for (String urlString: jarFileUrlsUntyped) {
094                    URL url = configurationBaseURI.resolve(urlString).normalize().toURL();
095                    jarFileUrls.add(url);
096                }
097            }
098            PersistenceUnitTransactionType persistenceUnitTransactionType = persistenceUnitTransactionTypeString == null? PersistenceUnitTransactionType.JTA: PersistenceUnitTransactionType.valueOf(persistenceUnitTransactionTypeString);
099    
100            if (persistenceProviderClassName == null) persistenceProviderClassName = "org.apache.openjpa.persistence.PersistenceProviderImpl";
101            
102            persistenceUnitInfo = new PersistenceUnitInfoImpl(persistenceUnitName,
103                    persistenceProviderClassName,
104                    persistenceUnitTransactionType,
105                    jtaDataSourceWrapper == null? null: (DataSource)jtaDataSourceWrapper.$getResource(),
106                    nonJtaDataSourceWrapper == null? null: (DataSource)nonJtaDataSourceWrapper.$getResource(),
107                    mappingFileNames,
108                    jarFileUrls,
109                    rootURL,
110                    managedClassNames,
111                    excludeUnlistedClassesValue,
112                    properties,
113                    classLoader);
114            try {
115                Class clazz = classLoader.loadClass(persistenceProviderClassName);
116                PersistenceProvider persistenceProvider = (PersistenceProvider) clazz.newInstance();
117                entityManagerFactory = persistenceProvider.createContainerEntityManagerFactory(persistenceUnitInfo, properties);
118            } catch (ClassNotFoundException e) {
119                persistenceUnitInfo.destroy();
120                throw new PersistenceException("Could not locate PersistenceProvider class: " + persistenceProviderClassName + " in classloader " + classLoader, e);
121            } catch (InstantiationException e) {
122                persistenceUnitInfo.destroy();
123                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
124            } catch (IllegalAccessException e) {
125                persistenceUnitInfo.destroy();
126                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
127            }
128            this.transactionManager = transactionManager;
129            this.entityManagerRegistry = new SingleElementCollection<ExtendedEntityManagerRegistry>(entityManagerRegistry);
130        }
131    
132        public EntityManagerFactory getEntityManagerFactory() {
133            return entityManagerFactory;
134        }
135    
136        public EntityManager getEntityManager(boolean transactionScoped, Map properties) {
137            if (transactionScoped) {
138                return new CMPEntityManagerTxScoped(transactionManager, getPersistenceUnitName(), entityManagerFactory, properties);
139            } else if (entityManagerRegistry.getElement() != null) {
140                return new CMPEntityManagerExtended(entityManagerRegistry.getElement(), entityManagerFactory, properties);
141            } else {
142                throw new NullPointerException("No ExtendedEntityManagerRegistry supplied, you cannot use extended persistence contexts");
143            }
144        }
145    
146        public String getPersistenceUnitName() {
147            return persistenceUnitInfo.getPersistenceUnitName();
148        }
149    
150    
151        public String getPersistenceUnitRoot() {
152            return persistenceUnitRoot;
153        }
154    
155        public String getPersistenceProviderClassName() {
156            return persistenceUnitInfo.getPersistenceProviderClassName();
157        }
158    
159        public PersistenceUnitTransactionType getTransactionType() {
160            return persistenceUnitInfo.getTransactionType();
161        }
162    
163        public DataSource getJtaDataSource() {
164            return persistenceUnitInfo.getJtaDataSource();
165        }
166    
167        public DataSource getNonJtaDataSource() {
168            return persistenceUnitInfo.getNonJtaDataSource();
169        }
170    
171        public List<String> getMappingFileNames() {
172            return persistenceUnitInfo.getMappingFileNames();
173        }
174    
175        public List<URL> getJarFileUrls() {
176            return persistenceUnitInfo.getJarFileUrls();
177        }
178    
179        public URL getPersistenceUnitRootUrl() {
180            return persistenceUnitInfo.getPersistenceUnitRootUrl();
181        }
182    
183        public List<String> getManagedClassNames() {
184            return persistenceUnitInfo.getManagedClassNames();
185        }
186    
187        public boolean excludeUnlistedClasses() {
188            return persistenceUnitInfo.excludeUnlistedClasses();
189        }
190    
191        public Properties getProperties() {
192            return persistenceUnitInfo.getProperties();
193        }
194    
195        public ClassLoader getClassLoader() {
196            return persistenceUnitInfo.getClassLoader();
197        }
198    
199        public void addTransformer(ClassTransformer classTransformer) {
200            persistenceUnitInfo.addTransformer(classTransformer);
201        }
202    
203        public ClassLoader getNewTempClassLoader() {
204            return persistenceUnitInfo.getNewTempClassLoader();
205        }
206    
207        public void doStart() throws Exception {
208        }
209    
210        public void doStop() throws Exception {
211            //TODO remove any classtransformers added
212            entityManagerFactory.close();
213            persistenceUnitInfo.destroy();
214        }
215    
216        public void doFail() {
217            entityManagerFactory.close();
218            persistenceUnitInfo.destroy();
219        }
220    
221        private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
222            private final String persistenceUnitName;
223            private final String persistenceProviderClassName;
224            private final PersistenceUnitTransactionType persistenceUnitTransactionType;
225            private final DataSource jtaDataSource;
226            private final DataSource nonJtaDataSource;
227            private final List<String> mappingFileNames;
228            private final List<URL> jarFileUrls;
229            private final URL persistenceUnitRootUrl;
230            private final List<String> managedClassNames;
231            private final boolean excludeUnlistedClassesValue;
232            private final Properties properties;
233            private final ClassLoader classLoader;
234            private final TemporaryClassLoader tempClassLoader;
235            private final List<TransformerWrapper> transformers;
236    
237    
238            public PersistenceUnitInfoImpl(String persistenceUnitName, String persistenceProviderClassName, PersistenceUnitTransactionType persistenceUnitTransactionType, DataSource jtaDataSource, DataSource nonJtaDataSource, List<String> mappingFileNames, List<URL> jarFileUrls, URL persistenceUnitRootUrl, List<String> managedClassNames, boolean excludeUnlistedClassesValue, Properties properties, ClassLoader classLoader) {
239                this.persistenceUnitName = persistenceUnitName;
240                this.persistenceProviderClassName = persistenceProviderClassName;
241                this.persistenceUnitTransactionType = persistenceUnitTransactionType;
242                this.jtaDataSource = jtaDataSource;
243                this.nonJtaDataSource = nonJtaDataSource;
244                this.mappingFileNames = mappingFileNames;
245                this.jarFileUrls = jarFileUrls;
246                this.persistenceUnitRootUrl = persistenceUnitRootUrl;
247                this.managedClassNames = managedClassNames;
248                this.excludeUnlistedClassesValue = excludeUnlistedClassesValue;
249                this.properties = properties;
250                this.classLoader = classLoader;
251                this.transformers = new ArrayList<TransformerWrapper>();
252                
253                // This classloader can only be used during PersistenceProvider.createContainerEntityManagerFactory() calls
254                // Possible that it could be cleaned up sooner, but for now it's destroyed when the PUGBean is stopped
255                this.tempClassLoader = new TemporaryClassLoader(classLoader); 
256            }
257    
258            public String getPersistenceUnitName() {
259                return persistenceUnitName;
260            }
261    
262            public String getPersistenceProviderClassName() {
263                return persistenceProviderClassName;
264            }
265    
266            public PersistenceUnitTransactionType getTransactionType() {
267                return persistenceUnitTransactionType;
268            }
269    
270            public DataSource getJtaDataSource() {
271                return jtaDataSource;
272            }
273    
274            public DataSource getNonJtaDataSource() {
275                return nonJtaDataSource;
276            }
277    
278            public List<String> getMappingFileNames() {
279                return mappingFileNames;
280            }
281    
282            public List<URL> getJarFileUrls() {
283                return jarFileUrls;
284            }
285    
286            public URL getPersistenceUnitRootUrl() {
287                return persistenceUnitRootUrl;
288            }
289    
290            public List<String> getManagedClassNames() {
291                return managedClassNames;
292            }
293    
294            public boolean excludeUnlistedClasses() {
295                return excludeUnlistedClassesValue;
296            }
297    
298            public Properties getProperties() {
299                return properties;
300            }
301    
302            public ClassLoader getClassLoader() {
303                return classLoader;
304            }
305    
306            public void addTransformer(ClassTransformer classTransformer) {
307                TransformerWrapper transformer = new TransformerWrapper(classTransformer, classLoader);
308                transformers.add(transformer);
309                TransformerAgent.addTransformer(transformer);
310            }
311    
312            public ClassLoader getNewTempClassLoader() {
313                return tempClassLoader;
314            }
315    
316            private void destroy() {
317                for (TransformerWrapper t : transformers) {
318                    TransformerAgent.removeTransformer(t);
319                }
320            }
321    
322        }
323    
324        public static final GBeanInfo GBEAN_INFO;
325    
326        static {
327            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitGBean.class, NameFactory.PERSISTENCE_UNIT);
328            infoBuilder.setPriority(GBeanInfo.PRIORITY_CLASSLOADER);
329    
330            infoBuilder.addAttribute("persistenceUnitName", String.class, true, true);
331            infoBuilder.addAttribute("persistenceProviderClassName", String.class, true, true);
332            infoBuilder.addAttribute("persistenceUnitTransactionType", String.class, true, true);
333            infoBuilder.addAttribute("mappingFileNames", List.class, true, true);
334            infoBuilder.addAttribute("jarFileUrls", List.class, true, true);
335            infoBuilder.addAttribute("persistenceUnitRoot", String.class, true, true);
336            infoBuilder.addAttribute("managedClassNames", List.class, true, true);
337            infoBuilder.addAttribute("excludeUnlistedClasses", boolean.class, true, true);
338            infoBuilder.addAttribute("properties", Properties.class, true, true);
339            infoBuilder.addAttribute("configurationBaseUrl", URL.class, true);
340    
341            infoBuilder.addReference("TransactionManager", TransactionManagerImpl.class, NameFactory.TRANSACTION_MANAGER);
342            infoBuilder.addReference("JtaDataSourceWrapper", ConnectionFactorySource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
343            infoBuilder.addReference("NonJtaDataSourceWrapper", ConnectionFactorySource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
344            infoBuilder.addReference("EntityManagerRegistry", ExtendedEntityManagerRegistry.class, NameFactory.GERONIMO_SERVICE);
345    
346            infoBuilder.setConstructor(new String[] {
347                    "persistenceUnitName",
348                    "persistenceProviderClassName",
349                    "persistenceUnitTransactionType",
350                    "JtaDataSourceWrapper",
351                    "NonJtaDataSourceWrapper",
352                    "mappingFileNames",
353                    "jarFileUrls",
354                    "persistenceUnitRoot",
355                    "managedClassNames",
356                    "excludeUnlistedClasses",
357                    "properties",
358                    "TransactionManager",
359                    "EntityManagerRegistry",
360                    "configurationBaseUrl",
361                    "classLoader"
362            });
363    
364            GBEAN_INFO = infoBuilder.getBeanInfo();
365    
366        }
367    
368        public static GBeanInfo getGBeanInfo() {
369            return GBEAN_INFO;
370        }
371    
372    }