001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.server.webservices;
018:
019: import org.apache.openejb.server.ServerService;
020: import org.apache.openejb.server.SelfManaging;
021: import org.apache.openejb.server.ServiceException;
022: import org.apache.openejb.server.httpd.HttpListener;
023: import org.apache.openejb.server.httpd.HttpListenerRegistry;
024: import org.apache.openejb.assembler.classic.DeploymentListener;
025: import org.apache.openejb.assembler.classic.Assembler;
026: import org.apache.openejb.assembler.classic.AppInfo;
027: import org.apache.openejb.assembler.classic.EjbJarInfo;
028: import org.apache.openejb.assembler.classic.PortInfo;
029: import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
030: import org.apache.openejb.assembler.classic.StatelessBeanInfo;
031: import org.apache.openejb.assembler.classic.WsBuilder;
032: import org.apache.openejb.assembler.classic.WebAppInfo;
033: import org.apache.openejb.assembler.classic.ServletInfo;
034: import org.apache.openejb.loader.SystemInstance;
035: import org.apache.openejb.core.webservices.PortAddressRegistryImpl;
036: import org.apache.openejb.core.webservices.PortAddressRegistry;
037: import org.apache.openejb.core.webservices.PortData;
038: import org.apache.openejb.core.CoreContainerSystem;
039: import org.apache.openejb.core.WebDeploymentInfo;
040: import org.apache.openejb.spi.ContainerSystem;
041: import org.apache.openejb.DeploymentInfo;
042: import org.apache.openejb.Injection;
043: import org.apache.openejb.util.Logger;
044: import org.apache.openejb.util.LogCategory;
045: import org.codehaus.swizzle.stream.StringTemplate;
046:
047: import javax.naming.Context;
048: import java.util.Properties;
049: import java.util.ArrayList;
050: import java.util.Map;
051: import java.util.HashMap;
052: import java.util.TreeMap;
053: import java.util.List;
054: import java.util.Collection;
055: import java.util.Set;
056: import java.util.HashSet;
057: import java.io.InputStream;
058: import java.io.OutputStream;
059: import java.io.IOException;
060: import java.io.File;
061: import java.net.Socket;
062: import java.net.URL;
063: import java.net.MalformedURLException;
064:
065: public abstract class WsService implements ServerService, SelfManaging,
066: DeploymentListener {
067: public static final Logger logger = Logger.getInstance(
068: LogCategory.OPENEJB_WS, WsService.class);
069: public static final String WS_ADDRESS_FORMAT = "openejb.wsAddress.format";
070: private StringTemplate wsAddressTemplate;
071:
072: private PortAddressRegistry portAddressRegistry;
073: private CoreContainerSystem containerSystem;
074: private Assembler assembler;
075: private WsRegistry wsRegistry;
076: private String realmName;
077: private String transportGuarantee;
078: private String authMethod;
079: private String virtualHost;
080: private final Set<AppInfo> deployedApplications = new HashSet<AppInfo>();
081: private final Set<WebAppInfo> deployedWebApps = new HashSet<WebAppInfo>();
082: private final Map<String, String> ejbLocations = new TreeMap<String, String>();
083: private final Map<String, String> ejbAddresses = new TreeMap<String, String>();
084: private final Map<String, String> servletAddresses = new TreeMap<String, String>();
085:
086: public WsService() {
087: String format = SystemInstance.get().getProperty(
088: WS_ADDRESS_FORMAT, "/{ejbDeploymentId}");
089: this .wsAddressTemplate = new StringTemplate(format);
090: }
091:
092: public StringTemplate getWsAddressTemplate() {
093: return wsAddressTemplate;
094: }
095:
096: public void setWsAddressTemplate(StringTemplate wsAddressTemplate) {
097: this .wsAddressTemplate = wsAddressTemplate;
098: }
099:
100: public String getRealmName() {
101: return realmName;
102: }
103:
104: public void setRealmName(String realmName) {
105: this .realmName = realmName;
106: }
107:
108: public String getTransportGuarantee() {
109: return transportGuarantee;
110: }
111:
112: public void setTransportGuarantee(String transportGuarantee) {
113: this .transportGuarantee = transportGuarantee;
114: }
115:
116: public String getAuthMethod() {
117: return authMethod;
118: }
119:
120: public void setAuthMethod(String authMethod) {
121: this .authMethod = authMethod;
122: }
123:
124: public String getVirtualHost() {
125: return virtualHost;
126: }
127:
128: public void setVirtualHost(String virtualHost) {
129: this .virtualHost = virtualHost;
130: }
131:
132: public String getIP() {
133: return "n/a";
134: }
135:
136: public int getPort() {
137: return -1;
138: }
139:
140: public void init(Properties props) throws Exception {
141: if (props == null)
142: return;
143:
144: String format = props.getProperty(WS_ADDRESS_FORMAT);
145: if (format != null) {
146: this .wsAddressTemplate = new StringTemplate(format);
147: }
148:
149: realmName = props.getProperty("realmName");
150: transportGuarantee = props.getProperty("transportGuarantee");
151: authMethod = props.getProperty("authMethod");
152: virtualHost = props.getProperty("virtualHost");
153: }
154:
155: public void start() throws ServiceException {
156: wsRegistry = SystemInstance.get()
157: .getComponent(WsRegistry.class);
158: if (wsRegistry == null
159: && SystemInstance.get().getComponent(
160: HttpListenerRegistry.class) != null) {
161: wsRegistry = new OpenEJBHttpWsRegistry();
162: }
163:
164: if (portAddressRegistry == null) {
165: portAddressRegistry = new PortAddressRegistryImpl();
166: SystemInstance.get().setComponent(
167: PortAddressRegistry.class, portAddressRegistry);
168: }
169: containerSystem = (CoreContainerSystem) SystemInstance.get()
170: .getComponent(ContainerSystem.class);
171: portAddressRegistry = SystemInstance.get().getComponent(
172: PortAddressRegistry.class);
173: assembler = SystemInstance.get().getComponent(Assembler.class);
174: SystemInstance.get().setComponent(WsService.class, this );
175: if (assembler != null) {
176: assembler.addDeploymentListener(this );
177: for (AppInfo appInfo : assembler.getDeployedApplications()) {
178: afterApplicationCreated(appInfo);
179: }
180: }
181: }
182:
183: public void stop() throws ServiceException {
184: if (assembler != null) {
185: assembler.removeDeploymentListener(this );
186: for (AppInfo appInfo : new ArrayList<AppInfo>(
187: deployedApplications)) {
188: beforeApplicationDestroyed(appInfo);
189: }
190: assembler = null;
191: if (SystemInstance.get().getComponent(WsService.class) == this ) {
192: SystemInstance.get().removeComponent(WsService.class);
193: }
194: }
195: }
196:
197: protected abstract HttpListener createEjbWsContainer(
198: URL moduleBaseUrl, PortData port,
199: DeploymentInfo deploymentInfo) throws Exception;
200:
201: protected abstract void destroyEjbWsContainer(String deploymentId);
202:
203: protected abstract HttpListener createPojoWsContainer(
204: URL moduleBaseUrl, PortData port, String serviceId,
205: Class target, Context context, String contextRoot)
206: throws Exception;
207:
208: protected abstract void destroyPojoWsContainer(String serviceId);
209:
210: public void afterApplicationCreated(AppInfo appInfo) {
211: if (deployedApplications.add(appInfo)) {
212: Map<String, String> contextData = new HashMap<String, String>();
213: contextData.put("appId", appInfo.jarPath);
214: for (EjbJarInfo ejbJar : appInfo.ejbJars) {
215: Map<String, PortInfo> ports = new TreeMap<String, PortInfo>();
216: for (PortInfo port : ejbJar.portInfos) {
217: ports.put(port.serviceLink, port);
218: }
219:
220: URL moduleBaseUrl = null;
221: try {
222: moduleBaseUrl = new File(ejbJar.jarPath).toURL();
223: } catch (MalformedURLException e) {
224: logger.error("Invalid ejb jar location "
225: + ejbJar.jarPath, e);
226: }
227:
228: StringTemplate deploymentIdTemplate = this .wsAddressTemplate;
229: if (ejbJar.properties.containsKey(WS_ADDRESS_FORMAT)) {
230: String format = ejbJar.properties
231: .getProperty(WS_ADDRESS_FORMAT);
232: logger.info("Using " + WS_ADDRESS_FORMAT + " '"
233: + format + "'");
234: deploymentIdTemplate = new StringTemplate(format);
235: }
236: contextData.put("ejbJarId", ejbJar.moduleId);
237:
238: for (EnterpriseBeanInfo bean : ejbJar.enterpriseBeans) {
239: if (bean instanceof StatelessBeanInfo) {
240: StatelessBeanInfo statelessBeanInfo = (StatelessBeanInfo) bean;
241:
242: DeploymentInfo deploymentInfo = containerSystem
243: .getDeploymentInfo(statelessBeanInfo.ejbDeploymentId);
244: if (deploymentInfo == null)
245: continue;
246:
247: PortInfo portInfo = ports
248: .get(statelessBeanInfo.ejbName);
249: if (portInfo == null)
250: continue;
251:
252: try {
253: PortData port = WsBuilder.toPortData(
254: portInfo, deploymentInfo
255: .getInjections(),
256: moduleBaseUrl, deploymentInfo
257: .getClassLoader());
258:
259: HttpListener container = createEjbWsContainer(
260: moduleBaseUrl, port, deploymentInfo);
261:
262: // generate a location if one was not assigned
263: String location = port.getLocation();
264: if (location == null) {
265: location = autoAssignWsLocation(bean,
266: port, contextData,
267: deploymentIdTemplate);
268: }
269: if (!location.startsWith("/"))
270: location = "/" + location;
271: ejbLocations.put(
272: statelessBeanInfo.ejbDeploymentId,
273: location);
274:
275: ClassLoader classLoader = deploymentInfo
276: .getClassLoader();
277: if (wsRegistry != null) {
278: // add servlet to web container
279: List<String> addresses = wsRegistry
280: .addWsContainer(location,
281: container, virtualHost,
282: realmName,
283: transportGuarantee,
284: authMethod, classLoader);
285:
286: // one of the registered addresses to be the connonical address
287: String address = selectSingleAddress(addresses);
288:
289: if (address != null) {
290: // register wsdl location
291: portAddressRegistry.addPort(
292: portInfo.serviceId,
293: portInfo.wsdlService,
294: portInfo.portId,
295: portInfo.wsdlPort,
296: portInfo.seiInterfaceName,
297: address);
298: logger.info("Webservice(wsdl="
299: + address + ", qname="
300: + port.getWsdlService()
301: + ") --> Ejb(id="
302: + portInfo.portId + ")");
303: ejbAddresses.put(
304: bean.ejbDeploymentId,
305: address);
306: }
307: }
308: } catch (Throwable e) {
309: logger.error(
310: "Error deploying CXF webservice for ejb "
311: + deploymentInfo
312: .getDeploymentID(),
313: e);
314: }
315: }
316: }
317: }
318: for (WebAppInfo webApp : appInfo.webApps) {
319: afterApplicationCreated(webApp);
320: }
321: }
322: }
323:
324: public void afterApplicationCreated(WebAppInfo webApp) {
325: WebDeploymentInfo deploymentInfo = containerSystem
326: .getWebDeploymentInfo(webApp.moduleId);
327: if (deploymentInfo == null)
328: return;
329:
330: // if already deployed skip this webapp
331: if (!deployedWebApps.add(webApp))
332: return;
333:
334: Map<String, PortInfo> ports = new TreeMap<String, PortInfo>();
335: for (PortInfo port : webApp.portInfos) {
336: ports.put(port.serviceLink, port);
337: }
338:
339: URL moduleBaseUrl = null;
340: try {
341: moduleBaseUrl = new File(webApp.codebase).toURL();
342: } catch (MalformedURLException e) {
343: logger.error("Invalid ejb jar location " + webApp.codebase,
344: e);
345: }
346:
347: for (ServletInfo servlet : webApp.servlets) {
348: PortInfo portInfo = ports.get(servlet.servletName);
349: if (portInfo == null)
350: continue;
351:
352: try {
353: ClassLoader classLoader = deploymentInfo
354: .getClassLoader();
355: Collection<Injection> injections = deploymentInfo
356: .getInjections();
357: Context context = deploymentInfo.getJndiEnc();
358: Class target = classLoader
359: .loadClass(servlet.servletClass);
360:
361: PortData port = WsBuilder.toPortData(portInfo,
362: injections, moduleBaseUrl, classLoader);
363:
364: HttpListener container = createPojoWsContainer(
365: moduleBaseUrl, port, portInfo.serviceLink,
366: target, context, webApp.contextRoot);
367:
368: if (wsRegistry != null) {
369: // give servlet a reference to the webservice container
370: List<String> addresses = wsRegistry.setWsContainer(
371: virtualHost, webApp.contextRoot,
372: servlet.servletName, container);
373:
374: // one of the registered addresses to be the connonical address
375: String address = selectSingleAddress(addresses);
376:
377: // add address to global registry
378: portAddressRegistry.addPort(portInfo.serviceId,
379: portInfo.wsdlService, portInfo.portId,
380: portInfo.wsdlPort,
381: portInfo.seiInterfaceName, address);
382: logger.info("Webservice(wsdl=" + address
383: + ", qname=" + port.getWsdlService()
384: + ") --> Pojo(id=" + portInfo.portId + ")");
385: servletAddresses.put(webApp.moduleId + "."
386: + servlet.servletName, address);
387: }
388: } catch (Throwable e) {
389: logger.error(
390: "Error deploying CXF webservice for servlet "
391: + portInfo.serviceLink, e);
392: }
393: }
394: }
395:
396: public void beforeApplicationDestroyed(AppInfo appInfo) {
397: if (deployedApplications.remove(appInfo)) {
398: for (EjbJarInfo ejbJar : appInfo.ejbJars) {
399: Map<String, PortInfo> ports = new TreeMap<String, PortInfo>();
400: for (PortInfo port : ejbJar.portInfos) {
401: ports.put(port.serviceLink, port);
402: }
403:
404: for (EnterpriseBeanInfo enterpriseBean : ejbJar.enterpriseBeans) {
405: if (enterpriseBean instanceof StatelessBeanInfo) {
406: StatelessBeanInfo statelessBeanInfo = (StatelessBeanInfo) enterpriseBean;
407:
408: PortInfo portInfo = ports
409: .get(statelessBeanInfo.ejbName);
410: if (portInfo == null)
411: continue;
412:
413: // remove wsdl addresses from global registry
414: String address = ejbAddresses
415: .remove(enterpriseBean.ejbDeploymentId);
416: if (address != null) {
417: portAddressRegistry.removePort(
418: portInfo.serviceId,
419: portInfo.wsdlService,
420: portInfo.portId);
421: }
422:
423: // remove container from web server
424: String location = ejbLocations
425: .get(statelessBeanInfo.ejbDeploymentId);
426: if (this .wsRegistry != null && location != null) {
427: this .wsRegistry.removeWsContainer(location);
428: }
429:
430: // destroy webservice container
431: destroyEjbWsContainer(statelessBeanInfo.ejbDeploymentId);
432: }
433: }
434: }
435: for (WebAppInfo webApp : appInfo.webApps) {
436: deployedWebApps.remove(webApp);
437:
438: Map<String, PortInfo> ports = new TreeMap<String, PortInfo>();
439: for (PortInfo port : webApp.portInfos) {
440: ports.put(port.serviceLink, port);
441: }
442:
443: for (ServletInfo servlet : webApp.servlets) {
444: PortInfo portInfo = ports.get(servlet.servletClass);
445: if (portInfo == null)
446: continue;
447:
448: // remove wsdl addresses from global registry
449: String address = servletAddresses
450: .remove(webApp.moduleId + "."
451: + servlet.servletName);
452: if (address != null) {
453: portAddressRegistry.removePort(
454: portInfo.serviceId,
455: portInfo.wsdlService, portInfo.portId);
456: }
457:
458: // clear servlet's reference to the webservice container
459: if (this .wsRegistry != null) {
460: this .wsRegistry
461: .clearWsContainer(virtualHost,
462: webApp.contextRoot,
463: servlet.servletName);
464: }
465:
466: // destroy webservice container
467: destroyPojoWsContainer(portInfo.serviceLink);
468: }
469: }
470: }
471: }
472:
473: private String selectSingleAddress(List<String> addresses) {
474: if (addresses == null || addresses.isEmpty())
475: return null;
476:
477: // return the first http address
478: for (String address : addresses) {
479: if (address.startsWith("http:")) {
480: return address;
481: }
482: }
483: // return the first https address
484: for (String address : addresses) {
485: if (address.startsWith("https:")) {
486: return address;
487: }
488: }
489: // just return the first address
490: String address = addresses.iterator().next();
491: return address;
492: }
493:
494: private String autoAssignWsLocation(EnterpriseBeanInfo bean,
495: PortData port, Map<String, String> contextData,
496: StringTemplate template) {
497: contextData.put("ejbDeploymentId", bean.ejbDeploymentId);
498: contextData.put("ejbType", getEjbType(bean.type));
499: contextData.put("ejbClass", bean.ejbClass);
500: contextData.put("ejbClass.simpleName", bean.ejbClass
501: .substring(bean.ejbClass.lastIndexOf('.') + 1));
502: contextData.put("ejbName", bean.ejbName);
503: contextData.put("portComponentName", port.getPortName()
504: .getLocalPart());
505: contextData.put("wsdlPort", port.getWsdlPort().getLocalPart());
506: contextData.put("wsdlService", port.getWsdlService()
507: .getLocalPart());
508: return template.apply(contextData);
509: }
510:
511: public static String getEjbType(int type) {
512: if (type == EnterpriseBeanInfo.STATEFUL) {
513: return "StatefulBean";
514: } else if (type == EnterpriseBeanInfo.STATELESS) {
515: return "StatelessBean";
516: } else if (type == EnterpriseBeanInfo.MESSAGE) {
517: return "MessageDrivenBean";
518: } else if (type == EnterpriseBeanInfo.ENTITY) {
519: return "StatefulBean";
520: } else {
521: return "UnknownBean";
522: }
523: }
524:
525: public void service(InputStream in, OutputStream out)
526: throws ServiceException, IOException {
527: throw new UnsupportedOperationException(
528: "CxfService can not be invoked directly");
529: }
530:
531: public void service(Socket socket) throws ServiceException,
532: IOException {
533: throw new UnsupportedOperationException(
534: "CxfService can not be invoked directly");
535: }
536: }
|