Package nexj.core.rpc.http

Source Code of nexj.core.rpc.http.IntegrationHTTPServer

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.rpc.http;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.security.cert.X509Certificate;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import nexj.core.integration.Input;
import nexj.core.integration.IntegrationException;
import nexj.core.integration.io.ObjectInput;
import nexj.core.integration.io.ReaderInput;
import nexj.core.integration.io.StreamInput;
import nexj.core.meta.Metadata;
import nexj.core.meta.Primitive;
import nexj.core.meta.integration.Channel;
import nexj.core.meta.integration.Format;
import nexj.core.meta.integration.MessageTable;
import nexj.core.meta.integration.SchemaExporter;
import nexj.core.meta.integration.channel.http.HTTPChannel;
import nexj.core.rpc.RPCException;
import nexj.core.rpc.RequestException;
import nexj.core.rpc.TransferObject;
import nexj.core.runtime.SecurityViolationException;
import nexj.core.runtime.UnitOfWork;
import nexj.core.util.HTTP;
import nexj.core.util.IOUtil;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.MIMEUtil;
import nexj.core.util.ObjUtil;
import nexj.core.util.PropertyIterator;

/**
* Integration HTTP channel server.
*/
public class IntegrationHTTPServer extends GenericHTTPServer
{
   // constants

   /**
    * The full HTTP GET Query String to pass to the server to trigger WSDL generation.
    */
   protected final static String GENERATE_WSDL_QUERY = "wsdl";


   // associations
  
   /**
    * The class logger.
    */
   protected final static Logger s_logger = Logger.getLogger(IntegrationHTTPServer.class);
  
   // operations

   /**
    * Verification will occur in the invoke() method.
    *
    * @see nexj.core.rpc.http.GenericHTTPServer#verifyAuthentication()
    */
   protected void verifyAuthentication()
   {
   }

   /**
    * @see nexj.core.rpc.http.GenericHTTPServer#getLocale()
    */
   protected String getLocale()
   {
      Locale locale = m_request.getLocale();

      if (locale != null)
      {
         return locale.toString();
      }

      return null;
   }

   /**
    * @see nexj.core.rpc.http.GenericHTTPServer#invoke()
    */
   protected void invoke() throws ServletException, IOException
   {
      String sPath = m_request.getPathInfo();

      if (sPath == null || sPath.length() <= 1)
      {
         throw new RequestException("err.rpc.http.channel");
      }

      int i = sPath.indexOf('/', 1);
      String sChannel;

      if (i < 0)
      {
         sChannel = sPath.substring(1);
         sPath = "";
      }
      else
      {
         sChannel = sPath.substring(1, i);
         sPath = sPath.substring(i);
      }

      Metadata metadata = m_context.getMetadata();
      Channel channel = metadata.getChannel(sChannel);

      if (!(channel instanceof HTTPChannel) || !channel.isReceivable())
      {
         throw new RPCException("err.rpc.http.notReceiver", new Object[]{sChannel});
      }
     
      HTTPChannel http = (HTTPChannel)channel;

      if (http.isSecure() && !m_request.isSecure())
      {
         throw new RequestException("err.rpc.http.insecure", new Object[]{sChannel});
      }

      // Deny anonymous access to non-anonymous channels, and vice-versa
      if (HTTPUtil.isAnonymousRequest(m_request, metadata))
      {
         if (http.getAuthMode() != HTTPChannel.AUTH_NONE)
         {
            throw new SecurityViolationException("err.rpc.anonymous");
         }
      }
      else if (http.getAuthMode() == HTTPChannel.AUTH_NONE)
      {
         throw new SecurityViolationException("err.rpc.notAnonymous", new Object[]{sChannel});
      }

      boolean bRequestUsesCertificateAuth = HTTPUtil.isUsingClientCertificateAuthentication(m_request);

      // Deny access to client certificate channels if no certificate present
      if (http.getAuthMode() == HTTPChannel.AUTH_CERT)
      {
         if (!bRequestUsesCertificateAuth)
         {
            throw new SecurityViolationException("err.rpc.http.certificateRequired", new Object[]{sChannel});
         }

         X509Certificate[] certs = (X509Certificate[])m_request.getAttribute(HTTPUtil.CLIENT_CERTIFICATE_ATTRIBUTE_NAME);

         if (certs == null)
         {
            throw new SecurityViolationException("err.integration.missingCertificate", new Object[]{sChannel});
         }

         // The certificate should now be validated against allowed certificates for this channel.
         if (!HTTPUtil.isCertificateMatched(http.getTrustedCertificate(), certs))
         {
            throw new SecurityViolationException("err.integration.unauthorized", new Object[]{sChannel});
         }
      }
      else if (bRequestUsesCertificateAuth)
      {
         // Deny access to non-certificate-auth channels through certificate authentication.
         throw new SecurityViolationException("err.integration.unauthorized", new Object[]{sChannel});
      }

      if (http.getPrivilege() != null && !m_context.getPrivilegeSet().contains(http.getPrivilege()))
      {
         throw new SecurityViolationException("err.integration.unauthorized", new Object[]{sChannel});
      }
     
      m_lMaxRequestSize = http.getMaxRequestSize();

      HTTPAdapter adapter = (HTTPAdapter)channel.getReceiver().getInstance(m_context);

      adapter.setServer(this);

      String sMethod = m_request.getMethod();
      boolean bImplemented;

      if (sMethod.equals("POST"))
      {
         bImplemented = http.isPostImplemented();
      }
      else if (sMethod.equals("GET"))
      {
         bImplemented = http.isGetImplemented();

         if (channel.getBindingCount() > 0 &&
            GENERATE_WSDL_QUERY.equalsIgnoreCase(m_request.getQueryString()))
         {
            SchemaExporter exporter = null;
            MessageTable msgTable = channel.getMessageTable();

            if (msgTable != null)
            {
               Format format = msgTable.getFormat();

               if (format != null)
               {
                  Class exporterClass = format.getExporter();

                  if (exporterClass != null)
                  {
                     Object obj = m_context.getClassInstance(exporterClass);

                     if (obj instanceof SchemaExporter)
                     {
                        exporter = (SchemaExporter)obj;
                     }
                  }
               }
            }

            if (exporter != null)
            {
               Writer writer;

               m_response.setStatus(HttpServletResponse.SC_OK);
               m_response.setContentType("text/xml; charset=UTF-8");
               writer = m_response.getWriter();
               exporter.exportSchema(channel, getRoot() + "/", writer);
               writer.close();
               return;
            }
         }
      }
      else if (sMethod.equals("HEAD"))
      {
         if (http.isHeadImplemented())
         {
            bImplemented = true;
         }
         else if (http.isGetImplemented())
         {
            sMethod = "GET";
            bImplemented = true;
         }
         else
         {
            bImplemented = false;
         }
      }
      else if (sMethod.equals("PUT"))
      {
         bImplemented = http.isPutImplemented();
      }
      else if (sMethod.equals("DELETE"))
      {
         bImplemented = http.isDeleteImplemented();
      }
      else if (sMethod.equals("TRACE"))
      {
         if (!http.isTraceImplemented())
         {
            doTrace();
            return;
         }

         bImplemented = true;
      }
      else if (sMethod.equals("OPTIONS"))
      {
         if (!http.isOptionsImplemented())
         {
            doOptions(http);
            return;
         }
        
         bImplemented = true;
      }
      else
      {
         bImplemented = false;
      }

      if (!bImplemented)
      {
         if (m_request.getProtocol().endsWith("1.1"))
         {
            m_response.setHeader("Allowed", getAllowedMethods(http));
            m_response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
         }
         else
         {
            m_response.sendError(HttpServletResponse.SC_BAD_REQUEST);
         }
      }
      else if (adapter.isBound(channel, m_context))
      {
         TransferObject tobj = new TransferObject(6);
         String sEncoding = m_request.getCharacterEncoding();

         if (sEncoding == null)
         {
            sEncoding = HTTPAdapter.getEncoding(http.getContentType(), HTTPAdapter.DEFAULT_ENCODING);
            m_request.setCharacterEncoding(sEncoding);
         }

         tobj.setValue(HTTPAdapter.CHANNEL, http.getName());
         tobj.setValue(HTTPAdapter.METHOD, sMethod);
         tobj.setValue(HTTPAdapter.PATH, sPath);

         TransferObject headers = new TransferObject();

         for (Enumeration enm = m_request.getHeaderNames(); enm.hasMoreElements();)
         {
            String sName = (String)enm.nextElement();
            String sNameLower = sName.toLowerCase(Locale.ENGLISH);
            Object value;

            switch (HTTP.getHeaderType(sNameLower))
            {
               case HTTP.TYPE_INTEGER:
                  value = Primitive.createInteger(m_request.getIntHeader(sName));
                  break;

               case HTTP.TYPE_DATE:
                  long lDate = m_request.getDateHeader(sName);
                  value = (lDate == -1) ? null : new Timestamp(lDate);
                  break;

               default:
                  value = m_request.getHeader(sName);
                  break;
            }

            headers.setValue(sNameLower, value);
         }

         tobj.setValue(HTTPAdapter.HEADERS, headers);

         Map parameterMap = getParameterMap();
         TransferObject parameters = null;

         if (parameterMap != null && parameterMap.size() > 0)
         {
            parameters = new TransferObject(parameterMap.size());

            for (Iterator itr = parameterMap.entrySet().iterator(); itr.hasNext(); )
            {
               Map.Entry entry = (Map.Entry)itr.next();
               String[] sValueArray = (String[])entry.getValue();
               Object value;

               if (sValueArray == null)
               {
                  value = null;
               }
               else if (sValueArray.length == 1)
               {
                  value = sValueArray[0];
               }
               else
               {
                  List valueList = new ArrayList(sValueArray.length);

                  for (int k = 0; k < sValueArray.length; k++)
                  {
                     valueList.add(sValueArray[k]);
                  }

                  value = valueList;
               }

               parameters.setValue((String)entry.getKey(), value);
            }
         }

         if (isMultipart())
         {
            Lookup paramMap = getMultipartParameters(null, 0);

            if (parameters == null && paramMap.size() > 0)
            {
               parameters = new TransferObject(paramMap.size());
            }

            for (Lookup.Iterator itr = paramMap.iterator(); itr.hasNext();)
            {
               itr.next();
               parameters.setValue((String)itr.getKey(), itr.getValue());
            }
         }

         if (parameters != null)
         {
            tobj.setValue(HTTPAdapter.PARAMETERS, parameters);
         }

         tobj.setValue(HTTPAdapter.BODY,
            (http.getDataType() == Primitive.BINARY ||
               http.getDataType() == null &&
               MIMEUtil.isBinaryMIMEType(m_request.getHeader(HTTP.HEADER_CONTENT_TYPE))) ?
               new StreamInput(getInputStream(), sEncoding) :
                  (Input)new ReaderInput(getReader()));

         try
         {
            UnitOfWork uow = m_context.initUnitOfWork();
  
            uow.checkLicense();
            m_context.beginTransaction();
            adapter.receive(tobj, channel, m_context);
            m_context.complete(true);        
         }
         catch (Throwable e)
         {
            m_context.complete(false);
            ObjUtil.rethrow(e);
         }
      }
   }

   /**
    * Implements the HTTP TRACE method.
    */
   protected void doTrace() throws IOException
   {
      StringBuffer buf = new StringBuffer(128);
     
      buf.append("TRACE ");
      buf.append(m_request.getRequestURI());
      buf.append(' ');
      buf.append(m_request.getProtocol());
      buf.append("\r\n");
     
      for (Enumeration enm = m_request.getHeaderNames(); enm.hasMoreElements();)
      {
         String sName = (String)enm.nextElement();
        
         buf.append(sName);
         buf.append(": ");
         buf.append(m_request.getHeader(sName));
         buf.append("\r\n");
      }

      byte[] response = buf.toString().getBytes(m_response.getCharacterEncoding());
      buf = null;

      m_response.setContentType("message/http");
      m_response.setContentLength(response.length);

      ServletOutputStream ostream = m_response.getOutputStream();

      ostream.write(response);
      ostream.close();
   }
  
   /**
    * Implements the HTTP OPTIONS method.
    * @param channel The HTTP channel.
    */
   protected void doOptions(HTTPChannel channel)
   {
      m_response.addHeader("Allowed", getAllowedMethods(channel));
      m_response.setContentLength(0);
   }
  
   /**
    * Returns a comma-separated list of allowed HTTP methods.
    * @param channel The HTTP channel.
    * @return The comma-separated list of HTTP methods.
    */
   protected String getAllowedMethods(HTTPChannel channel)
   {
      StringBuffer buf = new StringBuffer(64);
     
      if (channel.isDeleteImplemented())
      {
         buf.append("DELETE, ");
      }
     
      if (channel.isGetImplemented())
      {
         buf.append("GET, ");
      }
     
      if (channel.isHeadImplemented() || channel.isGetImplemented())
      {
         buf.append("HEAD, ");
      }
     
      buf.append("OPTIONS, ");
     
      if (channel.isPostImplemented())
      {
         buf.append("POST, ");
      }
     
      if (channel.isPutImplemented())
      {
         buf.append("PUT, ");
      }

      buf.append("TRACE");
     
      return buf.toString();
   }

   /**
    * Adds a header to the response with type conversion.
    * @param sName The header name.
    * @param value The header value.
    */
   protected void setHeader(String sName, Object value)
   {
      if (value != null)
      {
         if (value instanceof Timestamp)
         {
            m_response.setDateHeader(sName, ((Timestamp)value).getTime());
         }
         else
         {
            m_response.setHeader(sName, Primitive.toString(value));
         }
      }
   }

   /**
    * Sends a response to the client.
    * @param tobj The response message.
    * @param channel The HTTP channel.
    */
   public void reply(TransferObject tobj, HTTPChannel channel) throws IntegrationException
   {
      try
      {
         if (s_logger.isDebugEnabled())
         {
            s_logger.debug("Sending a response on channel \"" + channel.getName() + "\"");
            s_logger.dump(tobj);
         }
        
         // TODO: Support cookies & session state

         TransferObject headers = (TransferObject)tobj.findValue(HTTPAdapter.HEADERS);

         if (headers != null)
         {
            for (PropertyIterator itr = headers.getIterator(); itr.hasNext();)
            {
               itr.next();

               Object value = itr.getValue();

               if (value instanceof Collection)
               {
                  for (Iterator citr = ((Collection)value).iterator(); citr.hasNext();)
                  {
                     setHeader(itr.getName(), citr.next());
                  }
               }
               else
               {
                  setHeader(itr.getName(), value);
               }
            }
         }

         Number status = (Number)tobj.findValue(HTTPAdapter.STATUS);
         int nStatus = (status == null) ? HttpServletResponse.SC_OK : status.intValue();
         int nSeverity = (nStatus / 100);

         if (nSeverity == 2)
         {
            m_response.setStatus(nStatus);
         }
         else if (nSeverity == 3)
         {
            m_response.setStatus(nStatus);
           
            switch (nStatus)
            {
               case HttpServletResponse.SC_MOVED_PERMANENTLY:
               case HttpServletResponse.SC_MOVED_TEMPORARILY:
               case HttpServletResponse.SC_SEE_OTHER:
               case HttpServletResponse.SC_USE_PROXY:
               case HttpServletResponse.SC_TEMPORARY_REDIRECT:
                  if (!m_response.containsHeader("Location"))
                  {
                     String sURL = (String)tobj.findValue(HTTPAdapter.URL);

                     if (sURL != null)
                     {
                        m_response.setHeader("Location", sURL);
                     }
                  }

                  break;
            }

            m_bHeadersOnly = true;
         }
         else
         {
            String sMessage = (String)tobj.findValue(HTTPAdapter.MESSAGE);

            if (sMessage != null)
            {
               m_response.sendError(nStatus, sMessage);
            }
            else
            {
               m_response.sendError(nStatus);
            }
           
            m_bHeadersOnly = true;
         }

         if (m_bHeadersOnly)
         {
            m_response.setContentLength(0);
         }
         else
         {
            Object body = tobj.findValue(HTTPAdapter.BODY);

            if (body != null)
            {
               if (m_response.getContentType() == null && channel.getContentType() != null)
               {
                  m_response.setContentType(channel.getContentType());
               }

               ObjectInput input = new ObjectInput(body);

               if (body instanceof String)
               {
                  if (m_response.getContentType() == null)
                  {
                     m_response.setContentType("text/plain");
                  }
                 
                  input.setEncoding(m_response.getCharacterEncoding());

                  Writer writer = m_response.getWriter();
                 
                  IOUtil.copy(writer, input.getReader());
                  writer.close();
               }
               else
               {
                  OutputStream ostream = m_response.getOutputStream();
                 
                  IOUtil.copy(ostream, input.getInputStream());
                  ostream.close();
               }
            }
         }
      }
      catch (IOException e)
      {
         throw new IntegrationException("err.integration.io", e);
      }
   }
}
TOP

Related Classes of nexj.core.rpc.http.IntegrationHTTPServer

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.