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