001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.http.rpc.server;
021 
022 import java.io.IOException;
023 
024 import javax.xml.parsers.DocumentBuilderFactory;
025 import javax.xml.parsers.ParserConfigurationException;
026 
027 import org.w3c.dom.Document;
028 import org.w3c.dom.Element;
029 import org.w3c.dom.Node;
030 import org.w3c.dom.NodeList;
031 import org.xml.sax.InputSource;
032 import org.xml.sax.SAXException;
033 
034 import js.ArrayLike;
035 import js.Js;
036 import js.ObjectLike;
037 import js.Vars;
038 import jsx.core.ArrayLikes;
039 import jsx.http.rpc.XMLHttpContext;
040 
041 /**
042  * <p>An abstract class for XML & HTTP serialization contexts at server-side.</p>
043  * 
044  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
045  */
046 public abstract class XMLHttpServer extends XMLHttpContext
047 {
048     /**
049      * <p>The default constructor for the contexts of this class type.</p>
050      * <p>The constructor simply invokes the default constructor of the super class.</p>
051      * @since 1.0
052      */
053     protected XMLHttpServer() {
054         super();
055     }
056 
057     /**
058      * <p>Calls this context as a server-side method with the base object and arguments.</p>
059      * @param base The base object for calling.
060      * @param args An array of arguments for the calling.
061      * @return The result of the call.
062      * @since 1.0
063      */
064     protected abstract Object call(ObjectLike base, ArrayLike<?> args);
065 
066     /**
067      * <p>Makes this connection context respond against a {@link InputSource}.</p>
068      * @param is The {@link InputSource} to the connection context.
069      * @return The response XML document for the remote call.
070      * @since 1.0
071      */
072     public final String respond(InputSource is)
073         throws ParserConfigurationException, SAXException, IOException {
074         Document request = DocumentBuilderFactory.newInstance()
075             .newDocumentBuilder().parse(is);
076         updateContext(request);
077         ObjectLike base = getBase(request);
078         ArrayLike<?> args = getArguments(request);
079         Object ret = call(base, args);
080         if (Js.not(type(ret))) {
081             sid(unwrap(ret));
082         }
083         String s = xelt(CNTX, serialize());
084         if (ret != null) {
085             s = Js.add(xelt(RTRN, serialize(ret, 0)), s);
086         }
087         s = Js.add(xelt(SIDS, sids()), s);
088         release();
089         return doc(xelt(XML, s));
090     }
091 
092     private static final Node getFirstByTag(Node n, String tag) {
093         return getElementsByTagName(n, tag).item(0);
094     }
095 
096     private final Object deserialize(Node n) {
097         String t = n.getNodeName();
098         if (t == NUM) {
099             return Double.parseDouble(getText(n));
100         }
101         if (t == STR) {
102             return getText(n);
103         }
104         return object(getText(n));
105     }
106 
107     private final ObjectLike getBase(Document request) {
108         return object(getText(getFirstByTag(request, BASE).getFirstChild()));
109     }
110 
111     private final ArrayLike<?> getArguments(Document request) {
112         ArrayLike<?> ret = new Vars<Object>().var();
113         Node arg = getFirstByTag(request, ARGS).getFirstChild();
114         while (arg != null) {
115             ArrayLikes.push(ret, deserialize(arg));
116             arg = arg.getNextSibling();
117         }
118         return ret;
119     }
120 
121     private final void updateContext(Document request) {
122         expand(getText(getFirstByTag(request, SIDS)));
123         Node n = getFirstByTag(request, CNTX).getFirstChild();
124         while (Js.be(n)) {
125             updateObject(n);
126             n = n.getNextSibling();
127         }
128     }
129 
130     private final void updateObject(Node n) {
131         ObjectLike o = object(getText(getFirstByTag(n, SID)));
132         NodeList flds = getElementsByTagName(n, FLD);
133         for (int i = 0, len = flds.getLength(); i < len; i++) {
134             Node f = flds.item(i);
135             o.var(
136                     getText(
137                             getFirstByTag(f, IDX)
138                     ),
139                     deserialize(
140                             getFirstByTag(f, VAL).getFirstChild()
141                     )
142             );
143         }
144     }
145 
146     private static final NodeList getElementsByTagName(Node n, String tagname) {
147         return n instanceof Document ? ((Document)n).getElementsByTagName(tagname) :
148             ((Element)n).getElementsByTagName(tagname);
149     }
150 
151     private static final String getText(Node n) {
152         return n.getFirstChild().getNodeValue();
153     }
154 }