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