001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.jsonwebservice.action;
016    
017    import com.liferay.portal.kernel.json.JSONFactoryUtil;
018    import com.liferay.portal.kernel.json.JSONIncludesManagerUtil;
019    import com.liferay.portal.kernel.json.JSONSerializable;
020    import com.liferay.portal.kernel.json.JSONSerializer;
021    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
022    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
023    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManagerUtil;
024    import com.liferay.portal.kernel.util.CamelCaseUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.Constants;
027    import com.liferay.portal.kernel.util.ListUtil;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Validator;
031    
032    import java.io.IOException;
033    
034    import java.lang.reflect.Array;
035    
036    import java.util.ArrayList;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Set;
042    
043    import javax.servlet.http.HttpServletRequest;
044    
045    import jodd.bean.BeanCopy;
046    import jodd.bean.BeanUtil;
047    
048    import jodd.servlet.ServletUtil;
049    
050    import jodd.util.NameValue;
051    
052    /**
053     * @author Igor Spasic
054     * @author Eduardo Lundgren
055     */
056    public class JSONWebServiceInvokerAction implements JSONWebServiceAction {
057    
058            public JSONWebServiceInvokerAction(HttpServletRequest request) {
059                    _request = request;
060    
061                    _command = request.getParameter(Constants.CMD);
062    
063                    if (_command == null) {
064                            try {
065                                    _command = ServletUtil.readRequestBody(request);
066                            }
067                            catch (IOException ioe) {
068                                    throw new IllegalArgumentException(ioe);
069                            }
070                    }
071            }
072    
073            @Override
074            public JSONWebServiceActionMapping getJSONWebServiceActionMapping() {
075                    return null;
076            }
077    
078            @Override
079            public Object invoke() throws Exception {
080                    Object command = JSONFactoryUtil.looseDeserializeSafe(_command);
081    
082                    List<Object> list = null;
083    
084                    boolean batchMode = false;
085    
086                    if (command instanceof List) {
087                            list = (List<Object>)command;
088    
089                            batchMode = true;
090                    }
091                    else if (command instanceof Map) {
092                            list = new ArrayList<Object>(1);
093    
094                            list.add(command);
095    
096                            batchMode = false;
097                    }
098                    else {
099                            throw new IllegalArgumentException();
100                    }
101    
102                    for (int i = 0; i < list.size(); i++) {
103                            Map<String, Map<String, Object>> map =
104                                    (Map<String, Map<String, Object>>)list.get(i);
105    
106                            if (map.isEmpty()) {
107                                    throw new IllegalArgumentException();
108                            }
109    
110                            Set<Map.Entry<String, Map<String, Object>>> entrySet =
111                                    map.entrySet();
112    
113                            Iterator<Map.Entry<String, Map<String, Object>>> iterator =
114                                    entrySet.iterator();
115    
116                            Map.Entry<String, Map<String, Object>> entry = iterator.next();
117    
118                            Statement statement = _parseStatement(
119                                    null, entry.getKey(), entry.getValue());
120    
121                            Object result = _executeStatement(statement);
122    
123                            list.set(i, result);
124                    }
125    
126                    Object result = null;
127    
128                    if (batchMode == false) {
129                            result = list.get(0);
130                    }
131                    else {
132                            result = list;
133                    }
134    
135                    return new InvokerResult(result);
136            }
137    
138            public class InvokerResult implements JSONSerializable {
139    
140                    @Override
141                    public String toJSONString() {
142                            if (_result == null) {
143                                    return JSONFactoryUtil.getNullJSON();
144                            }
145    
146                            JSONSerializer jsonSerializer =
147                                    JSONFactoryUtil.createJSONSerializer();
148    
149                            jsonSerializer.exclude("*.class");
150    
151                            for (Statement statement : _statements) {
152                                    if (_includes != null) {
153                                            for (String include : _includes) {
154                                                    jsonSerializer.include(include);
155                                            }
156                                    }
157    
158                                    String name = statement.getName();
159    
160                                    if (name == null) {
161                                            continue;
162                                    }
163    
164                                    String includeName = name.substring(1);
165    
166                                    _checkJSONSerializerIncludeName(includeName);
167    
168                                    jsonSerializer.include(includeName);
169                            }
170    
171                            return jsonSerializer.serialize(_result);
172                    }
173    
174                    public Object getResult() {
175                            return _result;
176                    }
177    
178                    private InvokerResult(Object result) {
179                            _result = result;
180                    }
181    
182                    private Object _result;
183    
184            }
185    
186            private void _addInclude(Statement statement, String name) {
187                    if (_includes == null) {
188                            _includes = new ArrayList<String>();
189                    }
190    
191                    StringBuilder sb = new StringBuilder();
192    
193                    while (statement._parentStatement != null) {
194                            String statementName = statement.getName().substring(1);
195    
196                            sb.insert(0, statementName + StringPool.PERIOD);
197    
198                            statement = statement._parentStatement;
199                    }
200    
201                    sb.append(name);
202    
203                    _includes.add(sb.toString());
204            }
205    
206            private Object _addVariableStatement(
207                            Statement variableStatement, Object result)
208                    throws Exception {
209    
210                    Statement statement = variableStatement.getParentStatement();
211    
212                    result = _populateFlags(statement, result);
213    
214                    String name = variableStatement.getName();
215    
216                    Object variableResult = _executeStatement(variableStatement);
217    
218                    Map<String, Object> map = _convertObjectToMap(statement, result, null);
219    
220                    if (!variableStatement.isInner()) {
221                            map.put(name.substring(1), variableResult);
222    
223                            return map;
224                    }
225    
226                    int index = name.indexOf(".$");
227    
228                    String innerObjectName = name.substring(0, index);
229    
230                    if (innerObjectName.contains(StringPool.PERIOD)) {
231                            throw new IllegalArgumentException(
232                                    "Inner properties with more than 1 level are not supported");
233                    }
234    
235                    Object innerObject = map.get(innerObjectName);
236    
237                    String innerPropertyName = name.substring(index + 2);
238    
239                    if (innerObject instanceof List) {
240                            List<Object> innerList = (List<Object>)innerObject;
241    
242                            List<Object> newInnerList = new ArrayList<Object>(innerList.size());
243    
244                            for (Object innerListElement : innerList) {
245                                    Map<String, Object> newInnerListElement = _convertObjectToMap(
246                                            statement, innerListElement, innerObjectName);
247    
248                                    newInnerListElement.put(innerPropertyName, variableResult);
249    
250                                    newInnerList.add(newInnerListElement);
251                            }
252    
253                            map.put(innerObjectName, newInnerList);
254                    }
255                    else {
256                            Map<String, Object> innerMap = _convertObjectToMap(
257                                    statement, innerObject, innerObjectName);
258    
259                            innerMap.put(innerPropertyName, variableResult);
260    
261                            map.put(innerObjectName, innerMap);
262                    }
263    
264                    return map;
265            }
266    
267            private Object _addVariableStatementList(
268                            Statement variableStatement, List<Object> resultList,
269                            List<Object> results)
270                    throws Exception {
271    
272                    for (Object object : resultList) {
273                            List<Object> listObject = _convertObjectToList(object);
274    
275                            if (listObject != null) {
276                                    Object value = _addVariableStatementList(
277                                            variableStatement, listObject, results);
278    
279                                    results.add(value);
280                            }
281                            else {
282                                    Object value = _addVariableStatement(variableStatement, object);
283    
284                                    results.add(value);
285                            }
286                    }
287    
288                    return results;
289            }
290    
291            private void _checkJSONSerializerIncludeName(String includeName) {
292                    if (includeName.contains(StringPool.STAR)) {
293                            throw new IllegalArgumentException(
294                                    includeName + " has special characters");
295                    }
296            }
297    
298            private List<Object> _convertObjectToList(Object object) {
299                    if (object == null) {
300                            return null;
301                    }
302    
303                    if (object instanceof List) {
304                            return (List<Object>)object;
305                    }
306    
307                    if (object instanceof Iterable) {
308                            List<Object> list = new ArrayList<Object>();
309    
310                            Iterable<?> iterable = (Iterable<?>)object;
311    
312                            Iterator<?> iterator = iterable.iterator();
313    
314                            while (iterator.hasNext()) {
315                                    list.add(iterator.next());
316                            }
317    
318                            return list;
319                    }
320    
321                    Class<?> clazz = object.getClass();
322    
323                    if (!clazz.isArray()) {
324                            return null;
325                    }
326    
327                    Class<?> componentType = clazz.getComponentType();
328    
329                    if (!componentType.isPrimitive()) {
330                            return ListUtil.toList((Object[])object);
331                    }
332    
333                    List<Object> list = new ArrayList<Object>();
334    
335                    for (int i = 0; i < Array.getLength(object); i++) {
336                            list.add(Array.get(object, i));
337                    }
338    
339                    return list;
340            }
341    
342            private Map<String, Object> _convertObjectToMap(
343                    Statement statement, Object object, String prefix) {
344    
345                    if (object instanceof Map) {
346                            return (Map<String, Object>)object;
347                    }
348    
349                    Class<?> clazz = object.getClass();
350    
351                    HashMap<Object, Object> destinationMap = new HashMap<Object, Object>();
352    
353                    BeanCopy beanCopy = BeanCopy.beans(object, destinationMap);
354    
355                    beanCopy.exclude(JSONIncludesManagerUtil.lookupExcludes(clazz));
356    
357                    beanCopy.copy();
358    
359                    object = destinationMap;
360    
361                    String[] includes = JSONIncludesManagerUtil.lookupIncludes(clazz);
362    
363                    for (String include : includes) {
364                            if (Validator.isNotNull(prefix)) {
365                                    include = prefix + StringPool.PERIOD + include;
366                            }
367    
368                            _addInclude(statement, include);
369                    }
370    
371                    return (Map<String, Object>)object;
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<String, Object>(
463                            whitelist.length);
464    
465                    for (String key : whitelist) {
466                            Object value = map.get(key);
467    
468                            whitelistMap.put(key, value);
469                    }
470    
471                    return whitelistMap;
472            }
473    
474            private Statement _parseStatement(
475                    Statement parentStatement, String assignment,
476                    Map<String, Object> statementBody) {
477    
478                    Statement statement = new Statement(parentStatement);
479    
480                    _statements.add(statement);
481    
482                    int x = assignment.indexOf(StringPool.EQUAL);
483    
484                    if (x == -1) {
485                            statement.setMethod(assignment.trim());
486                    }
487                    else {
488                            String name = assignment.substring(0, x).trim();
489    
490                            int y = name.indexOf(StringPool.OPEN_BRACKET);
491    
492                            if (y != -1) {
493                                    String whitelistString = name.substring(
494                                            y + 1, name.length() - 1);
495    
496                                    String[] whiteList = StringUtil.split(whitelistString);
497    
498                                    for (int i = 0; i < whiteList.length; i++) {
499                                            whiteList[i] = whiteList[i].trim();
500                                    }
501    
502                                    statement.setWhitelist(whiteList);
503    
504                                    name = name.substring(0, y);
505                            }
506    
507                            statement.setName(name);
508    
509                            statement.setMethod(assignment.substring(x + 1).trim());
510                    }
511    
512                    HashMap<String, Object> parameterMap = new HashMap<String, Object>(
513                            statementBody.size());
514    
515                    statement.setParameterMap(parameterMap);
516    
517                    for (String key : statementBody.keySet()) {
518                            if (key.startsWith(StringPool.AT)) {
519                                    String value = (String)statementBody.get(key);
520    
521                                    List<Flag> flags = statement.getFlags();
522    
523                                    if (flags == null) {
524                                            flags = new ArrayList<Flag>();
525    
526                                            statement.setFlags(flags);
527                                    }
528    
529                                    Flag flag = new Flag();
530    
531                                    flag.setName(key.substring(1));
532                                    flag.setValue(value);
533    
534                                    flags.add(flag);
535                            }
536                            else if (key.startsWith(StringPool.DOLLAR) || key.contains(".$")) {
537                                    Map<String, Object> map =
538                                            (Map<String, Object>)statementBody.get(key);
539    
540                                    List<Statement> variableStatements =
541                                            statement.getVariableStatements();
542    
543                                    if (variableStatements == null) {
544                                            variableStatements = new ArrayList<Statement>();
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 = statementBody.get(key);
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                                            Map<String, Object> parameterMap =
634                                                    statement.getParameterMap();
635    
636                                            Object propertyValue = BeanUtil.getDeclaredProperty(
637                                                    object, value.substring(name.length()));
638    
639                                            parameterMap.put(flag.getName(), propertyValue);
640                                    }
641                                    else if (statement.isPushed() && value.startsWith(pushedName)) {
642                                            Map<String, Object> parameterMap =
643                                                    statement.getParameterMap();
644    
645                                            Object propertyValue = BeanUtil.getDeclaredProperty(
646                                                    statement._pushTarget,
647                                                    value.substring(pushedName.length()));
648    
649                                            parameterMap.put(flag.getName(), propertyValue);
650                                    }
651                            }
652                    }
653            }
654    
655            private String _command;
656            private List<String> _includes;
657            private HttpServletRequest _request;
658            private List<Statement> _statements = new ArrayList<Statement>();
659    
660            private class Flag extends NameValue<String, String> {
661            }
662    
663            private class Statement {
664    
665                    public List<Flag> getFlags() {
666                            return _flags;
667                    }
668    
669                    public String getMethod() {
670                            return _method;
671                    }
672    
673                    public String getName() {
674                            return _name;
675                    }
676    
677                    public Map<String, Object> getParameterMap() {
678                            return _parameterMap;
679                    }
680    
681                    public Statement getParentStatement() {
682                            return _parentStatement;
683                    }
684    
685                    public List<Statement> getVariableStatements() {
686                            return _variableStatements;
687                    }
688    
689                    public String[] getWhitelist() {
690                            return _whitelist;
691                    }
692    
693                    public boolean isExecuted() {
694                            return _executed;
695                    }
696    
697                    public boolean isInner() {
698                            return _inner;
699                    }
700    
701                    public boolean isPushed() {
702                            if (_pushTarget != null) {
703                                    return true;
704                            }
705    
706                            return false;
707                    }
708    
709                    public Object push(Object result) {
710                            if (_parentStatement == null) {
711                                    return null;
712                            }
713    
714                            _pushTarget = result;
715    
716                            Statement statement = getParentStatement();
717    
718                            String variableName = getName();
719    
720                            int index = variableName.indexOf(".$");
721    
722                            String beanName = variableName.substring(0, index);
723    
724                            result = BeanUtil.getDeclaredProperty(result, beanName);
725    
726                            statement.setName(
727                                    statement.getName() + StringPool.PERIOD + beanName);
728    
729                            variableName = variableName.substring(index + 1);
730    
731                            setName(variableName);
732    
733                            return result;
734                    }
735    
736                    public Object pop(Object result) {
737                            if (_pushTarget == null) {
738                                    return null;
739                            }
740    
741                            Statement statement = getParentStatement();
742    
743                            String statementName = statement.getName();
744    
745                            int index = statementName.lastIndexOf('.');
746    
747                            String beanName = statementName.substring(index + 1);
748    
749                            statementName = statementName.substring(0, index);
750    
751                            statement.setName(statementName);
752    
753                            setName(beanName + StringPool.PERIOD + getName());
754    
755                            BeanUtil.setDeclaredProperty(_pushTarget, beanName, result);
756    
757                            result = _pushTarget;
758    
759                            _pushTarget = null;
760    
761                            return result;
762                    }
763    
764                    public void setExecuted(boolean executed) {
765                            _executed = executed;
766                    }
767    
768                    public void setFlags(List<Flag> flags) {
769                            _flags = flags;
770                    }
771    
772                    public void setMethod(String method) {
773                            _method = method;
774                    }
775    
776                    public void setName(String name) {
777                            if (name.contains(".$")) {
778                                    _inner = true;
779                            }
780                            else {
781                                    _inner = false;
782                            }
783    
784                            _name = name;
785                    }
786    
787                    public void setParameterMap(Map<String, Object> parameterMap) {
788                            _parameterMap = parameterMap;
789                    }
790    
791                    public void setVariableStatements(List<Statement> variableStatements) {
792                            _variableStatements = variableStatements;
793                    }
794    
795                    public void setWhitelist(String[] whitelist) {
796                            _whitelist = whitelist;
797                    }
798    
799                    private Statement(Statement parentStatement) {
800                            _parentStatement = parentStatement;
801                    }
802    
803                    private boolean _executed;
804                    private List<Flag> _flags;
805                    private boolean _inner;
806                    private String _method;
807                    private String _name;
808                    private Map<String, Object> _parameterMap;
809                    private Statement _parentStatement;
810                    private Object _pushTarget;
811                    private List<Statement> _variableStatements;
812                    private String[] _whitelist;
813    
814            }
815    
816    }