001    /**
002     * Copyright (c) 2000-2012 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.RegExpFindFile;
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                            "*_wl_cls_gen.jar", "*-hook-service*.jar", "*-portlet-service*.jar",
065                            "*-web-service*.jar", "*portal-impl.jar", "*portal-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 RegExpFindFile(
133                                    ".*-(hook|portlet|web)-service.*\\.jar");
134    
135                            findFile.searchPath(libDir);
136    
137                            classPathFiles[1] = findFile.nextFile();
138    
139                            if (classPathFiles[1] == null) {
140                                    File classesDir = new File(libDir.getParent(), "classes");
141    
142                                    classPathFiles[1] = classesDir;
143                            }
144                    }
145                    else {
146                            Thread currentThread = Thread.currentThread();
147    
148                            classLoader = currentThread.getContextClassLoader();
149    
150                            File portalImplJarFile = new File(
151                                    PortalUtil.getPortalLibDir(), "portal-impl.jar");
152                            File portalServiceJarFile = new File(
153                                    PortalUtil.getGlobalLibDir(), "portal-service.jar");
154    
155                            if (portalImplJarFile.exists() && portalServiceJarFile.exists()) {
156                                    classPathFiles = new File[2];
157    
158                                    classPathFiles[0] = portalImplJarFile;
159                                    classPathFiles[1] = portalServiceJarFile;
160                            }
161                            else {
162                                    classPathFiles = ClassLoaderUtil.getDefaultClasspath(
163                                            classLoader);
164                            }
165                    }
166    
167                    _classLoader = classLoader;
168    
169                    _configure(classPathFiles);
170            }
171    
172            @Override
173            protected void onEntry(EntryData entryData) throws Exception {
174                    String className = entryData.getName();
175    
176                    if (className.endsWith("Service") ||
177                            className.endsWith("ServiceImpl")) {
178    
179                            InputStream inputStream = entryData.openInputStream();
180    
181                            if (!isTypeSignatureInUse(
182                                            inputStream, _jsonWebServiceAnnotationBytes)) {
183    
184                                    return;
185                            }
186    
187                            if (!entryData.isArchive()) {
188                                    StreamUtil.cleanUp(inputStream);
189    
190                                    ClassReader classReader = new ClassReader(
191                                            entryData.openInputStream());
192    
193                                    JSONWebServiceClassVisitor jsonWebServiceClassVisitor =
194                                            new JSONWebServiceClassVisitor();
195    
196                                    try {
197                                            classReader.accept(jsonWebServiceClassVisitor, 0);
198                                    }
199                                    catch (Exception e) {
200                                            return;
201                                    }
202    
203                                    if (!className.equals(
204                                                    jsonWebServiceClassVisitor.getClassName())) {
205    
206                                            return;
207                                    }
208                            }
209    
210                            _onJSONWebServiceClass(className);
211                    }
212            }
213    
214            private void _configure(File... classPathFiles) throws PortalException {
215                    StopWatch stopWatch = null;
216    
217                    if (_log.isDebugEnabled()) {
218                            _log.debug("Configure JSON web service actions");
219    
220                            stopWatch = new StopWatch();
221    
222                            stopWatch.start();
223                    }
224    
225                    try {
226                            scanPaths(classPathFiles);
227                    }
228                    catch (Exception e) {
229                            throw new PortalException(e.getMessage(), e);
230                    }
231    
232                    if (_log.isDebugEnabled()) {
233                            _log.debug(
234                                    "Configured " + _registeredActionsCount + " actions in " +
235                                            stopWatch.getTime() + " ms");
236                    }
237            }
238    
239            private boolean _hasAnnotatedServiceImpl(String className) {
240                    StringBundler implClassName = new StringBundler(4);
241    
242                    int pos = className.lastIndexOf(CharPool.PERIOD);
243    
244                    implClassName.append(className.substring(0, pos));
245                    implClassName.append(".impl");
246                    implClassName.append(className.substring(pos));
247                    implClassName.append("Impl");
248    
249                    Class<?> implClass = null;
250    
251                    try {
252                            implClass = _classLoader.loadClass(implClassName.toString());
253                    }
254                    catch (ClassNotFoundException cnfe) {
255                            return false;
256                    }
257    
258                    if (implClass.getAnnotation(JSONWebService.class) != null) {
259                            return true;
260                    }
261                    else {
262                            return false;
263                    }
264            }
265    
266            private boolean _isJSONWebServiceClass(Class<?> clazz) {
267                    if (!clazz.isAnonymousClass() && !clazz.isArray() && !clazz.isEnum() &&
268                            !clazz.isLocalClass() && !clazz.isPrimitive() &&
269                            !(clazz.isMemberClass() ^
270                                    Modifier.isStatic(clazz.getModifiers()))) {
271    
272                            return true;
273                    }
274    
275                    return false;
276            }
277    
278            private Class<?> _loadUtilClass(Class<?> implementationClass)
279                    throws ClassNotFoundException {
280    
281                    Class<?> utilClass = _utilClasses.get(implementationClass);
282    
283                    if (utilClass != null) {
284                            return utilClass;
285                    }
286    
287                    String utilClassName = implementationClass.getName();
288    
289                    if (utilClassName.endsWith("Impl")) {
290                            utilClassName = utilClassName.substring(
291                                    0, utilClassName.length() - 4);
292    
293                    }
294    
295                    utilClassName += "Util";
296    
297                    utilClassName = StringUtil.replace(utilClassName, ".impl.", ".");
298    
299                    utilClass = _classLoader.loadClass(utilClassName);
300    
301                    _utilClasses.put(implementationClass, utilClass);
302    
303                    return utilClass;
304            }
305    
306            private void _onJSONWebServiceClass(String className) throws Exception {
307                    Class<?> actionClass = _classLoader.loadClass(className);
308    
309                    if (!_isJSONWebServiceClass(actionClass)) {
310                            return;
311                    }
312    
313                    if (actionClass.isInterface() && _hasAnnotatedServiceImpl(className)) {
314                            return;
315                    }
316    
317                    JSONWebService classAnnotation = actionClass.getAnnotation(
318                            JSONWebService.class);
319    
320                    JSONWebServiceMode classAnnotationMode = JSONWebServiceMode.MANUAL;
321    
322                    if (classAnnotation != null) {
323                            classAnnotationMode = classAnnotation.mode();
324                    }
325    
326                    Method[] methods = actionClass.getMethods();
327    
328                    for (Method method : methods) {
329                            Class<?> methodDeclaringClass = method.getDeclaringClass();
330    
331                            if (!methodDeclaringClass.equals(actionClass)) {
332                                    continue;
333                            }
334    
335                            boolean registerMethod = false;
336    
337                            JSONWebService methodAnnotation = method.getAnnotation(
338                                    JSONWebService.class);
339    
340                            if (classAnnotationMode.equals(JSONWebServiceMode.AUTO)) {
341                                    registerMethod = true;
342    
343                                    if (methodAnnotation != null) {
344                                            JSONWebServiceMode methodAnnotationMode =
345                                                    methodAnnotation.mode();
346    
347                                            if (methodAnnotationMode.equals(
348                                                            JSONWebServiceMode.IGNORE)) {
349    
350                                                    registerMethod = false;
351                                            }
352                                    }
353                            }
354                            else {
355                                    if (methodAnnotation != null) {
356                                            JSONWebServiceMode methodAnnotationMode =
357                                                    methodAnnotation.mode();
358    
359                                            if (!methodAnnotationMode.equals(
360                                                            JSONWebServiceMode.IGNORE)) {
361    
362                                                    registerMethod = true;
363                                            }
364                                    }
365                            }
366    
367                            if (registerMethod) {
368                                    _registerJSONWebServiceAction(actionClass, method);
369                            }
370                    }
371            }
372    
373            private void _registerJSONWebServiceAction(
374                            Class<?> implementationClass, Method method)
375                    throws Exception {
376    
377                    String path = _jsonWebServiceMappingResolver.resolvePath(
378                            implementationClass, method);
379    
380                    String httpMethod = _jsonWebServiceMappingResolver.resolveHttpMethod(
381                            method);
382    
383                    if (_invalidHttpMethods.contains(httpMethod)) {
384                            return;
385                    }
386    
387                    Class<?> utilClass = _loadUtilClass(implementationClass);
388    
389                    try {
390                            method = utilClass.getMethod(
391                                    method.getName(), method.getParameterTypes());
392                    }
393                    catch (NoSuchMethodException nsme) {
394                            return;
395                    }
396    
397                    JSONWebServiceActionsManagerUtil.registerJSONWebServiceAction(
398                            _servletContextPath, method.getDeclaringClass(), method, path,
399                            httpMethod);
400    
401                    _registeredActionsCount++;
402            }
403    
404            private static Log _log = LogFactoryUtil.getLog(
405                    JSONWebServiceConfigurator.class);
406    
407            private ClassLoader _classLoader;
408            private Set<String> _invalidHttpMethods = SetUtil.fromArray(
409                    PropsValues.JSONWS_WEB_SERVICE_INVALID_HTTP_METHODS);
410            private byte[] _jsonWebServiceAnnotationBytes = getTypeSignatureBytes(
411                    JSONWebService.class);
412            private JSONWebServiceMappingResolver _jsonWebServiceMappingResolver =
413                    new JSONWebServiceMappingResolver();
414            private int _registeredActionsCount;
415            private String _servletContextPath;
416            private Map<Class<?>, Class<?>> _utilClasses =
417                    new HashMap<Class<?>, Class<?>>();
418    
419    }