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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
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                for (String urlString: jarFileUrlsUntyped) {
096                    URL url = configurationBaseURI.resolve(urlString).normalize().toURL();
097                    jarFileUrls.add(url);
098                }
099            }
100            if (managedClassNames == null) {
101                managedClassNames = NO_STRINGS;
102            }
103            if (properties == null) {
104                properties = new Properties();
105            }
106            PersistenceUnitTransactionType persistenceUnitTransactionType = persistenceUnitTransactionTypeString == null? PersistenceUnitTransactionType.JTA: PersistenceUnitTransactionType.valueOf(persistenceUnitTransactionTypeString);
107    
108            if (persistenceProviderClassName == null) persistenceProviderClassName = "org.apache.openjpa.persistence.PersistenceProviderImpl";
109            
110            persistenceUnitInfo = new PersistenceUnitInfoImpl(persistenceUnitName,
111                    persistenceProviderClassName,
112                    persistenceUnitTransactionType,
113                    jtaDataSourceWrapper == null? null: (DataSource)jtaDataSourceWrapper.$getResource(),
114                    nonJtaDataSourceWrapper == null? null: (DataSource)nonJtaDataSourceWrapper.$getResource(),
115                    mappingFileNames,
116                    jarFileUrls,
117                    rootURL,
118                    managedClassNames,
119                    excludeUnlistedClassesValue,
120                    properties,
121                    classLoader);
122            try {
123                Class clazz = classLoader.loadClass(persistenceProviderClassName);
124                PersistenceProvider persistenceProvider = (PersistenceProvider) clazz.newInstance();
125                entityManagerFactory = persistenceProvider.createContainerEntityManagerFactory(persistenceUnitInfo, properties);
126            } catch (ClassNotFoundException e) {
127                persistenceUnitInfo.destroy();
128                throw new PersistenceException("Could not locate PersistenceProvider class: " + persistenceProviderClassName + " in classloader " + classLoader, e);
129            } catch (InstantiationException e) {
130                persistenceUnitInfo.destroy();
131                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
132            } catch (IllegalAccessException e) {
133                persistenceUnitInfo.destroy();
134                throw new PersistenceException("Could not create PersistenceProvider instance: " + persistenceProviderClassName + " loaded from classloader " + classLoader, e);
135            }
136            this.transactionManager = transactionManager;
137            this.entityManagerRegistry = new SingleElementCollection<ExtendedEntityManagerRegistry>(entityManagerRegistry);
138        }
139    
140        public EntityManagerFactory getEntityManagerFactory() {
141            return entityManagerFactory;
142        }
143    
144        public EntityManager getEntityManager(boolean transactionScoped, Map properties) {
145            if (transactionScoped) {
146                return new CMPEntityManagerTxScoped(transactionManager, getPersistenceUnitName(), entityManagerFactory, properties);
147            } else if (entityManagerRegistry.getElement() != null) {
148                return new CMPEntityManagerExtended(entityManagerRegistry.getElement(), entityManagerFactory, properties);
149            } else {
150                throw new NullPointerException("No ExtendedEntityManagerRegistry supplied, you cannot use extended persistence contexts");
151            }
152        }
153    
154        public String getPersistenceUnitName() {
155            return persistenceUnitInfo.getPersistenceUnitName();
156        }
157    
158    
159        public String getPersistenceUnitRoot() {
160            return persistenceUnitRoot;
161        }
162    
163        public String getPersistenceProviderClassName() {
164            return persistenceUnitInfo.getPersistenceProviderClassName();
165        }
166    
167        public PersistenceUnitTransactionType getTransactionType() {
168            return persistenceUnitInfo.getTransactionType();
169        }
170    
171        public DataSource getJtaDataSource() {
172            return persistenceUnitInfo.getJtaDataSource();
173        }
174    
175        public DataSource getNonJtaDataSource() {
176            return persistenceUnitInfo.getNonJtaDataSource();
177        }
178    
179        public List<String> getMappingFileNames() {
180            return persistenceUnitInfo.getMappingFileNames();
181        }
182    
183        public List<URL> getJarFileUrls() {
184            return persistenceUnitInfo.getJarFileUrls();
185        }
186    
187        public URL getPersistenceUnitRootUrl() {
188            return persistenceUnitInfo.getPersistenceUnitRootUrl();
189        }
190    
191        public List<String> getManagedClassNames() {
192            return persistenceUnitInfo.getManagedClassNames();
193        }
194    
195        public boolean excludeUnlistedClasses() {
196            return persistenceUnitInfo.excludeUnlistedClasses();
197        }
198    
199        public Properties getProperties() {
200            return persistenceUnitInfo.getProperties();
201        }
202    
203        public ClassLoader getClassLoader() {
204            return persistenceUnitInfo.getClassLoader();
205        }
206    
207        public void addTransformer(ClassTransformer classTransformer) {
208            persistenceUnitInfo.addTransformer(classTransformer);
209        }
210    
211        public ClassLoader getNewTempClassLoader() {
212            return persistenceUnitInfo.getNewTempClassLoader();
213        }
214    
215        public void doStart() throws Exception {
216        }
217    
218        public void doStop() throws Exception {
219            //TODO remove any classtransformers added
220            entityManagerFactory.close();
221            persistenceUnitInfo.destroy();
222        }
223    
224        public void doFail() {
225            entityManagerFactory.close();
226            persistenceUnitInfo.destroy();
227        }
228    
229        private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
230            private final String persistenceUnitName;
231            private final String persistenceProviderClassName;
232            private final PersistenceUnitTransactionType persistenceUnitTransactionType;
233            private final DataSource jtaDataSource;
234            private final DataSource nonJtaDataSource;
235            private final List<String> mappingFileNames;
236            private final List<URL> jarFileUrls;
237            private final URL persistenceUnitRootUrl;
238            private final List<String> managedClassNames;
239            private final boolean excludeUnlistedClassesValue;
240            private final Properties properties;
241            private final ClassLoader classLoader;
242            private final TemporaryClassLoader tempClassLoader;
243            private final List<TransformerWrapper> transformers;
244    
245    
246            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) {
247                this.persistenceUnitName = persistenceUnitName;
248                this.persistenceProviderClassName = persistenceProviderClassName;
249                this.persistenceUnitTransactionType = persistenceUnitTransactionType;
250                this.jtaDataSource = jtaDataSource;
251                this.nonJtaDataSource = nonJtaDataSource;
252                this.mappingFileNames = mappingFileNames;
253                this.jarFileUrls = jarFileUrls;
254                this.persistenceUnitRootUrl = persistenceUnitRootUrl;
255                this.managedClassNames = managedClassNames;
256                this.excludeUnlistedClassesValue = excludeUnlistedClassesValue;
257                this.properties = properties;
258                this.classLoader = classLoader;
259                this.transformers = new ArrayList<TransformerWrapper>();
260                
261                // This classloader can only be used during PersistenceProvider.createContainerEntityManagerFactory() calls
262                // Possible that it could be cleaned up sooner, but for now it's destroyed when the PUGBean is stopped
263                this.tempClassLoader = new TemporaryClassLoader(classLoader); 
264            }
265    
266            public String getPersistenceUnitName() {
267                return persistenceUnitName;
268            }
269    
270            public String getPersistenceProviderClassName() {
271                return persistenceProviderClassName;
272            }
273    
274            public PersistenceUnitTransactionType getTransactionType() {
275                return persistenceUnitTransactionType;
276            }
277    
278            public DataSource getJtaDataSource() {
279                return jtaDataSource;
280            }
281    
282            public DataSource getNonJtaDataSource() {
283                return nonJtaDataSource;
284            }
285    
286            public List<String> getMappingFileNames() {
287                return mappingFileNames;
288            }
289    
290            public List<URL> getJarFileUrls() {
291                return jarFileUrls;
292            }
293    
294            public URL getPersistenceUnitRootUrl() {
295                return persistenceUnitRootUrl;
296            }
297    
298            public List<String> getManagedClassNames() {
299                return managedClassNames;
300            }
301    
302            public boolean excludeUnlistedClasses() {
303                return excludeUnlistedClassesValue;
304            }
305    
306            public Properties getProperties() {
307                return properties;
308            }
309    
310            public ClassLoader getClassLoader() {
311                return classLoader;
312            }
313    
314            public void addTransformer(ClassTransformer classTransformer) {
315                TransformerWrapper transformer = new TransformerWrapper(classTransformer, classLoader);
316                transformers.add(transformer);
317                TransformerAgent.addTransformer(transformer);
318            }
319    
320            public ClassLoader getNewTempClassLoader() {
321                return tempClassLoader;
322            }
323    
324            private void destroy() {
325                for (TransformerWrapper t : transformers) {
326                    TransformerAgent.removeTransformer(t);
327                }
328            }
329    
330        }
331    
332        public static final GBeanInfo GBEAN_INFO;
333    
334        static {
335            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitGBean.class, NameFactory.PERSISTENCE_UNIT);
336            infoBuilder.setPriority(GBeanInfo.PRIORITY_CLASSLOADER);
337    
338            infoBuilder.addAttribute("persistenceUnitName", String.class, true, true);
339            infoBuilder.addAttribute("persistenceProviderClassName", String.class, true, true);
340            infoBuilder.addAttribute("persistenceUnitTransactionType", String.class, true, true);
341            infoBuilder.addAttribute("mappingFileNames", List.class, true, true);
342            infoBuilder.addAttribute("jarFileUrls", List.class, true, true);
343            infoBuilder.addAttribute("persistenceUnitRoot", String.class, true, true);
344            infoBuilder.addAttribute("managedClassNames", List.class, true, true);
345            infoBuilder.addAttribute("excludeUnlistedClasses", boolean.class, true, true);
346            infoBuilder.addAttribute("properties", Properties.class, true, true);
347            infoBuilder.addAttribute("configurationBaseUrl", URL.class, true);
348    
349            infoBuilder.addReference("TransactionManager", TransactionManagerImpl.class, NameFactory.JTA_RESOURCE);
350            infoBuilder.addReference("JtaDataSourceWrapper", ResourceSource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
351            infoBuilder.addReference("NonJtaDataSourceWrapper", ResourceSource.class, NameFactory.JCA_MANAGED_CONNECTION_FACTORY);
352            infoBuilder.addReference("EntityManagerRegistry", ExtendedEntityManagerRegistry.class, NameFactory.GERONIMO_SERVICE);
353    
354            infoBuilder.setConstructor(new String[] {
355                    "persistenceUnitName",
356                    "persistenceProviderClassName",
357                    "persistenceUnitTransactionType",
358                    "JtaDataSourceWrapper",
359                    "NonJtaDataSourceWrapper",
360                    "mappingFileNames",
361                    "jarFileUrls",
362                    "persistenceUnitRoot",
363                    "managedClassNames",
364                    "excludeUnlistedClasses",
365                    "properties",
366                    "TransactionManager",
367                    "EntityManagerRegistry",
368                    "configurationBaseUrl",
369                    "classLoader"
370            });
371    
372            GBEAN_INFO = infoBuilder.getBeanInfo();
373    
374        }
375    
376        public static GBeanInfo getGBeanInfo() {
377            return GBEAN_INFO;
378        }
379    
380    }