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.kernel.jsonwebservice;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.CharPool;
022    import com.liferay.portal.kernel.util.ContextPathUtil;
023    import com.liferay.portal.kernel.util.PropsKeys;
024    import com.liferay.portal.kernel.util.PropsUtil;
025    import com.liferay.portal.kernel.util.SetUtil;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringUtil;
028    
029    import java.io.IOException;
030    import java.io.InputStream;
031    
032    import java.lang.reflect.Method;
033    import java.lang.reflect.Modifier;
034    
035    import java.util.Arrays;
036    import java.util.HashMap;
037    import java.util.Map;
038    import java.util.Set;
039    
040    import javax.servlet.ServletContext;
041    
042    /**
043     * @author Igor Spasic
044     * @author Raymond Augé
045     */
046    public abstract class BaseJSONWebServiceConfigurator
047            implements JSONWebServiceConfigurator {
048    
049            public void clean() {
050                    int count =
051                            JSONWebServiceActionsManagerUtil.unregisterJSONWebServiceActions(
052                                    _contextPath);
053    
054                    _registeredActionsCount -= count;
055    
056                    if (_log.isDebugEnabled()) {
057                            if (count != 0) {
058                                    _log.debug(
059                                            "Removed " + count +
060                                                    " existing JSON Web Service actions that belonged to " +
061                                                            _contextPath);
062                            }
063                    }
064            }
065    
066            public abstract void configure() throws PortalException, SystemException;
067    
068            public ClassLoader getClassLoader() {
069                    return _classLoader;
070            }
071    
072            public String getContextPath() {
073                    return _contextPath;
074            }
075    
076            public int getRegisteredActionsCount() {
077                    return _registeredActionsCount;
078            }
079    
080            public ServletContext getServletContext() {
081                    return _servletContext;
082            }
083    
084            public void init(ServletContext servletContext, ClassLoader classLoader) {
085                    _servletContext = servletContext;
086                    _classLoader = classLoader;
087    
088                    _contextPath = ContextPathUtil.getContextPath(servletContext);
089            }
090    
091            public void registerClass(String className, InputStream inputStream)
092                    throws Exception {
093    
094                    if (!className.endsWith("Service") &&
095                            !className.endsWith("ServiceImpl")) {
096    
097                            return;
098                    }
099    
100                    if (inputStream.markSupported()) {
101                            inputStream.mark(Integer.MAX_VALUE);
102                    }
103    
104                    if (!isTypeSignatureInUse(inputStream)) {
105                            return;
106                    }
107    
108                    if (inputStream.markSupported()) {
109                            inputStream.reset();
110    
111                            try {
112                                    JSONWebServiceClassVisitor jsonWebServiceClassVisitor =
113                                            JSONWebServiceClassVisitorFactoryUtil.create(inputStream);
114    
115                                    jsonWebServiceClassVisitor.accept();
116    
117                                    if (!className.equals(
118                                                    jsonWebServiceClassVisitor.getClassName())) {
119    
120                                            return;
121                                    }
122                            }
123                            catch (Exception e) {
124                                    _log.error(e, e);
125    
126                                    return;
127                            }
128                    }
129    
130                    onJSONWebServiceClass(className);
131            }
132    
133            protected byte[] getTypeSignatureBytes(Class<?> clazz) {
134                    return ('L' + clazz.getName().replace('.', '/') + ';').getBytes();
135            }
136    
137            protected boolean hasAnnotatedServiceImpl(String className) {
138                    StringBundler sb = new StringBundler(4);
139    
140                    int pos = className.lastIndexOf(CharPool.PERIOD);
141    
142                    sb.append(className.substring(0, pos));
143                    sb.append(".impl");
144                    sb.append(className.substring(pos));
145                    sb.append("Impl");
146    
147                    Class<?> implClass = null;
148    
149                    try {
150                            implClass = _classLoader.loadClass(sb.toString());
151                    }
152                    catch (ClassNotFoundException cnfe) {
153                            return false;
154                    }
155    
156                    if (implClass.getAnnotation(JSONWebService.class) != null) {
157                            return true;
158                    }
159                    else {
160                            return false;
161                    }
162            }
163    
164            protected boolean isJSONWebServiceClass(Class<?> clazz) {
165                    if (!clazz.isAnonymousClass() && !clazz.isArray() && !clazz.isEnum() &&
166                            !clazz.isLocalClass() && !clazz.isPrimitive() &&
167                            !(clazz.isMemberClass() ^
168                                    Modifier.isStatic(clazz.getModifiers()))) {
169    
170                            return true;
171                    }
172    
173                    return false;
174            }
175    
176            protected boolean isTypeSignatureInUse(InputStream inputStream) {
177                    try {
178    
179                            // Create a buffer the same length as the type signature array
180    
181                            byte[] buffer = new byte[_jsonWebServiceAnnotationBytes.length];
182    
183                            // Read an initial number of bytes into the buffer
184    
185                            int value = inputStream.read(buffer);
186    
187                            // Number of bytes read must be more than the type signature length
188    
189                            if (value < _jsonWebServiceAnnotationBytes.length) {
190                                    return false;
191                            }
192    
193                            // Pass if the the buffer equals the type signature
194    
195                            if (Arrays.equals(buffer, _jsonWebServiceAnnotationBytes)) {
196                                    return true;
197                            }
198    
199                            // Read a single byte from the stream
200    
201                            while ((value = inputStream.read()) != -1) {
202    
203                                    // Fail if we are at the end of the stream
204    
205                                    if (value == -1) {
206                                            return false;
207                                    }
208    
209                                    // Shift the buffer left
210    
211                                    System.arraycopy(buffer, 1, buffer, 0, buffer.length - 1);
212    
213                                    // Add the read byte to the end of the buffer
214    
215                                    buffer[buffer.length - 1] = (byte)value;
216    
217                                    // Compare the updated buffer to the signature
218    
219                                    if (Arrays.equals(buffer, _jsonWebServiceAnnotationBytes)) {
220                                            return true;
221                                    }
222                            }
223                    }
224                    catch (IOException ioe) {
225                            throw new IllegalStateException(
226                                    "Unable to read bytes from input stream", ioe);
227                    }
228    
229                    return false;
230            }
231    
232            protected Class<?> loadUtilClass(Class<?> implementationClass)
233                    throws ClassNotFoundException {
234    
235                    Class<?> utilClass = _utilClasses.get(implementationClass);
236    
237                    if (utilClass != null) {
238                            return utilClass;
239                    }
240    
241                    String utilClassName = implementationClass.getName();
242    
243                    if (utilClassName.endsWith("Impl")) {
244                            utilClassName = utilClassName.substring(
245                                    0, utilClassName.length() - 4);
246    
247                    }
248    
249                    utilClassName += "Util";
250    
251                    utilClassName = StringUtil.replace(utilClassName, ".impl.", ".");
252    
253                    utilClass = _classLoader.loadClass(utilClassName);
254    
255                    _utilClasses.put(implementationClass, utilClass);
256    
257                    return utilClass;
258            }
259    
260            protected void onJSONWebServiceClass(String className) throws Exception {
261                    Class<?> actionClass = _classLoader.loadClass(className);
262    
263                    if (!isJSONWebServiceClass(actionClass)) {
264                            return;
265                    }
266    
267                    if (actionClass.isInterface() && hasAnnotatedServiceImpl(className)) {
268                            return;
269                    }
270    
271                    JSONWebService classJSONWebService = actionClass.getAnnotation(
272                            JSONWebService.class);
273    
274                    JSONWebServiceMode classJSONWebServiceMode = JSONWebServiceMode.MANUAL;
275    
276                    if (classJSONWebService != null) {
277                            classJSONWebServiceMode = classJSONWebService.mode();
278                    }
279    
280                    Method[] methods = actionClass.getMethods();
281    
282                    for (Method method : methods) {
283                            Class<?> methodDeclaringClass = method.getDeclaringClass();
284    
285                            if (!methodDeclaringClass.equals(actionClass)) {
286                                    continue;
287                            }
288    
289                            if ((_excludedMethodNames != null) &&
290                                    _excludedMethodNames.contains(method.getName())) {
291    
292                                    continue;
293                            }
294    
295                            boolean registerMethod = false;
296    
297                            JSONWebService methodJSONWebService = method.getAnnotation(
298                                    JSONWebService.class);
299    
300                            if (classJSONWebServiceMode.equals(JSONWebServiceMode.AUTO)) {
301                                    registerMethod = true;
302    
303                                    if (methodJSONWebService != null) {
304                                            JSONWebServiceMode methodJSONWebServiceMode =
305                                                    methodJSONWebService.mode();
306    
307                                            if (methodJSONWebServiceMode.equals(
308                                                            JSONWebServiceMode.IGNORE)) {
309    
310                                                    registerMethod = false;
311                                            }
312                                    }
313                            }
314                            else {
315                                    if (methodJSONWebService != null) {
316                                            JSONWebServiceMode methodJSONWebServiceMode =
317                                                    methodJSONWebService.mode();
318    
319                                            if (!methodJSONWebServiceMode.equals(
320                                                            JSONWebServiceMode.IGNORE)) {
321    
322                                                    registerMethod = true;
323                                            }
324                                    }
325                            }
326    
327                            if (registerMethod) {
328                                    registerJSONWebServiceAction(actionClass, method);
329                            }
330                    }
331            }
332    
333            protected void registerJSONWebServiceAction(
334                            Class<?> implementationClass, Method method)
335                    throws Exception {
336    
337                    String path = _jsonWebServiceMappingResolver.resolvePath(
338                            implementationClass, method);
339    
340                    String httpMethod = _jsonWebServiceMappingResolver.resolveHttpMethod(
341                            method);
342    
343                    if (_invalidHttpMethods.contains(httpMethod)) {
344                            return;
345                    }
346    
347                    Class<?> utilClass = loadUtilClass(implementationClass);
348    
349                    try {
350                            method = utilClass.getMethod(
351                                    method.getName(), method.getParameterTypes());
352                    }
353                    catch (NoSuchMethodException nsme) {
354                            return;
355                    }
356    
357                    JSONWebServiceActionsManagerUtil.registerJSONWebServiceAction(
358                            _contextPath, method.getDeclaringClass(), method, path, httpMethod);
359    
360                    _registeredActionsCount++;
361            }
362    
363            private static Log _log = LogFactoryUtil.getLog(
364                    BaseJSONWebServiceConfigurator.class);
365    
366            private static Set<String> _excludedMethodNames = SetUtil.fromArray(
367                    new String[] {"getBeanIdentifier", "setBeanIdentifier"});
368    
369            private ClassLoader _classLoader;
370            private String _contextPath;
371            private Set<String> _invalidHttpMethods = SetUtil.fromArray(
372                    PropsUtil.getArray(PropsKeys.JSONWS_WEB_SERVICE_INVALID_HTTP_METHODS));
373            private final byte[] _jsonWebServiceAnnotationBytes = getTypeSignatureBytes(
374                    JSONWebService.class);
375            private JSONWebServiceMappingResolver _jsonWebServiceMappingResolver =
376                    new JSONWebServiceMappingResolver();
377            private int _registeredActionsCount;
378            private ServletContext _servletContext;
379            private Map<Class<?>, Class<?>> _utilClasses =
380                    new HashMap<Class<?>, Class<?>>();
381    
382    }