Package org.xmlBlaster.client.protocol.xmlrpc

Source Code of org.xmlBlaster.client.protocol.xmlrpc.XmlRpcCallbackServer

/*------------------------------------------------------------------------------
Name:      XmlRpcCallbackServer.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
Comment:   Helper to connect to xmlBlaster using IIOP
Author:    xmlBlaster@marcelruff.info
------------------------------------------------------------------------------*/
package org.xmlBlaster.client.protocol.xmlrpc;


import org.apache.xmlrpc.server.PropertyHandlerMapping;
import org.apache.xmlrpc.server.XmlRpcHttpServer;
import org.apache.xmlrpc.server.XmlRpcServerConfigImpl;
import org.apache.xmlrpc.webserver.WebServer;
import org.xmlBlaster.client.protocol.I_CallbackExtended;
import org.xmlBlaster.client.protocol.I_CallbackServer;

import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.def.Constants;
import org.xmlBlaster.util.qos.address.CallbackAddress;
import org.xmlBlaster.util.plugin.PluginInfo;
import org.xmlBlaster.util.property.PropBoolean;
import org.xmlBlaster.util.protocol.socket.SocketUrl;
import org.xmlBlaster.util.protocol.xmlrpc.XblRequestFactoryFactory;
import org.xmlBlaster.util.protocol.xmlrpc.XblWriterFactory;
import org.xmlBlaster.protocol.xmlrpc.XblWebServer;
import org.xmlBlaster.protocol.xmlrpc.XmlRpcUrl;

/**
* Example for a XmlRpc callback implementation.
* <p />
* You can use this default callback handling with your clients,
* but if you need other handling of callbacks, take a copy
* of this Callback implementation and add your own code.
* <p />
* The xmlBlaster callback client call the update() from XmlRpcCallbackImpl
* which delegates it to this update() method.
* <p />
* <pre>
*     -dispatch/callback/plugin/xmlrpc/port    Specify a port number where xmlrpc callback webserver listens.
*                         Default is port 8081, the port 0 switches this feature off.
*     -dispatch/callback/plugin/xmlrpc/hostname  Specify a hostname where xmlrp callback server runs.
*                         Default is the localhost.
* </pre>
* If the callback server can't be established because of the port is not free,
* this driver loops and tries with a port number one higher until it finds a free port
* to listen for callbacks.<br />
* The correct port is automatically transferred in the login - QoS - so that xmlBlaster
* can find the callback server.
* <p />
* @author <a href="mailto:michele@laghi.eu">Michele Laghi</a>
* @author <a href="mailto:xmlBlaster@marcelruff.info">Marcel Ruff</a>.
*/
public class XmlRpcCallbackServer implements I_CallbackServer {
  
  
   private class CbRunner extends Thread {
     
      private boolean doRun = true;
     
      private XmlRpcConnection conn;
     
      public CbRunner() throws XmlBlasterException {
         XmlRpcConnection tmpConn = getXmlRpcConnection();
         conn = tmpConn.getCopy();
      }
     
      public void shutdown() {
         if (conn != null)
            conn.sendShutdownCb();
         doRun = false;
      }
     
      private final void doSleep(long delay) {
         try {
            Thread.sleep(delay);
         }
         catch (Throwable e) {
            e.printStackTrace();
         }
      }
     
      public void run() {
         while (doRun) {
            if (conn != null) {
               try {
                  conn.getUpdates(client);
               }
               catch (XmlBlasterException ex) {
                  log.warning("An Exception occured when checking for updates. This may be common if the server has shut down while waiting");
                  // conn.sendAckOrEx("0", null, ex.getMessage());
                  doRun = false;
                  // doSleep(5000L);
               }
            }
            else {
               doSleep(5000L);
            }
         }
         shutdown();
      }
     
   }
  
   private String ME = "XmlRpcCallbackServer";
   private Global glob = null;
   private static Logger log = Logger.getLogger(XmlRpcCallbackServer.class.getName());
   private I_CallbackExtended client;
   private String loginName;
   /** Holds the XmlRpc URL string of the callback server */
   private XmlRpcUrl xmlRpcUrlCallback;
   /** XmlBlaster XMLRPC callback web server listen port is 8081 */
   public static final int DEFAULT_CALLBACK_PORT = 8081; // org.xmlBlaster.protocol.xmlrpc.XmlRpcDriver.DEFAULT_CALLBACK_PORT;
   public CallbackAddress callbackAddress;
   private WebServer webServer = null;
   protected PluginInfo pluginInfo;

   private CbRunner singleChRunner;

   /** You must call initialize after constructing me */
   public XmlRpcCallbackServer() {}

  
   private XmlRpcConnection getXmlRpcConnection() {
      return (XmlRpcConnection)glob.getObjectEntry("xmlrpc3-connection");
   }

   private XmlScriptSerializer getSerializer() {
      XmlRpcConnection conn = getXmlRpcConnection();
      if (conn == null)
         return null;
      return conn.getSerializer();
   }
  
   /** Enforced by I_Plugin */
   public String getType() {
      return getCbProtocol();
   }

   /** Enforced by I_Plugin */
   public String getVersion() {
      return "1.0";
   }

   /**
    * This method is called by the PluginManager (enforced by I_Plugin).
    * @see org.xmlBlaster.util.plugin.I_Plugin#init(org.xmlBlaster.util.Global,org.xmlBlaster.util.plugin.PluginInfo)
    */
   public void init(org.xmlBlaster.util.Global glob, org.xmlBlaster.util.plugin.PluginInfo pluginInfo) {
      this.pluginInfo = pluginInfo;
   }

   /**
    * Construct a persistently named object.
    * @param client    Your implementation of I_CallbackExtended, or null if you don't want any updates.
    */
   public void initialize(Global glob, String name, CallbackAddress callbackAddress,
                          I_CallbackExtended client) throws XmlBlasterException
   {
      this.ME = "XmlRpcCallbackServer-" + name;
      this.glob = glob;

      this.callbackAddress = callbackAddress;
      if (this.pluginInfo != null)
         this.callbackAddress.setPluginInfoParameters(this.pluginInfo.getParameters());
      this.client = client;
      this.loginName = name;

      PropBoolean tmp = callbackAddress.getEnv("singleChannel", false);
      boolean useCDATA = callbackAddress.getEnv("useCDATA", false).getValue();

      if (!tmp.getValue())
         createCallbackServer(useCDATA);
      else {
         xmlRpcUrlCallback = new XmlRpcUrl(glob, callbackAddress.getBootstrapHostname(), DEFAULT_CALLBACK_PORT, "tunneled");
         callbackAddress.setRawAddress(xmlRpcUrlCallback.getUrl()); // e.g. "http://127.168.1.1:8082/"
         ME = "XmlRpcCallbackServer-" + xmlRpcUrlCallback.getUrl();
      }
      glob.addObjectEntry("xmlrpc-callback", this);
      log.info("Success, created XMLRPC callback server for " + loginName);
   }

   public void postInitialize() throws XmlBlasterException {
      PropBoolean tmp = callbackAddress.getEnv("singleChannel", false);
      if (tmp.getValue()) {
         xmlRpcUrlCallback = new XmlRpcUrl(glob, callbackAddress, false, DEFAULT_CALLBACK_PORT);
         singleChRunner = new CbRunner();
         singleChRunner.start();
      }
     
   }

   /**
    * Building a Callback server, using the tie approach.
    *
    * @param the BlasterCallback server
    * @exception XmlBlasterException if the BlasterCallback server can't be created
    *            id="CallbackCreationError"
    */
   private void createCallbackServer(boolean useCDATA) throws XmlBlasterException {
      if (log.isLoggable(Level.FINER)) log.finer("createCallbackServer() ...");

      this.xmlRpcUrlCallback = new XmlRpcUrl(glob, this.callbackAddress, false, DEFAULT_CALLBACK_PORT);
      try {
         if (this.xmlRpcUrlCallback.getPort() > 0) {
            // Start an 'xmlrpc webserver' if desired
            int numTries = 20; // start looking for a free port, begin with default port -dispatch/callback/plugin/xmlrpc/port <port>
            for (int ii=0; ii<numTries; ii++) {
               try {
                  // webServer = new WebServer(xmlRpcUrlCallback.getPort(), xmlRpcUrlCallback.getInetAddress());
                  SocketUrl socketUrl = new SocketUrl(glob, xmlRpcUrlCallback.getHostname(), xmlRpcUrlCallback.getPort());
                  webServer = new XblWebServer(xmlRpcUrlCallback, socketUrl, callbackAddress);
                  break;
               }
               /*
               catch(java.net.BindException e) {
                  log.warning("Port " + this.xmlRpcUrlCallback.getPort() + " for XMLRPC callback server is in use already, trying with port " +  (this.xmlRpcUrlCallback.getPort()+1) + ": " + e.toString());
                  this.xmlRpcUrlCallback.setPort(this.xmlRpcUrlCallback.getPort() + 1);
               }
               catch(java.io.IOException e) {
               */
               catch(Throwable e) {
                  if (e.getMessage().indexOf("Cannot assign requested address") != -1) {
                     if (log.isLoggable(Level.FINE)) log.warning("Host " + this.xmlRpcUrlCallback.getHostname() + " for XMLRPC callback server is invalid: " + e.toString());
                     throw new XmlBlasterException(glob, ErrorCode.USER_CONFIGURATION, ME, "Local host IP '" + this.xmlRpcUrlCallback.getHostname() + "' for XMLRPC callback server is invalid: " + e.toString());
                  }
                  else // e.getMessage() = "Address already in use"
                     log.warning("Port " + this.xmlRpcUrlCallback.getPort() + " for XMLRPC callback server is in use already, trying with port " (this.xmlRpcUrlCallback.getPort()+1) + ": " + e.toString());
                  }
                  this.xmlRpcUrlCallback.setPort(this.xmlRpcUrlCallback.getPort() + 1);
               }
               if (ii == (numTries-1)) {
                  log.severe("Can't find free port " + this.xmlRpcUrlCallback.getPort() + " for XMLRPC callback server, please use '-dispatch/callback/plugin/xmlrpc/port <port>' to specify a free one.");
               }
            }
           
            boolean inhibitCbExceptions = callbackAddress.getEnv("inhibitCbExceptions", false).getValue();
           
            XmlRpcCallbackImpl xblImpl = new XmlRpcCallbackImpl(this, inhibitCbExceptions);
            PropertyHandlerMapping mapping = new PropertyHandlerMapping();
            XblRequestFactoryFactory factoryFactory = new XblRequestFactoryFactory();
            factoryFactory.add(xblImpl);
            mapping.setRequestProcessorFactoryFactory(factoryFactory);
            mapping.addHandler("$default", xblImpl.getClass());      // register update() method

            this.callbackAddress.setRawAddress(this.xmlRpcUrlCallback.getUrl()); // e.g. "http://127.168.1.1:8082/"
            this.ME = "XmlRpcCallbackServer-" + this.xmlRpcUrlCallback.getUrl();
            //log.info(ME, "Created XmlRpc callback http server");
        
            XmlRpcHttpServer xmlRpcServer = (XmlRpcHttpServer)webServer.getXmlRpcServer();
            XmlRpcServerConfigImpl serverCfg = new XmlRpcServerConfigImpl();
            serverCfg.setEnabledForExceptions(true);
            serverCfg.setEnabledForExtensions(true);
            xmlRpcServer.setConfig(serverCfg);
            xmlRpcServer.setHandlerMapping(mapping);
           
            XblWriterFactory writerFactory = new XblWriterFactory(useCDATA);
            xmlRpcServer.setXMLWriterFactory(writerFactory);
            webServer.start();
         }
         else
            log.info("XmlRpc callback http server not created, because of -dispatch/callback/plugin/xmlrpc/port is 0");
      }
      catch (XmlBlasterException e) {
         throw e;
      }
      catch (Exception e) {
         e.printStackTrace();
         throw new XmlBlasterException(glob, ErrorCode.USER_CONFIGURATION, ME, "Could not initialize XMLRPC callback server on '" + this.xmlRpcUrlCallback.getUrl() + "': " + e.toString());
      }
   }

   /**
    * Returns the 'well known' protocol type.
    * @return "XMLRPC"
    */
   public String getCbProtocol() {
      return "XMLRPC";
   }
  
   /**
    * Returns the current callback address.
    * @return Something like "http://myserver.com:8081/"
    */
   public String getCbAddress() throws XmlBlasterException {
      if (xmlRpcUrlCallback == null)
         return null;
      return xmlRpcUrlCallback.getUrl();
   }
  
   /**
    * Shutdown the callback server.
    * @return true if everything went fine.
    */
   public void shutdown() {
      glob.removeObjectEntry("xmlrpc-callback");
     
      if (webServer != null) {
         try {
            webServer.getXmlRpcServer().setHandlerMapping(null);
            // getHandlerMapping().removeHandler("$default");
            webServer.shutdown();
         }
         catch(Throwable e) {
            log.warning("Problems during shutdown of XMLRPC callback web server: " + e.toString());
            return;
         }
      }
      else if (this.singleChRunner != null) {
         singleChRunner.shutdown();
         singleChRunner = null;
      }
      log.info("The XMLRPC callback server is shutdown.");
   }

   /**
    * The update method.
    * <p />
    * Gets invoked from XmlRpcCallbackImpl.java (which was called by xmlBlaster)
    */
   public String update(String cbSessionId, String updateKey, byte[] content,
                       String updateQos) throws XmlBlasterException
   {
      if (log.isLoggable(Level.FINER)) log.finer("Entering update(): sessionId: " + cbSessionId);
      String ret = client.update(cbSessionId, updateKey, content, updateQos);
      // check if it is an inhibited Exception
      if (ret != null && XmlRpcCallbackImpl.INHIBITED_CALLBACK_EXCEPTION.equals(ret)) {
         throw new XmlBlasterException(glob, ErrorCode.COMMUNICATION_USER_HOLDBACK, ME, "Exception in Client Callback Method. The cause has been inhibited on the client side by the 'inhibitCbExceptions' flag. You can look at the client logs");
      }
     
      try {
         XmlScriptSerializer serializer = getSerializer();
         if (serializer != null)
            ret = serializer.getUpdateResponse(ret);
      }
      catch (Exception ex) {
         log.severe("Could not process update return due to " + ex.getMessage());
      }
      return ret;
   }

   /**
    * The 'oneway' update method.
    * <p />
    * oneway is not natively supported by XmlRpc
    * <p />
    * Gets invoked from XmlRpcCallbackImpl.java (which was called by xmlBlaster)
    */
   public void updateOneway(String cbSessionId, String updateKey, byte[] content,
                       String updateQos)
   {
      if (log.isLoggable(Level.FINER)) log.finer("Entering updateOneway(): sessionId: " + cbSessionId);
      try {
         client.updateOneway(cbSessionId, updateKey, content, updateQos);
      }
      catch (Throwable e) {
         log.severe("Caught exception which can't be delivered to xmlBlaster because of 'oneway' mode: " + e.toString());
      }
   }

   /**
    * Ping to check if the callback server is alive.
    * @see org.xmlBlaster.protocol.I_CallbackDriver#ping(String)
    */
   public String ping(String str) {
      try {
         XmlScriptSerializer serializer = getSerializer();
         if (serializer != null)
            return serializer.getPingResponse(Constants.RET_OK);
         return Constants.RET_OK;
      }
      catch (Exception ex) {
         return null;
      }
   }
} // class XmlRpcCallbackServer
TOP

Related Classes of org.xmlBlaster.client.protocol.xmlrpc.XmlRpcCallbackServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.