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