001    /**
002     * Copyright (c) 2000-present 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.action;
016    
017    import com.liferay.portal.json.data.FileData;
018    import com.liferay.portal.json.transformer.BeanAnalyzerTransformer;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.javadoc.JavadocManagerUtil;
021    import com.liferay.portal.kernel.javadoc.JavadocMethod;
022    import com.liferay.portal.kernel.json.JSONFactoryUtil;
023    import com.liferay.portal.kernel.json.JSONSerializable;
024    import com.liferay.portal.kernel.json.JSONSerializer;
025    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
026    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
027    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManagerUtil;
028    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceNaming;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.MethodParameter;
031    import com.liferay.portal.kernel.util.ParamUtil;
032    import com.liferay.portal.kernel.util.ReleaseInfo;
033    import com.liferay.portal.kernel.util.StringBundler;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.kernel.util.StringUtil;
036    import com.liferay.portal.kernel.util.Validator;
037    
038    import java.io.File;
039    import java.io.Serializable;
040    
041    import java.lang.reflect.Method;
042    import java.lang.reflect.Modifier;
043    import java.lang.reflect.ParameterizedType;
044    import java.lang.reflect.Type;
045    
046    import java.util.ArrayList;
047    import java.util.Collection;
048    import java.util.Date;
049    import java.util.HashMap;
050    import java.util.LinkedHashMap;
051    import java.util.List;
052    import java.util.Locale;
053    import java.util.Map;
054    import java.util.TimeZone;
055    
056    import javax.servlet.ServletContext;
057    import javax.servlet.http.HttpServletRequest;
058    
059    import jodd.util.ReflectUtil;
060    
061    /**
062     * @author Igor Spasic
063     * @author Raymond Aug??
064     */
065    public class JSONWebServiceDiscoverAction implements JSONWebServiceAction {
066    
067            public JSONWebServiceDiscoverAction(HttpServletRequest request) {
068                    _basePath = request.getServletPath();
069                    _baseURL = String.valueOf(request.getRequestURL());
070    
071                    ServletContext servletContext = request.getServletContext();
072    
073                    _contextName = GetterUtil.getString(
074                            ParamUtil.getString(
075                                    request, "contextName", servletContext.getServletContextName()),
076                            StringPool.BLANK);
077                    _jsonWebServiceNaming =
078                            JSONWebServiceActionsManagerUtil.getJSONWebServiceNaming();
079            }
080    
081            @Override
082            public JSONWebServiceActionMapping getJSONWebServiceActionMapping() {
083                    return null;
084            }
085    
086            @Override
087            public Object invoke() throws Exception {
088                    Map<String, Object> resultsMap = new LinkedHashMap<String, Object>();
089    
090                    resultsMap.put("contextName", _contextName);
091                    resultsMap.put("basePath", _basePath);
092                    resultsMap.put("baseURL", _baseURL);
093                    resultsMap.put("services", _buildJsonWebServiceActionMappingMaps());
094                    resultsMap.put("types", _buildTypes());
095                    resultsMap.put("version", ReleaseInfo.getVersion());
096    
097                    return new DiscoveryContent(resultsMap);
098            }
099    
100            public static class DiscoveryContent implements JSONSerializable {
101    
102                    public DiscoveryContent(Map<String, Object> resultsMap) {
103                            _resultsMap = resultsMap;
104                    }
105    
106                    @Override
107                    public String toJSONString() {
108                            JSONSerializer jsonSerializer =
109                                    JSONFactoryUtil.createJSONSerializer();
110    
111                            jsonSerializer.include("types");
112    
113                            return jsonSerializer.serializeDeep(_resultsMap);
114                    }
115    
116                    private Map<String, Object> _resultsMap;
117    
118            }
119    
120            private List<Map<String, Object>> _buildJsonWebServiceActionMappingMaps()
121                    throws PortalException {
122    
123                    List<JSONWebServiceActionMapping> jsonWebServiceActionMappings =
124                            JSONWebServiceActionsManagerUtil.getJSONWebServiceActionMappings(
125                                    _contextName);
126    
127                    List<Map<String, Object>> jsonWebServiceActionMappingMaps =
128                            new ArrayList<Map<String, Object>>(
129                                    jsonWebServiceActionMappings.size());
130    
131                    for (JSONWebServiceActionMapping jsonWebServiceActionMapping :
132                                    jsonWebServiceActionMappings) {
133    
134                            String path = jsonWebServiceActionMapping.getPath();
135    
136                            Map<String, Object> jsonWebServiceActionMappingMap =
137                                    new LinkedHashMap<String, Object>();
138    
139                            if (jsonWebServiceActionMapping.isDeprecated()) {
140                                    jsonWebServiceActionMappingMap.put("deprecated", Boolean.TRUE);
141                            }
142    
143                            JavadocMethod javadocMethod =
144                                    JavadocManagerUtil.lookupJavadocMethod(
145                                            jsonWebServiceActionMapping.getRealActionMethod());
146    
147                            if (javadocMethod != null) {
148                                    String methodComment = javadocMethod.getComment();
149    
150                                    if (methodComment != null) {
151                                            jsonWebServiceActionMappingMap.put(
152                                                    "description", javadocMethod.getComment());
153                                    }
154                            }
155    
156                            jsonWebServiceActionMappingMap.put(
157                                    "method", jsonWebServiceActionMapping.getMethod());
158    
159                            jsonWebServiceActionMappingMap.put(
160                                    "name", _getName(jsonWebServiceActionMapping));
161    
162                            MethodParameter[] methodParameters =
163                                    jsonWebServiceActionMapping.getMethodParameters();
164    
165                            List<Map<String, String>> parametersList =
166                                    new ArrayList<Map<String, String>>(methodParameters.length);
167    
168                            for (int i = 0; i < methodParameters.length; i++) {
169                                    MethodParameter methodParameter = methodParameters[i];
170    
171                                    Class<?>[] genericTypes = null;
172    
173                                    try {
174                                            genericTypes = methodParameter.getGenericTypes();
175                                    }
176                                    catch (ClassNotFoundException cnfe) {
177                                            throw new PortalException(cnfe);
178                                    }
179    
180                                    Map<String, String> parameterMap =
181                                            new HashMap<String, String>();
182    
183                                    if (javadocMethod != null) {
184                                            String parameterComment = javadocMethod.getParameterComment(
185                                                    i);
186    
187                                            if (!Validator.isBlank(parameterComment)) {
188                                                    parameterMap.put("description", parameterComment);
189                                            }
190                                    }
191    
192                                    parameterMap.put("name", methodParameter.getName());
193                                    parameterMap.put(
194                                            "type",
195                                            _formatType(
196                                                    methodParameter.getType(), genericTypes, false));
197    
198                                    parametersList.add(parameterMap);
199                            }
200    
201                            jsonWebServiceActionMappingMap.put("parameters", parametersList);
202    
203                            jsonWebServiceActionMappingMap.put("path", path);
204    
205                            Map<String, String> returnsMap =
206                                    new LinkedHashMap<String, String>();
207    
208                            if (javadocMethod != null) {
209                                    String returnComment = javadocMethod.getReturnComment();
210    
211                                    if (!Validator.isBlank(returnComment)) {
212                                            returnsMap.put("description", returnComment);
213                                    }
214                            }
215    
216                            Method actionMethod = jsonWebServiceActionMapping.getActionMethod();
217    
218                            returnsMap.put(
219                                    "type",
220                                    _formatType(
221                                            actionMethod.getReturnType(),
222                                            _getGenericReturnTypes(jsonWebServiceActionMapping), true));
223    
224                            jsonWebServiceActionMappingMap.put("returns", returnsMap);
225    
226                            jsonWebServiceActionMappingMaps.add(jsonWebServiceActionMappingMap);
227                    }
228    
229                    return jsonWebServiceActionMappingMaps;
230            }
231    
232            private List<Map<String, String>> _buildPropertiesList(Class<?> type) {
233                    try {
234                            BeanAnalyzerTransformer beanAnalyzerTransformer =
235                                    new BeanAnalyzerTransformer(type) {
236    
237                                            @Override
238                                            protected String getTypeName(Class<?> type) {
239                                                    return _formatType(type, null, false);
240                                            }
241    
242                                    };
243    
244                            return beanAnalyzerTransformer.collect();
245                    }
246                    catch (Exception e) {
247                            return null;
248                    }
249            }
250    
251            private List<Map<String, Object>> _buildTypes() {
252                    List<Map<String, Object>> types = new ArrayList<Map<String, Object>>();
253    
254                    for (int i = 0; i < _types.size(); i++) {
255                            Class<?> type = _types.get(i);
256    
257                            Map<String, Object> map = new LinkedHashMap<String, Object>();
258    
259                            types.add(map);
260    
261                            Class<?> modelType = type;
262    
263                            if (type.isInterface()) {
264                                    try {
265                                            Class<?> clazz = getClass();
266    
267                                            ClassLoader classLoader = clazz.getClassLoader();
268    
269                                            String modelImplClassName =
270                                                    _jsonWebServiceNaming.convertModelClassToImplClassName(
271                                                            type);
272    
273                                            modelType = classLoader.loadClass(modelImplClassName);
274                                    }
275                                    catch (ClassNotFoundException cnfe) {
276                                    }
277                            }
278    
279                            if (modelType.isInterface() ||
280                                    Modifier.isAbstract(modelType.getModifiers())) {
281    
282                                    map.put("interface", Boolean.TRUE);
283                            }
284    
285                            List<Map<String, String>> propertiesList = _buildPropertiesList(
286                                    modelType);
287    
288                            if (propertiesList != null) {
289                                    map.put("properties", propertiesList);
290                            }
291    
292                            map.put("type", type.getName());
293                    }
294    
295                    return types;
296            }
297    
298            private String _formatType(
299                    Class<?> type, Class<?>[] genericTypes, boolean returnType) {
300    
301                    if (type.isArray()) {
302                            Class<?> componentType = type.getComponentType();
303    
304                            return _formatType(componentType, genericTypes, returnType) + "[]";
305                    }
306    
307                    if (type.isPrimitive()) {
308                            return type.getSimpleName();
309                    }
310    
311                    if (type.equals(Boolean.class)) {
312                            return "boolean";
313                    }
314                    else if (type.equals(Class.class)) {
315                            if (!returnType) {
316                                    return "string";
317                            }
318                    }
319                    else if (type.equals(Date.class)) {
320                            return "long";
321                    }
322                    else if (type.equals(File.class)) {
323                            if (!returnType) {
324                                    return "file";
325                            }
326                            else {
327                                    type = FileData.class;
328                            }
329                    }
330                    else if (type.equals(Locale.class) || type.equals(String.class) ||
331                                     type.equals(TimeZone.class)) {
332    
333                            return "string";
334                    }
335                    else if (type.equals(Object.class) || type.equals(Serializable.class)) {
336                            return "map";
337                    }
338                    else if (ReflectUtil.isTypeOf(type, Number.class)) {
339                            String typeName = null;
340    
341                            if (type == Character.class) {
342                                    typeName = "char";
343                            }
344                            else if (type == Integer.class) {
345                                    typeName = "int";
346                            }
347                            else {
348                                    typeName = StringUtil.toLowerCase(type.getSimpleName());
349                            }
350    
351                            return typeName;
352                    }
353    
354                    String typeName = type.getName();
355    
356                    if ((type == Collection.class) ||
357                            ReflectUtil.isTypeOf(type, List.class)) {
358    
359                            typeName = "list";
360                    }
361                    else if (ReflectUtil.isTypeOf(type, Map.class)) {
362                            typeName = "map";
363                    }
364                    else {
365                            if (!_types.contains(type)) {
366                                    _types.add(type);
367                            }
368                    }
369    
370                    if (genericTypes == null) {
371                            return typeName;
372                    }
373    
374                    StringBundler sb = new StringBundler(genericTypes.length * 2 + 1);
375    
376                    sb.append(StringPool.LESS_THAN);
377    
378                    for (int i = 0; i < genericTypes.length; i++) {
379                            Class<?> genericType = genericTypes[i];
380    
381                            if (i != 0) {
382                                    sb.append(StringPool.COMMA);
383                            }
384    
385                            if (genericType == null) {
386                                    sb.append(StringPool.STAR);
387                            }
388                            else {
389                                    sb.append(_formatType(genericType, null, returnType));
390                            }
391                    }
392    
393                    sb.append(StringPool.GREATER_THAN);
394    
395                    return typeName + sb.toString();
396            }
397    
398            private Class<?>[] _getGenericReturnTypes(
399                    JSONWebServiceActionMapping jsonWebServiceActionMapping) {
400    
401                    Method realActionMethod =
402                            jsonWebServiceActionMapping.getRealActionMethod();
403    
404                    Type genericReturnType = realActionMethod.getGenericReturnType();
405    
406                    if (!(genericReturnType instanceof ParameterizedType)) {
407                            return null;
408                    }
409    
410                    ParameterizedType parameterizedType =
411                            (ParameterizedType)genericReturnType;
412    
413                    Type[] genericTypes = parameterizedType.getActualTypeArguments();
414    
415                    Class<?>[] genericReturnTypes = new Class[genericTypes.length];
416    
417                    for (int i = 0; i < genericTypes.length; i++) {
418                            Type genericType = genericTypes[i];
419    
420                            genericReturnTypes[i] = ReflectUtil.getRawType(
421                                    genericType, jsonWebServiceActionMapping.getActionClass());
422                    }
423    
424                    return genericReturnTypes;
425            }
426    
427            private String _getName(
428                    JSONWebServiceActionMapping jsonWebServiceActionMapping) {
429    
430                    Class<?> clazz = jsonWebServiceActionMapping.getActionClass();
431    
432                    String className =
433                            _jsonWebServiceNaming.convertServiceClassToSimpleName(clazz);
434    
435                    Method method = jsonWebServiceActionMapping.getRealActionMethod();
436    
437                    return className.concat(StringPool.POUND).concat(method.getName());
438            }
439    
440            private String _basePath;
441            private String _baseURL;
442            private String _contextName;
443            private JSONWebServiceNaming _jsonWebServiceNaming;
444            private List<Class<?>> _types = new ArrayList<Class<?>>();
445    
446    }