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 (String key : statementBody.keySet()) {
517                            if (key.startsWith(StringPool.AT)) {
518                                    String value = (String)statementBody.get(key);
519    
520                                    List<Flag> flags = statement.getFlags();
521    
522                                    if (flags == null) {
523                                            flags = new ArrayList<>();
524    
525                                            statement.setFlags(flags);
526                                    }
527    
528                                    Flag flag = new Flag();
529    
530                                    flag.setName(key.substring(1));
531                                    flag.setValue(value);
532    
533                                    flags.add(flag);
534                            }
535                            else if (key.startsWith(StringPool.DOLLAR) || key.contains(".$")) {
536                                    Map<String, Object> map =
537                                            (Map<String, Object>)statementBody.get(key);
538    
539                                    List<Statement> variableStatements =
540                                            statement.getVariableStatements();
541    
542                                    if (variableStatements == null) {
543                                            variableStatements = new ArrayList<>();
544    
545                                            statement.setVariableStatements(variableStatements);
546                                    }
547    
548                                    Statement variableStatement = _parseStatement(
549                                            statement, key, map);
550    
551                                    variableStatements.add(variableStatement);
552                            }
553                            else {
554                                    Object value = statementBody.get(key);
555    
556                                    parameterMap.put(CamelCaseUtil.normalizeCamelCase(key), value);
557                            }
558                    }
559    
560                    return statement;
561            }
562    
563            private Object _populateFlags(Statement statement, Object result) {
564                    List<Object> listResult = _convertObjectToList(result);
565    
566                    if (listResult != null) {
567                            result = _populateFlagsList(
568                                    statement.getName(), listResult, new ArrayList<Object>());
569                    }
570                    else {
571                            _populateFlagsObject(statement.getName(), result);
572                    }
573    
574                    return result;
575            }
576    
577            private List<Object> _populateFlagsList(
578                    String name, List<Object> list, List<Object> results) {
579    
580                    for (Object object : list) {
581                            List<Object> listObject = _convertObjectToList(object);
582    
583                            if (listObject != null) {
584                                    Object value = _populateFlagsList(name, listObject, results);
585    
586                                    results.add(value);
587                            }
588                            else {
589                                    _populateFlagsObject(name, object);
590    
591                                    results.add(object);
592                            }
593                    }
594    
595                    return results;
596            }
597    
598            private void _populateFlagsObject(String name, Object object) {
599                    if (name == null) {
600                            return;
601                    }
602    
603                    String pushedName = null;
604    
605                    int index = name.indexOf(CharPool.PERIOD);
606    
607                    if (index != -1) {
608                            pushedName = name.substring(0, index + 1);
609                    }
610    
611                    name = name.concat(StringPool.PERIOD);
612    
613                    for (Statement statement : _statements) {
614                            if (statement.isExecuted()) {
615                                    continue;
616                            }
617    
618                            List<Flag> flags = statement.getFlags();
619    
620                            if (flags == null) {
621                                    continue;
622                            }
623    
624                            for (Flag flag : flags) {
625                                    String value = flag.getValue();
626    
627                                    if (value == null) {
628                                            continue;
629                                    }
630    
631                                    if (value.startsWith(name)) {
632                                            Map<String, Object> parameterMap =
633                                                    statement.getParameterMap();
634    
635                                            Object propertyValue = BeanUtil.getDeclaredProperty(
636                                                    object, value.substring(name.length()));
637    
638                                            parameterMap.put(flag.getName(), propertyValue);
639                                    }
640                                    else if (statement.isPushed() && value.startsWith(pushedName)) {
641                                            Map<String, Object> parameterMap =
642                                                    statement.getParameterMap();
643    
644                                            Object propertyValue = BeanUtil.getDeclaredProperty(
645                                                    statement._pushTarget,
646                                                    value.substring(pushedName.length()));
647    
648                                            parameterMap.put(flag.getName(), propertyValue);
649                                    }
650                            }
651                    }
652            }
653    
654            private static final JsonSerializer _jsonSerializer = new JsonSerializer();
655    
656            private final String _command;
657            private List<String> _includes;
658            private final HttpServletRequest _request;
659            private final List<Statement> _statements = new ArrayList<>();
660    
661            private class Flag extends NameValue<String, String> {
662            }
663    
664            private class Statement {
665    
666                    public List<Flag> getFlags() {
667                            return _flags;
668                    }
669    
670                    public String getMethod() {
671                            return _method;
672                    }
673    
674                    public String getName() {
675                            return _name;
676                    }
677    
678                    public Map<String, Object> getParameterMap() {
679                            return _parameterMap;
680                    }
681    
682                    public Statement getParentStatement() {
683                            return _parentStatement;
684                    }
685    
686                    public List<Statement> getVariableStatements() {
687                            return _variableStatements;
688                    }
689    
690                    public String[] getWhitelist() {
691                            return _whitelist;
692                    }
693    
694                    public boolean isExecuted() {
695                            return _executed;
696                    }
697    
698                    public boolean isInner() {
699                            return _inner;
700                    }
701    
702                    public boolean isPushed() {
703                            if (_pushTarget != null) {
704                                    return true;
705                            }
706    
707                            return false;
708                    }
709    
710                    public Object pop(Object result) {
711                            if (_pushTarget == null) {
712                                    return null;
713                            }
714    
715                            Statement statement = getParentStatement();
716    
717                            String statementName = statement.getName();
718    
719                            int index = statementName.lastIndexOf('.');
720    
721                            String beanName = statementName.substring(index + 1);
722    
723                            statementName = statementName.substring(0, index);
724    
725                            statement.setName(statementName);
726    
727                            setName(beanName + StringPool.PERIOD + getName());
728    
729                            BeanUtil.setDeclaredProperty(_pushTarget, beanName, result);
730    
731                            result = _pushTarget;
732    
733                            _pushTarget = null;
734    
735                            return result;
736                    }
737    
738                    public Object push(Object result) {
739                            if (_parentStatement == null) {
740                                    return null;
741                            }
742    
743                            _pushTarget = result;
744    
745                            Statement statement = getParentStatement();
746    
747                            String variableName = getName();
748    
749                            int index = variableName.indexOf(".$");
750    
751                            String beanName = variableName.substring(0, index);
752    
753                            result = BeanUtil.getDeclaredProperty(result, beanName);
754    
755                            statement.setName(
756                                    statement.getName() + StringPool.PERIOD + beanName);
757    
758                            variableName = variableName.substring(index + 1);
759    
760                            setName(variableName);
761    
762                            return result;
763                    }
764    
765                    public void setExecuted(boolean executed) {
766                            _executed = executed;
767                    }
768    
769                    public void setFlags(List<Flag> flags) {
770                            _flags = flags;
771                    }
772    
773                    public void setMethod(String method) {
774                            _method = method;
775                    }
776    
777                    public void setName(String name) {
778                            if (name.contains(".$")) {
779                                    _inner = true;
780                            }
781                            else {
782                                    _inner = false;
783                            }
784    
785                            _name = name;
786                    }
787    
788                    public void setParameterMap(Map<String, Object> parameterMap) {
789                            _parameterMap = parameterMap;
790                    }
791    
792                    public void setVariableStatements(List<Statement> variableStatements) {
793                            _variableStatements = variableStatements;
794                    }
795    
796                    public void setWhitelist(String[] whitelist) {
797                            _whitelist = whitelist;
798                    }
799    
800                    private Statement(Statement parentStatement) {
801                            _parentStatement = parentStatement;
802                    }
803    
804                    private boolean _executed;
805                    private List<Flag> _flags;
806                    private boolean _inner;
807                    private String _method;
808                    private String _name;
809                    private Map<String, Object> _parameterMap;
810                    private Statement _parentStatement;
811                    private Object _pushTarget;
812                    private List<Statement> _variableStatements;
813                    private String[] _whitelist;
814    
815            }
816    
817    }