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