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 }