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.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.List;
025    
026    import javax.jws.HandlerChain;
027    import javax.xml.ws.WebServiceClient;
028    import javax.xml.ws.WebServiceRef;
029    import javax.xml.ws.WebServiceRefs;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.apache.geronimo.common.DeploymentException;
034    import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
035    import org.apache.geronimo.xbeans.javaee.JndiNameType;
036    import org.apache.geronimo.xbeans.javaee.ServiceRefType;
037    import org.apache.geronimo.xbeans.javaee.XsdAnyURIType;
038    import org.apache.geronimo.xbeans.javaee.XsdStringType;
039    import org.apache.xbean.finder.ClassFinder;
040    
041    
042    /**
043     * Static helper class used to encapsulate all the functions related to the translation of
044     * <strong>@WebServieRef</strong> and <strong>@WebServieRef</strong> annotations to deployment
045     * descriptor tags. The WebServiceRefAnnotationHelper class can be used as part of the deployment of
046     * a module into the Geronimo server. It performs the following major functions:
047     * <p/>
048     * <ol>
049     * <li>Translates annotations into corresponding deployment descriptor elements (so that the
050     * actual deployment descriptor in the module can be updated or even created if necessary)
051     * </ol>
052     * <p/>
053     * <p><strong>Note(s):</strong>
054     * <ul>
055     * <li>The user is responsible for invoking change to metadata-complete
056     * <li>This helper class will validate any changes it makes to the deployment descriptor. An
057     * exception will be thrown if it fails to parse
058     * </ul>
059     * <p/>
060     * <p><strong>Remaining ToDo(s):</strong>
061     * <ul>
062     * <li>None
063     * </ul>
064     *
065     * @version $Rev $Date
066     * @since 03-2007
067     */
068    public final class WebServiceRefAnnotationHelper extends AnnotationHelper {
069    
070        // Private instance variables
071        private static final Log log = LogFactory.getLog(WebServiceRefAnnotationHelper.class);
072    
073        // Private constructor to prevent instantiation
074        private WebServiceRefAnnotationHelper() {
075        }
076    
077        /**
078         * Update the deployment descriptor from the WebServiceRef and WebServiceRefs annotations
079         *
080         * @param annotatedApp Access to the spec dd
081         * @param classFinder Access to the 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) {
086                if (classFinder.isAnnotationPresent(WebServiceRefs.class)) {
087                    processWebServiceRefs(annotatedApp, classFinder);
088                }
089                if (classFinder.isAnnotationPresent(WebServiceRef.class)) {
090                    processWebServiceRef(annotatedApp, classFinder);
091                }
092            }
093        }
094    
095    
096        /**
097         * Process annotations
098         *
099         * @param annotatedApp Access to the spec dd
100         * @param classFinder Access to the classes of interest
101         * @throws DeploymentException if parsing or validation error
102         */
103        private static void processWebServiceRef(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException {
104            log.debug("processWebServiceRef(): Entry: AnnotatedApp: " + annotatedApp.toString());
105    
106            List<Class> classeswithWebServiceRef = classFinder.findAnnotatedClasses(WebServiceRef.class);
107            List<Method> methodswithWebServiceRef = classFinder.findAnnotatedMethods(WebServiceRef.class);
108            List<Field> fieldswithWebServiceRef = classFinder.findAnnotatedFields(WebServiceRef.class);
109    
110            // Class-level annotation
111            for (Class cls : classeswithWebServiceRef) {
112                WebServiceRef webServiceRef = (WebServiceRef) cls.getAnnotation(WebServiceRef.class);
113                if (webServiceRef != null) {
114                    addWebServiceRef(annotatedApp, webServiceRef, cls, null, null);
115                }
116            }
117    
118            // Method-level annotation
119            for (Method method : methodswithWebServiceRef) {
120                WebServiceRef webServiceRef = method.getAnnotation(WebServiceRef.class);
121                if (webServiceRef != null) {
122                    addWebServiceRef(annotatedApp, webServiceRef, null, method, null);
123                }
124            }
125    
126            // Field-level annotation
127            for (Field field : fieldswithWebServiceRef) {
128                WebServiceRef webServiceRef = field.getAnnotation(WebServiceRef.class);
129                if (webServiceRef != null) {
130                    addWebServiceRef(annotatedApp, webServiceRef, null, null, field);
131                }
132            }
133    
134            // Validate deployment descriptor to ensure it's still okay
135            validateDD(annotatedApp);
136    
137            log.debug("processWebServiceRef(): Exit: AnnotatedApp: " + annotatedApp.toString());
138        }
139    
140    
141        /**
142         * Process multiple annotations
143         *
144         * @param annotatedApp Access to the spec dd
145         * @param classFinder Access to the classes of interest
146         * @throws DeploymentException if parsing or validation error
147         */
148        private static void processWebServiceRefs(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException {
149            log.debug("processWebServiceRefs(): Entry");
150    
151            List<Class> classeswithWebServiceRefs = classFinder.findAnnotatedClasses(WebServiceRefs.class);
152    
153            // Class-level annotation(s)
154            List<WebServiceRef> webServiceRefList = new ArrayList<WebServiceRef>();
155            for (Class cls : classeswithWebServiceRefs) {
156                WebServiceRefs webServiceRefs = (WebServiceRefs) cls.getAnnotation(WebServiceRefs.class);
157                if (webServiceRefs != null) {
158                    webServiceRefList.addAll(Arrays.asList(webServiceRefs.value()));
159                }
160                for (WebServiceRef webServiceRef : webServiceRefList) {
161                    addWebServiceRef(annotatedApp, webServiceRef, cls, null, null);
162                }
163                webServiceRefList.clear();
164            }
165    
166            log.debug("processWebServiceRefs(): Exit");
167        }
168    
169    
170        /**
171         * Add @WebServiceRef and @WebServiceRefs annotations to the deployment descriptor. XMLBeans are used to
172         * read and manipulate the deployment descriptor as necessary. The WebServiceRef annotation(s) will be
173         * converted to one of the following deployment descriptors:
174         * <p/>
175         * <ol>
176         * <li><service-ref> -- Declares a reference to a Web Service
177         * </ol>
178         * <p/>
179         * <p><strong>Note(s):</strong>
180         * <ul>
181         * <li>The deployment descriptor is the authoritative source so this method ensures that
182         * existing elements in it are not overwritten by annoations
183         * </ul>
184         *
185         * @param annotation @WebServiceRef annotation
186         * @param cls        Class name with the @WebServiceRef annoation
187         * @param method     Method name with the @WebServiceRef annoation
188         * @param field      Field name with the @WebServiceRef annoation
189         * @param annotatedApp  Access to the specc dd
190         */
191        private static void addWebServiceRef(AnnotatedApp annotatedApp, WebServiceRef annotation, Class cls, Method method, Field field) {
192            log.debug("addWebServiceRef( [annotatedApp] " + annotatedApp.toString() + "," + '\n' +
193                    "[annotation] " + annotation.toString() + "," + '\n' +
194                    "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' +
195                    "[method] " + (method != null ? method.getName() : null) + "," + '\n' +
196                    "[field] " + (field != null ? field.getName() : null) + " ): Entry");
197    
198            //------------------------------------------------------------------------------------------
199            // WebServiceRef name:
200            // -- When annotation is applied on a class:    Name must be provided (cannot be inferred)
201            // -- When annotation is applied on a method:   Name is JavaBeans property name qualified
202            //                                              by the class (or as provided on the
203            //                                              annotation)
204            // -- When annotation is applied on a field:    Name is the field name qualified by the
205            //                                              class (or as provided on the annotation)
206            //------------------------------------------------------------------------------------------
207            String webServiceRefName = annotation.name();
208            if (webServiceRefName.equals("")) {
209                if (method != null) {
210                    StringBuilder stringBuilder = new StringBuilder(method.getName().substring(3));
211                    stringBuilder.setCharAt(0, Character.toLowerCase(stringBuilder.charAt(0)));
212                    webServiceRefName = method.getDeclaringClass().getName() + "/" + stringBuilder.toString();
213                } else if (field != null) {
214                    webServiceRefName = field.getDeclaringClass().getName() + "/" + field.getName();
215                }
216            }
217            log.debug("addWebServiceRef(): webServiceRefName: " + webServiceRefName);
218    
219            //------------------------------------------------------------------------------------------
220            // WebServiceRef types:
221            //
222            // 1. Generated Service Class (extends javax.xml.ws.Service)
223            // 2. Service Endpoint Interface (SEI)
224            //
225            // -- When annotation is applied on a class:    Type and Value must be provided (cannot be
226            //                                              inferred)
227            // -- When annotation is applied on a method:   Type is the JavaBeans property type (or as
228            //                                              provided on the annotation)
229            // -- When annotation is applied on a field:    Type is the field type (or as provided on
230            //                                              the annotation)
231            //------------------------------------------------------------------------------------------
232            Class webServiceRefType = annotation.type();
233            Class webServiceRefValue = annotation.value();
234            if (webServiceRefType == null || webServiceRefType.equals(Object.class)) {
235                if (method != null) {
236                    webServiceRefType = method.getParameterTypes()[0];
237                } else if (field != null) {
238                    webServiceRefType = field.getType();
239                }
240            }
241            log.debug("addWebServiceRef(): webServiceRefType: " + webServiceRefType);
242            log.debug("addWebServiceRef(): webServiceRefValue: " + webServiceRefValue);
243    
244            //------------------------------------------------------------------------------------------
245            // Method name (for setter-based injection) must follow JavaBeans conventions:
246            // -- Must start with "set"
247            // -- Have one parameter
248            // -- Return void
249            //------------------------------------------------------------------------------------------
250    
251            //------------------------------------------------------------------------------------------
252            // 1. <service-ref>
253            //------------------------------------------------------------------------------------------
254    
255            ServiceRefType serviceRef = null;
256    
257            ServiceRefType[] serviceRefs = annotatedApp.getServiceRefArray();
258            for (ServiceRefType currServiceRef : serviceRefs) {
259                if (currServiceRef.getServiceRefName().getStringValue().trim().equals(webServiceRefName)) {
260                    serviceRef = currServiceRef;
261                    break;
262                }
263            }
264    
265            if (serviceRef == null) {
266                // Doesn't exist in deployment descriptor -- add new
267                serviceRef = annotatedApp.addNewServiceRef();
268    
269                // ------------------------------------------------------------------------------
270                // <service-ref> required elements:
271                // ------------------------------------------------------------------------------
272    
273                // service-ref-name
274                JndiNameType serviceRefName = serviceRef.addNewServiceRefName();
275                serviceRefName.setStringValue(webServiceRefName);
276                serviceRef.setServiceRefName(serviceRefName);
277    
278                // service-ref-interface
279                if (!webServiceRefValue.equals(Object.class)) {
280                    FullyQualifiedClassType qualifiedClass = serviceRef.addNewServiceInterface();
281                    qualifiedClass.setStringValue(webServiceRefValue.getName());
282                    serviceRef.setServiceInterface(qualifiedClass);
283                } else {
284                    FullyQualifiedClassType qualifiedClass = serviceRef.addNewServiceInterface();
285                    qualifiedClass.setStringValue(webServiceRefType.getName());
286                    serviceRef.setServiceInterface(qualifiedClass);
287                }
288            }
289    
290            //------------------------------------------------------------------------------
291            // <service-ref> optional elements:
292            //------------------------------------------------------------------------------
293    
294            // service-ref-type
295            if (!serviceRef.isSetServiceRefType() && !webServiceRefType.equals(Object.class)) {
296                FullyQualifiedClassType qualifiedClass = serviceRef.addNewServiceRefType();
297                qualifiedClass.setStringValue(webServiceRefType.getName());
298                serviceRef.setServiceRefType(qualifiedClass);
299            }
300    
301            // mapped-name
302            if (!serviceRef.isSetMappedName() && annotation.mappedName().trim().length() > 0) {
303                XsdStringType mappedName = serviceRef.addNewMappedName();
304                mappedName.setStringValue(annotation.mappedName().trim());
305                serviceRef.setMappedName(mappedName);
306            }
307    
308            // WSDL document location
309            if (!serviceRef.isSetWsdlFile()) {
310                String wsdlLocation = annotation.wsdlLocation();
311    
312                if (wsdlLocation == null || wsdlLocation.trim().length() == 0) {
313                    WebServiceClient wsClient = null;
314                    if (Object.class.equals(webServiceRefValue)) {
315                        wsClient = (WebServiceClient) webServiceRefType.getAnnotation(WebServiceClient.class);
316                    } else {
317                        wsClient = (WebServiceClient) webServiceRefValue.getAnnotation(WebServiceClient.class);
318                    }
319                    if (wsClient == null) {
320                        wsdlLocation = null;
321                    } else {
322                        wsdlLocation = wsClient.wsdlLocation();
323                    }
324                }
325    
326                if (wsdlLocation != null && wsdlLocation.trim().length() > 0) {
327                    XsdAnyURIType wsdlFile = serviceRef.addNewWsdlFile();
328                    wsdlFile.setStringValue(wsdlLocation);
329                    serviceRef.setWsdlFile(wsdlFile);
330                }
331            }
332    
333            // handler-chains
334            if (!serviceRef.isSetHandlerChains()) {
335                HandlerChain handlerChain = null;
336                Class annotatedClass = null;
337                if (method != null) {
338                    handlerChain = method.getAnnotation(HandlerChain.class);
339                    annotatedClass = method.getDeclaringClass();
340                } else if (field != null) {
341                    handlerChain = field.getAnnotation(HandlerChain.class);
342                    annotatedClass = field.getDeclaringClass();
343                }
344                
345                // if not specified on method or field, try to get it from Service class
346                if (handlerChain == null) {
347                    if (Object.class.equals(webServiceRefValue)) {
348                        handlerChain = (HandlerChain) webServiceRefType.getAnnotation(HandlerChain.class);
349                        annotatedClass = webServiceRefType;
350                    } else {
351                        handlerChain = (HandlerChain) webServiceRefValue.getAnnotation(HandlerChain.class);
352                        annotatedClass = webServiceRefValue;
353                    }
354                }
355                
356                if (handlerChain != null) {
357                    HandlerChainAnnotationHelper.insertHandlers(serviceRef, handlerChain, annotatedClass);
358                }
359            }
360            
361            if (method != null || field != null) {
362                configureInjectionTarget(serviceRef.addNewInjectionTarget(), method, field);
363            }
364    
365        }
366    
367    }