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                        // Find the <service-ref> entry this handler chain belongs to and insert it
212                        ServiceRefType[] serviceRefs = annotatedApp.getServiceRefArray();
213                        boolean exists = false;
214                        for ( ServiceRefType serviceRef : serviceRefs ) {
215                            if ( serviceRef.getServiceRefName().getStringValue().trim().equals(serviceRefName) && !serviceRef.isSetHandlerChains()) {
216                                insertHandlers(serviceRef, url);
217                                exists = true;
218                                break;
219                            }
220                        }
221                        if (exists) {
222                            log.debug("HandlerChainAnnotationHelper: <service-ref> entry found: " + serviceRefName);
223                        }
224                        else {
225                            log.debug("HandlerChainAnnotationHelper: <service-ref> entry NOT found: " + serviceRefName);
226                        }
227                    }
228                    else {
229                        log.debug("HandlerChainAnnotationHelper: Handler chain file NOT found: " + handlerChainFile );
230                    }
231                }
232                catch ( Exception anyException ) {
233                    log.debug("HandlerChainAnnotationHelper: Exception caught while processing <handler-chain>");
234                }
235            }
236            log.debug("addHandlerChain(): Exit");
237        }
238    
239    
240        private static URL getURL(Class clazz, String file) {
241            log.debug("getURL( " + clazz.getName() + ", " + file + " ): Entry");
242    
243            URL url = clazz.getResource(file);
244            if (url == null) {
245                url = Thread.currentThread().getContextClassLoader().getResource(file);
246            }
247            if (url == null) {
248                String loc= clazz.getPackage().getName().replace('.', '/') + "/" + file;
249                url = Thread.currentThread().getContextClassLoader().getResource(loc);
250            }
251    
252            log.debug("getURL(): Exit: url: " + (url != null ? url.toString() : null) );
253            return url;
254        }
255        
256        public static void insertHandlers(ServiceRefType serviceRef, HandlerChain annotation, Class clazz) {
257            String handlerChainFile = annotation.file();
258            log.debug("handlerChainFile: " + handlerChainFile);
259            if (handlerChainFile == null || handlerChainFile.trim().length() == 0) {
260                return;
261            }
262            URL url = null;
263            try {
264                // Assume URL format first
265                url = new URL(handlerChainFile);
266            } catch (MalformedURLException mfe) {
267                // Not URL format -- see if it's relative to the annotated class
268                url = getURL(clazz, handlerChainFile);
269            }
270            if (url != null) {
271                try {
272                    insertHandlers(serviceRef, url);
273                } catch (Exception e) {
274                    log.debug("Error while processing <handler-chain>", e);
275                }
276            }
277        }
278        
279        public static void insertHandlers(ServiceRefType serviceRef, URL url) throws Exception {
280            // Bind the XML handler chain file to an XMLBeans document
281            XmlObject xml = XmlBeansUtil.parse(url, null);
282            HandlerChainsDocument hcd = (HandlerChainsDocument) XmlBeansUtil.typedCopy(xml, HandlerChainsDocument.type);
283            HandlerChainsType handlerChains = hcd.getHandlerChains();
284            
285            ServiceRefHandlerChainsType serviceRefHandlerChains = serviceRef.addNewHandlerChains();
286            for (HandlerChainType handlerChain : handlerChains.getHandlerChainArray()) {
287                ServiceRefHandlerChainType serviceRefHandlerChain = serviceRefHandlerChains.addNewHandlerChain();
288                if (handlerChain.getPortNamePattern() != null) {
289                    serviceRefHandlerChain.setPortNamePattern(handlerChain.getPortNamePattern());
290                }
291                if (handlerChain.getServiceNamePattern() != null) {
292                    serviceRefHandlerChain.setServiceNamePattern(handlerChain.getServiceNamePattern());
293                }
294                if (handlerChain.getProtocolBindings() != null) {
295                    serviceRefHandlerChain.setProtocolBindings(handlerChain.getProtocolBindings());
296                }
297                for ( PortComponentHandlerType handler : handlerChain.getHandlerArray()) {
298                    ServiceRefHandlerType serviceRefHandler = serviceRefHandlerChain.addNewHandler();
299                    serviceRefHandler.setHandlerName(handler.getHandlerName());
300                    serviceRefHandler.setHandlerClass(handler.getHandlerClass());
301                    if (handler.getDescriptionArray().length>0) {
302                        serviceRefHandler.setDescriptionArray(handler.getDescriptionArray());
303                    }
304                    if (handler.getInitParamArray().length>0) {
305                        serviceRefHandler.setInitParamArray(handler.getInitParamArray());
306                    }
307                    if (handler.getSoapHeaderArray().length>0) {
308                        serviceRefHandler.setSoapHeaderArray(handler.getSoapHeaderArray());
309                    }
310                    if (handler.getSoapRoleArray().length>0) {
311                        serviceRefHandler.setSoapRoleArray(handler.getSoapRoleArray());
312                    }
313                }
314            }
315        }
316    
317    }