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