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.security.pacl.PACLClassLoaderUtil;
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.HashMap;
045    import java.util.Map;
046    import java.util.Set;
047    
048    import jodd.io.findfile.ClassFinder;
049    import jodd.io.findfile.FindFile;
050    import jodd.io.findfile.RegExpFindFile;
051    
052    import jodd.util.ClassLoaderUtil;
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                            classPathFiles = new File[2];
130    
131                            classPathFiles[0] = classPathFile;
132    
133                            FindFile findFile = new RegExpFindFile(
134                                    ".*-(hook|portlet|web)-service.*\\.jar");
135    
136                            findFile.searchPath(libDir);
137    
138                            classPathFiles[1] = findFile.nextFile();
139    
140                            if (classPathFiles[1] == null) {
141                                    File classesDir = new File(libDir.getParent(), "classes");
142    
143                                    classPathFiles[1] = classesDir;
144                            }
145                    }
146                    else {
147                            classLoader = PACLClassLoaderUtil.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 ((_excludedMethodNames != null) &&
367                                    _excludedMethodNames.contains(method.getName())) {
368    
369                                    registerMethod = false;
370                            }
371    
372                            if (registerMethod) {
373                                    _registerJSONWebServiceAction(actionClass, method);
374                            }
375                    }
376            }
377    
378            private void _registerJSONWebServiceAction(
379                            Class<?> implementationClass, Method method)
380                    throws Exception {
381    
382                    String path = _jsonWebServiceMappingResolver.resolvePath(
383                            implementationClass, method);
384    
385                    String httpMethod = _jsonWebServiceMappingResolver.resolveHttpMethod(
386                            method);
387    
388                    if (_invalidHttpMethods.contains(httpMethod)) {
389                            return;
390                    }
391    
392                    Class<?> utilClass = _loadUtilClass(implementationClass);
393    
394                    try {
395                            method = utilClass.getMethod(
396                                    method.getName(), method.getParameterTypes());
397                    }
398                    catch (NoSuchMethodException nsme) {
399                            return;
400                    }
401    
402                    JSONWebServiceActionsManagerUtil.registerJSONWebServiceAction(
403                            _servletContextPath, method.getDeclaringClass(), method, path,
404                            httpMethod);
405    
406                    _registeredActionsCount++;
407            }
408    
409            private static Log _log = LogFactoryUtil.getLog(
410                    JSONWebServiceConfigurator.class);
411    
412            private static Set<String> _excludedMethodNames = SetUtil.fromArray(
413                    new String[] {"getBeanIdentifier", "setBeanIdentifier"});
414    
415            private ClassLoader _classLoader;
416            private Set<String> _invalidHttpMethods = SetUtil.fromArray(
417                    PropsValues.JSONWS_WEB_SERVICE_INVALID_HTTP_METHODS);
418            private byte[] _jsonWebServiceAnnotationBytes = getTypeSignatureBytes(
419                    JSONWebService.class);
420            private JSONWebServiceMappingResolver _jsonWebServiceMappingResolver =
421                    new JSONWebServiceMappingResolver();
422            private int _registeredActionsCount;
423            private String _servletContextPath;
424            private Map<Class<?>, Class<?>> _utilClasses =
425                    new HashMap<Class<?>, Class<?>>();
426    
427    }