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    }