001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.remoting.jaxrpc.support;
018:
019: import java.util.Enumeration;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.Properties;
024:
025: import javax.xml.namespace.QName;
026: import javax.xml.rpc.Service;
027: import javax.xml.rpc.encoding.TypeMapping;
028: import javax.xml.rpc.encoding.TypeMappingRegistry;
029:
030: import org.apache.axis.encoding.ser.BeanDeserializerFactory;
031: import org.apache.axis.encoding.ser.BeanSerializerFactory;
032:
033: import org.springframework.beans.factory.BeanClassLoaderAware;
034: import org.springframework.remoting.jaxrpc.JaxRpcServicePostProcessor;
035: import org.springframework.util.ClassUtils;
036:
037: /**
038: * Axis-specific {@link JaxRpcServicePostProcessor} that registers bean
039: * mappings for domain objects that follow the JavaBean pattern.
040: *
041: * <p>The same mappings are usually also registered at the server in
042: * Axis' "server-config.wsdd" file.
043: *
044: * <p>To be registered as a service post-processor on a
045: * {@link org.springframework.remoting.jaxrpc.LocalJaxRpcServiceFactoryBean} or
046: * {@link org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean},
047: * carrying appropriate configuration.
048: *
049: * <p>Note: Without such explicit bean mappings, a complex type like a
050: * domain object cannot be transferred via SOAP.
051: *
052: * @author Juergen Hoeller
053: * @since 2.0
054: * @see org.apache.axis.encoding.ser.BeanSerializerFactory
055: * @see org.apache.axis.encoding.ser.BeanDeserializerFactory
056: * @see org.springframework.remoting.jaxrpc.LocalJaxRpcServiceFactoryBean#setServicePostProcessors
057: * @see org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean#setServicePostProcessors
058: */
059: public class AxisBeanMappingServicePostProcessor implements
060: JaxRpcServicePostProcessor, BeanClassLoaderAware {
061:
062: private String encodingStyleUri;
063:
064: private String typeNamespaceUri;
065:
066: private Map beanMappings;
067:
068: private ClassLoader beanClassLoader = ClassUtils
069: .getDefaultClassLoader();
070:
071: /**
072: * Set the encoding style URI to use for the type mapping.
073: * <p>A typical value is "http://schemas.xmlsoap.org/soap/encoding/",
074: * as suggested by the JAX-RPC javadoc. However, note that the default
075: * behavior of this post-processor is to register the type mapping
076: * as JAX-RPC default if no explicit encoding style URI is given.
077: * @see javax.xml.rpc.encoding.TypeMappingRegistry#register
078: * @see javax.xml.rpc.encoding.TypeMappingRegistry#registerDefault
079: */
080: public void setEncodingStyleUri(String encodingStyleUri) {
081: this .encodingStyleUri = encodingStyleUri;
082: }
083:
084: /**
085: * Set the application-specific namespace to use for XML types,
086: * for example "urn:JPetStore".
087: * @see javax.xml.rpc.encoding.TypeMapping#register
088: */
089: public void setTypeNamespaceUri(String typeNamespaceUri) {
090: this .typeNamespaceUri = typeNamespaceUri;
091: }
092:
093: /**
094: * Specify the bean mappings to register as String-String pairs,
095: * with the Java type name as key and the WSDL type name as value.
096: */
097: public void setBeanMappings(Properties beanMappingProps) {
098: if (beanMappingProps != null) {
099: this .beanMappings = new HashMap(beanMappingProps.size());
100: Enumeration propertyNames = beanMappingProps
101: .propertyNames();
102: while (propertyNames.hasMoreElements()) {
103: String javaTypeName = (String) propertyNames
104: .nextElement();
105: String wsdlTypeName = beanMappingProps
106: .getProperty(javaTypeName);
107: this .beanMappings.put(javaTypeName, wsdlTypeName);
108: }
109: } else {
110: this .beanMappings = null;
111: }
112: }
113:
114: /**
115: * Specify the bean mappings to register as Java types,
116: * with the WSDL type names inferred from the Java type names
117: * (using the short, that is, non-fully-qualified class name).
118: */
119: public void setBeanClasses(Class[] beanClasses) {
120: if (beanClasses != null) {
121: this .beanMappings = new HashMap(beanClasses.length);
122: for (int i = 0; i < beanClasses.length; i++) {
123: Class beanClass = beanClasses[i];
124: String wsdlTypeName = ClassUtils
125: .getShortName(beanClass);
126: this .beanMappings.put(beanClass, wsdlTypeName);
127: }
128: } else {
129: this .beanMappings = null;
130: }
131: }
132:
133: public void setBeanClassLoader(ClassLoader beanClassLoader) {
134: this .beanClassLoader = beanClassLoader;
135: }
136:
137: /**
138: * Register the specified bean mappings on the given Service's
139: * {@link TypeMappingRegistry}.
140: * @see javax.xml.rpc.Service#getTypeMappingRegistry()
141: * @see #setBeanMappings
142: * @see #registerBeanMappings(javax.xml.rpc.encoding.TypeMapping)
143: */
144: public void postProcessJaxRpcService(Service service) {
145: TypeMappingRegistry registry = service.getTypeMappingRegistry();
146: TypeMapping mapping = registry.createTypeMapping();
147:
148: registerBeanMappings(mapping);
149:
150: if (this .encodingStyleUri != null) {
151: registry.register(this .encodingStyleUri, mapping);
152: } else {
153: registry.registerDefault(mapping);
154: }
155: }
156:
157: /**
158: * Perform the actual bean mapping registration.
159: * @param mapping the JAX-RPC {@link TypeMapping} to operate on
160: * @see #setBeanMappings
161: * @see #registerBeanMapping(javax.xml.rpc.encoding.TypeMapping, Class, String)
162: */
163: protected void registerBeanMappings(TypeMapping mapping) {
164: if (this .beanMappings != null) {
165: for (Iterator it = this .beanMappings.entrySet().iterator(); it
166: .hasNext();) {
167: Map.Entry entry = (Map.Entry) it.next();
168: Object key = entry.getKey();
169: Class javaType = null;
170: if (key instanceof Class) {
171: javaType = (Class) key;
172: } else {
173: javaType = ClassUtils.resolveClassName(
174: (String) key, this .beanClassLoader);
175: }
176: String wsdlTypeName = (String) entry.getValue();
177: registerBeanMapping(mapping, javaType, wsdlTypeName);
178: }
179: }
180: }
181:
182: /**
183: * Register a bean mapping for the given Java type and WSDL type name.
184: * @param mapping the JAX-RPC {@link TypeMapping} to operate on
185: * @param javaType the Java type
186: * @param wsdlTypeName the WSDL type name (as a {@link String})
187: */
188: protected void registerBeanMapping(TypeMapping mapping,
189: Class javaType, String wsdlTypeName) {
190: registerBeanMapping(mapping, javaType,
191: getTypeQName(wsdlTypeName));
192: }
193:
194: /**
195: * Register a bean mapping for the given Java type and WSDL type.
196: * @param mapping the JAX-RPC {@link TypeMapping} to operate on
197: * @param javaType the Java type
198: * @param wsdlType the WSDL type (as XML {@link QName})
199: */
200: protected void registerBeanMapping(TypeMapping mapping,
201: Class javaType, QName wsdlType) {
202: mapping.register(javaType, wsdlType, new BeanSerializerFactory(
203: javaType, wsdlType), new BeanDeserializerFactory(
204: javaType, wsdlType));
205: }
206:
207: /**
208: * Return a {@link QName} for the given name, relative to the
209: * {@link #setTypeNamespaceUri namespace URI} of this post-processor, if given.
210: */
211: protected final QName getTypeQName(String name) {
212: return (this .typeNamespaceUri != null ? new QName(
213: this .typeNamespaceUri, name) : new QName(name));
214: }
215:
216: }
|