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    import java.util.Collections;
031    
032    import javax.persistence.EntityManager;
033    import javax.persistence.EntityManagerFactory;
034    import javax.persistence.PersistenceException;
035    import javax.persistence.spi.ClassTransformer;
036    import javax.persistence.spi.PersistenceProvider;
037    import javax.persistence.spi.PersistenceUnitInfo;
038    import javax.persistence.spi.PersistenceUnitTransactionType;
039    import javax.resource.ResourceException;
040    import javax.sql.DataSource;
041    
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.naming.ResourceSource;
047    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
048    import org.apache.geronimo.kernel.classloader.TemporaryClassLoader;
049    import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
050    import org.apache.geronimo.transformer.TransformerAgent;
051    
052    /**
053     * @version $Rev: 735769 $ $Date: 2009-01-20 02:29:38 +0800 (Tue, 20 Jan 2009) $
054     */
055    public class PersistenceUnitGBean implements GBeanLifecycle {
056        private static final List<URL> NO_URLS = Collections.emptyList();
057        private static final List<String> NO_STRINGS = Collections.emptyList();
058        private final String persistenceUnitRoot;
059        private final PersistenceUnitInfoImpl persistenceUnitInfo;
060        private final EntityManagerFactory entityManagerFactory;
061        private final TransactionManagerImpl transactionManager;
062        private final SingleElementCollection<ExtendedEntityManagerRegistry> entityManagerRegistry;
063    
064    
065        public PersistenceUnitGBean() {
066            persistenceUnitRoot = null;
067            persistenceUnitInfo = null;
068            entityManagerFactory = null;
069            transactionManager = null;
070            entityManagerRegistry = null;
071        }
072    
073        public PersistenceUnitGBean(String persistenceUnitName,
074                String persistenceProviderClassName,
075                String persistenceUnitTransactionTypeString,
076                ResourceSource<ResourceException> jtaDataSourceWrapper,
077                ResourceSource<ResourceException> nonJtaDataSourceWrapper,
078                List<String> mappingFileNamesUntyped,
079                List<String> jarFileUrlsUntyped,
080                String persistenceUnitRoot,
081                List<String> managedClassNames,
082                boolean excludeUnlistedClassesValue,
083                Properties properties,
084                TransactionManagerImpl transactionManager,
085                Collection<ExtendedEntityManagerRegistry > entityManagerRegistry,
086                URL configurationBaseURL,
087                ClassLoader classLoader) throws URISyntaxException, MalformedURLException, ResourceException {
088            List<String> mappingFileNames = mappingFileNamesUntyped == null? NO_STRINGS: new ArrayList<String>(mappingFileNamesUntyped);
089            this.persistenceUnitRoot = persistenceUnitRoot;
090            URI configurationBaseURI = new File(configurationBaseURL.getFile()).toURI();
091            URL rootURL = configurationBaseURI.resolve(persistenceUnitRoot).normalize().toURL();
092            List<URL> jarFileUrls = NO_URLS;
093            if (!excludeUnlistedClassesValue) {
094                jarFileUrls = new ArrayList<URL>();
095                //Per the EJB3.0 Persistence Specification section 6.2, the jar-file should be related to the Persistence Unit Root, which is the jar or directory where the persistence.xml is found             
096                URI persistenceUnitBaseURI = configurationBaseURI.resolve(persistenceUnitRoot);
097                for (String urlString: jarFileUrlsUntyped) {
098                    URL url = persistenceUnitBaseURI.resolve(urlString).normalize().toURL();
099                    jarFileUrls.add(url);
100                }
101            }
102            if (managedClassNames == null) {
103                managedClassNames = NO_STRINGS;
104            }
105            if (properties == null) {
106                properties = new Properties();
107            }
108            PersistenceUnitTransactionType persistenceUnitTransactionType = persistenceUnitTransactionTypeString == null? PersistenceUnitTransactionType.JTA: PersistenceUnitTransactionType.valueOf(persistenceUnitTransactionTypeString);
109    
110            if (persistenceProviderClassName == null) persistenceProviderClassName = "org.apache.openjpa.persistence.PersistenceProviderImpl";
111            
112            persistenceUnitInfo = new PersistenceUnitInfoImpl(persistenceUnitName,
113                    persistenceProviderClassName,
114                    persistenceUnitTransactionType,
115                    jtaDataSourceWrapper == null? null: (DataSource)jtaDataSourceWrapper.$getResource(),
116                    nonJtaDataSourceWrapper == null? null: (DataSource)nonJtaDataSourceWrapper.$getResource(),
117                    mappingFileNames,
118                    jarFileUrls,
119                    rootURL,
120                    managedClassNames,
121                    excludeUnlistedClassesValue,
122                    properties,
123                    classLoader);
124            try {
125                Class clazz = classLoader.loadClass(persistenceProviderClassName);
126                PersistenceProvider persistenceProvider = (PersistenceProvider) clazz.newInstance();
127                entityManagerFactory = persistenceProvider.createContainerEntityManagerFactory(persistenceUnitInfo, properties);
128            } catch (ClassNotFoundException e) {
129                persistenceUnitInfo.destroy();
130                throw new PersistenceException("Could not locate PersistenceProvider class: " + persistenceProviderClassName + " in classloader " + classLoader, e);
131            } catch (InstantiationException e) {
132                persistenceUnitInfo.destroy();
133                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
134            } catch (IllegalAccessException e) {
135                persistenceUnitInfo.destroy();
136                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
137            }
138            this.transactionManager = transactionManager;
139            this.entityManagerRegistry = new SingleElementCollection<ExtendedEntityManagerRegistry>(entityManagerRegistry);
140        }
141    
142        public EntityManagerFactory getEntityManagerFactory() {
143            return entityManagerFactory;
144        }
145    
146        public EntityManager getEntityManager(boolean transactionScoped, Map properties) {
147            if (transactionScoped) {
148                return new CMPEntityManagerTxScoped(transactionManager, getPersistenceUnitName(), entityManagerFactory, properties);
149            } else if (entityManagerRegistry.getElement() != null) {
150                return new CMPEntityManagerExtended(entityManagerRegistry.getElement(), entityManagerFactory, properties);
151            } else {
152                throw new NullPointerException("No ExtendedEntityManagerRegistry supplied, you cannot use extended persistence contexts");
153            }
154        }
155    
156        public String getPersistenceUnitName() {
157            return persistenceUnitInfo.getPersistenceUnitName();
158        }
159    
160    
161        public String getPersistenceUnitRoot() {
162            return persistenceUnitRoot;
163        }
164    
165        public String getPersistenceProviderClassName() {
166            return persistenceUnitInfo.getPersistenceProviderClassName();
167        }
168    
169        public PersistenceUnitTransactionType getTransactionType() {
170            return persistenceUnitInfo.getTransactionType();
171        }
172    
173        public DataSource getJtaDataSource() {
174            return persistenceUnitInfo.getJtaDataSource();
175        }
176    
177        public DataSource getNonJtaDataSource() {
178            return persistenceUnitInfo.getNonJtaDataSource();
179        }
180    
181        public List<String> getMappingFileNames() {
182            return persistenceUnitInfo.getMappingFileNames();
183        }
184    
185        public List<URL> getJarFileUrls() {
186            return persistenceUnitInfo.getJarFileUrls();
187        }
188    
189        public URL getPersistenceUnitRootUrl() {
190            return persistenceUnitInfo.getPersistenceUnitRootUrl();
191        }
192    
193        public List<String> getManagedClassNames() {
194            return persistenceUnitInfo.getManagedClassNames();
195        }
196    
197        public boolean excludeUnlistedClasses() {
198            return persistenceUnitInfo.excludeUnlistedClasses();
199        }
200    
201        public Properties getProperties() {
202            return persistenceUnitInfo.getProperties();
203        }
204    
205        public ClassLoader getClassLoader() {
206            return persistenceUnitInfo.getClassLoader();
207        }
208    
209        public void addTransformer(ClassTransformer classTransformer) {
210            persistenceUnitInfo.addTransformer(classTransformer);
211        }
212    
213        public ClassLoader getNewTempClassLoader() {
214            return persistenceUnitInfo.getNewTempClassLoader();
215        }
216    
217        public void doStart() throws Exception {
218        }
219    
220        public void doStop() throws Exception {
221            //TODO remove any classtransformers added
222            entityManagerFactory.close();
223            persistenceUnitInfo.destroy();
224        }
225    
226        public void doFail() {
227            entityManagerFactory.close();
228            persistenceUnitInfo.destroy();
229        }
230    
231        private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
232            private final String persistenceUnitName;
233            private final String persistenceProviderClassName;
234            private final PersistenceUnitTransactionType persistenceUnitTransactionType;
235            private final DataSource jtaDataSource;
236            private final DataSource nonJtaDataSource;
237            private final List<String> mappingFileNames;
238            private final List<URL> jarFileUrls;
239            private final URL persistenceUnitRootUrl;
240            private final List<String> managedClassNames;
241            private final boolean excludeUnlistedClassesValue;
242            private final Properties properties;
243            private final ClassLoader classLoader;
244            private final TemporaryClassLoader tempClassLoader;
245            private final List<TransformerWrapper> transformers;
246    
247    
248            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) {
249                this.persistenceUnitName = persistenceUnitName;
250                this.persistenceProviderClassName = persistenceProviderClassName;
251                this.persistenceUnitTransactionType = persistenceUnitTransactionType;
252                this.jtaDataSource = jtaDataSource;
253                this.nonJtaDataSource = nonJtaDataSource;
254                this.mappingFileNames = mappingFileNames;
255                this.jarFileUrls = jarFileUrls;
256                this.persistenceUnitRootUrl = persistenceUnitRootUrl;
257                this.managedClassNames = managedClassNames;
258                this.excludeUnlistedClassesValue = excludeUnlistedClassesValue;
259                this.properties = properties;
260                this.classLoader = classLoader;
261                this.transformers = new ArrayList<TransformerWrapper>();
262                
263                // This classloader can only be used during PersistenceProvider.createContainerEntityManagerFactory() calls
264                // Possible that it could be cleaned up sooner, but for now it's destroyed when the PUGBean is stopped
265                this.tempClassLoader = new TemporaryClassLoader(classLoader); 
266            }
267    
268            public String getPersistenceUnitName() {
269                return persistenceUnitName;
270            }
271    
272            public String getPersistenceProviderClassName() {
273                return persistenceProviderClassName;
274            }
275    
276            public PersistenceUnitTransactionType getTransactionType() {
277                return persistenceUnitTransactionType;
278            }
279    
280            public DataSource getJtaDataSource() {
281                return jtaDataSource;
282            }
283    
284            public DataSource getNonJtaDataSource() {
285                return nonJtaDataSource;
286            }
287    
288            public List<String> getMappingFileNames() {
289                return mappingFileNames;
290            }
291    
292            public List<URL> getJarFileUrls() {
293                return jarFileUrls;
294            }
295    
296            public URL getPersistenceUnitRootUrl() {
297                return persistenceUnitRootUrl;
298            }
299    
300            public List<String> getManagedClassNames() {
301                return managedClassNames;
302            }
303    
304            public boolean excludeUnlistedClasses() {
305                return excludeUnlistedClassesValue;
306            }
307    
308            public Properties getProperties() {
309                return properties;
310            }
311    
312            public ClassLoader getClassLoader() {
313                return classLoader;
314            }
315    
316            public void addTransformer(ClassTransformer classTransformer) {
317                TransformerWrapper transformer = new TransformerWrapper(classTransformer, classLoader);
318                transformers.add(transformer);
319                TransformerAgent.addTransformer(transformer);
320            }
321    
322            public ClassLoader getNewTempClassLoader() {
323                return tempClassLoader;
324            }
325    
326            private void destroy() {
327                for (TransformerWrapper t : transformers) {
328                    TransformerAgent.removeTransformer(t);
329                }
330            }
331    
332        }
333    
334        public static final GBeanInfo GBEAN_INFO;
335    
336        static {
337            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitGBean.class, NameFactory.PERSISTENCE_UNIT);
338            infoBuilder.setPriority(GBeanInfo.PRIORITY_CLASSLOADER);
339    
340            infoBuilder.addAttribute("persistenceUnitName", String.class, true, true);
341            infoBuilder.addAttribute("persistenceProviderClassName", String.class, true, true);
342            infoBuilder.addAttribute("persistenceUnitTransactionType", String.class, true, true);
343            infoBuilder.addAttribute("mappingFileNames", List.class, true, true);
344            infoBuilder.addAttribute("jarFileUrls", List.class, true, true);
345            infoBuilder.addAttribute("persistenceUnitRoot", String.class, true, true);
346            infoBuilder.addAttribute("managedClassNames", List.class, true, true);
347            infoBuilder.addAttribute("excludeUnlistedClasses", boolean.class, true, true);
348            infoBuilder.addAttribute("properties", Properties.class, true, true);
349            infoBuilder.addAttribute("configurationBaseUrl", URL.class, true);
350    
351            infoBuilder.addReference("TransactionManager", TransactionManagerImpl.class, NameFactory.JTA_RESOURCE);
352            infoBuilder.addReference("JtaDataSourceWrapper", ResourceSource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
353            infoBuilder.addReference("NonJtaDataSourceWrapper", ResourceSource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
354            infoBuilder.addReference("EntityManagerRegistry", ExtendedEntityManagerRegistry.class, NameFactory.GERONIMO_SERVICE);
355    
356            infoBuilder.setConstructor(new String[] {
357                    "persistenceUnitName",
358                    "persistenceProviderClassName",
359                    "persistenceUnitTransactionType",
360                    "JtaDataSourceWrapper",
361                    "NonJtaDataSourceWrapper",
362                    "mappingFileNames",
363                    "jarFileUrls",
364                    "persistenceUnitRoot",
365                    "managedClassNames",
366                    "excludeUnlistedClasses",
367                    "properties",
368                    "TransactionManager",
369                    "EntityManagerRegistry",
370                    "configurationBaseUrl",
371                    "classLoader"
372            });
373    
374            GBEAN_INFO = infoBuilder.getBeanInfo();
375    
376        }
377    
378        public static GBeanInfo getGBeanInfo() {
379            return GBEAN_INFO;
380        }
381    
382    }