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.builder; 019 020 import java.util.ArrayList; 021 import java.util.Collections; 022 import java.util.HashMap; 023 import java.util.LinkedHashSet; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 028 import javax.xml.namespace.QName; 029 030 import org.apache.geronimo.common.DeploymentException; 031 import org.apache.geronimo.gbean.AbstractName; 032 import org.apache.geronimo.gbean.AbstractNameQuery; 033 import org.apache.geronimo.gbean.GBeanData; 034 import org.apache.geronimo.gbean.GBeanInfo; 035 import org.apache.geronimo.gbean.GBeanInfoBuilder; 036 import org.apache.geronimo.j2ee.deployment.Module; 037 import org.apache.geronimo.j2ee.deployment.NamingBuilder; 038 import org.apache.geronimo.j2ee.deployment.annotation.PersistenceContextAnnotationHelper; 039 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 040 import org.apache.geronimo.kernel.GBeanNotFoundException; 041 import org.apache.geronimo.kernel.config.Configuration; 042 import org.apache.geronimo.kernel.repository.Environment; 043 import org.apache.geronimo.naming.deployment.AbstractNamingBuilder; 044 import org.apache.geronimo.naming.reference.PersistenceContextReference; 045 import org.apache.geronimo.schema.NamespaceElementConverter; 046 import org.apache.geronimo.schema.SchemaConversionUtils; 047 import org.apache.geronimo.xbeans.geronimo.naming.GerPatternType; 048 import org.apache.geronimo.xbeans.geronimo.naming.GerPersistenceContextRefDocument; 049 import org.apache.geronimo.xbeans.geronimo.naming.GerPersistenceContextRefType; 050 import org.apache.geronimo.xbeans.geronimo.naming.GerPersistenceContextTypeType; 051 import org.apache.geronimo.xbeans.geronimo.naming.GerPropertyType; 052 import org.apache.geronimo.xbeans.javaee.PersistenceContextRefType; 053 import org.apache.geronimo.xbeans.javaee.PersistenceContextTypeType; 054 import org.apache.geronimo.xbeans.javaee.PropertyType; 055 import org.apache.xmlbeans.QNameSet; 056 import org.apache.xmlbeans.XmlObject; 057 058 /** 059 * @version $Rev: 535381 $ $Date: 2007-05-04 17:04:18 -0400 (Fri, 04 May 2007) $ 060 */ 061 public class PersistenceContextRefBuilder extends AbstractNamingBuilder { 062 private static final QName PERSISTENCE_CONTEXT_REF_QNAME = new QName(JEE_NAMESPACE, "persistence-context-ref"); 063 private static final QNameSet PERSISTENCE_CONTEXT_REF_QNAME_SET = QNameSet.singleton(PERSISTENCE_CONTEXT_REF_QNAME); 064 private static final QName GER_PERSISTENCE_CONTEXT_REF_QNAME = GerPersistenceContextRefDocument.type.getDocumentElementName(); 065 private static final QNameSet GER_PERSISTENCE_CONTEXT_REF_QNAME_SET = QNameSet.singleton(GER_PERSISTENCE_CONTEXT_REF_QNAME); 066 private static final Set<String> PERSISTENCE_UNIT_INTERFACE_TYPES = Collections.singleton("org.apache.geronimo.persistence.PersistenceUnitGBean"); 067 private final AbstractNameQuery defaultPersistenceUnitAbstractNameQuery; 068 private final boolean strictMatching; 069 070 public PersistenceContextRefBuilder(Environment defaultEnvironment, AbstractNameQuery defaultPersistenceUnitAbstractNameQuery, boolean strictMatching) { 071 super(defaultEnvironment); 072 this.defaultPersistenceUnitAbstractNameQuery = defaultPersistenceUnitAbstractNameQuery; 073 this.strictMatching = strictMatching; 074 } 075 076 protected boolean willMergeEnvironment(XmlObject specDD, XmlObject plan) throws DeploymentException { 077 return plan != null && plan.selectChildren(PersistenceContextRefBuilder.GER_PERSISTENCE_CONTEXT_REF_QNAME_SET).length > 0; 078 } 079 080 public void buildNaming(XmlObject specDD, XmlObject plan, Module module, Map componentContext) throws DeploymentException { 081 082 // Discover and process any @PersistenceContextRef annotations (if !metadata-complete) 083 if ((module != null) && (module.getClassFinder() != null)) { 084 processAnnotations(module); 085 } 086 087 List<PersistenceContextRefType> specPersistenceContextRefsUntyped = convert(specDD.selectChildren(PERSISTENCE_CONTEXT_REF_QNAME_SET), JEE_CONVERTER, PersistenceContextRefType.class, PersistenceContextRefType.type); 088 Map<String, GerPersistenceContextRefType> gerPersistenceContextRefsUntyped = getGerPersistenceContextRefs(plan); 089 List<DeploymentException> problems = new ArrayList<DeploymentException>(); 090 Configuration localConfiguration = module.getEarContext().getConfiguration(); 091 for (PersistenceContextRefType persistenceContextRef : specPersistenceContextRefsUntyped) { 092 try { 093 String persistenceContextRefName = persistenceContextRef.getPersistenceContextRefName().getStringValue().trim(); 094 095 addInjections(persistenceContextRefName, persistenceContextRef.getInjectionTargetArray(), componentContext); 096 PersistenceContextTypeType persistenceContextType = persistenceContextRef.getPersistenceContextType(); 097 boolean transactionScoped = persistenceContextType == null || !persistenceContextType.getStringValue().equalsIgnoreCase("extended"); 098 099 PropertyType[] propertyTypes = persistenceContextRef.getPersistencePropertyArray(); 100 Map<String, String> properties = new HashMap<String, String>(); 101 for (PropertyType propertyType : propertyTypes) { 102 String key = propertyType.getName().getStringValue(); 103 String value = propertyType.getValue().getStringValue(); 104 properties.put(key, value); 105 } 106 107 AbstractNameQuery persistenceUnitNameQuery; 108 GerPersistenceContextRefType gerPersistenceContextRef = gerPersistenceContextRefsUntyped.remove(persistenceContextRefName); 109 if (gerPersistenceContextRef != null) { 110 persistenceUnitNameQuery = findPersistenceUnit(gerPersistenceContextRef); 111 addProperties(gerPersistenceContextRef, properties); 112 checkForGBean(localConfiguration, persistenceUnitNameQuery, true); 113 } else if (persistenceContextRef.isSetPersistenceUnitName() && persistenceContextRef.getPersistenceUnitName().getStringValue().trim().length() > 0) { 114 String persistenceUnitName = persistenceContextRef.getPersistenceUnitName().getStringValue().trim(); 115 persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES); 116 if (!checkForGBean(localConfiguration, persistenceUnitNameQuery, strictMatching)) { 117 persistenceUnitName = "persistence/" + persistenceUnitName; 118 persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES); 119 checkForGBean(localConfiguration, persistenceUnitNameQuery, true); 120 } 121 } else { 122 persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.EMPTY_MAP, PERSISTENCE_UNIT_INTERFACE_TYPES); 123 Set<AbstractNameQuery> patterns = Collections.singleton(persistenceUnitNameQuery); 124 LinkedHashSet<GBeanData> gbeans = localConfiguration.findGBeanDatas(localConfiguration, patterns); 125 persistenceUnitNameQuery = checkForDefaultPersistenceUnit(gbeans); 126 if (gbeans.isEmpty()) { 127 gbeans = localConfiguration.findGBeanDatas(patterns); 128 persistenceUnitNameQuery = checkForDefaultPersistenceUnit(gbeans); 129 130 if (gbeans.isEmpty()) { 131 if (defaultPersistenceUnitAbstractNameQuery == null) { 132 throw new DeploymentException("No default PersistenceUnit specified, and none located"); 133 } 134 persistenceUnitNameQuery = defaultPersistenceUnitAbstractNameQuery; 135 } 136 } 137 checkForGBean(localConfiguration, persistenceUnitNameQuery, true); 138 } 139 140 PersistenceContextReference reference = new PersistenceContextReference(module.getConfigId(), persistenceUnitNameQuery, transactionScoped, properties); 141 142 NamingBuilder.JNDI_KEY.get(componentContext).put(ENV + persistenceContextRefName, reference); 143 } catch (DeploymentException e) { 144 problems.add(e); 145 } 146 } 147 148 // Support persistence context refs that are mentioned only in the geronimo plan 149 for (GerPersistenceContextRefType gerPersistenceContextRef : gerPersistenceContextRefsUntyped.values()) { 150 try { 151 String persistenceContextRefName = gerPersistenceContextRef.getPersistenceContextRefName(); 152 GerPersistenceContextTypeType.Enum persistenceContextType = gerPersistenceContextRef.getPersistenceContextType(); 153 boolean transactionScoped = persistenceContextType == null || !persistenceContextType.equals(GerPersistenceContextTypeType.EXTENDED); 154 Map properties = new HashMap(); 155 addProperties(gerPersistenceContextRef, properties); 156 157 158 AbstractNameQuery persistenceUnitNameQuery = findPersistenceUnit(gerPersistenceContextRef); 159 160 checkForGBean(localConfiguration, persistenceUnitNameQuery, true); 161 162 PersistenceContextReference reference = new PersistenceContextReference(module.getConfigId(), persistenceUnitNameQuery, transactionScoped, properties); 163 164 getJndiContextMap(componentContext).put(ENV + persistenceContextRefName, reference); 165 } catch (DeploymentException e) { 166 problems.add(e); 167 } 168 169 } 170 if (!problems.isEmpty()) { 171 // something is broken with cmp references that stops deployment... this is just a patch around the real problem 172 // throw new DeploymentException("Could not resolve reference at deploy time for query " + persistenceUnitNameQuery, e); 173 for (DeploymentException e : problems) { 174 e.printStackTrace(); 175 } 176 //TODO throw a "multi-exception" 177 } 178 } 179 180 private AbstractNameQuery checkForDefaultPersistenceUnit(LinkedHashSet<GBeanData> gbeans) throws DeploymentException { 181 AbstractNameQuery persistenceUnitNameQuery = null; 182 for (java.util.Iterator it = gbeans.iterator(); it.hasNext();) { 183 GBeanData gbean = (GBeanData) it.next(); 184 AbstractName name = gbean.getAbstractName(); 185 Map nameMap = name.getName(); 186 if ("cmp".equals(nameMap.get("name"))) { 187 it.remove(); 188 } else { 189 persistenceUnitNameQuery = new AbstractNameQuery(name); 190 } 191 } 192 if (gbeans.size() > 1) { 193 throw new DeploymentException("Too many matches for no-name persistence unit: " + gbeans); 194 } 195 return persistenceUnitNameQuery; 196 } 197 198 private boolean checkForGBean(Configuration localConfiguration, AbstractNameQuery persistenceUnitNameQuery, boolean complainIfMissing) throws DeploymentException { 199 try { 200 localConfiguration.findGBeanData(persistenceUnitNameQuery); 201 return true; 202 } catch (GBeanNotFoundException e) { 203 if (complainIfMissing || e.hasMatches()) { 204 String reason = e.hasMatches() ? "More than one GBean reference found." : "No GBeans found."; 205 throw new DeploymentException("Could not resolve reference at deploy time for query " + persistenceUnitNameQuery + ". " + reason, e); 206 } 207 return false; 208 } 209 } 210 211 private void addProperties(GerPersistenceContextRefType persistenceContextRef, Map<String, String> properties) { 212 GerPropertyType[] propertyTypes = persistenceContextRef.getPropertyArray(); 213 for (GerPropertyType propertyType : propertyTypes) { 214 String key = propertyType.getKey(); 215 String value = propertyType.getValue(); 216 properties.put(key, value); 217 } 218 } 219 220 private Map<String, GerPersistenceContextRefType> getGerPersistenceContextRefs(XmlObject plan) throws DeploymentException { 221 Map<String, GerPersistenceContextRefType> map = new HashMap<String, GerPersistenceContextRefType>(); 222 if (plan != null) { 223 List<GerPersistenceContextRefType> refs = convert(plan.selectChildren(GER_PERSISTENCE_CONTEXT_REF_QNAME_SET), NAMING_CONVERTER, GerPersistenceContextRefType.class, GerPersistenceContextRefType.type); 224 for (GerPersistenceContextRefType ref : refs) { 225 map.put(ref.getPersistenceContextRefName().trim(), ref); 226 } 227 } 228 return map; 229 } 230 231 private AbstractNameQuery findPersistenceUnit(GerPersistenceContextRefType persistenceContextRef) { 232 AbstractNameQuery persistenceUnitNameQuery; 233 if (persistenceContextRef.isSetPersistenceUnitName()) { 234 String persistenceUnitName = persistenceContextRef.getPersistenceUnitName(); 235 persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES); 236 } else { 237 GerPatternType gbeanLocator = persistenceContextRef.getPattern(); 238 239 persistenceUnitNameQuery = buildAbstractNameQuery(gbeanLocator, null, null, PERSISTENCE_UNIT_INTERFACE_TYPES); 240 } 241 return persistenceUnitNameQuery; 242 } 243 244 private void processAnnotations(Module module) throws DeploymentException { 245 // Process all the annotations for this naming builder type 246 PersistenceContextAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder()); 247 } 248 249 public QNameSet getSpecQNameSet() { 250 SchemaConversionUtils.registerNamespaceConversions(Collections.singletonMap(GER_PERSISTENCE_CONTEXT_REF_QNAME.getLocalPart(), new NamespaceElementConverter(GER_PERSISTENCE_CONTEXT_REF_QNAME.getNamespaceURI()))); 251 return QNameSet.EMPTY; 252 } 253 254 public QNameSet getPlanQNameSet() { 255 return GER_PERSISTENCE_CONTEXT_REF_QNAME_SET; 256 } 257 258 public static final GBeanInfo GBEAN_INFO; 259 260 static { 261 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceContextRefBuilder.class, NameFactory.MODULE_BUILDER); 262 infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true); 263 infoBuilder.addAttribute("defaultPersistenceUnitAbstractNameQuery", AbstractNameQuery.class, true, true); 264 infoBuilder.addAttribute("strictMatching", boolean.class, true, true); 265 266 infoBuilder.setConstructor(new String[]{"defaultEnvironment", "defaultPersistenceUnitAbstractNameQuery", "strictMatching"}); 267 GBEAN_INFO = infoBuilder.getBeanInfo(); 268 } 269 270 public static GBeanInfo getGBeanInfo() { 271 return GBEAN_INFO; 272 } 273 }