1 /*------------------------------------------------------------------------------
  2 Name:      XmlScriptClient.java
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 ------------------------------------------------------------------------------*/
  6 package org.xmlBlaster.client.script;
  7 
  8 import java.util.logging.Logger;
  9 import java.util.logging.Level;
 10 import org.xmlBlaster.util.Global;
 11 import org.xmlBlaster.util.MsgUnit;
 12 import org.xmlBlaster.util.XmlBlasterException;
 13 import org.xmlBlaster.client.I_Callback;
 14 import org.xmlBlaster.client.I_XmlBlasterAccess;
 15 import org.xmlBlaster.client.key.UpdateKey;
 16 import org.xmlBlaster.client.qos.ConnectQos;
 17 import org.xmlBlaster.client.qos.ConnectReturnQos;
 18 import org.xmlBlaster.client.qos.DisconnectQos;
 19 import org.xmlBlaster.client.qos.EraseReturnQos;
 20 import org.xmlBlaster.client.qos.PublishReturnQos;
 21 import org.xmlBlaster.client.qos.SubscribeReturnQos;
 22 import org.xmlBlaster.client.qos.UnSubscribeReturnQos;
 23 import org.xmlBlaster.client.qos.UpdateQos;
 24 import org.xmlBlaster.util.def.ErrorCode;
 25 import org.xmlBlaster.util.def.MethodName;
 26 import org.xmlBlaster.util.qos.ConnectQosData;
 27 import org.xmlBlaster.util.qos.ConnectQosSaxFactory;
 28 import org.xmlBlaster.util.qos.DisconnectQosData;
 29 import org.xmlBlaster.util.qos.DisconnectQosSaxFactory;
 30 import org.xmlBlaster.util.xbformat.MsgInfo;
 31 
 32 import java.io.FileOutputStream;
 33 import java.io.FileReader;
 34 import java.io.OutputStream;
 35 import java.util.HashMap;
 36 
 37 /**
 38  * XmlScriptClient
 39  * <p>
 40  * Example for usage:
 41  * </p>
 42  * <p>
 43  * <tt>
 44  * java javaclients.XmlScript -requestFile inFile.xml -responseFile outFile.xml -updateFile updFile.xml
 45  * </tt>
 46  * </p>
 47  * @author <a href="mailto:michele@laghi.eu">Michele Laghi</a>
 48  * @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/client.script.html">The client.script requirement</a>
 49  */
 50 public class XmlScriptClient extends XmlScriptInterpreter implements I_Callback {
 51    
 52    private final String ME = "XmlScriptClient";
 53    private static Logger log = Logger.getLogger(XmlScriptClient.class.getName());
 54    private I_XmlBlasterAccess access;
 55    
 56    private boolean isConnected;
 57 
 58    private I_MsgUnitCb msgUnitCb;
 59    
 60    private final Global glob;
 61    private I_Callback callback;
 62    private ConnectQosSaxFactory connectQosFactory;
 63    private DisconnectQosSaxFactory disconnectQosFactory;
 64    
 65    // Teh default is to re-throw only connect() exception
 66    private boolean throwAllExceptions;
 67    
 68    private boolean allowImplicitConnect = true;
 69 
 70    /**
 71     * This constructor is the most generic one (more degrees of freedom)
 72     * @param glob the global to use
 73     * @param access the I_XmlBlasterAccess to use (can be different from the default 
 74     *        given by the global.
 75     * @param callback The I_Callback implementation to be used (you can provide your own desidered behaviour)
 76     * @param attachments the attachments where to search when a content is stored in the attachment (with the 'link' attribute)
 77     * @param out the OutputStream where to send the responses of the invocations done to xmlBlaster
 78     */
 79    public XmlScriptClient(Global glob, I_XmlBlasterAccess access, I_Callback callback, HashMap attachments, OutputStream out) {
 80       super(glob, attachments, out);
 81       this.glob = glob;
 82 
 83       this.access = access;
 84       this.callback = callback;
 85       this.connectQosFactory = new ConnectQosSaxFactory(this.glob);
 86       this.disconnectQosFactory = new DisconnectQosSaxFactory(this.glob);
 87       
 88       try {
 89        this.allowImplicitConnect = this.glob.get("xmlBlaster/XmlScript/allowImplicitConnect", this.allowImplicitConnect, null, null);
 90       } catch (XmlBlasterException e) {
 91        e.printStackTrace();
 92      }
 93 
 94       if (this.access != null) {
 95          this.isConnected = this.access.isConnected();
 96       }
 97    }
 98 
 99    /**
100     * This is a convenience constructor which takes the default I_Callback implementation provided
101     * (StreamCallback).
102     *  
103     * @param glob the global to use
104     * @param access the I_XmlBlasterAccess to use (can be different from the default 
105     *        given by the global.
106     * @param cbStream the OutputStream where to send the information coming in
107     *        asynchroneously via the update method (could be different from the
108     *        synchroneous output stream).
109     * @param responseStream the synchroneous OutputStream
110     * @param attachments the attachments where to find attached contents
111     * 
112     * @see StreamCallback
113     */
114    public XmlScriptClient(Global glob, I_XmlBlasterAccess access, OutputStream cbStream, OutputStream responseStream, HashMap attachments) {
115       this(glob, access, new StreamCallback(glob, cbStream, "  "), attachments, responseStream);
116    }
117 
118    /**
119     * Convenience constructor which takes a minimal amount of parameters. The 
120     * accessor taken is the one provided by the given global. The I_Callback 
121     * implementation used is the StreamCallback. The asynchroneous output is sent
122     * to the same stream as the synchroneous one. 
123     * @param glob the global to use. The I_XmlBlasterAccess will be taken from
124     *        it.
125     * @param out. The OutputStream used for all outputs (sync and async).
126     */
127    public XmlScriptClient(Global glob, OutputStream out) {
128       this(glob, glob.getXmlBlasterAccess(), out, out, null);
129    }
130    
131    /**
132     * You can register a callback which can manipulate the MsgUnit just
133     * before it is sent. 
134     */
135    public void registerMsgUnitCb(I_MsgUnitCb msgUnitCb) {
136       this.msgUnitCb = msgUnitCb;
137    }
138    
139    public void setProperty(String key, String value) throws XmlBlasterException {
140       this.glob.getProperty().set(key, value);
141    }
142    
143    public boolean fireMethod(MethodName methodName,
144          String sessionId, String requestId, byte type)
145          throws XmlBlasterException {
146       if (log.isLoggable(Level.FINE)) XmlScriptClient.log.fine("fireMethod "
147             + MsgInfo.getTypeStr(type)
148             + ": " + methodName.toString()
149             + ": " + this.key.toString()
150             + " " + this.qos.toString());
151       if (type != MsgInfo.INVOKE_BYTE)
152          log.warning("Unexpected message of type '" + MsgInfo.getTypeStr(type) + "'");
153       try {
154          if (MethodName.CONNECT.equals(methodName) || !this.isConnected) {
155             boolean implicitConnect = !MethodName.CONNECT.equals(methodName);
156             if (implicitConnect && !this.allowImplicitConnect)
157                throw new XmlBlasterException(glob, ErrorCode.USER_CLIENTCODE, ME, "Please provide an initial connect string or set -xmlBlaster/XmlScript/allowImplicitConnect true", null);
158             // if (this.qos.length() < 1) this.qos.append("<qos />");
159             String ret = null;
160             I_Callback cb = null;
161             if (this.callback != null) cb = this; // we intercept callbacks
162             if (implicitConnect || this.qos.length() < 1) {
163                log.warning("Doing implicit xmlBlaster.connect() as no valid <connect/> markup is in the script");
164                ConnectQos connectQos = new ConnectQos(this.glob);
165                ret = this.access.connect(connectQos, cb).toXml();
166             }
167             else {
168                ConnectQosData data = this.connectQosFactory.readObject(this.qos.toString());
169                // nectQosData data = new ConnectQosServer(this.glob, this.qos.toString()).getData();
170                ConnectReturnQos tmp = this.access.connect(new ConnectQos(this.glob, data), cb);
171                if (tmp != null) ret = tmp.toXml("  ");
172                else ret = "";
173             }
174             writeResponse(methodName, ret);
175             this.isConnected = true;
176             if (!implicitConnect) {
177                return true;
178             }
179          }
180          if (MethodName.DISCONNECT.equals(methodName)) {
181             if (this.qos.length() < 1) this.qos.append("<qos />");
182             DisconnectQosData disconnectQosData = this.disconnectQosFactory.readObject(this.qos.toString());
183             boolean ret = this.access.disconnect(new DisconnectQos(this.glob, disconnectQosData));
184             writeResponse(methodName, "\n"+ret);
185             return true;
186          }
187 
188          if (this.qos.length() < 1) this.qos.append("<qos />");
189          if (this.key.length() < 1) this.key.append("<key />");
190 
191          if (MethodName.PUBLISH.equals(methodName)) {
192             MsgUnit msgUnit = buildMsgUnit();
193             if (this.msgUnitCb != null) {
194                this.msgUnitCb.intercept(msgUnit);
195             }
196             if (log.isLoggable(Level.FINE)) XmlScriptClient.log.fine("appendEndOfElement publish: " + msgUnit.toXml());
197             PublishReturnQos ret = this.access.publish(msgUnit);
198             writeResponse(methodName, (ret != null)?ret.toXml("  "):null);
199             return true;
200          }
201          if (MethodName.PUBLISH_ARR.equals(methodName)) {
202             int size = this.messageList.size();
203             MsgUnit[] msgs = new MsgUnit[size];
204             for (int i=0; i < size; i++) {
205                if (log.isLoggable(Level.FINE)) XmlScriptClient.log.fine("appendEndOfElement publishArr: " + msgs[i].toXml());
206                msgs[i] = (MsgUnit)this.messageList.get(i);
207             }
208             PublishReturnQos[] ret = this.access.publishArr(msgs);
209             String[] retStr = new String[ret.length];
210             for (int i=0; i < ret.length; i++) retStr[i] = ret[i].toXml("    ");
211             writeResponse(methodName, retStr);
212             return true;
213          }
214          if (MethodName.PUBLISH_ONEWAY.equals(methodName)) {
215             int size = this.messageList.size();
216             MsgUnit[] msgs = new MsgUnit[size];
217             for (int i=0; i < size; i++) {
218                if (log.isLoggable(Level.FINE)) XmlScriptClient.log.fine("appendEndOfElement publishArr: " + msgs[i].toXml());
219                msgs[i] = (MsgUnit)this.messageList.get(i);
220             }
221             this.access.publishOneway(msgs);
222             return true;
223          }
224          if (MethodName.SUBSCRIBE.equals(methodName)) {
225             SubscribeReturnQos ret = this.access.subscribe(this.key.toString(), this.qos.toString());
226             writeResponse(methodName, ret.toXml("    "));
227             return true;
228          }
229          if (MethodName.UNSUBSCRIBE.equals(methodName)) {
230             UnSubscribeReturnQos[] ret = this.access.unSubscribe(this.key.toString(), this.qos.toString());
231             String[] retStr = new String[ret.length];
232             for (int i=0; i < ret.length; i++) retStr[i] = ret[i].toXml("    ");
233             writeResponse(methodName, retStr);
234             return true;
235          }
236          if (MethodName.ERASE.equals(methodName)) {
237             EraseReturnQos[] ret = this.access.erase(this.key.toString(), this.qos.toString());
238             String[] retStr = new String[ret.length];
239             for (int i=0; i < ret.length; i++) retStr[i] = ret[i].toXml("    ");
240             writeResponse(methodName, retStr);
241             return true;
242          }
243          if (MethodName.GET.equals(methodName)) {
244             MsgUnit[] ret = this.access.get(this.key.toString(), this.qos.toString());
245             String[] retStr = new String[ret.length];
246             for (int i=0; i < ret.length; i++) retStr[i] = ret[i].toXml("    ");
247             writeResponse(methodName, retStr);
248             return true;
249          }
250       }
251       catch (XmlBlasterException e) {
252          log.warning(e.getMessage());
253          
254          // The exception has already a <exception> root tag
255          writeResponse(null/*MethodName.EXCEPTION*/, e.toXml("    "));
256          
257          // For connect exceptions we stop parsing
258          if (MethodName.CONNECT.equals(methodName))
259             throw new XmlBlasterException(glob, ErrorCode.INTERNAL_STOP, ME, e.toString(), e);
260          
261          if (this.throwAllExceptions)
262              throw new XmlBlasterException(glob, ErrorCode.INTERNAL_STOP, ME, e.toString(), e);
263          
264          return true;
265       }
266       catch (Throwable e) {
267          log.severe(e.toString());
268          throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME, "", e);
269       }
270       
271       log.warning("fireMethod with methodName=" + methodName.toString() + " is not implemented: " + this.key.toString() + " " + this.qos.toString());
272       return false;
273    }
274 
275    /*
276 <xmlBlasterResponse>
277   <publish>
278      <qos>
279       <key oid='1'/>
280       <rcvTimestamp nanos='1131654994574000000'/>
281      <isPublish/>
282      </qos>
283   </publish>
284   <publishArr>
285     <message>
286        <qos>
287           <key oid='test'/>
288           <rcvTimestamp nanos='1075409585342000001'/>
289           <isPublish/>
290        </qos>
291     </message>
292     <message>
293        <qos>
294           <key oid='test'/>
295           <rcvTimestamp nanos='1075409585348000001'/>
296           <isPublish/>
297        </qos>
298     </message>
299   </publishArr>
300 </xmlBlasterResponse>
301     */
302    /**
303     * Write respone message to OutputStream. 
304     * @param methodName Can be null
305     * @param message A well formed XML message or null
306     */
307    private void writeResponse(MethodName methodName, String message) throws XmlBlasterException {
308       String[] messages = new String[(message==null) ? 0 : 1];
309       if (messages.length == 1) messages[0] = message; 
310       writeResponse(methodName, messages);
311    }
312    
313    private void writeResponse(MethodName methodName, String[] messages) throws XmlBlasterException {
314       //super.response.append("\n<!-- __________________________________ ").append((methodName==null)?"":methodName.toString()).append(" response _____________________ -->");
315       if (methodName != null) super.response.append("\n<").append(methodName.toString()).append(">");
316       if (messages != null && messages.length > 0) {
317          if (messages.length == 1)
318             super.response.append(messages[0]);
319          else {
320             for (int i=0; i < messages.length; i++) {
321                super.response.append("\n  <message>");
322                super.response.append(messages[i]);
323                super.response.append("\n  </message>\n");
324             }
325          }
326       }
327       if (methodName != null) super.response.append("\n</").append(methodName.toString()).append(">\n");
328       flushResponse();
329    }
330    
331    public static void main(String[] args) {
332       /*
333       String request = "<xmlBlaster>\n" +
334                        "  <connect>" +
335                        "    <securityPlugin type='aaa' version='bbb'>\n" +
336                        "      <user>michele</user>\n" +
337                        "      <passwd><![CDATA[secret    ]]></passwd>\n" +
338                        "    </securityPlugin>\n" +
339                        "  </connect>\n" +
340                        "  <publish>\n" +
341                        "    <key>xxxx</key>\n" +
342                        "    <content xlink='sss'/>\n" +
343                        "    <qos></qos>\n" +
344                        "  </publish>\n" +
345                        "  <subscribe/>\n" +
346                        "  <disconnect/>\n" +
347                        "</xmlBlaster>";
348       */
349       try {
350          Global glob = new Global();
351          String[] tmp = new String[args.length-2];
352          for (int i=0; i < tmp.length; i++) tmp[i] = args[i+2];
353          
354          glob.init(tmp);
355          FileOutputStream out = new FileOutputStream(args[1]);
356          XmlScriptClient client = new XmlScriptClient(glob, out);
357          FileReader in = new FileReader(args[0]);
358          client.parse(in);
359       }
360       catch (Exception ex) {
361          ex.printStackTrace();
362       }
363    }
364 
365    public String update(String cbSessionId, UpdateKey updateKey, byte[] content, UpdateQos updateQos) throws XmlBlasterException {
366       super.update(cbSessionId, updateKey, content, updateQos);
367       if (this.callback != null) {
368          return this.callback.update(cbSessionId, updateKey, content, updateQos);
369       }
370       return null;
371    }
372 
373    public boolean isThrowAllExceptions() {
374       return throwAllExceptions;
375    }
376 
377    public void setThrowAllExceptions(boolean throwAllExceptions) {
378       this.throwAllExceptions = throwAllExceptions;
379    }
380 }


syntax highlighted by Code2HTML, v. 0.9.1