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.j2ee.deployment.annotation;
019
020 import java.lang.reflect.Field;
021 import java.lang.reflect.Method;
022 import java.net.MalformedURLException;
023 import java.net.URL;
024 import java.util.List;
025
026 import javax.jws.HandlerChain;
027 import javax.xml.ws.WebServiceRef;
028
029 import org.apache.commons.logging.Log;
030 import org.apache.commons.logging.LogFactory;
031 import org.apache.geronimo.common.DeploymentException;
032 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
033 import org.apache.geronimo.xbeans.javaee.HandlerChainType;
034 import org.apache.geronimo.xbeans.javaee.HandlerChainsDocument;
035 import org.apache.geronimo.xbeans.javaee.HandlerChainsType;
036 import org.apache.geronimo.xbeans.javaee.PortComponentHandlerType;
037 import org.apache.geronimo.xbeans.javaee.ServiceRefHandlerChainType;
038 import org.apache.geronimo.xbeans.javaee.ServiceRefHandlerChainsType;
039 import org.apache.geronimo.xbeans.javaee.ServiceRefHandlerType;
040 import org.apache.geronimo.xbeans.javaee.ServiceRefType;
041 import org.apache.xbean.finder.ClassFinder;
042 import org.apache.xmlbeans.XmlException;
043 import org.apache.xmlbeans.XmlObject;
044
045
046 /**
047 * Static helper class used to encapsulate all the functions related to the translation of
048 * <strong>@HandlerChain</strong> annotations to deployment descriptor tags. The
049 * HandlerChainAnnotationHelper class can be used as part of the deployment of a module into the
050 * Geronimo server. It performs the following major functions:
051 *
052 * <ol>
053 * <li>Translates annotations into corresponding deployment descriptor elements (so that the
054 * actual deployment descriptor in the module can be updated or even created if necessary)
055 * </ol>
056 *
057 * <p><strong>Note(s):</strong>
058 * <ul>
059 * <li>The user is responsible for invoking change to metadata-complete
060 * <li>This helper class will validate any changes it makes to the deployment descriptor. An
061 * exception will be thrown if it fails to parse
062 * </ul>
063 *
064 * @version $Rev $Date
065 * @since 03-2007
066 */
067 public final class HandlerChainAnnotationHelper extends AnnotationHelper {
068
069 // Private instance variables
070 private static final Log log = LogFactory.getLog(HandlerChainAnnotationHelper.class);
071
072 // Private constructor to prevent instantiation
073 private HandlerChainAnnotationHelper() {
074 }
075
076
077 /**
078 * Updates the deployment descriptor with handler chain info from HandlerChain annotations
079 *
080 * @param annotatedApp Wrapper around spec dd
081 * @param classFinder ClassFinder containing classes of interest
082 * @throws DeploymentException if parsing or validation error
083 */
084 public static void processAnnotations(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException {
085 if ( annotatedApp != null && classFinder.isAnnotationPresent(HandlerChain.class)) {
086 processHandlerChain(annotatedApp, classFinder);
087 }
088 }
089
090
091 /**
092 * Updates the deployment descriptor with handler chain info from HandlerChain annotations
093 *
094 * @param annotatedApp Wrapper around spec dd
095 * @param classFinder ClassFinder containing classes of interest
096 * @throws DeploymentException if parsing or validation error
097 */
098 private static void processHandlerChain(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException {
099 log.debug("processHandlerChain(): Entry: AnnotatedApp: " + annotatedApp.toString());
100
101 List<Method> methodswithHandlerChain = classFinder.findAnnotatedMethods(HandlerChain.class);
102 List<Field> fieldswithHandlerChain = classFinder.findAnnotatedFields(HandlerChain.class);
103
104
105 // Method-level annotation
106 for ( Method method : methodswithHandlerChain ) {
107 HandlerChain handlerChain = method.getAnnotation(HandlerChain.class);
108 if ( handlerChain != null ) {
109 addHandlerChain(annotatedApp, handlerChain, null, method, null);
110 }
111 }
112
113 // Field-level annotation
114 for ( Field field : fieldswithHandlerChain ) {
115 HandlerChain handlerChain = field.getAnnotation(HandlerChain.class);
116 if ( handlerChain != null ) {
117 addHandlerChain(annotatedApp, handlerChain, null, null, field);
118 }
119 }
120
121 // Validate deployment descriptor to ensure it's still okay
122 validateDD(annotatedApp);
123
124 log.debug("processHandlerChain(): Exit: AnnotatedApp: " + annotatedApp.toString());
125 }
126
127
128 /**
129 * Add to the deployment descriptor for a single @HandlerChain annotation. XMLBeans are used to read and
130 * manipulate the deployment descriptor as necessary. The HandlerChain annotation(s) will be
131 * converted to one of the following deployment descriptors:
132 *
133 * <ol>
134 * <li><handler-chain> -- Associates the Web Service with an externally defined handler
135 * chain
136 * </ol>
137 *
138 * <p><strong>Note(s):</strong>
139 * <ul>
140 * <li>If a field/method has the @HandlerChain annotation then The corresponding
141 * <service-ref> is obtained via the @WebServiceRef annotation
142 * </ul>
143 *
144 * @param annotatedApp wrapper around spec dd
145 * @param annotation @HandlerChain annotation
146 * @param cls Class name with the @HandlerChain annotation
147 * @param method Method name with the @HandlerChain annotation
148 * @param field Field name with the @HandlerChain annotation
149 */
150 private static void addHandlerChain(AnnotatedApp annotatedApp, final HandlerChain annotation, Class cls, Method method, Field field) {
151 log.debug("addHandlerChain( [annotatedApp] " + annotatedApp.toString() + "," + '\n' +
152 "[annotation] " + annotation.toString() + "," + '\n' +
153 "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' +
154 "[method] " + (method != null ? method.getName() : null) + "," + '\n' +
155 "[field] " + (field != null ? field.getName() : null) + " ): Entry");
156
157 //------------------------------------------------------------------------------------------
158 // HandlerChain members:
159 // -- name: Deprecated -- must be empty string
160 // -- file: Location of handler chain file in either absolute URL format or a relative path
161 // from the class file. Cannot be emptry string.
162 //------------------------------------------------------------------------------------------
163 String handlerChainFile = annotation.file();
164 log.debug("addHandlerChain(): handlerChainFile: " + handlerChainFile);
165
166 // Determine ServiceRef name
167 String serviceRefName;
168 WebServiceRef webServiceRef = null;
169 if ( method != null ) {
170 webServiceRef = method.getAnnotation(WebServiceRef.class);
171 }
172 else if ( field != null ) {
173 webServiceRef = field.getAnnotation(WebServiceRef.class);
174 }
175 if ( webServiceRef != null ) {
176 serviceRefName = webServiceRef.name();
177 }
178 else {
179 //TODO is this guaranteed to be ""? If so, simplify the code here
180 serviceRefName = annotation.name();
181 }
182 if ( serviceRefName.equals("") ) {
183 serviceRefName = getInjectionJavaType(method, field);
184 }
185 log.debug("addHandlerChain().serviceRefName : " + serviceRefName);
186
187 if (!serviceRefName.equals("") && !handlerChainFile.equals("")) {
188 try {
189 // Locate the handler chain XML file
190 URL url = null;
191 try {
192 // Assume URL format first
193 url = new URL(handlerChainFile);
194 }
195 catch (MalformedURLException mfe) {
196 log.debug("addHandlerChain().MalformedURLException" );
197
198 // Not URL format -- see if it's relative to the annotated class
199 if (cls != null) {
200 url = getURL(cls.getClass(), handlerChainFile);
201 }
202 else if (method != null) {
203 url = getURL(method.getDeclaringClass(), handlerChainFile);
204 }
205 else if (field != null) {
206 url = getURL(field.getDeclaringClass(), handlerChainFile);
207 }
208 }
209
210 if (url != null) {
211 // Bind the XML handler chain file to an XMLBeans document
212 XmlObject xml = XmlBeansUtil.parse(url, null);
213 HandlerChainsDocument hcd = (HandlerChainsDocument) XmlBeansUtil.typedCopy(xml, HandlerChainsDocument.type);
214 HandlerChainsType handlerChains = hcd.getHandlerChains();
215
216 // Find the <service-ref> entry this handler chain belongs to and insert it
217 ServiceRefType[] serviceRefs = annotatedApp.getServiceRefArray();
218 boolean exists = false;
219 for ( ServiceRefType serviceRef : serviceRefs ) {
220 if ( serviceRef.getServiceRefName().getStringValue().trim().equals(serviceRefName) ) {
221 ServiceRefHandlerChainsType serviceRefHandlerChains = serviceRef.addNewHandlerChains();
222 for (HandlerChainType handlerChain : handlerChains.getHandlerChainArray()) {
223 ServiceRefHandlerChainType serviceRefHandlerChain = serviceRefHandlerChains.addNewHandlerChain();
224 if (handlerChain.getPortNamePattern() != null) {
225 serviceRefHandlerChain.setPortNamePattern(handlerChain.getPortNamePattern());
226 }
227 if (handlerChain.getServiceNamePattern() != null) {
228 serviceRefHandlerChain.setServiceNamePattern(handlerChain.getServiceNamePattern());
229 }
230 if (handlerChain.getProtocolBindings() != null) {
231 serviceRefHandlerChain.setProtocolBindings(handlerChain.getProtocolBindings());
232 }
233 for ( PortComponentHandlerType handler : handlerChain.getHandlerArray()) {
234 ServiceRefHandlerType serviceRefHandler = serviceRefHandlerChain.addNewHandler();
235 serviceRefHandler.setHandlerName(handler.getHandlerName());
236 serviceRefHandler.setHandlerClass(handler.getHandlerClass());
237 if (handler.getDescriptionArray().length>0) {
238 serviceRefHandler.setDescriptionArray(handler.getDescriptionArray());
239 }
240 if (handler.getInitParamArray().length>0) {
241 serviceRefHandler.setInitParamArray(handler.getInitParamArray());
242 }
243 if (handler.getSoapHeaderArray().length>0) {
244 serviceRefHandler.setSoapHeaderArray(handler.getSoapHeaderArray());
245 }
246 if (handler.getSoapRoleArray().length>0) {
247 serviceRefHandler.setSoapRoleArray(handler.getSoapRoleArray());
248 }
249 }
250 }
251 exists = true;
252 break;
253 }
254 }
255 if (exists) {
256 log.debug("HandlerChainAnnotationHelper: <service-ref> entry found: " + serviceRefName);
257 }
258 else {
259 log.debug("HandlerChainAnnotationHelper: <service-ref> entry NOT found: " + serviceRefName);
260 }
261 }
262 else {
263 log.debug("HandlerChainAnnotationHelper: Handler chain file NOT found: " + handlerChainFile );
264 }
265 }
266 catch ( Exception anyException ) {
267 log.debug("HandlerChainAnnotationHelper: Exception caught while processing <handler-chain>");
268 }
269 }
270 log.debug("addHandlerChain(): Exit");
271 }
272
273
274 private static URL getURL(Class clazz, String file) {
275 log.debug("getURL( " + clazz.getName() + ", " + file + " ): Entry");
276
277 URL url = clazz.getResource(file);
278 if (url == null) {
279 url = Thread.currentThread().getContextClassLoader().getResource(file);
280 }
281 if (url == null) {
282 String loc= clazz.getPackage().getName().replace('.', '/') + "/" + file;
283 url = Thread.currentThread().getContextClassLoader().getResource(loc);
284 }
285
286 log.debug("getURL(): Exit: url: " + (url != null ? url.toString() : null) );
287 return url;
288 }
289
290 }