001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.jsonwebservice;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.jsonwebservice.JSONWebService;
020    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManagerUtil;
021    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceMode;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.util.CharPool;
025    import com.liferay.portal.kernel.util.SetUtil;
026    import com.liferay.portal.kernel.util.StreamUtil;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.util.PortalUtil;
031    import com.liferay.portal.util.PropsValues;
032    
033    import java.io.File;
034    import java.io.InputStream;
035    import java.io.UnsupportedEncodingException;
036    
037    import java.lang.reflect.Method;
038    import java.lang.reflect.Modifier;
039    
040    import java.net.URL;
041    import java.net.URLDecoder;
042    
043    import java.util.HashMap;
044    import java.util.Map;
045    import java.util.Set;
046    
047    import jodd.io.findfile.ClassFinder;
048    import jodd.io.findfile.FindFile;
049    import jodd.io.findfile.WildcardFindFile;
050    
051    import jodd.util.ClassLoaderUtil;
052    
053    import org.apache.commons.lang.time.StopWatch;
054    
055    import org.objectweb.asm.ClassReader;
056    
057    /**
058     * @author Igor Spasic
059     */
060    public class JSONWebServiceConfigurator extends ClassFinder {
061    
062            public JSONWebServiceConfigurator(String servletContextPath) {
063                    setIncludedJars(
064                            "*portal-impl.jar", "*portal-service.jar", "*_wl_cls_gen.jar",
065                            "*-portlet-service*.jar");
066    
067                    _servletContextPath = servletContextPath;
068            }
069    
070            public void clean() {
071                    int count =
072                            JSONWebServiceActionsManagerUtil.unregisterJSONWebServiceActions(
073                                    _servletContextPath);
074    
075                    _registeredActionsCount -= count;
076    
077                    if (_log.isDebugEnabled()) {
078                            if (count != 0) {
079                                    _log.debug(
080                                            "Removed " + count +
081                                                    " existing JSON Web Service actions that belonged to " +
082                                                            _servletContextPath);
083                            }
084                    }
085            }
086    
087            public void configure(ClassLoader classLoader)
088                    throws PortalException, SystemException {
089    
090                    File[] classPathFiles = null;
091    
092                    if (classLoader != null) {
093                            URL servicePropertiesURL = classLoader.getResource(
094                                    "service.properties");
095    
096                            String servicePropertiesPath = null;
097    
098                            try {
099                                    servicePropertiesPath = URLDecoder.decode(
100                                            servicePropertiesURL.getPath(), StringPool.UTF8);
101                            }
102                            catch (UnsupportedEncodingException uee) {
103                                    throw new SystemException(uee);
104                            }
105    
106                            File classPathFile = null;
107    
108                            File libDir = null;
109    
110                            int pos = servicePropertiesPath.indexOf("_wl_cls_gen.jar!");
111    
112                            if (pos != -1) {
113                                    String wlClsGenJarPath = servicePropertiesPath.substring(
114                                            0, pos + 15);
115    
116                                    classPathFile = new File(wlClsGenJarPath);
117    
118                                    libDir = new File(classPathFile.getParent());
119                            }
120                            else {
121                                    File servicePropertiesFile = new File(servicePropertiesPath);
122    
123                                    classPathFile = servicePropertiesFile.getParentFile();
124    
125                                    libDir = new File(classPathFile.getParent(), "lib");
126                            }
127    
128                            classPathFiles = new File[2];
129    
130                            classPathFiles[0] = classPathFile;
131    
132                            FindFile findFile = new WildcardFindFile("*-portlet-service*.jar");
133    
134                            findFile.searchPath(libDir);
135    
136                            classPathFiles[1] = findFile.nextFile();
137    
138                            if (classPathFiles[1] == null) {
139                                    File classesDir = new File(libDir.getParent(), "classes");
140    
141                                    classPathFiles[1] = classesDir;
142                            }
143                    }
144                    else {
145                            Thread currentThread = Thread.currentThread();
146    
147                            classLoader = currentThread.getContextClassLoader();
148    
149                            File portalImplJarFile = new File(
150                                    PortalUtil.getPortalLibDir(), "portal-impl.jar");
151                            File portalServiceJarFile = new File(
152                                    PortalUtil.getGlobalLibDir(), "portal-service.jar");
153    
154                            if (portalImplJarFile.exists() && portalServiceJarFile.exists()) {
155                                    classPathFiles = new File[2];
156    
157                                    classPathFiles[0] = portalImplJarFile;
158                                    classPathFiles[1] = portalServiceJarFile;
159                            }
160                            else {
161                                    classPathFiles = ClassLoaderUtil.getDefaultClasspath(
162                                            classLoader);
163                            }
164                    }
165    
166                    _classLoader = classLoader;
167    
168                    _configure(classPathFiles);
169            }
170    
171            @Override
172            protected void onEntry(EntryData entryData) throws Exception {
173                    String className = entryData.getName();
174    
175                    if (className.endsWith("Service") ||
176                            className.endsWith("ServiceImpl")) {
177    
178                            InputStream inputStream = entryData.openInputStream();
179    
180                            if (!isTypeSignatureInUse(
181                                            inputStream, _jsonWebServiceAnnotationBytes)) {
182    
183                                    return;
184                            }
185    
186                            if (!entryData.isArchive()) {
187                                    StreamUtil.cleanUp(inputStream);
188    
189                                    ClassReader classReader = new ClassReader(
190                                            entryData.openInputStream());
191    
192                                    JSONWebServiceClassVisitor jsonWebServiceClassVisitor =
193                                            new JSONWebServiceClassVisitor();
194    
195                                    try {
196                                            classReader.accept(jsonWebServiceClassVisitor, 0);
197                                    }
198                                    catch (Exception e) {
199                                            return;
200                                    }
201    
202                                    if (!className.equals(
203                                                    jsonWebServiceClassVisitor.getClassName())) {
204    
205                                            return;
206                                    }
207                            }
208    
209                            _onJSONWebServiceClass(className);
210                    }
211            }
212    
213            private void _configure(File... classPathFiles) throws PortalException {
214                    StopWatch stopWatch = null;
215    
216                    if (_log.isDebugEnabled()) {
217                            _log.debug("Configure JSON web service actions");
218    
219                            stopWatch = new StopWatch();
220    
221                            stopWatch.start();
222                    }
223    
224                    try {
225                            scanPaths(classPathFiles);
226                    }
227                    catch (Exception e) {
228                            throw new PortalException(e.getMessage(), e);
229                    }
230    
231                    if (_log.isDebugEnabled()) {
232                            _log.debug(
233                                    "Configured " + _registeredActionsCount + " actions in " +
234                                            stopWatch.getTime() + " ms");
235                    }
236            }
237    
238            private boolean _hasAnnotatedServiceImpl(String className) {
239                    StringBundler implClassName = new StringBundler(4);
240    
241                    int pos = className.lastIndexOf(CharPool.PERIOD);
242    
243                    implClassName.append(className.substring(0, pos));
244                    implClassName.append(".impl");
245                    implClassName.append(className.substring(pos));
246                    implClassName.append("Impl");
247    
248                    Class<?> implClass = null;
249    
250                    try {
251                            implClass = _classLoader.loadClass(implClassName.toString());
252                    }
253                    catch (ClassNotFoundException cnfe) {
254                            return false;
255                    }
256    
257                    if (implClass.getAnnotation(JSONWebService.class) != null) {
258                            return true;
259                    }
260                    else {
261                            return false;
262                    }
263            }
264    
265            private boolean _isJSONWebServiceClass(Class<?> clazz) {
266                    if (!clazz.isAnonymousClass() && !clazz.isArray() && !clazz.isEnum() &&
267                            !clazz.isLocalClass() && !clazz.isPrimitive() &&
268                            !(clazz.isMemberClass() ^
269                                    Modifier.isStatic(clazz.getModifiers()))) {
270    
271                            return true;
272                    }
273    
274                    return false;
275            }
276    
277            private Class<?> _loadUtilClass(Class<?> implementationClass)
278                    throws ClassNotFoundException {
279    
280                    Class<?> utilClass = _utilClasses.get(implementationClass);
281    
282                    if (utilClass != null) {
283                            return utilClass;
284                    }
285    
286                    String utilClassName = implementationClass.getName();
287    
288                    if (utilClassName.endsWith("Impl")) {
289                            utilClassName = utilClassName.substring(
290                                    0, utilClassName.length() - 4);
291    
292                    }
293    
294                    utilClassName += "Util";
295    
296                    utilClassName = StringUtil.replace(utilClassName, ".impl.", ".");
297    
298                    utilClass = _classLoader.loadClass(utilClassName);
299    
300                    _utilClasses.put(implementationClass, utilClass);
301    
302                    return utilClass;
303            }
304    
305            private void _onJSONWebServiceClass(String className) throws Exception {
306                    Class<?> actionClass = _classLoader.loadClass(className);
307    
308                    if (!_isJSONWebServiceClass(actionClass)) {
309                            return;
310                    }
311    
312                    if (actionClass.isInterface() && _hasAnnotatedServiceImpl(className)) {
313                            return;
314                    }
315    
316                    JSONWebService classAnnotation = actionClass.getAnnotation(
317                            JSONWebService.class);
318    
319                    JSONWebServiceMode classAnnotationMode = JSONWebServiceMode.MANUAL;
320    
321                    if (classAnnotation != null) {
322                            classAnnotationMode = classAnnotation.mode();
323                    }
324    
325                    Method[] methods = actionClass.getMethods();
326    
327                    for (Method method : methods) {
328                            Class<?> methodDeclaringClass = method.getDeclaringClass();
329    
330                            if (!methodDeclaringClass.equals(actionClass)) {
331                                    continue;
332                            }
333    
334                            boolean registerMethod = false;
335    
336                            JSONWebService methodAnnotation = method.getAnnotation(
337                                    JSONWebService.class);
338    
339                            if (classAnnotationMode.equals(JSONWebServiceMode.AUTO)) {
340                                    registerMethod = true;
341    
342                                    if (methodAnnotation != null) {
343                                            JSONWebServiceMode methodAnnotationMode =
344                                                    methodAnnotation.mode();
345    
346                                            if (methodAnnotationMode.equals(
347                                                            JSONWebServiceMode.IGNORE)) {
348    
349                                                    registerMethod = false;
350                                            }
351                                    }
352                            }
353                            else {
354                                    if (methodAnnotation != null) {
355                                            JSONWebServiceMode methodAnnotationMode =
356                                                    methodAnnotation.mode();
357    
358                                            if (!methodAnnotationMode.equals(
359                                                            JSONWebServiceMode.IGNORE)) {
360    
361                                                    registerMethod = true;
362                                            }
363                                    }
364                            }
365    
366                            if (registerMethod) {
367                                    _registerJSONWebServiceAction(actionClass, method);
368                            }
369                    }
370            }
371    
372            private void _registerJSONWebServiceAction(
373                            Class<?> implementationClass, Method method)
374                    throws Exception {
375    
376                    String path = _jsonWebServiceMappingResolver.resolvePath(
377                            implementationClass, method);
378    
379                    String httpMethod = _jsonWebServiceMappingResolver.resolveHttpMethod(
380                            method);
381    
382                    if (_invalidHttpMethods.contains(httpMethod)) {
383                            return;
384                    }
385    
386                    Class<?> utilClass = _loadUtilClass(implementationClass);
387    
388                    try {
389                            method = utilClass.getMethod(
390                                    method.getName(), method.getParameterTypes());
391                    }
392                    catch (NoSuchMethodException nsme) {
393                            return;
394                    }
395    
396                    JSONWebServiceActionsManagerUtil.registerJSONWebServiceAction(
397                            _servletContextPath, method.getDeclaringClass(), method, path,
398                            httpMethod);
399    
400                    _registeredActionsCount++;
401            }
402    
403            private static Log _log = LogFactoryUtil.getLog(
404                    JSONWebServiceConfigurator.class);
405    
406            private ClassLoader _classLoader;
407            private Set<String> _invalidHttpMethods = SetUtil.fromArray(
408                    PropsValues.JSONWS_WEB_SERVICE_INVALID_HTTP_METHODS);
409            private byte[] _jsonWebServiceAnnotationBytes =
410                    getTypeSignatureBytes(JSONWebService.class);
411            private JSONWebServiceMappingResolver _jsonWebServiceMappingResolver =
412                    new JSONWebServiceMappingResolver();
413            private int _registeredActionsCount;
414            private String _servletContextPath;
415            private Map<Class<?>, Class<?>> _utilClasses =
416                    new HashMap<Class<?>, Class<?>>();
417    
418    }