001    /**
002     * Copyright (c) 2000-2013 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.kernel.json.JSONFactoryUtil;
018    import com.liferay.portal.kernel.json.JSONIncludesManagerUtil;
019    import com.liferay.portal.kernel.json.JSONSerializable;
020    import com.liferay.portal.kernel.json.JSONSerializer;
021    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
022    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
023    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManagerUtil;
024    import com.liferay.portal.kernel.util.CamelCaseUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.Constants;
027    import com.liferay.portal.kernel.util.ListUtil;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Validator;
031    
032    import java.io.IOException;
033    
034    import java.lang.reflect.Array;
035    
036    import java.util.ArrayList;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Set;
042    
043    import javax.servlet.http.HttpServletRequest;
044    
045    import jodd.bean.BeanCopy;
046    import jodd.bean.BeanUtil;
047    
048    import jodd.servlet.ServletUtil;
049    
050    import jodd.util.NameValue;
051    
052    /**
053     * @author Igor Spasic
054     * @author Eduardo Lundgren
055     */
056    public class JSONWebServiceInvokerAction implements JSONWebServiceAction {
057    
058            public JSONWebServiceInvokerAction(HttpServletRequest request) {
059                    _request = request;
060    
061                    _command = request.getParameter(Constants.CMD);
062    
063                    if (_command == null) {
064                            try {
065                                    _command = ServletUtil.readRequestBody(request);
066                            }
067                            catch (IOException ioe) {
068                                    throw new IllegalArgumentException(ioe);
069                            }
070                    }
071            }
072    
073            @Override
074            public JSONWebServiceActionMapping getJSONWebServiceActionMapping() {
075                    return null;
076            }
077    
078            @Override
079            public Object invoke() throws Exception {
080                    Object command = JSONFactoryUtil.looseDeserializeSafe(_command);
081    
082                    List<Object> list = null;
083    
084                    boolean batchMode = false;
085    
086                    if (command instanceof List) {
087                            list = (List<Object>)command;
088    
089                            batchMode = true;
090                    }
091                    else if (command instanceof Map) {
092                            list = new ArrayList<Object>(1);
093    
094                            list.add(command);
095    
096                            batchMode = false;
097                    }
098                    else {
099                            throw new IllegalArgumentException();
100                    }
101    
102                    for (int i = 0; i < list.size(); i++) {
103                            Map<String, Map<String, Object>> map =
104                                    (Map<String, Map<String, Object>>)list.get(i);
105    
106                            if (map.isEmpty()) {
107                                    throw new IllegalArgumentException();
108                            }
109    
110                            Set<Map.Entry<String, Map<String, Object>>> entrySet =
111                                    map.entrySet();
112    
113                            Iterator<Map.Entry<String, Map<String, Object>>> iterator =
114                                    entrySet.iterator();
115    
116                            Map.Entry<String, Map<String, Object>> entry = iterator.next();
117    
118                            Statement statement = _parseStatement(
119                                    null, entry.getKey(), entry.getValue());
120    
121                            Object result = _executeStatement(statement);
122    
123                            list.set(i, result);
124                    }
125    
126                    Object result = null;
127    
128                    if (batchMode == false) {
129                            result = list.get(0);
130                    }
131                    else {
132                            result = list;
133                    }
134    
135                    return new InvokerResult(result);
136            }
137    
138            public class InvokerResult implements JSONSerializable {
139    
140                    public JSONWebServiceInvokerAction getJSONWebServiceInvokerAction() {
141                            return JSONWebServiceInvokerAction.this;
142                    }
143    
144                    @Override
145                    public String toJSONString() {
146                            if (_result == null) {
147                                    return JSONFactoryUtil.getNullJSON();
148                            }
149    
150                            JSONSerializer jsonSerializer = createJSONSerializer();
151    
152                            for (Statement statement : _statements) {
153                                    if (_includes != null) {
154                                            for (String include : _includes) {
155                                                    jsonSerializer.include(include);
156                                            }
157                                    }
158    
159                                    String name = statement.getName();
160    
161                                    if (name == null) {
162                                            continue;
163                                    }
164    
165                                    String includeName = name.substring(1);
166    
167                                    _checkJSONSerializerIncludeName(includeName);
168    
169                                    jsonSerializer.include(includeName);
170                            }
171    
172                            return jsonSerializer.serialize(_result);
173                    }
174    
175                    public Object getResult() {
176                            return _result;
177                    }
178    
179                    public InvokerResult(Object result) {
180                            _result = result;
181                    }
182    
183                    protected JSONSerializer createJSONSerializer() {
184                            JSONSerializer jsonSerializer =
185                                    JSONFactoryUtil.createJSONSerializer();
186    
187                            jsonSerializer.exclude("*.class");
188    
189                            return jsonSerializer;
190                    }
191    
192                    private Object _result;
193    
194            }
195    
196            private void _addInclude(Statement statement, String name) {
197                    if (_includes == null) {
198                            _includes = new ArrayList<String>();
199                    }
200    
201                    StringBuilder sb = new StringBuilder();
202    
203                    while (statement._parentStatement != null) {
204                            String statementName = statement.getName().substring(1);
205    
206                            sb.insert(0, statementName + StringPool.PERIOD);
207    
208                            statement = statement._parentStatement;
209                    }
210    
211                    sb.append(name);
212    
213                    _includes.add(sb.toString());
214            }
215    
216            private Object _addVariableStatement(
217                            Statement variableStatement, Object result)
218                    throws Exception {
219    
220                    Statement statement = variableStatement.getParentStatement();
221    
222                    result = _populateFlags(statement, result);
223    
224                    String name = variableStatement.getName();
225    
226                    Object variableResult = _executeStatement(variableStatement);
227    
228                    Map<String, Object> map = _convertObjectToMap(statement, result, null);
229    
230                    if (!variableStatement.isInner()) {
231                            map.put(name.substring(1), variableResult);
232    
233                            return map;
234                    }
235    
236                    int index = name.indexOf(".$");
237    
238                    String innerObjectName = name.substring(0, index);
239    
240                    if (innerObjectName.contains(StringPool.PERIOD)) {
241                            throw new IllegalArgumentException(
242                                    "Inner properties with more than 1 level are not supported");
243                    }
244    
245                    Object innerObject = map.get(innerObjectName);
246    
247                    String innerPropertyName = name.substring(index + 2);
248    
249                    if (innerObject instanceof List) {
250                            List<Object> innerList = (List<Object>)innerObject;
251    
252                            List<Object> newInnerList = new ArrayList<Object>(innerList.size());
253    
254                            for (Object innerListElement : innerList) {
255                                    Map<String, Object> newInnerListElement = _convertObjectToMap(
256                                            statement, innerListElement, innerObjectName);
257    
258                                    newInnerListElement.put(innerPropertyName, variableResult);
259    
260                                    newInnerList.add(newInnerListElement);
261                            }
262    
263                            map.put(innerObjectName, newInnerList);
264                    }
265                    else {
266                            Map<String, Object> innerMap = _convertObjectToMap(
267                                    statement, innerObject, innerObjectName);
268    
269                            innerMap.put(innerPropertyName, variableResult);
270    
271                            map.put(innerObjectName, innerMap);
272                    }
273    
274                    return map;
275            }
276    
277            private Object _addVariableStatementList(
278                            Statement variableStatement, List<Object> resultList,
279                            List<Object> results)
280                    throws Exception {
281    
282                    for (Object object : resultList) {
283                            List<Object> listObject = _convertObjectToList(object);
284    
285                            if (listObject != null) {
286                                    Object value = _addVariableStatementList(
287                                            variableStatement, listObject, results);
288    
289                                    results.add(value);
290                            }
291                            else {
292                                    Object value = _addVariableStatement(variableStatement, object);
293    
294                                    results.add(value);
295                            }
296                    }
297    
298                    return results;
299            }
300    
301            private void _checkJSONSerializerIncludeName(String includeName) {
302                    if (includeName.contains(StringPool.STAR)) {
303                            throw new IllegalArgumentException(
304                                    includeName + " has special characters");
305                    }
306            }
307    
308            private List<Object> _convertObjectToList(Object object) {
309                    if (object == null) {
310                            return null;
311                    }
312    
313                    if (object instanceof List) {
314                            return (List<Object>)object;
315                    }
316    
317                    if (object instanceof Iterable) {
318                            List<Object> list = new ArrayList<Object>();
319    
320                            Iterable<?> iterable = (Iterable<?>)object;
321    
322                            Iterator<?> iterator = iterable.iterator();
323    
324                            while (iterator.hasNext()) {
325                                    list.add(iterator.next());
326                            }
327    
328                            return list;
329                    }
330    
331                    Class<?> clazz = object.getClass();
332    
333                    if (!clazz.isArray()) {
334                            return null;
335                    }
336    
337                    Class<?> componentType = clazz.getComponentType();
338    
339                    if (!componentType.isPrimitive()) {
340                            return ListUtil.toList((Object[])object);
341                    }
342    
343                    List<Object> list = new ArrayList<Object>();
344    
345                    for (int i = 0; i < Array.getLength(object); i++) {
346                            list.add(Array.get(object, i));
347                    }
348    
349                    return list;
350            }
351    
352            private Map<String, Object> _convertObjectToMap(
353                    Statement statement, Object object, String prefix) {
354    
355                    if (object instanceof Map) {
356                            return (Map<String, Object>)object;
357                    }
358    
359                    Class<?> clazz = object.getClass();
360    
361                    HashMap<Object, Object> destinationMap = new HashMap<Object, Object>();
362    
363                    BeanCopy beanCopy = BeanCopy.beans(object, destinationMap);
364    
365                    beanCopy.exclude(JSONIncludesManagerUtil.lookupExcludes(clazz));
366    
367                    beanCopy.copy();
368    
369                    object = destinationMap;
370    
371                    String[] includes = JSONIncludesManagerUtil.lookupIncludes(clazz);
372    
373                    for (String include : includes) {
374                            if (Validator.isNotNull(prefix)) {
375                                    include = prefix + StringPool.PERIOD + include;
376                            }
377    
378                            _addInclude(statement, include);
379                    }
380    
381                    return (Map<String, Object>)object;
382            }
383    
384            private Object _executeStatement(Statement statement) throws Exception {
385                    JSONWebServiceAction jsonWebServiceAction =
386                            JSONWebServiceActionsManagerUtil.getJSONWebServiceAction(
387                                    _request, statement.getMethod(), null,
388                                    statement.getParameterMap());
389    
390                    Object result = jsonWebServiceAction.invoke();
391    
392                    result = _filterResult(statement, result);
393    
394                    List<Statement> variableStatements = statement.getVariableStatements();
395    
396                    if (variableStatements == null) {
397                            return result;
398                    }
399    
400                    for (Statement variableStatement : variableStatements) {
401                            boolean innerStatement = variableStatement.isInner();
402    
403                            if (innerStatement) {
404                                    result = variableStatement.push(result);
405                            }
406    
407                            List<Object> resultList = _convertObjectToList(result);
408    
409                            if (resultList != null) {
410                                    result = _addVariableStatementList(
411                                            variableStatement, resultList, new ArrayList<Object>());
412    
413                                    variableStatement.setExecuted(true);
414    
415                                    if (innerStatement) {
416                                            result = variableStatement.pop(result);
417                                    }
418                            }
419                            else {
420                                    if (innerStatement) {
421                                            result = variableStatement.pop(result);
422                                    }
423    
424                                    result = _addVariableStatement(variableStatement, result);
425    
426                                    variableStatement.setExecuted(true);
427                            }
428                    }
429    
430                    return result;
431            }
432    
433            private Object _filterResult(Statement statement, Object result) {
434                    List<Object> resultList = _convertObjectToList(result);
435    
436                    if (resultList != null) {
437                            result = _filterResultList(
438                                    statement, resultList, new ArrayList<Object>());
439                    }
440                    else {
441                            result = _filterResultObject(statement, result);
442                    }
443    
444                    return result;
445            }
446    
447            private Object _filterResultList(
448                    Statement statement, List<Object> resultList, List<Object> results) {
449    
450                    for (Object object : resultList) {
451                            Object value = _filterResultObject(statement, object);
452    
453                            results.add(value);
454                    }
455    
456                    return results;
457            }
458    
459            private Object _filterResultObject(Statement statement, Object result) {
460                    if (result == null) {
461                            return result;
462                    }
463    
464                    String[] whitelist = statement.getWhitelist();
465    
466                    if (whitelist == null) {
467                            return result;
468                    }
469    
470                    Map<String, Object> map = _convertObjectToMap(statement, result, null);
471    
472                    Map<String, Object> whitelistMap = new HashMap<String, Object>(
473                            whitelist.length);
474    
475                    for (String key : whitelist) {
476                            Object value = map.get(key);
477    
478                            whitelistMap.put(key, value);
479                    }
480    
481                    return whitelistMap;
482            }
483    
484            private Statement _parseStatement(
485                    Statement parentStatement, String assignment,
486                    Map<String, Object> statementBody) {
487    
488                    Statement statement = new Statement(parentStatement);
489    
490                    _statements.add(statement);
491    
492                    int x = assignment.indexOf(StringPool.EQUAL);
493    
494                    if (x == -1) {
495                            statement.setMethod(assignment.trim());
496                    }
497                    else {
498                            String name = assignment.substring(0, x).trim();
499    
500                            int y = name.indexOf(StringPool.OPEN_BRACKET);
501    
502                            if (y != -1) {
503                                    String whitelistString = name.substring(
504                                            y + 1, name.length() - 1);
505    
506                                    String[] whiteList = StringUtil.split(whitelistString);
507    
508                                    for (int i = 0; i < whiteList.length; i++) {
509                                            whiteList[i] = whiteList[i].trim();
510                                    }
511    
512                                    statement.setWhitelist(whiteList);
513    
514                                    name = name.substring(0, y);
515                            }
516    
517                            statement.setName(name);
518    
519                            statement.setMethod(assignment.substring(x + 1).trim());
520                    }
521    
522                    HashMap<String, Object> parameterMap = new HashMap<String, Object>(
523                            statementBody.size());
524    
525                    statement.setParameterMap(parameterMap);
526    
527                    for (String key : statementBody.keySet()) {
528                            if (key.startsWith(StringPool.AT)) {
529                                    String value = (String)statementBody.get(key);
530    
531                                    List<Flag> flags = statement.getFlags();
532    
533                                    if (flags == null) {
534                                            flags = new ArrayList<Flag>();
535    
536                                            statement.setFlags(flags);
537                                    }
538    
539                                    Flag flag = new Flag();
540    
541                                    flag.setName(key.substring(1));
542                                    flag.setValue(value);
543    
544                                    flags.add(flag);
545                            }
546                            else if (key.startsWith(StringPool.DOLLAR) || key.contains(".$")) {
547                                    Map<String, Object> map =
548                                            (Map<String, Object>)statementBody.get(key);
549    
550                                    List<Statement> variableStatements =
551                                            statement.getVariableStatements();
552    
553                                    if (variableStatements == null) {
554                                            variableStatements = new ArrayList<Statement>();
555    
556                                            statement.setVariableStatements(variableStatements);
557                                    }
558    
559                                    Statement variableStatement = _parseStatement(
560                                            statement, key, map);
561    
562                                    variableStatements.add(variableStatement);
563                            }
564                            else {
565                                    Object value = statementBody.get(key);
566    
567                                    parameterMap.put(CamelCaseUtil.normalizeCamelCase(key), value);
568                            }
569                    }
570    
571                    return statement;
572            }
573    
574            private Object _populateFlags(Statement statement, Object result) {
575                    List<Object> listResult = _convertObjectToList(result);
576    
577                    if (listResult != null) {
578                            result = _populateFlagsList(
579                                    statement.getName(), listResult, new ArrayList<Object>());
580                    }
581                    else {
582                            _populateFlagsObject(statement.getName(), result);
583                    }
584    
585                    return result;
586            }
587    
588            private List<Object> _populateFlagsList(
589                    String name, List<Object> list, List<Object> results) {
590    
591                    for (Object object : list) {
592                            List<Object> listObject = _convertObjectToList(object);
593    
594                            if (listObject != null) {
595                                    Object value = _populateFlagsList(name, listObject, results);
596    
597                                    results.add(value);
598                            }
599                            else {
600                                    _populateFlagsObject(name, object);
601    
602                                    results.add(object);
603                            }
604                    }
605    
606                    return results;
607            }
608    
609            private void _populateFlagsObject(String name, Object object) {
610                    if (name == null) {
611                            return;
612                    }
613    
614                    String pushedName = null;
615    
616                    int index = name.indexOf(CharPool.PERIOD);
617    
618                    if (index != -1) {
619                            pushedName = name.substring(0, index + 1);
620                    }
621    
622                    name = name.concat(StringPool.PERIOD);
623    
624                    for (Statement statement : _statements) {
625                            if (statement.isExecuted()) {
626                                    continue;
627                            }
628    
629                            List<Flag> flags = statement.getFlags();
630    
631                            if (flags == null) {
632                                    continue;
633                            }
634    
635                            for (Flag flag : flags) {
636                                    String value = flag.getValue();
637    
638                                    if (value == null) {
639                                            continue;
640                                    }
641    
642                                    if (value.startsWith(name)) {
643                                            Map<String, Object> parameterMap =
644                                                    statement.getParameterMap();
645    
646                                            Object propertyValue = BeanUtil.getDeclaredProperty(
647                                                    object, value.substring(name.length()));
648    
649                                            parameterMap.put(flag.getName(), propertyValue);
650                                    }
651                                    else if (statement.isPushed() && value.startsWith(pushedName)) {
652                                            Map<String, Object> parameterMap =
653                                                    statement.getParameterMap();
654    
655                                            Object propertyValue = BeanUtil.getDeclaredProperty(
656                                                    statement._pushTarget,
657                                                    value.substring(pushedName.length()));
658    
659                                            parameterMap.put(flag.getName(), propertyValue);
660                                    }
661                            }
662                    }
663            }
664    
665            private String _command;
666            private List<String> _includes;
667            private HttpServletRequest _request;
668            private List<Statement> _statements = new ArrayList<Statement>();
669    
670            private class Flag extends NameValue<String, String> {
671            }
672    
673            private class Statement {
674    
675                    public List<Flag> getFlags() {
676                            return _flags;
677                    }
678    
679                    public String getMethod() {
680                            return _method;
681                    }
682    
683                    public String getName() {
684                            return _name;
685                    }
686    
687                    public Map<String, Object> getParameterMap() {
688                            return _parameterMap;
689                    }
690    
691                    public Statement getParentStatement() {
692                            return _parentStatement;
693                    }
694    
695                    public List<Statement> getVariableStatements() {
696                            return _variableStatements;
697                    }
698    
699                    public String[] getWhitelist() {
700                            return _whitelist;
701                    }
702    
703                    public boolean isExecuted() {
704                            return _executed;
705                    }
706    
707                    public boolean isInner() {
708                            return _inner;
709                    }
710    
711                    public boolean isPushed() {
712                            if (_pushTarget != null) {
713                                    return true;
714                            }
715    
716                            return false;
717                    }
718    
719                    public Object push(Object result) {
720                            if (_parentStatement == null) {
721                                    return null;
722                            }
723    
724                            _pushTarget = result;
725    
726                            Statement statement = getParentStatement();
727    
728                            String variableName = getName();
729    
730                            int index = variableName.indexOf(".$");
731    
732                            String beanName = variableName.substring(0, index);
733    
734                            result = BeanUtil.getDeclaredProperty(result, beanName);
735    
736                            statement.setName(
737                                    statement.getName() + StringPool.PERIOD + beanName);
738    
739                            variableName = variableName.substring(index + 1);
740    
741                            setName(variableName);
742    
743                            return result;
744                    }
745    
746                    public Object pop(Object result) {
747                            if (_pushTarget == null) {
748                                    return null;
749                            }
750    
751                            Statement statement = getParentStatement();
752    
753                            String statementName = statement.getName();
754    
755                            int index = statementName.lastIndexOf('.');
756    
757                            String beanName = statementName.substring(index + 1);
758    
759                            statementName = statementName.substring(0, index);
760    
761                            statement.setName(statementName);
762    
763                            setName(beanName + StringPool.PERIOD + getName());
764    
765                            BeanUtil.setDeclaredProperty(_pushTarget, beanName, result);
766    
767                            result = _pushTarget;
768    
769                            _pushTarget = null;
770    
771                            return result;
772                    }
773    
774                    public void setExecuted(boolean executed) {
775                            _executed = executed;
776                    }
777    
778                    public void setFlags(List<Flag> flags) {
779                            _flags = flags;
780                    }
781    
782                    public void setMethod(String method) {
783                            _method = method;
784                    }
785    
786                    public void setName(String name) {
787                            if (name.contains(".$")) {
788                                    _inner = true;
789                            }
790                            else {
791                                    _inner = false;
792                            }
793    
794                            _name = name;
795                    }
796    
797                    public void setParameterMap(Map<String, Object> parameterMap) {
798                            _parameterMap = parameterMap;
799                    }
800    
801                    public void setVariableStatements(List<Statement> variableStatements) {
802                            _variableStatements = variableStatements;
803                    }
804    
805                    public void setWhitelist(String[] whitelist) {
806                            _whitelist = whitelist;
807                    }
808    
809                    private Statement(Statement parentStatement) {
810                            _parentStatement = parentStatement;
811                    }
812    
813                    private boolean _executed;
814                    private List<Flag> _flags;
815                    private boolean _inner;
816                    private String _method;
817                    private String _name;
818                    private Map<String, Object> _parameterMap;
819                    private Statement _parentStatement;
820                    private Object _pushTarget;
821                    private List<Statement> _variableStatements;
822                    private String[] _whitelist;
823    
824            }
825    
826    }