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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 File rootBaseFile = module.getRootEarContext().getConfiguration().getConfigurationDir(); 109 String rootBase = rootBaseFile.toURI().normalize().toString(); 110 URI moduleBaseURI = moduleContext.getBaseDir().toURI(); 111 Map rootGeneralData = module.getRootEarContext().getGeneralData(); 112 ClassPathList manifestcp = (ClassPathList) module.getEarContext().getGeneralData().get(ClassPathList.class); 113 if (manifestcp == null) { 114 manifestcp = new ClassPathList(); 115 manifestcp.add(module.getTargetPath()); 116 } 117 URL[] urls = new URL[manifestcp.size()]; 118 int i = 0; 119 for (String path : manifestcp) { 120 path = path.replaceAll(" ", "%20"); 121 URL url = moduleBaseURI.resolve(path).toURL(); 122 urls[i++] = url; 123 } 124 ResourceFinder finder = new ResourceFinder("", null, urls); 125 List<URL> knownPersistenceUrls = (List<URL>) rootGeneralData.get(PersistenceUnitBuilder.class.getName()); 126 if (knownPersistenceUrls == null) { 127 knownPersistenceUrls = new ArrayList<URL>(); 128 rootGeneralData.put(PersistenceUnitBuilder.class.getName(), knownPersistenceUrls); 129 } 130 List<URL> persistenceUrls = finder.findAll("META-INF/persistence.xml"); 131 persistenceUrls.removeAll(knownPersistenceUrls); 132 if (raws.length > 0 || persistenceUrls.size() > 0) { 133 EnvironmentBuilder.mergeEnvironments(module.getEnvironment(), defaultEnvironment); 134 } 135 for (URL persistenceUrl : persistenceUrls) { 136 String persistenceLocation; 137 try { 138 persistenceLocation = persistenceUrl.toURI().toString(); 139 } catch (URISyntaxException e) { 140 //???? 141 continue; 142 } 143 int pos = persistenceLocation.indexOf(rootBase); 144 if (pos < 0) { 145 //not in the ear 146 continue; 147 } 148 int endPos = persistenceLocation.lastIndexOf("!/"); 149 if (endPos < 0) { 150 // if unable to find the '!/' marker, try to see if this is 151 // a war file with the persistence.xml directly embeded - no ejb-jar 152 endPos = persistenceLocation.lastIndexOf("META-INF"); 153 } 154 if (endPos >= 0) { 155 //path relative to ear base uri 156 String relative = persistenceLocation.substring(pos + rootBase.length(), endPos); 157 //find path relative to module base uri 158 relative = module.getRelativePath(relative); 159 PersistenceDocument persistenceDocument; 160 try { 161 XmlObject xmlObject = XmlBeansUtil.parse(persistenceUrl, moduleContext.getClassLoader()); 162 persistenceDocument = (PersistenceDocument) xmlObject.changeType(PersistenceDocument.type); 163 } catch (XmlException e) { 164 throw new DeploymentException("Could not parse persistence.xml file: " + persistenceUrl, e); 165 } 166 PersistenceDocument.Persistence persistence = persistenceDocument.getPersistence(); 167 buildPersistenceUnits(persistence, overrides, module, relative); 168 knownPersistenceUrls.add(persistenceUrl); 169 } else { 170 throw new DeploymentException("Could not find persistence.xml file: " + persistenceUrl); 171 } 172 } 173 } catch (IOException e) { 174 throw new DeploymentException("Could not look for META-INF/persistence.xml files", e); 175 } 176 177 for (PersistenceDocument.Persistence.PersistenceUnit persistenceUnit : overrides.values()) { 178 GBeanData data = installPersistenceUnitGBean(persistenceUnit, module, module.getTargetPath()); 179 respectExcludeUnlistedClasses(data); 180 } 181 } 182 183 public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException { 184 } 185 186 private void buildPersistenceUnits(PersistenceDocument.Persistence persistence, Map<String, PersistenceDocument.Persistence.PersistenceUnit> overrides, Module module, String persistenceModulePath) throws DeploymentException { 187 PersistenceDocument.Persistence.PersistenceUnit[] persistenceUnits = persistence.getPersistenceUnitArray(); 188 for (PersistenceDocument.Persistence.PersistenceUnit persistenceUnit : persistenceUnits) { 189 GBeanData data = installPersistenceUnitGBean(persistenceUnit, module, persistenceModulePath); 190 String unitName = persistenceUnit.getName().trim(); 191 if (overrides.get(unitName) != null) { 192 setOverrideableProperties(overrides.remove(unitName), data); 193 } 194 respectExcludeUnlistedClasses(data); 195 } 196 } 197 198 private GBeanData installPersistenceUnitGBean(PersistenceDocument.Persistence.PersistenceUnit persistenceUnit, Module module, String persistenceModulePath) throws DeploymentException { 199 EARContext moduleContext = module.getEarContext(); 200 String persistenceUnitName = persistenceUnit.getName().trim(); 201 if (persistenceUnitName.length() == 0) { 202 persistenceUnitName = ANON_PU_NAME; 203 } 204 AbstractName abstractName; 205 if (persistenceModulePath == null || persistenceModulePath.length() == 0) { 206 abstractName = moduleContext.getNaming().createChildName(module.getModuleName(), persistenceUnitName, PersistenceUnitGBean.GBEAN_INFO.getJ2eeType()); 207 } else { 208 abstractName = moduleContext.getNaming().createChildName(module.getModuleName(), persistenceModulePath, NameFactory.PERSISTENCE_UNIT_MODULE); 209 abstractName = moduleContext.getNaming().createChildName(abstractName, persistenceUnitName, PersistenceUnitGBean.GBEAN_INFO.getJ2eeType()); 210 } 211 GBeanData gbeanData = new GBeanData(abstractName, PersistenceUnitGBean.GBEAN_INFO); 212 try { 213 moduleContext.addGBean(gbeanData); 214 } catch (GBeanAlreadyExistsException e) { 215 throw new DeploymentException("Duplicate persistenceUnit name " + persistenceUnitName, e); 216 } 217 gbeanData.setAttribute("persistenceUnitName", persistenceUnitName); 218 gbeanData.setAttribute("persistenceUnitRoot", persistenceModulePath); 219 220 //set defaults: 221 gbeanData.setAttribute("persistenceProviderClassName", defaultPersistenceProviderClassName); 222 //spec 6.2.1.2 the default is JTA 223 gbeanData.setAttribute("persistenceUnitTransactionType", "JTA"); 224 if (defaultJtaDataSourceName != null) { 225 gbeanData.setReferencePattern("JtaDataSourceWrapper", defaultJtaDataSourceName); 226 } 227 if (defaultNonJtaDataSourceName != null) { 228 gbeanData.setReferencePattern("NonJtaDataSourceWrapper", defaultNonJtaDataSourceName); 229 } 230 231 gbeanData.setAttribute("mappingFileNames", new ArrayList<String>()); 232 gbeanData.setAttribute("excludeUnlistedClasses", false); 233 gbeanData.setAttribute("managedClassNames", new ArrayList<String>()); 234 gbeanData.setAttribute("jarFileUrls", new ArrayList<String>()); 235 Properties properties = new Properties(); 236 gbeanData.setAttribute("properties", properties); 237 properties.putAll(defaultPersistenceUnitProperties); 238 AbstractNameQuery transactionManagerName = moduleContext.getTransactionManagerName(); 239 gbeanData.setReferencePattern("TransactionManager", transactionManagerName); 240 gbeanData.setReferencePattern("EntityManagerRegistry", extendedEntityManagerRegistryName); 241 242 setOverrideableProperties(persistenceUnit, gbeanData); 243 return gbeanData; 244 } 245 246 private void setOverrideableProperties(PersistenceDocument.Persistence.PersistenceUnit persistenceUnit, GBeanData gbeanData) throws DeploymentException { 247 if (persistenceUnit.isSetProvider()) { 248 gbeanData.setAttribute("persistenceProviderClassName", persistenceUnit.getProvider().trim()); 249 } 250 if (persistenceUnit.isSetTransactionType()) { 251 gbeanData.setAttribute("persistenceUnitTransactionType", persistenceUnit.getTransactionType().toString()); 252 } 253 if (persistenceUnit.isSetJtaDataSource()) { 254 String jtaDataSourceString = persistenceUnit.getJtaDataSource().trim(); 255 try { 256 AbstractNameQuery jtaDataSourceNameQuery = getAbstractNameQuery(jtaDataSourceString); 257 gbeanData.setReferencePattern("JtaDataSourceWrapper", jtaDataSourceNameQuery); 258 } catch (URISyntaxException e) { 259 throw new DeploymentException("Could not create jta-data-source AbstractNameQuery from string: " + jtaDataSourceString, e); 260 } 261 } 262 263 if (persistenceUnit.isSetNonJtaDataSource()) { 264 String nonJtaDataSourceString = persistenceUnit.getNonJtaDataSource().trim(); 265 try { 266 AbstractNameQuery nonJtaDataSourceNameQuery = getAbstractNameQuery(nonJtaDataSourceString); 267 gbeanData.setReferencePattern("NonJtaDataSourceWrapper", nonJtaDataSourceNameQuery); 268 } catch (URISyntaxException e) { 269 throw new DeploymentException("Could not create non-jta-data-source AbstractNameQuery from string: " + nonJtaDataSourceString, e); 270 } 271 } 272 273 List<String> mappingFileNames = (List<String>) gbeanData.getAttribute("mappingFileNames"); 274 String[] mappingFileNameStrings = persistenceUnit.getMappingFileArray(); 275 for (String mappingFileNameString : mappingFileNameStrings) { 276 mappingFileNames.add(mappingFileNameString.trim()); 277 } 278 279 if (persistenceUnit.isSetExcludeUnlistedClasses()) { 280 gbeanData.setAttribute("excludeUnlistedClasses", persistenceUnit.getExcludeUnlistedClasses()); 281 } 282 283 String[] managedClassNameStrings = persistenceUnit.getClass1Array(); 284 List<String> managedClassNames = (List<String>) gbeanData.getAttribute("managedClassNames"); 285 for (String managedClassNameString : managedClassNameStrings) { 286 managedClassNames.add(managedClassNameString.trim()); 287 } 288 List<String> jarFileUrls = (List<String>) gbeanData.getAttribute("jarFileUrls"); 289 //add the specified locations in the ear 290 String[] jarFileUrlStrings = persistenceUnit.getJarFileArray(); 291 for (String jarFileUrlString : jarFileUrlStrings) { 292 jarFileUrls.add(jarFileUrlString.trim()); 293 } 294 295 if (persistenceUnit.isSetProperties()) { 296 Properties properties = (Properties) gbeanData.getAttribute("properties"); 297 PersistenceDocument.Persistence.PersistenceUnit.Properties.Property[] propertyObjects = persistenceUnit.getProperties().getPropertyArray(); 298 for (PersistenceDocument.Persistence.PersistenceUnit.Properties.Property propertyObject : propertyObjects) { 299 String key = propertyObject.getName().trim(); 300 String value = propertyObject.getValue().trim(); 301 properties.setProperty(key, value); 302 } 303 } 304 305 } 306 307 private void respectExcludeUnlistedClasses(GBeanData gbeanData) { 308 boolean excludeUnlistedClasses = (Boolean) gbeanData.getAttribute("excludeUnlistedClasses"); 309 310 if (excludeUnlistedClasses) { 311 gbeanData.clearAttribute("jarFileUrls"); 312 } else { 313 gbeanData.clearAttribute("managedClassNames"); 314 } 315 } 316 317 private AbstractNameQuery getAbstractNameQuery(String dataSourceString) throws URISyntaxException { 318 if (dataSourceString.indexOf('=') == -1) { 319 dataSourceString = "?name=" + dataSourceString; 320 } 321 AbstractNameQuery dataSourceNameQuery = new AbstractNameQuery(new URI(dataSourceString + "#" + RESOURCE_SOURCE_CLASS_NAME)); 322 return dataSourceNameQuery; 323 } 324 325 public QNameSet getSpecQNameSet() { 326 return QNameSet.EMPTY; 327 } 328 329 public QNameSet getPlanQNameSet() { 330 return QNameSet.singleton(PERSISTENCE_QNAME); 331 } 332 333 public static final GBeanInfo GBEAN_INFO; 334 335 static { 336 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitBuilder.class, NameFactory.MODULE_BUILDER); 337 338 infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true); 339 infoBuilder.addAttribute("defaultPersistenceProviderClassName", String.class, true, true); 340 infoBuilder.addAttribute("defaultJtaDataSourceName", String.class, true, true); 341 infoBuilder.addAttribute("defaultNonJtaDataSourceName", String.class, true, true); 342 infoBuilder.addAttribute("extendedEntityManagerRegistryName", AbstractNameQuery.class, true, true); 343 infoBuilder.addAttribute("defaultPersistenceUnitProperties", Properties.class, true, true); 344 345 infoBuilder.setConstructor(new String[]{ 346 "defaultEnvironment", 347 "defaultPersistenceProviderClassName", 348 "defaultJtaDataSourceName", 349 "defaultNonJtaDataSourceName", 350 "extendedEntityManagerRegistryName", 351 "defaultPersistenceUnitProperties" 352 }); 353 354 GBEAN_INFO = infoBuilder.getBeanInfo(); 355 356 } 357 358 public static GBeanInfo getGBeanInfo() { 359 return GBEAN_INFO; 360 } 361 362 363 }