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.jaxws.builder;
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.net.URL;
023    import java.util.Map;
024    import java.util.jar.JarFile;
025    
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    import org.apache.geronimo.common.DeploymentException;
029    import org.apache.geronimo.deployment.DeploymentContext;
030    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
031    import org.apache.geronimo.deployment.util.DeploymentUtil;
032    import org.apache.geronimo.gbean.AbstractName;
033    import org.apache.geronimo.gbean.GBeanData;
034    import org.apache.geronimo.gbean.GBeanInfo;
035    import org.apache.geronimo.j2ee.annotation.Holder;
036    import org.apache.geronimo.j2ee.deployment.EARContext;
037    import org.apache.geronimo.j2ee.deployment.Module;
038    import org.apache.geronimo.j2ee.deployment.WebModule;
039    import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
040    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
041    import org.apache.geronimo.jaxws.JAXWSUtils;
042    import org.apache.geronimo.jaxws.PortInfo;
043    import org.apache.geronimo.jaxws.annotations.AnnotationHolder;
044    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
045    import org.apache.geronimo.kernel.GBeanNotFoundException;
046    import org.apache.geronimo.kernel.repository.Environment;
047    
048    public abstract class JAXWSServiceBuilder implements WebServiceBuilder {
049        private static final Log LOG = LogFactory.getLog(JAXWSServiceBuilder.class);
050    
051        protected final Environment defaultEnvironment;
052        protected WebServiceFinder webServiceFinder;
053    
054        public JAXWSServiceBuilder(Environment defaultEnvironment) {
055            this.defaultEnvironment = defaultEnvironment;
056        }
057    
058        protected void setWebServiceFinder(WebServiceFinder finder) {
059            this.webServiceFinder = finder;        
060        }
061        
062        protected String getKey() {
063            return getClass().getName();
064        }
065        
066        public void findWebServices(Module module,
067                                    boolean isEJB,
068                                    Map servletLocations,
069                                    Environment environment,
070                                    Map sharedContext) throws DeploymentException {
071            Map portMap = null;
072            String path = isEJB ? "META-INF/webservices.xml" : "WEB-INF/webservices.xml";
073            JarFile moduleFile = module.getModuleFile();
074            try {
075                URL wsDDUrl = DeploymentUtil.createJarURL(moduleFile, path);
076                InputStream in = wsDDUrl.openStream();
077                portMap = parseWebServiceDescriptor(in, wsDDUrl, moduleFile, isEJB, servletLocations);
078            } catch (IOException e) {
079                // webservices.xml does not exist
080                portMap = discoverWebServices(module, isEJB, servletLocations);
081            }
082    
083            if (portMap != null && !portMap.isEmpty()) {
084                EnvironmentBuilder.mergeEnvironments(environment, defaultEnvironment);
085                sharedContext.put(getKey(), portMap);
086            }
087    
088        }
089    
090        private Map<String, PortInfo> discoverWebServices(Module module,
091                                                          boolean isEJB,
092                                                          Map correctedPortLocations)
093                throws DeploymentException {
094            if (this.webServiceFinder == null) {
095                throw new DeploymentException("WebServiceFinder not configured");
096            }
097            return this.webServiceFinder.discoverWebServices(module, isEJB, correctedPortLocations);
098        }
099                                   
100        protected abstract Map<String, PortInfo> parseWebServiceDescriptor(InputStream in,
101                                                                           URL wsDDUrl,
102                                                                           JarFile moduleFile,
103                                                                           boolean isEJB,
104                                                                           Map correctedPortLocations)
105                throws DeploymentException;
106    
107        public boolean configurePOJO(GBeanData targetGBean,
108                                     String servletName,
109                                     Module module,
110                                     String servletClassName,
111                                     DeploymentContext context)
112                throws DeploymentException {
113            Map sharedContext = ((WebModule) module).getSharedContext();
114            Map portInfoMap = (Map) sharedContext.get(getKey());
115            if (portInfoMap == null) {
116                // not ours
117                return false;
118            }
119            PortInfo portInfo = (PortInfo) portInfoMap.get(servletName);
120            if (portInfo == null) {
121                // not ours
122                return false;
123            }
124    
125            // verify that the class is loadable and is a JAX-WS web service
126            ClassLoader classLoader = context.getClassLoader();
127            Class servletClass = loadClass(servletClassName, classLoader);
128            if (!JAXWSUtils.isWebService(servletClass)) {
129                return false;
130            }
131            
132            Map componentContext = null;
133            Holder moduleHolder = null;
134            try {
135                GBeanData moduleGBean = context.getGBeanInstance(context.getModuleName());
136                componentContext = (Map)moduleGBean.getAttribute("componentContext");
137                moduleHolder = (Holder)moduleGBean.getAttribute("holder");
138            } catch (GBeanNotFoundException e) {
139                LOG.warn("ModuleGBean not found. JNDI resource injection will not work.");
140            }
141    
142            AnnotationHolder serviceHolder = 
143                (AnnotationHolder)sharedContext.get(WebServiceContextAnnotationHelper.class.getName());
144            if (serviceHolder == null) {
145                serviceHolder = new AnnotationHolder(moduleHolder);            
146                sharedContext.put(WebServiceContextAnnotationHelper.class.getName(), serviceHolder);
147            }
148            WebServiceContextAnnotationHelper.addWebServiceContextInjections(serviceHolder, servletClass);
149            
150            String location = portInfo.getLocation();
151            LOG.info("Configuring JAX-WS Web Service: " + servletName + " at " + location);
152    
153            AbstractName containerFactoryName = context.getNaming().createChildName(targetGBean.getAbstractName(), getContainerFactoryGBeanInfo().getName(), NameFactory.GERONIMO_SERVICE);
154            GBeanData containerFactoryData = new GBeanData(containerFactoryName, getContainerFactoryGBeanInfo());
155            containerFactoryData.setAttribute("portInfo", portInfo);
156            containerFactoryData.setAttribute("endpointClassName", servletClassName);
157            containerFactoryData.setAttribute("componentContext", componentContext);
158            containerFactoryData.setAttribute("holder", serviceHolder);
159            containerFactoryData.setAttribute("contextRoot", ((WebModule) module).getContextRoot());
160            try {
161                context.addGBean(containerFactoryData);
162            } catch (GBeanAlreadyExistsException e) {
163                throw new DeploymentException("Could not add web service container factory gbean", e);
164            }
165    
166            targetGBean.setReferencePattern("WebServiceContainerFactory", containerFactoryName);
167            // our web container does not use that property
168            targetGBean.setAttribute("pojoClassName", "java.lang.Object");
169    
170            if (context instanceof EARContext) {
171                containerFactoryData.setReferencePattern("TransactionManager",
172                                                         ((EARContext)context).getTransactionManagerName());
173            }
174            
175            initialize(containerFactoryData, servletClass, portInfo, module);
176            
177            return true;
178        }
179            
180        protected abstract GBeanInfo getContainerFactoryGBeanInfo();
181    
182        public boolean configureEJB(GBeanData targetGBean,
183                                    String ejbName,
184                                    Module module,
185                                    Map sharedContext,
186                                    ClassLoader classLoader)
187                throws DeploymentException {        
188            Map portInfoMap = (Map) sharedContext.get(getKey());
189            if (portInfoMap == null) {
190                // not ours
191                return false;
192            }
193            PortInfo portInfo = (PortInfo) portInfoMap.get(ejbName);
194            if (portInfo == null) {
195                // not ours
196                return false;
197            }
198           
199            String beanClassName = (String)targetGBean.getAttribute("ejbClass");
200            // verify that the class is loadable and is a JAX-WS web service
201            Class beanClass = loadClass(beanClassName, classLoader);
202            if (!JAXWSUtils.isWebService(beanClass)) {
203                return false;
204            }
205            
206            String location = portInfo.getLocation();
207            if (location == null) {                   
208                throw new DeploymentException("Endpoint URI for EJB WebService is missing");
209            }
210    
211            LOG.info("Configuring EJB JAX-WS Web Service: " + ejbName + " at " + location);
212            
213            targetGBean.setAttribute("portInfo", portInfo);
214            
215            initialize(targetGBean, beanClass, portInfo, module);
216            
217            return true;
218        }
219        
220        protected void initialize(GBeanData targetGBean, Class wsClass, PortInfo info, Module module) 
221            throws DeploymentException {
222        }
223        
224        Class<?> loadClass(String className, ClassLoader loader) throws DeploymentException {
225            try {
226                return loader.loadClass(className);
227            } catch (ClassNotFoundException ex) {
228                throw new DeploymentException("Unable to load Web Service class: " + className, ex);
229            }
230        }
231    }