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