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.util.ArrayList;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.LinkedHashSet;
026    
027    import javax.xml.namespace.QName;
028    
029    import org.apache.geronimo.common.DeploymentException;
030    import org.apache.geronimo.gbean.AbstractNameQuery;
031    import org.apache.geronimo.gbean.GBeanInfo;
032    import org.apache.geronimo.gbean.GBeanInfoBuilder;
033    import org.apache.geronimo.gbean.GBeanData;
034    import org.apache.geronimo.gbean.AbstractName;
035    import org.apache.geronimo.j2ee.deployment.Module;
036    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
037    import org.apache.geronimo.j2ee.deployment.annotation.PersistenceUnitAnnotationHelper;
038    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
039    import org.apache.geronimo.kernel.GBeanNotFoundException;
040    import org.apache.geronimo.kernel.config.Configuration;
041    import org.apache.geronimo.kernel.repository.Environment;
042    import org.apache.geronimo.naming.deployment.AbstractNamingBuilder;
043    import org.apache.geronimo.naming.reference.PersistenceUnitReference;
044    import org.apache.geronimo.schema.NamespaceElementConverter;
045    import org.apache.geronimo.schema.SchemaConversionUtils;
046    import org.apache.geronimo.xbeans.geronimo.naming.GerPatternType;
047    import org.apache.geronimo.xbeans.geronimo.naming.GerPersistenceUnitRefDocument;
048    import org.apache.geronimo.xbeans.geronimo.naming.GerPersistenceUnitRefType;
049    import org.apache.geronimo.xbeans.javaee.PersistenceUnitRefType;
050    import org.apache.xmlbeans.QNameSet;
051    import org.apache.xmlbeans.XmlObject;
052    
053    /**
054     * @version $Rev: 535381 $ $Date: 2007-05-04 17:04:18 -0400 (Fri, 04 May 2007) $
055     */
056    public class PersistenceUnitRefBuilder extends AbstractNamingBuilder {
057        private static final QName PERSISTENCE_UNIT_REF_QNAME = new QName(JEE_NAMESPACE, "persistence-unit-ref");
058        private static final QNameSet PERSISTENCE_UNIT_REF_QNAME_SET = QNameSet.singleton(PERSISTENCE_UNIT_REF_QNAME);
059        private static final QName GER_PERSISTENCE_UNIT_REF_QNAME = GerPersistenceUnitRefDocument.type.getDocumentElementName();
060        private static final QNameSet GER_PERSISTENCE_UNIT_REF_QNAME_SET = QNameSet.singleton(GER_PERSISTENCE_UNIT_REF_QNAME);
061        private static final Set PERSISTENCE_UNIT_INTERFACE_TYPES = Collections.singleton("org.apache.geronimo.persistence.PersistenceUnitGBean");
062        private final AbstractNameQuery defaultPersistenceUnitAbstractNameQuery;
063        private final boolean strictMatching;
064    
065    
066        public PersistenceUnitRefBuilder(Environment defaultEnvironment, AbstractNameQuery defaultPersistenceUnitAbstractNameQuery, boolean strictMatching) {
067            super(defaultEnvironment);
068            this.defaultPersistenceUnitAbstractNameQuery = defaultPersistenceUnitAbstractNameQuery;
069            this.strictMatching = strictMatching;
070        }
071    
072        protected boolean willMergeEnvironment(XmlObject specDD, XmlObject plan) throws DeploymentException {
073            if (specDD != null && specDD.selectChildren(PersistenceUnitRefBuilder.PERSISTENCE_UNIT_REF_QNAME_SET).length > 0) {
074                return true;
075            }
076            return plan != null && plan.selectChildren(PersistenceUnitRefBuilder.GER_PERSISTENCE_UNIT_REF_QNAME_SET).length > 0;
077        }
078    
079        public void buildNaming(XmlObject specDD, XmlObject plan, Module module, Map componentContext) throws DeploymentException {
080            Configuration localConfiguration = module.getEarContext().getConfiguration();
081            // Discover and process any @PersistenceUnitRef annotations (if !metadata-complete)
082            if (module.getClassFinder() != null) {
083                processAnnotations(module);
084            }
085    
086            List<PersistenceUnitRefType> specPersistenceUnitRefsUntyped = convert(specDD.selectChildren(PersistenceUnitRefBuilder.PERSISTENCE_UNIT_REF_QNAME_SET), JEE_CONVERTER, PersistenceUnitRefType.class, PersistenceUnitRefType.type);
087            Map<String, GerPersistenceUnitRefType> gerPersistenceUnitRefsUntyped = getGerPersistenceUnitRefs(plan);
088            List<DeploymentException> problems = new ArrayList<DeploymentException>();
089            for (PersistenceUnitRefType persistenceUnitRef : specPersistenceUnitRefsUntyped) {
090                try {
091                    String persistenceUnitRefName = persistenceUnitRef.getPersistenceUnitRefName().getStringValue().trim();
092    
093                    addInjections(persistenceUnitRefName, persistenceUnitRef.getInjectionTargetArray(), componentContext);
094                    AbstractNameQuery persistenceUnitNameQuery;
095                    GerPersistenceUnitRefType gerPersistenceUnitRef = gerPersistenceUnitRefsUntyped.remove(persistenceUnitRefName);
096                    if (gerPersistenceUnitRef != null) {
097                        persistenceUnitNameQuery = findPersistenceUnit(gerPersistenceUnitRef);
098                        checkForGBean(localConfiguration, persistenceUnitNameQuery, true);
099                    } else if (persistenceUnitRef.isSetPersistenceUnitName() && persistenceUnitRef.getPersistenceUnitName().getStringValue().trim().length() > 0) {
100                        String persistenceUnitName = persistenceUnitRef.getPersistenceUnitName().getStringValue().trim();
101                        persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES);
102                        if (!checkForGBean(localConfiguration, persistenceUnitNameQuery, strictMatching)) {
103                            persistenceUnitName = "persistence/" + persistenceUnitName;
104                            persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES);
105                            checkForGBean(localConfiguration, persistenceUnitNameQuery, true);
106                        }
107                    } else {
108                        persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.EMPTY_MAP, PERSISTENCE_UNIT_INTERFACE_TYPES);
109                        Set<AbstractNameQuery> patterns = Collections.singleton(persistenceUnitNameQuery);
110                        LinkedHashSet<GBeanData> gbeans = localConfiguration.findGBeanDatas(localConfiguration, patterns);
111                        persistenceUnitNameQuery = checkForDefaultPersistenceUnit(gbeans);
112                        if (gbeans.isEmpty()) {
113                            gbeans = localConfiguration.findGBeanDatas(patterns);
114                            persistenceUnitNameQuery = checkForDefaultPersistenceUnit(gbeans);
115    
116                            if (gbeans.isEmpty()) {
117                                if (defaultPersistenceUnitAbstractNameQuery == null) {
118                                    throw new DeploymentException("No default PersistenceUnit specified, and none located");
119                                }
120                                persistenceUnitNameQuery = defaultPersistenceUnitAbstractNameQuery;
121                            }
122                        }
123                    }
124                    checkForGBean(localConfiguration, persistenceUnitNameQuery, true);
125    
126                    PersistenceUnitReference reference = new PersistenceUnitReference(module.getConfigId(), persistenceUnitNameQuery);
127    
128                    NamingBuilder.JNDI_KEY.get(componentContext).put(ENV + persistenceUnitRefName, reference);
129                } catch (DeploymentException e) {
130                    problems.add(e);
131                }
132    
133            }
134    
135    
136            for (GerPersistenceUnitRefType gerPersistenceUnitRef : gerPersistenceUnitRefsUntyped.values()) {
137                try {
138                    String PersistenceUnitRefName = gerPersistenceUnitRef.getPersistenceUnitRefName();
139    
140                    AbstractNameQuery persistenceUnitNameQuery = findPersistenceUnit(gerPersistenceUnitRef);
141    
142                    checkForGBean(localConfiguration, persistenceUnitNameQuery, true);
143    
144                    PersistenceUnitReference reference = new PersistenceUnitReference(module.getConfigId(), persistenceUnitNameQuery);
145    
146                    NamingBuilder.JNDI_KEY.get(componentContext).put(ENV + PersistenceUnitRefName, reference);
147                } catch (DeploymentException e) {
148                    problems.add(e);
149                }
150    
151            }
152            if (!problems.isEmpty()) {
153                //TODO make DeploymentException accept a list of exceptions as causes.
154                throw new DeploymentException("At least one deployment problem:" + problems);
155            }
156        }
157    
158        private AbstractNameQuery checkForDefaultPersistenceUnit(LinkedHashSet<GBeanData> gbeans) throws DeploymentException {
159            AbstractNameQuery persistenceUnitNameQuery = null;
160            for (java.util.Iterator it = gbeans.iterator(); it.hasNext();) {
161                GBeanData gbean = (GBeanData) it.next();
162                AbstractName name = gbean.getAbstractName();
163                Map nameMap = name.getName();
164                if ("cmp".equals(nameMap.get("name"))) {
165                    it.remove();
166                } else {
167                    persistenceUnitNameQuery = new AbstractNameQuery(name);
168                }
169            }
170            if (gbeans.size() > 1) {
171                throw new DeploymentException("Too many matches for no-name persistence unit: " + gbeans);
172            }
173            return persistenceUnitNameQuery;
174        }
175    
176        private boolean checkForGBean(Configuration localConfiguration, AbstractNameQuery persistenceUnitNameQuery, boolean complainIfMissing) throws DeploymentException {
177            try {
178                localConfiguration.findGBeanData(persistenceUnitNameQuery);
179                return true;
180            } catch (GBeanNotFoundException e) {
181                if (complainIfMissing || e.hasMatches()) {
182                    String reason = e.hasMatches() ? "More than one GBean reference found." : "No GBean references found.";
183                    throw new DeploymentException("Could not resolve reference at deploy time for query " + persistenceUnitNameQuery + ". " + reason, e);
184                }
185                return false;
186            }
187        }
188    
189        private void processAnnotations(Module module) throws DeploymentException {
190            // Process all the annotations for this naming builder type
191            PersistenceUnitAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder());
192        }
193    
194        private AbstractNameQuery findPersistenceUnit(GerPersistenceUnitRefType gerPersistenceUnitRef) {
195            AbstractNameQuery persistenceUnitNameQuery;
196            if (gerPersistenceUnitRef.isSetPersistenceUnitName()) {
197                String persistenceUnitName = gerPersistenceUnitRef.getPersistenceUnitName();
198                persistenceUnitNameQuery = new AbstractNameQuery(null, Collections.singletonMap("name", persistenceUnitName), PERSISTENCE_UNIT_INTERFACE_TYPES);
199            } else {
200                GerPatternType gbeanLocator = gerPersistenceUnitRef.getPattern();
201    
202                persistenceUnitNameQuery = buildAbstractNameQuery(gbeanLocator, null, null, PERSISTENCE_UNIT_INTERFACE_TYPES);
203            }
204            return persistenceUnitNameQuery;
205        }
206    
207        public QNameSet getSpecQNameSet() {
208            SchemaConversionUtils.registerNamespaceConversions(Collections.singletonMap(PersistenceUnitRefBuilder.GER_PERSISTENCE_UNIT_REF_QNAME.getLocalPart(), new NamespaceElementConverter(PersistenceUnitRefBuilder.GER_PERSISTENCE_UNIT_REF_QNAME.getNamespaceURI())));
209            return PERSISTENCE_UNIT_REF_QNAME_SET;
210        }
211    
212        public QNameSet getPlanQNameSet() {
213            return GER_PERSISTENCE_UNIT_REF_QNAME_SET;
214        }
215    
216        private Map<String, GerPersistenceUnitRefType> getGerPersistenceUnitRefs(XmlObject plan) throws DeploymentException {
217            Map<String, GerPersistenceUnitRefType> map = new HashMap<String, GerPersistenceUnitRefType>();
218            if (plan != null) {
219                List<GerPersistenceUnitRefType> refs = convert(plan.selectChildren(PersistenceUnitRefBuilder.GER_PERSISTENCE_UNIT_REF_QNAME_SET), NAMING_CONVERTER, GerPersistenceUnitRefType.class, GerPersistenceUnitRefType.type);
220                for (GerPersistenceUnitRefType ref : refs) {
221                    map.put(ref.getPersistenceUnitRefName().trim(), ref);
222                }
223            }
224            return map;
225        }
226    
227        public static final GBeanInfo GBEAN_INFO;
228    
229        static {
230            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(PersistenceUnitRefBuilder.class, NameFactory.MODULE_BUILDER);
231            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
232            infoBuilder.addAttribute("defaultPersistenceUnitAbstractNameQuery", AbstractNameQuery.class, true, true);
233            infoBuilder.addAttribute("strictMatching", boolean.class, true, true);
234    
235            infoBuilder.setConstructor(new String[]{"defaultEnvironment", "defaultPersistenceUnitAbstractNameQuery", "strictMatching"});
236            GBEAN_INFO = infoBuilder.getBeanInfo();
237        }
238    
239        public static GBeanInfo getGBeanInfo() {
240            return PersistenceUnitRefBuilder.GBEAN_INFO;
241        }
242    }