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