001/*
002 * (C) Copyright 2006-2009 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo
016 */
017
018package org.nuxeo.ecm.platform.publisher.remoting.marshaling;
019
020import org.dom4j.Document;
021import org.dom4j.DocumentException;
022import org.dom4j.DocumentFactory;
023import org.dom4j.DocumentHelper;
024import org.dom4j.QName;
025import org.nuxeo.ecm.core.api.CoreSession;
026import org.nuxeo.ecm.core.api.DocumentLocation;
027import org.nuxeo.ecm.core.api.DocumentModel;
028import org.nuxeo.ecm.core.api.NuxeoException;
029import org.nuxeo.ecm.platform.publisher.api.PublicationNode;
030import org.nuxeo.ecm.platform.publisher.api.PublishedDocument;
031import org.nuxeo.ecm.platform.publisher.remoting.marshaling.interfaces.DocumentLocationMarshaler;
032import org.nuxeo.ecm.platform.publisher.remoting.marshaling.interfaces.DocumentModelMarshaler;
033import org.nuxeo.ecm.platform.publisher.remoting.marshaling.interfaces.PublicationNodeMarshaler;
034import org.nuxeo.ecm.platform.publisher.remoting.marshaling.interfaces.PublishedDocumentMarshaler;
035import org.nuxeo.ecm.platform.publisher.remoting.marshaling.interfaces.RemotePublisherMarshaler;
036
037import java.util.ArrayList;
038import java.util.HashMap;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Map;
042
043/**
044 * Default marshaler for RPC calls between 2 servers
045 *
046 * @author tiry
047 */
048public class DefaultMarshaler extends AbstractDefaultXMLMarshaler implements RemotePublisherMarshaler {
049
050    private static final String PARAM_PATTERN = "$PARAM";
051
052    private static final String RESULT_PATTERN = "$RESULT$";
053
054    protected PublicationNodeMarshaler nodeMarshaler;
055
056    protected PublishedDocumentMarshaler publishedDocumentMarshaler;
057
058    protected DocumentModelMarshaler documentModelMarshaler;
059
060    protected DocumentLocationMarshaler docLocMarshaler;
061
062    protected Map<String, String> params = new HashMap<String, String>();
063
064    protected static QName rootParametersTag = DocumentFactory.getInstance().createQName("parameters",
065            publisherSerializerNSPrefix, publisherSerializerNS);
066
067    protected static QName parameterTag = DocumentFactory.getInstance().createQName("parameter",
068            publisherSerializerNSPrefix, publisherSerializerNS);
069
070    protected static QName rootResultTag = DocumentFactory.getInstance().createQName("result",
071            publisherSerializerNSPrefix, publisherSerializerNS);
072
073    protected CoreSession session;
074
075    public DefaultMarshaler() {
076        this(null);
077    }
078
079    public DefaultMarshaler(CoreSession session) {
080        this.nodeMarshaler = new DefaultPublicationNodeMarshaler();
081        this.publishedDocumentMarshaler = new DefaultPublishedDocumentMarshaler();
082        this.documentModelMarshaler = new CoreIODocumentModelMarshaler();
083        this.docLocMarshaler = new DefaultDocumentLocationMarshaler();
084        this.session = session;
085    }
086
087    public String marshallParameters(List<Object> params) {
088
089        if (params == null) {
090            return "null";
091        }
092        String env = buildParameterEnvelope(params.size());
093        int idx = 1;
094        for (Object param : params) {
095            String strParam = marshalSingleObject(param);
096            env = env.replace(PARAM_PATTERN + idx + "$", strParam);
097            idx += 1;
098        }
099        return env;
100    }
101
102    public String marshallResult(Object result) {
103        String res = buildResultEnvelope();
104        String strResult = marshalSingleObject(result);
105        res = res.replace(RESULT_PATTERN, strResult);
106        return res;
107    }
108
109    public List<Object> unMarshallParameters(String data) {
110        return unMarshallParameters(data, session);
111    }
112
113    protected List<Object> unMarshallParameters(String data, CoreSession session) {
114
115        List<Object> params = new ArrayList<Object>();
116
117        Document document;
118        try {
119            document = DocumentHelper.parseText(data);
120            org.dom4j.Element rootElem = document.getRootElement();
121            for (Iterator i = rootElem.elementIterator(parameterTag); i.hasNext();) {
122                org.dom4j.Element param = (org.dom4j.Element) i.next();
123                if (param.elements().size() > 0) {
124                    String xmlParam = ((org.dom4j.Element) param.elements().get(0)).asXML();
125                    params.add(unMarshalSingleObject(xmlParam, session));
126                } else {
127                    String value = param.getText();
128                    if ("null".equals(value)) {
129                        value = null;
130                    }
131                    params.add(value);
132                }
133            }
134        } catch (DocumentException e) {
135            throw new NuxeoException("Error during unmarshaling of parameters", e);
136        }
137        return params;
138    }
139
140    public Object unMarshallResult(String data) {
141        return unMarshallResult(data, session);
142    }
143
144    protected Object unMarshallResult(String data, CoreSession coreSession) {
145        Document document;
146        try {
147            document = DocumentHelper.parseText(data);
148            org.dom4j.Element rootElem = document.getRootElement();
149
150            if (rootElem.elements().size() == 0) {
151                return rootElem.getText();
152            } else {
153                return unMarshalSingleObject(((org.dom4j.Element) rootElem.elements().get(0)).asXML(), coreSession);
154            }
155        } catch (DocumentException e) {
156            throw new NuxeoException("Error during unmarshaling Result", e);
157        }
158    }
159
160    protected String buildParameterEnvelope(int nbParams) {
161
162        org.dom4j.Element rootElem = DocumentFactory.getInstance().createElement(rootParametersTag);
163        rootElem.addNamespace(publisherSerializerNSPrefix, publisherSerializerNS);
164        org.dom4j.Document rootDoc = DocumentFactory.getInstance().createDocument(rootElem);
165
166        for (int i = 1; i <= nbParams; i++) {
167            org.dom4j.Element pathElem = rootElem.addElement(parameterTag);
168            pathElem.setText(PARAM_PATTERN + i + "$");
169        }
170        return rootDoc.asXML();
171    }
172
173    protected String buildResultEnvelope() {
174
175        org.dom4j.Element rootElem = DocumentFactory.getInstance().createElement(rootResultTag);
176        rootElem.addNamespace(publisherSerializerNSPrefix, publisherSerializerNS);
177        org.dom4j.Document rootDoc = DocumentFactory.getInstance().createDocument(rootElem);
178        rootElem.setText(RESULT_PATTERN);
179        return rootDoc.asXML();
180    }
181
182    protected Object unMarshalSingleObject(String xml, CoreSession coreSession) {
183        Document document;
184        try {
185            document = DocumentHelper.parseText(xml);
186        } catch (DocumentException e) {
187            throw new NuxeoException("Error during unmarshaling", e);
188        }
189        org.dom4j.Element rootElem = document.getRootElement();
190
191        QName qname = rootElem.getQName();
192        String name = rootElem.getName();
193
194        if (name.equals("publicationNode")) {
195            return nodeMarshaler.unMarshalPublicationNode(xml);
196        } else if (name.equals("publishedDocument")) {
197            return publishedDocumentMarshaler.unMarshalPublishedDocument(xml);
198        } else if (name.equals("document")) {
199            return documentModelMarshaler.unMarshalDocument(xml, coreSession);
200        } else if (name.equals("documentLocation")) {
201            return docLocMarshaler.unMarshalDocumentLocation(xml);
202        } else if (name.equals("list")) {
203            List<Object> lst = new ArrayList<Object>();
204            for (Iterator i = rootElem.elementIterator("listitem"); i.hasNext();) {
205                org.dom4j.Element el = (org.dom4j.Element) i.next();
206                if (el.elements().size() == 0) {
207                    lst.add(el.getText());
208                } else {
209                    lst.add(unMarshalSingleObject(((org.dom4j.Element) el.elements().get(0)).asXML(), coreSession));
210                }
211            }
212            return lst;
213        } else if (name.equals("map")) {
214            Map map = new HashMap();
215            for (Iterator i = rootElem.elementIterator("mapitem"); i.hasNext();) {
216                org.dom4j.Element el = (org.dom4j.Element) i.next();
217
218                Object value = null;
219                if (el.elements().size() > 0) {
220                    value = unMarshalSingleObject(((org.dom4j.Element) (el).elements().get(0)).asXML(), coreSession);
221                } else {
222                    value = el.getText();
223                }
224                String key = el.attributeValue("name");
225                map.put(key, value);
226            }
227            return map;
228        }
229
230        throw new NuxeoException("Unable to unmarshal data");
231
232    }
233
234    protected String marshalSingleObject(Object ob) {
235        if (ob == null) {
236            return "null";
237        } else if (ob instanceof String) {
238            return (String) ob;
239        } else if (ob instanceof DocumentModel) {
240            return cleanUpXml(documentModelMarshaler.marshalDocument((DocumentModel) ob));
241        } else if (ob instanceof PublicationNode) {
242            return nodeMarshaler.marshalPublicationNode((PublicationNode) ob);
243        } else if (ob instanceof PublishedDocument) {
244            return publishedDocumentMarshaler.marshalPublishedDocument((PublishedDocument) ob);
245        } else if (ob instanceof DocumentLocation) {
246            return docLocMarshaler.marshalDocumentLocation((DocumentLocation) ob);
247        } else if (ob instanceof List) {
248            StringBuffer sb = new StringBuffer();
249            sb.append("<list>");
250
251            List list = (List) ob;
252            for (Object itemOb : list) {
253                sb.append("<listitem>");
254                sb.append(marshalSingleObject(itemOb));
255                sb.append("</listitem>");
256            }
257            sb.append("</list>");
258            return sb.toString();
259        } else if (ob instanceof Map) {
260            StringBuffer sb = new StringBuffer();
261            sb.append("<map>");
262            Map map = (Map) ob;
263            for (Object key : map.keySet()) {
264                sb.append("<mapitem ");
265                sb.append("name=\"");
266                sb.append((String) key);
267                sb.append("\">");
268                sb.append(marshalSingleObject(map.get(key)));
269                sb.append("</mapitem>");
270            }
271            sb.append("</map>");
272            return sb.toString();
273        }
274        throw new NuxeoException("unable to marshal object of class " + ob.getClass().getName());
275    }
276
277    public void setAssociatedCoreSession(CoreSession session) {
278        this.session = session;
279    }
280
281    public void setParameters(Map<String, String> params) {
282        this.params.putAll(params);
283        docLocMarshaler.setOriginatingServer(params.get("originalServer"));
284        documentModelMarshaler.setOriginatingServer(params.get("originalServer"));
285    }
286}