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    package org.apache.geronimo.persistence.builder;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.net.URI;
022    import java.net.URISyntaxException;
023    import java.net.URL;
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.HashMap;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Properties;
030    import java.util.jar.JarFile;
031    
032    import javax.xml.namespace.QName;
033    
034    import org.apache.geronimo.common.DeploymentException;
035    import org.apache.geronimo.deployment.ClassPathList;
036    import org.apache.geronimo.deployment.ModuleIDBuilder;
037    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
038    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
039    import org.apache.geronimo.gbean.AbstractName;
040    import org.apache.geronimo.gbean.AbstractNameQuery;
041    import org.apache.geronimo.gbean.GBeanData;
042    import org.apache.geronimo.gbean.GBeanInfo;
043    import org.apache.geronimo.gbean.GBeanInfoBuilder;
044    import org.apache.geronimo.j2ee.deployment.EARContext;
045    import org.apache.geronimo.j2ee.deployment.Module;
046    import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
047    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
048    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
049    import org.apache.geronimo.kernel.Naming;
050    import org.apache.geronimo.kernel.config.ConfigurationStore;
051    import org.apache.geronimo.kernel.repository.Environment;
052    import org.apache.geronimo.naming.ResourceSource;
053    import org.apache.geronimo.persistence.PersistenceUnitGBean;
054    import org.apache.geronimo.xbeans.persistence.PersistenceDocument;
055    import org.apache.xbean.finder.ResourceFinder;
056    import org.apache.xmlbeans.QNameSet;
057    import org.apache.xmlbeans.XmlException;
058    import org.apache.xmlbeans.XmlObject;
059    
060    /**
061     * @version $Rev: 1034290 $ $Date: 2010-11-12 16:12:09 +0800 (Fri, 12 Nov 2010) $
062     */
063    public class PersistenceUnitBuilder implements ModuleBuilderExtension {
064        private static final QName PERSISTENCE_QNAME = PersistenceDocument.type.getDocumentElementName();
065    
066        private final Environment defaultEnvironment;
067        private final String defaultPersistenceProviderClassName;
068        private final Properties defaultPersistenceUnitProperties;
069        private final AbstractNameQuery defaultJtaDataSourceName;
070        private final AbstractNameQuery defaultNonJtaDataSourceName;
071        private final AbstractNameQuery extendedEntityManagerRegistryName;
072        private static final String ANON_PU_NAME = "AnonymousPersistenceUnit";
073        private static final String RESOURCE_SOURCE_CLASS_NAME = ResourceSource.class.getName();
074    
075        public PersistenceUnitBuilder(Environment defaultEnvironment,
076                                      String defaultPersistenceProviderClassName,
077                                      String defaultJtaDataSourceName,
078                                      String defaultNonJtaDataSourceName,
079                                      AbstractNameQuery extendedEntityManagerRegistryName, Properties defaultPersistenceUnitProperties) throws URISyntaxException {
080            this.defaultEnvironment = defaultEnvironment;
081            this.defaultPersistenceProviderClassName = defaultPersistenceProviderClassName;
082            this.defaultJtaDataSourceName = defaultJtaDataSourceName == null ? null : getAbstractNameQuery(defaultJtaDataSourceName);
083            this.defaultNonJtaDataSourceName = defaultNonJtaDataSourceName == null ? null : getAbstractNameQuery(defaultNonJtaDataSourceName);
084            this.extendedEntityManagerRegistryName = extendedEntityManagerRegistryName;
085            this.defaultPersistenceUnitProperties = defaultPersistenceUnitProperties == null ? new Properties() : defaultPersistenceUnitProperties;
086        }
087    
088        public void createModule(Module module, Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
089        }
090    
091        public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repository) throws DeploymentException {
092        }
093    
094        public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
095            XmlObject container = module.getVendorDD();
096            EARContext moduleContext = module.getEarContext();
097            XmlObject[] raws = container.selectChildren(PERSISTENCE_QNAME);
098    
099            Map<String, PersistenceDocument.Persistence.PersistenceUnit> overrides = new HashMap<String, PersistenceDocument.Persistence.PersistenceUnit>();
100            for (XmlObject raw : raws) {
101                PersistenceDocument.Persistence persistence = (PersistenceDocument.Persistence) raw.copy().changeType(PersistenceDocument.Persistence.type);
102                for (PersistenceDocument.Persistence.PersistenceUnit unit : persistence.getPersistenceUnitArray()) {
103                    overrides.put(unit.getName().trim(), unit);
104                }
105    //            buildPersistenceUnits(persistence, module, module.getTargetPath());
106            }
107            try {
108                
109                File rootBaseFile;
110                URI moduleBaseURI;
111                if (module.getRootEarContext().getInPlaceConfigurationDir() == null) {
112                    rootBaseFile = module.getRootEarContext().getConfiguration().getConfigurationDir();
113                    moduleBaseURI = moduleContext.getBaseDir().toURI();
114                } else {
115                    rootBaseFile = module.getRootEarContext().getInPlaceConfigurationDir();
116                    moduleBaseURI = moduleContext.getInPlaceConfigurationDir().toURI();
117                }
118                String rootBase = rootBaseFile.toURI().normalize().toString();
119    
120                Map rootGeneralData = module.getRootEarContext().getGeneralData();
121                ClassPathList manifestcp = (ClassPathList) module.getEarContext().getGeneralData().get(ClassPathList.class);
122                if (manifestcp == null) {
123                    manifestcp = new ClassPathList();
124                    manifestcp.add(module.getTargetPath());
125                }
126                URL[] urls = new URL[manifestcp.size()];
127                int i = 0;
128                for (String path : manifestcp) {
129                    path = path.replaceAll(" ", "%20");
130                    URL url = moduleBaseURI.resolve(path).toURL();
131                    urls[i++] = url;
132                }
133                ResourceFinder finder = new ResourceFinder("", null, urls);
134                List<URL> knownPersistenceUrls = (List<URL>) rootGeneralData.get(PersistenceUnitBuilder.class.getName());
135                if (knownPersistenceUrls == null) {
136                    knownPersistenceUrls = new ArrayList<URL>();
137                    rootGeneralData.put(PersistenceUnitBuilder.class.getName(), knownPersistenceUrls);
138                }
139                List<URL> persistenceUrls = finder.findAll("META-INF/persistence.xml");
140                persistenceUrls.removeAll(knownPersistenceUrls);
141                if (raws.length > 0 || persistenceUrls.size() > 0) {
142                    EnvironmentBuilder.mergeEnvironments(module.getEnvironment(), defaultEnvironment);
143                }
144                for (URL persistenceUrl : persistenceUrls) {
145                    String persistenceLocation;
146                    try {
147                        persistenceLocation = persistenceUrl.toURI().toString();
148                    } catch (URISyntaxException e) {
149                        //????
150                        continue;
151                    }
152                    int pos = persistenceLocation.indexOf(rootBase);
153                    if (pos < 0) {
154                        //not in the ear
155                        continue;
156                    }
157                    int endPos = persistenceLocation.lastIndexOf("!/");
158                    if (endPos < 0) {
159                        // if unable to find the '!/' marker, try to see if this is
160                        // a war file with the persistence.xml directly embeded - no ejb-jar
161                        endPos = persistenceLocation.lastIndexOf("META-INF");
162                    }
163                    if (endPos >= 0) {
164                        //path relative to ear base uri
165                        String relative = persistenceLocation.substring(pos + rootBase.length(), endPos);
166                        //find path relative to module base uri
167                        relative = module.getRelativePath(relative);
168                        PersistenceDocument persistenceDocument;
169                        try {
170                            XmlObject xmlObject = XmlBeansUtil.parse(persistenceUrl, moduleContext.getClassLoader());
171                            persistenceDocument = (PersistenceDocument) xmlObject.changeType(PersistenceDocument.type);
172                        } catch (XmlException e) {
173                            throw new DeploymentException("Could not parse persistence.xml file: " + persistenceUrl, e);
174                        }
175                        PersistenceDocument.Persistence persistence = persistenceDocument.getPersistence();
176                        buildPersistenceUnits(persistence, overrides, module, relative);
177                        knownPersistenceUrls.add(persistenceUrl);
178                    } else {
179                        throw new DeploymentException("Could not find persistence.xml file: " + persistenceUrl);
180                    }
181                }
182            } catch (IOException e) {
183                throw new DeploymentException("Could not look for META-INF/persistence.xml files", e);
184            }
185    
186            for (PersistenceDocument.Persistence.PersistenceUnit persistenceUnit : overrides.values()) {
187                GBeanData data = installPersistenceUnitGBean(persistenceUnit, module, module.getTargetPath());
188                respectExcludeUnlistedClasses(data);
189            }
190        }
191    
192        public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
193        }
194    
195        private void buildPersistenceUnits(PersistenceDocument.Persistence persistence, Map<String, PersistenceDocument.Persistence.PersistenceUnit> overrides, Module module, String persistenceModulePath) throws DeploymentException {
196            PersistenceDocument.Persistence.PersistenceUnit[] persistenceUnits = persistence.getPersistenceUnitArray();
197            for (PersistenceDocument.Persistence.PersistenceUnit persistenceUnit : persistenceUnits) {
198                GBeanData data = installPersistenceUnitGBean(persistenceUnit, module, persistenceModulePath);
199                String unitName = persistenceUnit.getName().trim();
200                if (overrides.get(unitName) != null) {
201                    setOverrideableProperties(overrides.remove(unitName), data);
202                }
203                respectExcludeUnlistedClasses(data);
204            }
205        }
206    
207        private GBeanData installPersistenceUnitGBean(PersistenceDocument.Persistence.PersistenceUnit persistenceUnit, Module module, String persistenceModulePath) throws DeploymentException {
208            EARContext moduleContext = module.getEarContext();
209            String persistenceUnitName = persistenceUnit.getName().trim();
210            if (persistenceUnitName.length() == 0) {
211                persistenceUnitName = ANON_PU_NAME;
212            }
213            AbstractName abstractName;
214            if (persistenceModulePath == null || persistenceModulePath.length() == 0) {
215                abstractName = moduleContext.getNaming().createChildName(module.getModuleName(), persistenceUnitName, PersistenceUnitGBean.GBEAN_INFO.getJ2eeType());
216            } else {
217                abstractName = moduleContext.getNaming().createChildName(module.getModuleName(), persistenceModulePath, NameFactory.PERSISTENCE_UNIT_MODULE);
218                abstractName = moduleContext.getNaming().createChildName(abstractName, moduleContext.getConfigID(), persistenceUnitName, PersistenceUnitGBean.GBEAN_INFO.getJ2eeType());
219            }
220            GBeanData gbeanData = new GBeanData(abstractName, PersistenceUnitGBean.GBEAN_INFO);
221            try {
222                moduleContext.addGBean(gbeanData);
223            } catch (GBeanAlreadyExistsException e) {
224                throw new DeploymentException("Duplicate persistenceUnit name " + persistenceUnitName, e);
225            }
226            gbeanData.setAttribute("persistenceUnitName", persistenceUnitName);
227            gbeanData.setAttribute("persistenceUnitRoot", persistenceModulePath);
228    
229            //set defaults:
230            gbeanData.setAttribute("persistenceProviderClassName", defaultPersistenceProviderClassName);
231            //spec 6.2.1.2 the default is JTA
232            gbeanData.setAttribute("persistenceUnitTransactionType", "JTA");
233            if (defaultJtaDataSourceName != null) {
234                gbeanData.setReferencePattern("JtaDataSourceWrapper", defaultJtaDataSourceName);
235            }
236            if (defaultNonJtaDataSourceName != null) {
237                gbeanData.setReferencePattern("NonJtaDataSourceWrapper", defaultNonJtaDataSourceName);
238            }
239    
240            gbeanData.setAttribute("mappingFileNames", new ArrayList<String>());
241            gbeanData.setAttribute("excludeUnlistedClasses", false);
242            gbeanData.setAttribute("managedClassNames", new ArrayList<String>());
243            gbeanData.setAttribute("jarFileUrls", new ArrayList<String>());
244            Properties properties = new Properties();
245            gbeanData.setAttribute("properties", properties);
246            properties.putAll(defaultPersistenceUnitProperties);
247            AbstractNameQuery transactionManagerName = moduleContext.getTransactionManagerName();
248            gbeanData.setReferencePattern("TransactionManager", transactionManagerName);
249            gbeanData.setReferencePattern("EntityManagerRegistry", extendedEntityManagerRegistryName);
250    
251            setOverrideableProperties(persistenceUnit, gbeanData);
252            return gbeanData;
253        }
254    
255        private void setOverrideableProperties(PersistenceDocument.Persistence.PersistenceUnit persistenceUnit, GBeanData gbeanData) throws DeploymentException {
256            if (persistenceUnit.isSetProvider()) {
257                gbeanData.setAttribute("persistenceProviderClassName", persistenceUnit.getProvider().trim());
258            }
259            if (persistenceUnit.isSetTransactionType()) {
260                gbeanData.setAttribute("persistenceUnitTransactionType", persistenceUnit.getTransactionType().toString());
261            }
262            if (persistenceUnit.isSetJtaDataSource()) {
263                String jtaDataSourceString = persistenceUnit.getJtaDataSource().trim();
264                try {
265                    AbstractNameQuery jtaDataSourceNameQuery = getAbstractNameQuery(jtaDataSourceString);
266                    gbeanData.setReferencePattern("JtaDataSourceWrapper", jtaDataSourceNameQuery);
267                } catch (URISyntaxException e) {
268                    throw new DeploymentException("Could not create jta-data-source AbstractNameQuery from string: " + jtaDataSourceString, e);
269                }
270            }
271    
272            if (persistenceUnit.isSetNonJtaDataSource()) {
273                String nonJtaDataSourceString = persistenceUnit.getNonJtaDataSource().trim();
274                try {
275                    AbstractNameQuery nonJtaDataSourceNameQuery = getAbstractNameQuery(nonJtaDataSourceString);
276                    gbeanData.setReferencePattern("NonJtaDataSourceWrapper", nonJtaDataSourceNameQuery);
277                } catch (URISyntaxException e) {
278                    throw new DeploymentException("Could not create non-jta-data-source AbstractNameQuery from string: " + nonJtaDataSourceString, e);
279                }
280            }
281    
282            List<String> mappingFileNames = (List<String>) gbeanData.getAttribute("mappingFileNames");
283            String[] mappingFileNameStrings = persistenceUnit.getMappingFileArray();
284            for (String mappingFileNameString : mappingFileNameStrings) {
285                mappingFileNames.add(mappingFileNameString.trim());
286            }
287    
288            if (persistenceUnit.isSetExcludeUnlistedClasses()) {
289                gbeanData.setAttribute("excludeUnlistedClasses", persistenceUnit.getExcludeUnlistedClasses());
290            }
291    
292            String[] managedClassNameStrings = persistenceUnit.getClass1Array();
293            List<String> managedClassNames = (List<String>) gbeanData.getAttribute("managedClassNames");
294            for (String managedClassNameString : managedClassNameStrings) {
295                managedClassNames.add(managedClassNameString.trim());
296            }
297            List<String> jarFileUrls = (List<String>) gbeanData.getAttribute("jarFileUrls");
298            //add the specified locations in the ear
299            String[] jarFileUrlStrings = persistenceUnit.getJarFileArray();
300            for (String jarFileUrlString : jarFileUrlStrings) {
301                jarFileUrls.add(jarFileUrlString.trim());
302            }
303    
304            if (persistenceUnit.isSetProperties()) {
305                Properties properties = (Properties) gbeanData.getAttribute("properties");
306                PersistenceDocument.Persistence.PersistenceUnit.Properties.Property[] propertyObjects = persistenceUnit.getProperties().getPropertyArray();
307                for (PersistenceDocument.Persistence.PersistenceUnit.Properties.Property propertyObject : propertyObjects) {
308                    String key = propertyObject.getName().trim();
309                    String value = propertyObject.getValue().trim();
310                    properties.setProperty(key, value);
311                }
312            }
313    
314        }
315    
316        private void respectExcludeUnlistedClasses(GBeanData gbeanData) {
317            boolean excludeUnlistedClasses = (Boolean) gbeanData.getAttribute("excludeUnlistedClasses");
318    
319            if (excludeUnlistedClasses) {
320                gbeanData.clearAttribute("jarFileUrls");
321            } else {
322                gbeanData.clearAttribute("managedClassNames");
323            }
324        }
325    
326        private AbstractNameQuery getAbstractNameQuery(String dataSourceString) throws URISyntaxException {
327            if (dataSourceString.indexOf('=') == -1) {
328                dataSourceString = "?name=" + dataSourceString;
329            }
330            AbstractNameQuery dataSourceNameQuery = new AbstractNameQuery(new URI(dataSourceString + "#" + RESOURCE_SOURCE_CLASS_NAME));
331            return dataSourceNameQuery;
332        }
333    
334        public QNameSet getSpecQNameSet() {
335            return QNameSet.EMPTY;
336        }
337    
338        public QNameSet getPlanQNameSet() {
339            return QNameSet.singleton(PERSISTENCE_QNAME);
340        }
341    
342        public static final GBeanInfo GBEAN_INFO;
343    
344        static {
345            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitBuilder.class, NameFactory.MODULE_BUILDER);
346    
347            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
348            infoBuilder.addAttribute("defaultPersistenceProviderClassName", String.class, true, true);
349            infoBuilder.addAttribute("defaultJtaDataSourceName", String.class, true, true);
350            infoBuilder.addAttribute("defaultNonJtaDataSourceName", String.class, true, true);
351            infoBuilder.addAttribute("extendedEntityManagerRegistryName", AbstractNameQuery.class, true, true);
352            infoBuilder.addAttribute("defaultPersistenceUnitProperties", Properties.class, true, true);
353    
354            infoBuilder.setConstructor(new String[]{
355                    "defaultEnvironment",
356                    "defaultPersistenceProviderClassName",
357                    "defaultJtaDataSourceName",
358                    "defaultNonJtaDataSourceName",
359                    "extendedEntityManagerRegistryName",
360                    "defaultPersistenceUnitProperties"
361            });
362    
363            GBEAN_INFO = infoBuilder.getBeanInfo();
364    
365        }
366    
367        public static GBeanInfo getGBeanInfo() {
368            return GBEAN_INFO;
369        }
370    
371    
372    }