[jboss-cvs] jboss-seam/src/remoting/org/jboss/seam/remoting ...

Shane Bryzak sbryzak at redhat.com
Tue Feb 27 17:15:23 EST 2007


  User: sbryzak2
  Date: 07/02/27 17:15:23

  Added:       src/remoting/org/jboss/seam/remoting              
                        BaseRequestHandler.java Call.java CallContext.java
                        ExecutionHandler.java InterfaceGenerator.java
                        MarshalUtils.java PollHandler.java Remoting.java
                        RequestContext.java RequestHandler.java
                        RequestHandlerFactory.java SubscriptionHandler.java
                        package-info.java remote.js
  Log:
  JBSEAM-915
  
  Revision  Changes    Path
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/BaseRequestHandler.java
  
  Index: BaseRequestHandler.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import javax.servlet.ServletContext;
  
  /**
   *
   * @author Shane Bryzak
   */
  public abstract class BaseRequestHandler implements RequestHandler
  {
    public void setServletContext(ServletContext context) {}
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/Call.java
  
  Index: Call.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Type;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.jboss.seam.Component;
  import org.jboss.seam.annotations.WebRemote;
  import org.jboss.seam.remoting.wrapper.ConversionException;
  import org.jboss.seam.remoting.wrapper.ConversionScore;
  import org.jboss.seam.remoting.wrapper.Wrapper;
  import org.jboss.seam.util.EJB;
  
  /**
   *
   * @author Shane Bryzak
   */
  public class Call
  {
    private String id;
    private String componentName;
    private String methodName;
  
    private List<Wrapper> params = new ArrayList<Wrapper> ();
  
    private Object result;
  
    private CallContext context;
  
    private List<String> constraints = null;
  
    /**
     * Constructor.
     *
     * @param componentName String
     * @param methodName String
     */
    public Call(String id, String componentName, String methodName)
    {
      this.id = id;
      this.componentName = componentName;
      this.methodName = methodName;
      this.context = new CallContext();
    }
  
    /**
     * Return the call context.
     *
     * @return CallContext
     */
    public CallContext getContext()
    {
      return context;
    }
  
    /**
     * Add a parameter to this call.
     *
     * @param param BaseWrapper
     */
    public void addParameter(Wrapper param)
    {
      params.add(param);
    }
  
    /**
     * Returns the result of this call.
     *
     * @return Wrapper
     */
    public Object getResult()
    {
      return result;
    }
  
    /**
     * Returns the id of this call.
     *
     * @return String
     */
    public String getId()
    {
      return id;
    }
  
    /**
     * Returns the object graph constraints annotated on the method that is called.
     *
     * @return List The constraints
     */
    public List<String> getConstraints()
    {
      return constraints;
    }
  
    /**
     * Execute this call
     *
     * @throws Exception
     */
    public void execute()
        throws Exception
    {
      // Find the component we're calling
      Component component = Component.forName(componentName);
  
      if (component == null)
        throw new RuntimeException("No such component: " + componentName);
  
      // Create an instance of the component
      Object instance = Component.getInstance(componentName, true);
  
      Class type = null;
  
      if (component.getType().isSessionBean() &&
          component.getBusinessInterfaces().size() > 0)
      {
        for (Class c : component.getBusinessInterfaces())
        {
          if (c.isAnnotationPresent(EJB.LOCAL))
          {
            type = component.getBusinessInterfaces().iterator().next();
            break;
          }
        }
  
        if (type == null)
          throw new RuntimeException(String.format(
          "Type cannot be determined for component [%s]. Please ensure that it has a local interface.", component));
      }
  
      if (type == null)
        type = component.getBeanClass();
  
      // Find the method according to the method name and the parameter classes
      Method m = findMethod(methodName, type);
      if (m == null)
        throw new RuntimeException("No compatible method found.");
  
      if (m.getAnnotation(WebRemote.class).exclude().length > 0)
        constraints = Arrays.asList(m.getAnnotation(WebRemote.class).exclude());
  
      // Invoke!
      result = m.invoke(instance, convertParams(m.getGenericParameterTypes()));
    }
  
    /**
     * Convert our parameter values to an Object array of the specified target
     * types.
     *
     * @param targetTypes Class[] An array containing the target class types.
     * @return Object[] The converted parameter values.
     */
    private Object[] convertParams(Type[] targetTypes)
        throws ConversionException
    {
      Object[] paramValues = new Object[targetTypes.length];
  
      for (int i = 0; i < targetTypes.length; i++)
        paramValues[i] = params.get(i).convert(targetTypes[i]);
  
      return paramValues;
    }
  
    /**
     * Find the best matching method within the specified class according to
     * the parameter types that were provided to the Call.
     *
     * @param name String The name of the method.
     * @param cls Class The Class to search in.
     * @return Method The best matching method.
     */
    private Method findMethod(String name, Class cls)
    {
      Map<Method, Integer> candidates = new HashMap<Method, Integer> ();
  
      for (Method m : cls.getDeclaredMethods()) {
        if (m.getAnnotation(WebRemote.class) == null)
          continue;
  
        if (name.equals(m.getName()) &&
            m.getParameterTypes().length == params.size()) {
          int score = 0;
  
          for (int i = 0; i < m.getParameterTypes().length; i++) {
            ConversionScore convScore = params.get(i).conversionScore(m.getParameterTypes()[
                i]);
            if (convScore == ConversionScore.nomatch)
              continue;
            score += convScore.getScore();
          }
          candidates.put(m, score);
        }
      }
  
      Method bestMethod = null;
      int bestScore = 0;
  
      for (Method m : candidates.keySet()) {
        int thisScore = candidates.get(m).intValue();
        if (bestMethod == null || thisScore > bestScore) {
          bestMethod = m;
          bestScore = thisScore;
        }
      }
  
      return bestMethod;
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/CallContext.java
  
  Index: CallContext.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.dom4j.Element;
  import org.jboss.seam.remoting.wrapper.Wrapper;
  import org.jboss.seam.remoting.wrapper.WrapperFactory;
  
  /**
   * Represents the context of an individual call.
   *
   * @author Shane Bryzak
   */
  public class CallContext
  {
    /**
     * Contains references to inbound bean objects identified by their ref.
     */
    private Map<String, Wrapper> inRefs = new HashMap<String, Wrapper>();
  
    /**
     * Contains references to outbound bean objects identified by their object.
     */
    private List<Wrapper> outRefs = new ArrayList<Wrapper>();
  
    /**
     *
     * @param element Element
     * @return Wrapper
     */
    public Wrapper createWrapperFromElement(Element element)
    {
      if ("ref".equals(element.getQualifiedName()))
      {
        if (inRefs.containsKey(element.attributeValue("id")))
          return inRefs.get(element.attributeValue("id"));
        else
        {
          Element value = (Element) element.elements().get(0);
  
          Wrapper w = WrapperFactory.getInstance().createWrapper(value.getQualifiedName());
          w.setElement(value);
          w.setCallContext(this);
          inRefs.put(element.attributeValue("id"), w);
          return w;
        }
      }
      else
      {
        Wrapper w = WrapperFactory.getInstance().createWrapper(element.getQualifiedName());
        w.setElement(element);
        w.setCallContext(this);
        return w;
      }
    }
  
    /**
     *
     * @return Wrapper
     */
    public Wrapper createWrapperFromObject(Object value, String path)
    {
      // Not very efficient but has to be done - may implement something better later
      for (Wrapper ref : outRefs)
      {
        if (ref.getValue().equals(value))
          return ref;
      }
  
      Wrapper w = WrapperFactory.getInstance().getWrapperForObject(value);
      w.setCallContext(this);
      w.setPath(path);
      return w;
    }
  
    /**
     * Returns the inbound object references
     *
     * @return Map
     */
    public Map<String, Wrapper> getInRefs()
    {
      return inRefs;
    }
  
    /**
     * Returns the outbound object references
     *
     * @return List
     */
    public List<Wrapper> getOutRefs()
    {
      return outRefs;
    }
  
    /**
     * Add an outbound object reference
     *
     * @param w Wrapper
     */
    public void addOutRef(Wrapper w)
    {
      if (!outRefs.contains(w))
        outRefs.add(w);
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/ExecutionHandler.java
  
  Index: ExecutionHandler.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  
  import javax.faces.event.PhaseId;
  import javax.servlet.ServletContext;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import org.jboss.seam.log.LogProvider;
  import org.jboss.seam.log.Logging;
  import org.dom4j.Document;
  import org.dom4j.Element;
  import org.dom4j.io.SAXReader;
  import org.jboss.seam.contexts.ContextAdaptor;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.core.Manager;
  import org.jboss.seam.remoting.wrapper.Wrapper;
  
  /**
   * Unmarshals the calls from an HttpServletRequest, executes them in order and
   * marshals the responses.
   *
   * @author Shane Bryzak
   */
  public class ExecutionHandler extends BaseRequestHandler implements RequestHandler
  {
     private static final LogProvider log = Logging.getLogProvider(ExecutionHandler.class);
  
    private static final byte[] HEADER_OPEN = "<header>".getBytes();
    private static final byte[] HEADER_CLOSE = "</header>".getBytes();
    private static final byte[] CONVERSATION_ID_TAG_OPEN = "<conversationId>".getBytes();
    private static final byte[] CONVERSATION_ID_TAG_CLOSE = "</conversationId>".getBytes();
  
    private static final byte[] CONTEXT_TAG_OPEN = "<context>".getBytes();
    private static final byte[] CONTEXT_TAG_CLOSE = "</context>".getBytes();
  
    private ServletContext servletContext;
  
    @Override
    public void setServletContext(ServletContext ctx)
    {
      this.servletContext = ctx;
    }
  
    /**
     * The entry point for handling a request.
     *
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     * @throws Exception
     */
    public void handle(HttpServletRequest request, HttpServletResponse response)
        throws Exception
    {
      try
      {
        // We're sending an XML response, so set the response content type to text/xml
        response.setContentType("text/xml");
  
        // Parse the incoming request as XML
        SAXReader xmlReader = new SAXReader();
        Document doc = xmlReader.read(request.getInputStream());
        Element env = doc.getRootElement();
  
        RequestContext ctx = unmarshalContext(env);
  
        // Reinstate the Seam conversation
        HttpSession session = request.getSession(true);
        Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
        Lifecycle.setServletRequest(request);
        Lifecycle.beginRequest(servletContext, session, request);
  
        Manager.instance().restoreConversation( ctx.getConversationId() );
        Lifecycle.resumeConversation(session);
  
        // Extract the calls from the request
        List<Call> calls = unmarshalCalls(env);
  
        // Execute each of the calls
        for (Call call : calls) {
          call.execute();
        }
  
        // Store the conversation ID in the outgoing context
        ctx.setConversationId( Manager.instance().getCurrentConversationId() );
        Manager.instance().endRequest( ContextAdaptor.getSession(session) );
        Lifecycle.endRequest();
  
        // Package up the response
        marshalResponse(calls, ctx, response.getOutputStream());
      }
      catch (Exception ex)
      {
        log.error("Error during remote request", ex);
        Lifecycle.endRequest();
      }
      finally
      {
        Lifecycle.setServletRequest(null);
        Lifecycle.setPhaseId(null);
        log.debug("ended request");
      }
    }
  
    /**
     * Unmarshals the context from the request envelope header.
     *
     * @param env Element
     * @return RequestContext
     */
    private RequestContext unmarshalContext(Element env)
    {
      RequestContext ctx = new RequestContext();
  
      Element header = env.element("header");
      if (header != null)
      {
        Element context = header.element("context");
        if (context != null)
        {
  
          Element convId = context.element("conversationId");
          if (convId != null)
            ctx.setConversationId(convId.getText());
        }
      }
  
      return ctx;
    }
  
    /**
     * Unmarshal the request into a list of Calls.
     *
     * @param env Element
     * @throws Exception
     */
    private List<Call> unmarshalCalls(Element env)
        throws Exception
    {
      try {
        List<Call> calls = new ArrayList<Call>();
  
        List<Element> callElements = env.element("body").elements("call");
  
        for (Element e : callElements) {
          Call call = new Call(e.attributeValue("id"),
                               e.attributeValue("component"),
                               e.attributeValue("method"));
  
          // First reconstruct all the references
          Element refsNode = e.element("refs");
  
          Iterator iter = refsNode.elementIterator("ref");
          while (iter.hasNext())
          {
            call.getContext().createWrapperFromElement((Element) iter.next());
          }
  
          // Now unmarshal the ref values
          for (Wrapper w : call.getContext().getInRefs().values())
            w.unmarshal();
  
          Element paramsNode = e.element("params");
  
          // Then process the param values
          iter = paramsNode.elementIterator("param");
          while (iter.hasNext()) {
            Element param = (Element) iter.next();
  
            call.addParameter(call.getContext().createWrapperFromElement(
              (Element) param.elementIterator().next()));
          }
  
          calls.add(call);
        }
  
        return calls;
      }
      catch (Exception ex) {
        log.error("Error unmarshalling calls from request", ex);
        throw ex;
      }
    }
  
    /**
     * Write the results to the output stream.
     *
     * @param calls List The list of calls to write
     * @param out OutputStream The stream to write to
     * @throws IOException
     */
    private void marshalResponse(List<Call> calls, RequestContext ctx, OutputStream out)
        throws IOException
    {
      out.write(ENVELOPE_TAG_OPEN);
  
      if (ctx.getConversationId() != null)
      {
        out.write(HEADER_OPEN);
        out.write(CONTEXT_TAG_OPEN);
        out.write(CONVERSATION_ID_TAG_OPEN);
        out.write(ctx.getConversationId().getBytes());
        out.write(CONVERSATION_ID_TAG_CLOSE);
        out.write(CONTEXT_TAG_CLOSE);
        out.write(HEADER_CLOSE);
      }
  
      out.write(BODY_TAG_OPEN);
  
      for (Call call : calls)
      {
        MarshalUtils.marshalResult(call.getId(), call.getContext(), out,
                                   call.getResult(), call.getConstraints());
      }
  
      out.write(BODY_TAG_CLOSE);
      out.write(ENVELOPE_TAG_CLOSE);
      out.flush();
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/InterfaceGenerator.java
  
  Index: InterfaceGenerator.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.util.Collection;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  import javax.faces.event.PhaseId;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import org.jboss.seam.log.LogProvider;
  import org.jboss.seam.log.Logging;
  import org.jboss.seam.Component;
  import org.jboss.seam.ComponentType;
  import org.jboss.seam.Seam;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.WebRemote;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.util.EJB;
  import org.jboss.seam.util.Reflections;
  
  /**
   * Generates JavaScript interface code.
   *
   * @author Shane Bryzak
   */
  public class InterfaceGenerator extends BaseRequestHandler implements RequestHandler
  {
     private static final LogProvider log = Logging.getLogProvider(InterfaceGenerator.class);
  
    /**
     * Maintain a cache of the accessible fields
     */
    private static Map<Class,Set<String>> accessibleProperties = new HashMap<Class,Set<String>>();
  
    /**
     * A cache of component interfaces, keyed by component name.
     */
    private Map<String,byte[]> interfaceCache = new HashMap<String,byte[]>();
  
    private ServletContext servletContext;
  
    @Override
    public void setServletContext(ServletContext ctx)
    {
      this.servletContext = ctx;
    }
  
    /**
     *
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     * @throws Exception
     */
    public void handle(HttpServletRequest request, HttpServletResponse response)
        throws Exception
    {
      try
      {
        HttpSession session = request.getSession(true);
        Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
        Lifecycle.setServletRequest(request);
        Lifecycle.beginRequest(servletContext, session, request);
  
        String[] componentNames = request.getQueryString().split("&");
        Component[] components = new Component[componentNames.length];
        Set<Type> types = new HashSet<Type>();
  
        for (int i = 0; i < componentNames.length; i++) {
          components[i] = Component.forName(componentNames[i]);
          if (components[i] == null)
          {
            try
            {
              Class c = Reflections.classForName(componentNames[i]);
              appendClassSource(response.getOutputStream(), c, types);
            }
            catch (ClassNotFoundException ex)
            {
              log.error(String.format("Component not found: [%s]", componentNames[i]));
              throw new ServletException("Invalid request - component not found.");
            }
          }
        }
  
        generateComponentInterface(components, response.getOutputStream(), types);
      }
      finally
      {
        Lifecycle.setServletRequest(null);
        Lifecycle.setPhaseId(null);
      }
    }
  
    /**
     * Generates the JavaScript code required to invoke the methods of a component/s.
     *
     * @param components Component[] The components to generate javascript for
     * @param out OutputStream The OutputStream to write the generated javascript to
     * @throws IOException Thrown if there is an error writing to the OutputStream
     */
    public void generateComponentInterface(Component[] components, OutputStream out, Set<Type> types)
        throws IOException
    {
      for (Component c : components)
      {
        if (c != null)
        {
          if (!interfaceCache.containsKey(c.getName()))
          {
            synchronized (interfaceCache)
            {
              if (!interfaceCache.containsKey(c.getName()))
              {
                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
                appendComponentSource(bOut, c, types);
                interfaceCache.put(c.getName(), bOut.toByteArray());
              }
            }
          }
          out.write(interfaceCache.get(c.getName()));
        }
      }
    }
  
    /**
     * A helper method, used internally by InterfaceGenerator and also when
     * serializing responses.  Returns a list of the property names for the specified
     * class which should be included in the generated interface for the type.
     *
     * @param cls Class
     * @return List
     */
    public static Set<String> getAccessibleProperties(Class cls)
    {
      /** @todo This is a hack to get the "real" class - find out if there is
                an API method in CGLIB that can be used instead */
      if (cls.getName().contains("EnhancerByCGLIB"))
        cls = cls.getSuperclass();
  
      if (!accessibleProperties.containsKey(cls))
      {
        synchronized(accessibleProperties)
        {
          if (!accessibleProperties.containsKey(cls))
          {
            Set<String> properties = new HashSet<String>();
  
            Class c = cls;
            while (c != null && !c.equals(Object.class))
            {
              for (Field f : c.getDeclaredFields())
              {
                if (!Modifier.isTransient(f.getModifiers()) &&
                    !Modifier.isStatic(f.getModifiers()))
                {
                  String fieldName = f.getName().substring(0, 1).toUpperCase() +
                      f.getName().substring(1);
                  String getterName = String.format("get%s", fieldName);
                  String setterName = String.format("set%s", fieldName);
                  Method getMethod = null;
                  Method setMethod = null;
  
                  try
                  {
                    getMethod = c.getMethod(getterName);
                  }
                  catch (SecurityException ex)
                  {}
                  catch (NoSuchMethodException ex)
                  {
                    // it might be an "is" method...
                    getterName = String.format("is%s", fieldName);
                    try
                    {
                      getMethod = c.getMethod(getterName);
                    }
                    catch (NoSuchMethodException ex2)
                    { /* don't care */}
                  }
  
                  try
                  {
                    setMethod = c.getMethod(setterName, new Class[] {f.getType()});
                  }
                  catch (SecurityException ex)
                  {}
                  catch (NoSuchMethodException ex)
                  { /* don't care */}
  
                  if (Modifier.isPublic(f.getModifiers()) ||
                      (getMethod != null &&
                       Modifier.isPublic(getMethod.getModifiers()) ||
                       (setMethod != null &&
                        Modifier.isPublic(setMethod.getModifiers()))))
                  {
                    properties.add(f.getName());
                  }
                }
              }
  
              //
              for (Method m : c.getDeclaredMethods())
              {
                if (m.getName().startsWith("get") || m.getName().startsWith("is"))
                {
                  int startIdx = m.getName().startsWith("get") ? 3 : 2;
  
                  try
                  {
                    c.getMethod(String.format("set%s",
                                              m.getName().substring(startIdx)), m.getReturnType());
                  }
                  catch (NoSuchMethodException ex)
                  {
                    continue;
                  }
  
                  String propertyName = String.format("%s%s",
                      Character.toLowerCase(m.getName().charAt(startIdx)),
                      m.getName().substring(startIdx + 1));
  
                  if (!properties.contains(propertyName))
                    properties.add(propertyName);
                }
              }
  
              c = c.getSuperclass();
            }
  
            accessibleProperties.put(cls, properties);
          }
        }
      }
  
      return accessibleProperties.get(cls);
    }
  
    /**
     * Appends component interface code to an outputstream for a specified component.
     *
     * @param out OutputStream The OutputStream to write to
     * @param component Component The component to generate an interface for
     * @param types Set A list of types that have already been generated for this
     * request.  If this component has already been generated (i.e. it is in the list)
     * then it won't be generated again
     * @throws IOException If there is an error writing to the OutputStream.
     */
    private void appendComponentSource(OutputStream out, Component component, Set<Type> types)
        throws IOException
    {
      StringBuilder componentSrc = new StringBuilder();
  
      Class type = null;
  
      if (component.getType().isSessionBean() &&
          component.getBusinessInterfaces().size() > 0)
      {
        for (Class c : component.getBusinessInterfaces())
        {
          if (c.isAnnotationPresent(EJB.LOCAL))
          {
            type = component.getBusinessInterfaces().iterator().next();
            break;
          }
        }
  
        if (type == null)
          throw new RuntimeException(String.format(
          "Type cannot be determined for component [%s]. Please ensure that it has a local interface.", component));
      }
      else if (component.getType().equals(ComponentType.ENTITY_BEAN))
      {
        appendTypeSource(out, component.getBeanClass(), types);
        return;
      }
      else if (component.getType().equals(ComponentType.JAVA_BEAN))
      {
        // Check if any of the methods are annotated with @WebRemote, and if so
        // treat it as an "action" component instead of a type component
        for (Method m : component.getBeanClass().getDeclaredMethods())
        {
          if (m.getAnnotation(WebRemote.class) != null)
          {
            type = component.getBeanClass();
            break;
          }
        }
  
        if (type == null)
        {
          appendTypeSource(out, component.getBeanClass(), types);
          return;
        }
      }
      else
        type = component.getBeanClass();
  
      if (types.contains(type))
        return;
  
      types.add(type);
  
      componentSrc.append("Seam.Remoting.type.");
      componentSrc.append(component.getName());
      componentSrc.append(" = function() {\n");
      componentSrc.append("  this.__callback = new Object();\n");
  
      for (Method m : type.getDeclaredMethods())
      {
        if (m.getAnnotation(WebRemote.class) == null)
          continue;
  
        // Append the return type to the source block
        appendTypeSource(out, m.getGenericReturnType(), types);
  
        componentSrc.append("  Seam.Remoting.type.");
        componentSrc.append(component.getName());
        componentSrc.append(".prototype.");
        componentSrc.append(m.getName());
        componentSrc.append(" = function(");
  
        // Insert parameters p0..pN
        for (int i = 0; i < m.getGenericParameterTypes().length; i++)
        {
          appendTypeSource(out, m.getGenericParameterTypes()[i], types);
  
          if (i > 0)
            componentSrc.append(", ");
          componentSrc.append("p");
          componentSrc.append(i);
        }
  
        if (m.getGenericParameterTypes().length > 0)
          componentSrc.append(", ");
        componentSrc.append("callback) {\n");
  
        componentSrc.append("    return Seam.Remoting.execute(this, \"");
        componentSrc.append(m.getName());
        componentSrc.append("\", [");
  
        for (int i = 0; i < m.getParameterTypes().length; i++)
        {
          if (i > 0)
            componentSrc.append(", ");
          componentSrc.append("p");
          componentSrc.append(i);
        }
  
        componentSrc.append("], callback);\n");
  
        componentSrc.append("  }\n");
      }
  
      componentSrc.append("}\n");
  
      // Set the component name
      componentSrc.append("Seam.Remoting.type.");
      componentSrc.append(component.getName());
      componentSrc.append(".__name = \"");
      componentSrc.append(component.getName());
      componentSrc.append("\";\n\n");
  
      // Register the component
      componentSrc.append("Seam.Component.register(Seam.Remoting.type.");
      componentSrc.append(component.getName());
      componentSrc.append(");\n\n");
  
      out.write(componentSrc.toString().getBytes());
    }
  
    /**
     * Append Javascript interface code for a specified class to a block of code.
     *
     * @param source StringBuilder The code block to append to
     * @param type Class The type to generate a Javascript interface for
     * @param types Set A list of the types already generated (only include each type once).
     */
    private void appendTypeSource(OutputStream out, Type type, Set<Type> types)
        throws IOException
    {
      if (type instanceof Class)
      {
        Class classType = (Class) type;
  
        if (classType.isArray())
        {
          appendTypeSource(out, classType.getComponentType(), types);
          return;
        }
  
        if (classType.getName().startsWith("java.") ||
            types.contains(type) || classType.isPrimitive())
        return;
  
        // Keep track of which types we've already added
        types.add(type);
  
        appendClassSource(out, classType, types);
      }
      else if (type instanceof ParameterizedType)
      {
        for (Type t : ((ParameterizedType) type).getActualTypeArguments())
          appendTypeSource(out, t, types);
      }
    }
  
    /**
     * Appends the interface code for a non-component class to an OutputStream.
     *
     * @param out OutputStream
     * @param classType Class
     * @param types Set
     * @throws IOException
     */
    private void appendClassSource(OutputStream out, Class classType, Set<Type> types)
        throws IOException
    {
      // Don't generate interfaces for enums
      if (classType.isEnum())
        return;
  
      StringBuilder typeSource = new StringBuilder();
  
      // Determine whether this class is a component; if so, use its name
      // otherwise use its class name.
      String componentName = Seam.getComponentName(classType);
      if (componentName == null)
        componentName = classType.getName();
  
      String typeName = componentName.replace('.', '$');
  
      typeSource.append("Seam.Remoting.type.");
      typeSource.append(typeName);
      typeSource.append(" = function() {\n");
  
      StringBuilder fields = new StringBuilder();
      StringBuilder accessors = new StringBuilder();
      StringBuilder mutators = new StringBuilder();
      Map<String,String> metadata = new HashMap<String,String>();
  
      String getMethodName = null;
      String setMethodName = null;
  
      for ( String propertyName : getAccessibleProperties(classType) )
      {
        Type propertyType = null;
  
        Field f = null;
        try
        {
          f = classType.getDeclaredField(propertyName);
          propertyType = f.getGenericType();
        }
        catch (NoSuchFieldException ex)
        {
          setMethodName = String.format("set%s%s",
              Character.toUpperCase(propertyName.charAt(0)),
                propertyName.substring(1));
  
          try
          {
            getMethodName = String.format("get%s%s",
                Character.toUpperCase(propertyName.charAt(0)),
                propertyName.substring(1));
            propertyType = classType.getMethod(getMethodName).getGenericReturnType();
          }
          catch (NoSuchMethodException ex2)
          {
            try
            {
              getMethodName = String.format("is%s%s",
                  Character.toUpperCase(propertyName.charAt(0)),
                  propertyName.substring(1));
  
              propertyType = classType.getMethod(getMethodName).getGenericReturnType();
            }
            catch (NoSuchMethodException ex3)
            {
              // ???
              continue;
            }
          }
        }
  
        appendTypeSource(out, propertyType, types);
  
        // Include types referenced by generic declarations
        if (propertyType instanceof ParameterizedType)
        {
          for (Type t : ((ParameterizedType) propertyType).getActualTypeArguments())
          {
            if (t instanceof Class)
              appendTypeSource(out, t, types);
          }
        }
  
        if (f != null)
        {
          String fieldName = propertyName.substring(0, 1).toUpperCase() +
              propertyName.substring(1);
          String getterName = String.format("get%s", fieldName);
          String setterName = String.format("set%s", fieldName);
  
          try
          {
            classType.getMethod(getterName);
            getMethodName = getterName;
          }
          catch (SecurityException ex){}
          catch (NoSuchMethodException ex)
          {
            getterName = String.format("is%s", fieldName);
            try
            {
              if (Modifier.isPublic(classType.getMethod(getterName).getModifiers()))
                getMethodName = getterName;
            }
            catch (NoSuchMethodException ex2)
            { /* don't care */}
          }
  
          try
          {
            if (Modifier.isPublic(classType.getMethod(setterName, f.getType()).getModifiers()))
              setMethodName = setterName;
          }
          catch (SecurityException ex) {}
          catch (NoSuchMethodException ex) { /* don't care */}
        }
  
        // Construct the list of fields.
        if (getMethodName != null || setMethodName != null)
        {
          metadata.put(propertyName, getFieldType(propertyType));
  
          fields.append("  this.");
          fields.append(propertyName);
          fields.append(" = undefined;\n");
  
          if (getMethodName != null)
          {
            accessors.append("  Seam.Remoting.type.");
            accessors.append(typeName);
            accessors.append(".prototype.");
            accessors.append(getMethodName);
            accessors.append(" = function() { return this.");
            accessors.append(propertyName);
            accessors.append("; }\n");
          }
  
          if (setMethodName != null)
          {
            mutators.append("  Seam.Remoting.type.");
            mutators.append(typeName);
            mutators.append(".prototype.");
            mutators.append(setMethodName);
            mutators.append(" = function(");
            mutators.append(propertyName);
            mutators.append(") { this.");
            mutators.append(propertyName);
            mutators.append(" = ");
            mutators.append(propertyName);
            mutators.append("; }\n");
          }
        }
      }
  
      typeSource.append(fields);
      typeSource.append(accessors);
      typeSource.append(mutators);
  
      typeSource.append("}\n\n");
  
      // Append the type name
      typeSource.append("Seam.Remoting.type.");
      typeSource.append(typeName);
      typeSource.append(".__name = \"");
      typeSource.append(componentName);
      typeSource.append("\";\n");
  
      // Append the metadata
      typeSource.append("Seam.Remoting.type.");
      typeSource.append(typeName);
      typeSource.append(".__metadata = [\n");
  
      boolean first = true;
  
      for (String key : metadata.keySet())
      {
        if (!first)
          typeSource.append(",\n");
  
        typeSource.append("  {field: \"");
        typeSource.append(key);
        typeSource.append("\", type: \"");
        typeSource.append(metadata.get(key));
        typeSource.append("\"}");
  
        first = false;
      }
  
      typeSource.append("];\n\n");
  
      // Register the type under Seam.Component if it is a component, otherwise
      // register it under Seam.Remoting
  
      if (classType.isAnnotationPresent(Name.class))
        typeSource.append("Seam.Component.register(Seam.Remoting.type.");
      else
        typeSource.append("Seam.Remoting.registerType(Seam.Remoting.type.");
  
      typeSource.append(typeName);
      typeSource.append(");\n\n");
  
      out.write(typeSource.toString().getBytes());
    }
  
    /**
     * Returns the remoting "type" for a specified class.
     *
     * @param type Class
     * @return String
     */
    protected String getFieldType(Type type)
    {
      if (type.equals(String.class) ||
          (type instanceof Class && ( (Class) type).isEnum()) ||
          type.equals(BigInteger.class) || type.equals(BigDecimal.class))
        return "str";
      else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE))
        return "bool";
      else if (type.equals(Short.class) || type.equals(Short.TYPE) ||
               type.equals(Integer.class) || type.equals(Integer.TYPE) ||
               type.equals(Long.class) || type.equals(Long.TYPE) ||
               type.equals(Float.class) || type.equals(Float.TYPE) ||
               type.equals(Double.class) || type.equals(Double.TYPE) ||
               type.equals(Byte.class) || type.equals(Byte.TYPE))
        return "number";
      else if (type instanceof Class)
      {
        Class cls = (Class) type;
        if (Date.class.isAssignableFrom(cls))
          return "date";
        else if (cls.isArray())
          return "bag";
        else if (cls.isAssignableFrom(Map.class))
          return "map";
        else if (cls.isAssignableFrom(Collection.class))
          return "bag";
      }
      else if (type instanceof ParameterizedType)
      {
        ParameterizedType pt = (ParameterizedType) type;
  
        if (pt.getRawType() instanceof Class && Map.class.isAssignableFrom((Class) pt.getRawType()))
          return "map";
        else if (pt.getRawType() instanceof Class && Collection.class.isAssignableFrom((Class) pt.getRawType()))
          return "bag";
      }
  
      return "bean";
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/MarshalUtils.java
  
  Index: MarshalUtils.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.io.OutputStream;
  import java.io.IOException;
  import org.jboss.seam.remoting.wrapper.BeanWrapper;
  import org.jboss.seam.remoting.wrapper.Wrapper;
  import java.util.List;
  
  /**
   *
   *
   * @author Shane Bryzak
   */
  public class MarshalUtils
  {
    private static final byte[] RESULT_TAG_OPEN_START = "<result id=\"".getBytes();
    private static final byte[] RESULT_TAG_OPEN_END = "\">".getBytes();
    private static final byte[] RESULT_TAG_OPEN = "<result>".getBytes();
    private static final byte[] RESULT_TAG_CLOSE = "</result>".getBytes();
  
    private static final byte[] VALUE_TAG_OPEN = "<value>".getBytes();
    private static final byte[] VALUE_TAG_CLOSE = "</value>".getBytes();
  
    public static void marshalResult(String callId, CallContext ctx, OutputStream out,
                                     Object result, List<String> constraints)
        throws IOException
    {
      if (callId != null)
      {
        out.write(RESULT_TAG_OPEN_START);
        out.write(callId.getBytes());
        out.write(RESULT_TAG_OPEN_END);
      }
      else
        out.write(RESULT_TAG_OPEN);
  
      out.write(VALUE_TAG_OPEN);
  
      ctx.createWrapperFromObject(result, "").marshal(out);
  
      out.write(VALUE_TAG_CLOSE);
  
      out.write(RequestHandler.REFS_TAG_OPEN);
  
      // Using a for-loop, because stuff can get added to outRefs as we recurse the object graph
      for (int i = 0; i < ctx.getOutRefs().size(); i++)
      {
        Wrapper wrapper = ctx.getOutRefs().get(i);
  
        out.write(RequestHandler.REF_TAG_OPEN_START);
        out.write(Integer.toString(i).getBytes());
        out.write(RequestHandler.REF_TAG_OPEN_END);
  
        if (wrapper instanceof BeanWrapper && constraints != null)
          ((BeanWrapper) wrapper).serialize(out, constraints);
        else
          wrapper.serialize(out);
  
        out.write(RequestHandler.REF_TAG_CLOSE);
      }
  
      out.write(RequestHandler.REFS_TAG_CLOSE);
      out.write(RESULT_TAG_CLOSE);
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/PollHandler.java
  
  Index: PollHandler.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.List;
  import javax.faces.event.PhaseId;
  import javax.jms.JMSException;
  import javax.jms.Message;
  import javax.jms.ObjectMessage;
  import javax.jms.TextMessage;
  import javax.servlet.ServletContext;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import org.jboss.seam.log.LogProvider;
  import org.jboss.seam.log.Logging;
  import org.dom4j.Document;
  import org.dom4j.Element;
  import org.dom4j.io.SAXReader;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.remoting.messaging.PollError;
  import org.jboss.seam.remoting.messaging.PollRequest;
  import org.jboss.seam.remoting.wrapper.Wrapper;
  
  /**
   * Handles JMS Message poll requests.
   *
   * @author Shane Bryzak
   */
  public class PollHandler extends BaseRequestHandler implements RequestHandler
  {
    private static final LogProvider log = Logging.getLogProvider(SubscriptionHandler.class);
  
    private static final byte[] ERRORS_TAG_OPEN_START = "<errors token=\"".getBytes();
    private static final byte[] ERRORS_TAG_OPEN_END = "\">".getBytes();
  
    private static final byte[] ERROR_TAG_OPEN_START = "<error code=\"".getBytes();
    private static final byte[] ERROR_TAG_OPEN_END = "\">".getBytes();
    private static final byte[] ERROR_TAG_CLOSE = "</error>".getBytes();
  
    private static final byte[] MESSAGES_TAG_OPEN_START = "<messages token=\"".getBytes();
    private static final byte[] MESSAGES_TAG_OPEN_END = "\">".getBytes();
    private static final byte[] MESSAGES_TAG_CLOSE = "</messages>".getBytes();
  
    private static final byte[] MESSAGE_TAG_OPEN_START = "<message type=\"".getBytes();
    private static final byte[] MESSAGE_TAG_OPEN_END = "\">".getBytes();
    private static final byte[] MESSAGE_TAG_CLOSE = "</message>".getBytes();
  
    private static final byte[] VALUE_TAG_OPEN = "<value>".getBytes();
    private static final byte[] VALUE_TAG_CLOSE = "</value>".getBytes();
  
    private ServletContext servletContext;
  
    @Override
    public void setServletContext(ServletContext ctx)
    {
      this.servletContext = ctx;
    }
  
    public void handle(HttpServletRequest request, HttpServletResponse response)
        throws Exception
    {
      // We're sending an XML response, so set the response content type to text/xml
      response.setContentType("text/xml");
  
      // Parse the incoming request as XML
      SAXReader xmlReader = new SAXReader();
      Document doc = xmlReader.read(request.getInputStream());
      Element env = doc.getRootElement();
  
      List<PollRequest> polls = unmarshalRequests(env);
  
      try
      {
        HttpSession session = request.getSession(true);
        Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
        Lifecycle.setServletRequest(request);
        Lifecycle.beginRequest(servletContext, session, request);
  
        for (PollRequest req : polls)
          req.poll();
      }
      finally
      {
        Lifecycle.endRequest();
        Lifecycle.setServletRequest(null);
        Lifecycle.setPhaseId(null);
      }
  
  
      // Package up the response
      marshalResponse(polls, response.getOutputStream());
    }
  
  
    private List<PollRequest> unmarshalRequests(Element env)
        throws Exception
    {
      try
      {
        List<PollRequest> requests = new ArrayList<PollRequest>();
  
        List<Element> requestElements = env.element("body").elements("poll");
        for (Element e : requestElements)
        {
          requests.add(new PollRequest(e.attributeValue("token"),
                                       Integer.parseInt(e.attributeValue("timeout"))));
        }
  
        return requests;
      }
      catch (Exception ex)
      {
        log.error("Error unmarshalling subscriptions from request", ex);
        throw ex;
      }
    }
  
    private void marshalResponse(List<PollRequest> reqs, OutputStream out)
        throws IOException
    {
      out.write(ENVELOPE_TAG_OPEN);
      out.write(BODY_TAG_OPEN);
  
      for (PollRequest req : reqs)
      {
        if (req.getErrors() != null && req.getErrors().size() > 0)
        {
          out.write(ERRORS_TAG_OPEN_START);
          out.write(req.getToken().getBytes());
          out.write(ERRORS_TAG_OPEN_END);
          for (PollError err : req.getErrors())
          {
            writeError(err, out);
          }
        }
        else  if (req.getMessages() != null && req.getMessages().size() > 0)
        {
          out.write(MESSAGES_TAG_OPEN_START);
          out.write(req.getToken().getBytes());
          out.write(MESSAGES_TAG_OPEN_END);
          for (Message m : req.getMessages()) {
            try {
              writeMessage(m, out);
            }
            catch (JMSException ex) {
            }
            catch (IOException ex) {
            }
          }
          out.write(MESSAGES_TAG_CLOSE);
        }
      }
  
      out.write(BODY_TAG_CLOSE);
      out.write(ENVELOPE_TAG_CLOSE);
      out.flush();
    }
  
    private void writeMessage(Message m, OutputStream out)
        throws IOException, JMSException
    {
      out.write(MESSAGE_TAG_OPEN_START);
  
      // We need one of these to maintain a list of outbound references
      CallContext ctx = new CallContext();
      Object value = null;
  
      if (m instanceof TextMessage)
      {
        out.write("text".getBytes());
        value = ((TextMessage) m).getText();
      }
      else if (m instanceof ObjectMessage)
      {
        out.write("object".getBytes());
        value = ((ObjectMessage) m).getObject();
      }
  
      out.write(MESSAGE_TAG_OPEN_END);
  
      out.write(VALUE_TAG_OPEN);
      ctx.createWrapperFromObject(value, "").marshal(out);
      out.write(VALUE_TAG_CLOSE);
  
      out.write(REFS_TAG_OPEN);
  
      // Using a for-loop, because stuff can get added to outRefs as we recurse the object graph
      for (int i = 0; i < ctx.getOutRefs().size(); i++)
      {
        Wrapper wrapper = ctx.getOutRefs().get(i);
  
        out.write(REF_TAG_OPEN_START);
        out.write(Integer.toString(i).getBytes());
        out.write(REF_TAG_OPEN_END);
  
        wrapper.serialize(out);
  
        out.write(REF_TAG_CLOSE);
      }
  
      out.write(REFS_TAG_CLOSE);
  
      out.write(MESSAGE_TAG_CLOSE);
    }
  
    private void writeError(PollError error, OutputStream out)
        throws IOException
    {
      out.write(ERROR_TAG_OPEN_START);
      out.write(error.getCode().getBytes());
      out.write(ERROR_TAG_OPEN_END);
      out.write(error.getMessage().getBytes());
      out.write(ERROR_TAG_CLOSE);
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/Remoting.java
  
  Index: Remoting.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import static org.jboss.seam.InterceptionType.NEVER;
  import static org.jboss.seam.ScopeType.APPLICATION;
  import static org.jboss.seam.annotations.Install.BUILT_IN;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import org.jboss.seam.annotations.Install;
  import org.jboss.seam.annotations.Intercept;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.annotations.Startup;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.log.LogProvider;
  import org.jboss.seam.log.Logging;
  import org.jboss.seam.servlet.AbstractResource;
  
  @Startup
  @Scope(APPLICATION)
  @Name("org.jboss.seam.remoting.remoting")
  @Install(precedence = BUILT_IN)
  @Intercept(NEVER)
  public class Remoting extends AbstractResource
  {   
     public static final int DEFAULT_POLL_TIMEOUT = 10; // 10 seconds
     public static final int DEFAULT_POLL_INTERVAL = 1; // 1 second
  
     private int pollTimeout = DEFAULT_POLL_TIMEOUT;
     
     private int pollInterval = DEFAULT_POLL_INTERVAL;
     
     private boolean debug = false;   
     
     /**
      * We use a Map for this because a Servlet can serve requests for more than
      * one context path.
      */
     private Map<String, byte[]> cachedConfig = new HashMap<String, byte[]>();
     
     private static final LogProvider log = Logging.getLogProvider(Remoting.class);
  
     private static final Pattern pathPattern = Pattern.compile("/(.*?)/([^/]+)");
  
     private static final String REMOTING_RESOURCE_PATH = "resource";   
     
     @Override
     protected String getResourcePath()
     {
        return "/remoting";
     }
     
     private synchronized void initConfig(String contextPath,
              HttpSession session, HttpServletRequest request)
     {
        if (!cachedConfig.containsKey(contextPath))
        {
           try
           {
              Lifecycle.beginRequest(getServletContext(), session, request);
  
              StringBuilder sb = new StringBuilder();
              sb.append("\nSeam.Remoting.resourcePath = \"");
              sb.append(contextPath);
              sb.append(request.getServletPath());
              sb.append(getResourcePath());
              sb.append("\";");
              sb.append("\nSeam.Remoting.debug = ");
              sb.append(getDebug() ? "true" : "false");
              sb.append(";");
              sb.append("\nSeam.Remoting.pollInterval = ");
              sb.append(getPollInterval());
              sb.append(";");
              sb.append("\nSeam.Remoting.pollTimeout = ");
              sb.append(getPollTimeout());
              sb.append(";");
  
              cachedConfig.put(contextPath, sb.toString().getBytes());
           }
           finally
           {
              Lifecycle.endRequest(session);
           }
        }
     }   
     
     @Override
     public void getResource(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
        try
        {         
           String pathInfo = request.getPathInfo().substring(getResourcePath().length());      
           
           RequestHandler handler = RequestHandlerFactory.getInstance()
                 .getRequestHandler(pathInfo);
           if (handler != null)
           {
              handler.setServletContext(getServletContext());
              handler.handle(request, response);
           }
           else
           {
              Matcher m = pathPattern.matcher(pathInfo);
              if (m.matches())
              {
                 String path = m.group(1);
                 String resource = m.group(2);
                 HttpSession session = request.getSession();
  
                 if (REMOTING_RESOURCE_PATH.equals(path))
                 {
                    writeResource(resource, response.getOutputStream());
                    if ("remote.js".equals(resource))
                    {
                       appendConfig(response.getOutputStream(), request
                             .getContextPath(), session, request);
                    }
                 }
                 response.getOutputStream().flush();               
              }
           }
        }
        catch (Exception ex)
        {
           log.error("Error", ex);
        }      
     }
  
     /**
      * Appends various configuration options to the remoting javascript client
      * api.
      * 
      * @param out OutputStream
      */
     private void appendConfig(OutputStream out, String contextPath,
           HttpSession session, HttpServletRequest request) throws IOException
     {
        if (!cachedConfig.containsKey(contextPath))
           initConfig(contextPath, session, request);
  
        out.write(cachedConfig.get(contextPath));
     }   
  
     /**
      * 
      * @param resourceName String
      * @param out OutputStream
      */
     private void writeResource(String resourceName, OutputStream out)
           throws IOException
     {
        // Only allow resource requests for .js files
        if (resourceName.endsWith(".js"))
        {
           InputStream in = this.getClass().getClassLoader().getResourceAsStream(
                 "org/jboss/seam/remoting/" + resourceName);
  
           if (in != null)
           {
              byte[] buffer = new byte[1024];
              int read = in.read(buffer);
              while (read != -1)
              {
                 out.write(buffer, 0, read);
                 read = in.read(buffer);
              }
           }
           else
              log.error(String.format("Resource [%s] not found.", resourceName));
        }
     }   
     
     public int getPollTimeout()
     {
       return pollTimeout;
     }
  
     public void setPollTimeout(int pollTimeout)
     {
       this.pollTimeout = pollTimeout;
     }
  
     public int getPollInterval()
     {
       return pollInterval;
     }
  
     public void setPollInterval(int pollInterval)
     {
       this.pollInterval = pollInterval;
     }
  
     public boolean getDebug()
     {
       return debug;
     }
  
     public void setDebug(boolean debug)
     {
       this.debug = debug;
     }   
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/RequestContext.java
  
  Index: RequestContext.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  /**
   *
   * @author Shane Bryzak
   */
  public class RequestContext
  {
    private String conversationId;
  
    public String getConversationId()
    {
      return conversationId;
    }
  
    public void setConversationId(String conversationId)
    {
      this.conversationId = conversationId;
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/RequestHandler.java
  
  Index: RequestHandler.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import javax.servlet.ServletContext;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  /**
   *
   * @author Shane Bryzak
   */
  public interface RequestHandler
  {
    static final byte[] ENVELOPE_TAG_OPEN = "<envelope>".getBytes();
    static final byte[] ENVELOPE_TAG_CLOSE = "</envelope>".getBytes();
    static final byte[] BODY_TAG_OPEN = "<body>".getBytes();
    static final byte[] BODY_TAG_CLOSE = "</body>".getBytes();
    static final byte[] REFS_TAG_OPEN = "<refs>".getBytes();
    static final byte[] REFS_TAG_CLOSE = "</refs>".getBytes();
    static final byte[] REF_TAG_OPEN_START = "<ref id=\"".getBytes();
    static final byte[] REF_TAG_OPEN_END = "\">".getBytes();
    static final byte[] REF_TAG_CLOSE = "</ref>".getBytes();
  
    void handle(HttpServletRequest request, HttpServletResponse response)
        throws Exception;
    void setServletContext(ServletContext context);
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/RequestHandlerFactory.java
  
  Index: RequestHandlerFactory.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.util.Map;
  import java.util.HashMap;
  
  /**
   * Provides request handlers for different request paths.
   *
   * @author Shane Bryzak
   */
  public class RequestHandlerFactory
  {
    private static final String REQUEST_PATH_EXECUTE = "/execute";
    private static final String REQUEST_PATH_SUBSCRIPTION = "/subscription";
    private static final String REQUEST_PATH_POLL = "/poll";
    private static final String REQUEST_PATH_INTERFACE = "/interface.js";
  
    private static RequestHandlerFactory instance = new RequestHandlerFactory();
  
    private Map<String,RequestHandler> handlers = new HashMap<String,RequestHandler>();
  
    private RequestHandlerFactory()
    {
      registerHandler(REQUEST_PATH_EXECUTE, new ExecutionHandler());
      registerHandler(REQUEST_PATH_SUBSCRIPTION, new SubscriptionHandler());
      registerHandler(REQUEST_PATH_POLL, new PollHandler());
      registerHandler(REQUEST_PATH_INTERFACE, new InterfaceGenerator());
    }
  
    public void registerHandler(String path, RequestHandler handler)
    {
      handlers.put(path, handler);
    }
  
    public RequestHandler getRequestHandler(String path)
    {
      return handlers.get(path);
    }
  
    public static RequestHandlerFactory getInstance()
    {
      return instance;
    }
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/SubscriptionHandler.java
  
  Index: SubscriptionHandler.java
  ===================================================================
  package org.jboss.seam.remoting;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.List;
  
  import javax.faces.event.PhaseId;
  import javax.servlet.ServletContext;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  
  import org.dom4j.Document;
  import org.dom4j.Element;
  import org.dom4j.io.SAXReader;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.core.Manager;
  import org.jboss.seam.remoting.messaging.RemoteSubscriber;
  import org.jboss.seam.remoting.messaging.SubscriptionRegistry;
  import org.jboss.seam.remoting.messaging.SubscriptionRequest;
  
  /**
   *
   * @author Shane Bryzak
   */
  public class SubscriptionHandler extends BaseRequestHandler implements RequestHandler
  {
  
    private ServletContext servletContext;
  
    @Override
    public void setServletContext(ServletContext ctx)
    {
      this.servletContext = ctx;
    }
  
    /**
     * The entry point for handling a request.
     *
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     * @throws Exception
     */
    public void handle(HttpServletRequest request, HttpServletResponse response)
        throws Exception
    {
      // We're sending an XML response, so set the response content type to text/xml
      response.setContentType("text/xml");
  
      // Parse the incoming request as XML
      SAXReader xmlReader = new SAXReader();
      Document doc = xmlReader.read(request.getInputStream());
      Element env = doc.getRootElement();
  
      Element body = env.element("body");
  
      // First handle any new subscriptions
      List<SubscriptionRequest> requests = new ArrayList<SubscriptionRequest>();
  
      List<Element> elements = body.elements("subscribe");
      for (Element e : elements)
      {
        requests.add(new SubscriptionRequest(e.attributeValue("topic")));
      }
  
      try
      {
        HttpSession session = request.getSession(true);
        Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
        Lifecycle.setServletRequest(request);
        Lifecycle.beginRequest(servletContext, session, request);
  
        Manager.instance().initializeTemporaryConversation();
        Lifecycle.resumeConversation(session);
  
        for (SubscriptionRequest req : requests)
          req.subscribe();
  
        // Then handle any unsubscriptions
        List<String> unsubscribeTokens = new ArrayList<String>();
  
        elements = body.elements("unsubscribe");
        for (Element e : elements) {
          unsubscribeTokens.add(e.attributeValue("token"));
        }
  
        for (String token : unsubscribeTokens) {
          RemoteSubscriber subscriber = SubscriptionRegistry.instance().
                                        getSubscription(token);
          if (subscriber != null)
            subscriber.unsubscribe();
        }
      }
      finally
      {
        Lifecycle.endRequest();
        Lifecycle.setServletRequest(null);
        Lifecycle.setPhaseId(null);
      }
  
      // Package up the response
      marshalResponse(requests, response.getOutputStream());
    }
  
    private void marshalResponse(List<SubscriptionRequest> requests, OutputStream out)
        throws IOException
    {
      out.write(ENVELOPE_TAG_OPEN);
      out.write(BODY_TAG_OPEN);
  
      for (SubscriptionRequest req : requests)
      {
        req.marshal(out);
      }
  
      out.write(BODY_TAG_CLOSE);
      out.write(ENVELOPE_TAG_CLOSE);
      out.flush();
    }
  
  }
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/package-info.java
  
  Index: package-info.java
  ===================================================================
  @Namespace(value="http://jboss.com/products/seam/remoting", prefix="org.jboss.seam.remoting")
  package org.jboss.seam.remoting;
  
  import org.jboss.seam.annotations.*;
  
  
  
  1.1      date: 2007/02/27 22:15:23;  author: sbryzak2;  state: Exp;jboss-seam/src/remoting/org/jboss/seam/remoting/remote.js
  
  Index: remote.js
  ===================================================================
  // Init base-level objects
  var Seam = new Object();
  Seam.Remoting = new Object();
  Seam.Component = new Object();
  Seam.pageContext = new Object();
  
  // Components registered here
  Seam.Component.components = new Array();
  Seam.Component.instances = new Array();
  
  Seam.Component.newInstance = function(name)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (Seam.Component.components[i].__name == name)
        return new Seam.Component.components[i];
    }
  }
  
  Seam.Component.getInstance = function(name)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (Seam.Component.components[i].__name == name)
      {
        if (Seam.Component.components[i].__instance == null)
          Seam.Component.components[i].__instance = new Seam.Component.components[i]();
        return Seam.Component.components[i].__instance;
      }
    }
    return null;
  }
  
  Seam.Component.getComponentType = function(obj)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (obj instanceof Seam.Component.components[i])
        return Seam.Component.components[i];
    }
    return null;
  }
  
  Seam.Component.getComponentName = function(obj)
  {
    var componentType = Seam.Component.getComponentType(obj);
    return componentType ? componentType.__name : null;
  }
  
  Seam.Component.register = function(component)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (Seam.Component.components[i].__name == component.__name)
      {
        // Replace the existing component with the new one
        Seam.Component.components[i] = component;
        return;
      }
    }
    Seam.Component.components.push(component);
    component.__instance = null;
  }
  
  Seam.Component.isRegistered = function(name)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (Seam.Component.components[i].__name == name)
        return true;
    }
    return false;
  }
  
  Seam.Component.getMetadata = function(obj)
  {
    for (var i = 0; i < Seam.Component.components.length; i++)
    {
      if (obj instanceof Seam.Component.components[i])
        return Seam.Component.components[i].__metadata;
    }
    return null;
  }
  
  Seam.Remoting.PATH_EXECUTE = "/execute";
  Seam.Remoting.PATH_SUBSCRIPTION = "/subscription";
  Seam.Remoting.PATH_POLL = "/poll";
  
  // Type declarations will live in this namespace
  Seam.Remoting.type = new Object();
  
  // Types are registered in an array
  Seam.Remoting.types = new Array();
  
  Seam.Remoting.debug = false;
  Seam.Remoting.debugWindow = null;
  
  Seam.Remoting.setDebug = function(val)
  {
    Seam.Remoting.debug = val;
  }
  
  // Log a message to a popup debug window
  Seam.Remoting.log = function(msg)
  {
    if (!Seam.Remoting.debug)
      return;
  
    if (!Seam.Remoting.debugWindow || Seam.Remoting.debugWindow.document == null)
    {
      var attr = "left=400,top=400,resizable=yes,scrollbars=yes,width=400,height=400";
      Seam.Remoting.debugWindow = window.open("", "__seamDebugWindow", attr);
      if (Seam.Remoting.debugWindow)
      {
        Seam.Remoting.debugWindow.document.write("<html><head><title>Seam Debug Window</title></head><body></body></html>");
        var bodyTag = Seam.Remoting.debugWindow.document.getElementsByTagName("body").item(0);
        bodyTag.style.fontFamily = "arial";
        bodyTag.style.fontSize = "8pt";
      }
    }
  
    if (Seam.Remoting.debugWindow)
    {
      msg = msg.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
      Seam.Remoting.debugWindow.document.write("<pre>" + (new Date()) + ": " + msg + "</pre><br/>");
    }
  }
  
  Seam.Remoting.__Context = function() {
    this.conversationId = null;
  
    Seam.Remoting.__Context.prototype.setConversationId = function(conversationId)
    {
      this.conversationId = conversationId;
    }
  
    Seam.Remoting.__Context.prototype.getConversationId = function()
    {
      return this.conversationId;
    }
  }
  
  Seam.Remoting.context = new Seam.Remoting.__Context();
  
  Seam.Remoting.getContext = function()
  {
    return Seam.Remoting.context;
  }
  
  Seam.Remoting.Map = function()
  {
    this.elements = new Array();
  
    Seam.Remoting.Map.prototype.size = function()
    {
      return this.elements.length;
    }
  
    Seam.Remoting.Map.prototype.isEmpty = function()
    {
      return this.elements.length == 0;
    }
  
    Seam.Remoting.Map.prototype.keySet = function()
    {
      var keySet = new Array();
      for (var i = 0; i < this.elements.length; i++)
        keySet[keySet.length] = this.elements[i].key;
      return keySet;
    }
  
    Seam.Remoting.Map.prototype.values = function()
    {
      var values = new Array();
      for (var i = 0; i < this.elements.length; i++)
        values[values.length] = this.elements[i].value;
      return values;
    }
  
    Seam.Remoting.Map.prototype.get = function(key)
    {
      for (var i = 0; i < this.elements.length; i++)
      {
        if (this.elements[i].key == key)
          return this.elements[i].value;
      }
      return null;
    }
  
    Seam.Remoting.Map.prototype.put = function(key, value)
    {
      for (var i = 0; i < this.elements.length; i++)
      {
        if (this.elements[i].key == key)
        {
          this.elements[i].value = value;
          return;
        }
      }
      this.elements.push({key:key,value:value});
    }
  
    Seam.Remoting.Map.prototype.remove = function(key)
    {
      for (var i = 0; i < this.elements.length; i++)
      {
        if (this.elements[i].key == key)
          this.elements.splice(i, 1);
      }
    }
  
    Seam.Remoting.Map.prototype.contains = function(key)
    {
      for (var i = 0; i < this.elements.length; i++)
      {
        if (this.elements[i].key == key)
          return true;
      }
      return false;
    }
  }
  
  Seam.Remoting.registerType = function(type)
  {
    for (var i = 0; i < Seam.Remoting.types.length; i++)
    {
      if (Seam.Remoting.types[i].__name == type.__name)
      {
        Seam.Remoting.types[i] = type;
        return;
      }
    }
    Seam.Remoting.types.push(type);
  }
  
  Seam.Remoting.createType = function(name)
  {
    for (var i = 0; i < Seam.Remoting.types.length; i++)
    {
      if (Seam.Remoting.types[i].__name == name)
        return new Seam.Remoting.types[i];
    }
  }
  
  Seam.Remoting.getType = function(obj)
  {
    for (var i = 0; i < Seam.Remoting.types.length; i++)
    {
      if (obj instanceof Seam.Remoting.types[i])
        return Seam.Remoting.types[i];
    }
    return null;
  }
  
  Seam.Remoting.getTypeName = function(obj)
  {
    var type = Seam.Remoting.getType(obj);
    return type ? type.__name : null;
  }
  
  Seam.Remoting.getMetadata = function(obj)
  {
    for (var i = 0; i < Seam.Remoting.types.length; i++)
    {
      if (obj instanceof Seam.Remoting.types[i])
        return Seam.Remoting.types[i].__metadata;
    }
    return null;
  }
  
  Seam.Remoting.serializeValue = function(value, type, refs)
  {
    if (value == null)
      return "<null/>";
    else if (type)
    {
      switch (type) {
        // Boolean
        case "bool": return "<bool>" + (value ? "true" : "false") + "</bool>";
  
        // Numerical types
        case "number": return "<number>" + value + "</number>";
  
        // Date
        case "date": return Seam.Remoting.serializeDate(value);
        // Beans
        case "bean": return Seam.Remoting.getTypeRef(value, refs);
  
        // Collections
        case "bag": return Seam.Remoting.serializeBag(value, refs);
        case "map": return Seam.Remoting.serializeMap(value, refs);
  
        default: return "<str>" + encodeURIComponent(value) + "</str>";
      }
    }
    else // We don't know the type.. try to guess
    {
      switch (typeof(value)) {
        case "number":
          return "<number>" + value + "</number>";
        case "boolean":
          return "<bool>" + (value ? "true" : "false") + "</bool>";
        case "object":
          if (value instanceof Array)
            return Seam.Remoting.serializeBag(value, refs);
          else if (value instanceof Date)
            return Seam.Remoting.serializeDate(value);
          else if (value instanceof Seam.Remoting.Map)
            return Seam.Remoting.serializeMap(value, refs);
          else
            return Seam.Remoting.getTypeRef(value, refs);
        default:
          return "<str>" + encodeURIComponent(value) + "</str>"; // Default to String
      }
    }
  }
  
  Seam.Remoting.serializeBag = function(value, refs)
  {
    var data = "<bag>";
  
    for (var i = 0; i < value.length; i++)
    {
      data += "<element>";
      data += Seam.Remoting.serializeValue(value[i], null, refs);
      data += "</element>";
    }
  
    data += "</bag>";
    return data;
  }
  
  Seam.Remoting.serializeMap = function(value, refs)
  {
    var data = "<map>";
  
    var keyset = value.keySet();
    for (var i = 0; i < keyset.length; i++)
    {
      data += "<element><k>";
      data += Seam.Remoting.serializeValue(keyset[i], null, refs);
      data += "</k><v>";
      data += Seam.Remoting.serializeValue(value.get(keyset[i]), null, refs);
      data += "</v></element>";
    }
  
    data += "</map>";
    return data;
  }
  
  Seam.Remoting.serializeDate = function(value)
  {
    var zeroPad = function(val, digits) { while (("" + val).length < digits) val = "0" + val; return val; };
  
    var data = "<date>";
    data += value.getFullYear();
    data += zeroPad(value.getMonth() + 1, 2);
    data += zeroPad(value.getDate(), 2);
    data += zeroPad(value.getHours(), 2);
    data += zeroPad(value.getMinutes(), 2);
    data += zeroPad(value.getSeconds(), 2);
    data += zeroPad(value.getMilliseconds(), 3);
    data += "</date>";
    return data;
  }
  
  Seam.Remoting.getTypeRef = function(obj, refs)
  {
    var refId = -1;
  
    for (var i = 0; i < refs.length; i++)
    {
      if (refs[i] == obj)
      {
        refId = i;
        break;
      }
    }
  
    if (refId == -1)
    {
      refId = refs.length;
      refs[refId] = obj;
    }
  
    return "<ref id=\"" + refId + "\"/>";
  }
  
  Seam.Remoting.serializeType = function(obj, refs)
  {
    var data = "<bean type=\"";
  
    var objType = Seam.Component.getComponentType(obj);
    var isComponent = objType != null;
  
    if (!isComponent)
      objType = Seam.Remoting.getType(obj);
  
    if (!objType)
    {
      alert("Unknown Type error.");
      return null;
    }
  
    data += objType.__name;
    data += "\">\n";
  
    var meta = isComponent ? Seam.Component.getMetadata(obj) : Seam.Remoting.getMetadata(obj);
    for (var i = 0; i < meta.length; i++)
    {
      data += "<member name=\"";
      data += meta[i].field;
      data += "\">";
      data += Seam.Remoting.serializeValue(obj[meta[i].field], meta[i].type, refs);
      data += "</member>\n";
    }
  
    data += "</bean>";
  
    return data;
  }
  
  Seam.Remoting.__callId = 0;
  
  Seam.Remoting.createCall = function(component, methodName, params, callback)
  {
    var callId = "" + Seam.Remoting.__callId++;
    if (!callback)
      callback = component.__callback[methodName];
  
    var data = "<call component=\"";
    data += Seam.Component.getComponentType(component).__name;
    data += "\" method=\"";
    data += methodName;
    data += "\" id=\"";
    data += callId;
    data += "\">\n";
  
    // Add parameters
    data += "<params>";
  
    var refs = new Array();
  
    for (var i = 0; i < params.length; i++)
    {
      data += "<param>";
      data += Seam.Remoting.serializeValue(params[i], null, refs);
      data += "</param>";
    }
  
    data += "</params>";
  
    // Add refs
    data += "<refs>";
    for (var i = 0; i < refs.length; i++)
    {
      data += "<ref id=\"" + i + "\">";
      data += Seam.Remoting.serializeType(refs[i], refs);
      data += "</ref>";
    }
    data += "</refs>";
  
    data += "</call>";
  
    return {data: data, id: callId, callback: callback};
  }
  
  Seam.Remoting.createHeader = function()
  {
    var header = "";
  
    header += "<context>";
    if (Seam.Remoting.getContext().getConversationId())
    {
      header += "<conversationId>";
      header += Seam.Remoting.getContext().getConversationId();
      header += "</conversationId>";
    }
    header += "</context>";
  
    return header;
  }
  
  Seam.Remoting.createEnvelope = function(header, body)
  {
    var data = "<envelope>";
  
    if (header)
    {
      data += "<header>";
      data += header;
      data += "</header>";
    }
  
    if (body)
    {
      data += "<body>";
      data += body;
      data += "</body>";
    }
  
    data += "</envelope>";
  
    return data;
  }
  
  Seam.Remoting.pendingCalls = new Seam.Remoting.Map();
  Seam.Remoting.inBatch = false;
  Seam.Remoting.batchedCalls = new Array();
  
  Seam.Remoting.startBatch = function()
  {
    Seam.Remoting.inBatch = true;
    Seam.Remoting.batchedCalls.length = 0;
  }
  
  Seam.Remoting.executeBatch = function()
  {
    if (!Seam.Remoting.inBatch)
      return;
  
    var data = "";
    for (var i = 0; i < Seam.Remoting.batchedCalls.length; i++)
    {
      Seam.Remoting.pendingCalls.put(Seam.Remoting.batchedCalls[i].id, Seam.Remoting.batchedCalls[i]);
      data += Seam.Remoting.batchedCalls[i].data;
    }
  
    var envelope = Seam.Remoting.createEnvelope(Seam.Remoting.createHeader(), data);
    Seam.Remoting.batchAsyncReq = Seam.Remoting.sendAjaxRequest(envelope, Seam.Remoting.PATH_EXECUTE, Seam.Remoting.processResponse, false);
    Seam.Remoting.inBatch = false;
  }
  
  Seam.Remoting.cancelBatch = function()
  {
    Seam.Remoting.inBatch = false;
    for (var i = 0; i < Seam.Remoting.batchedCalls.length; i++)
      Seam.Remoting.pendingCalls.remove(Seam.Remoting.batchedCalls[i].id);
  }
  
  Seam.Remoting.cancelCall = function(callId)
  {
    var call = Seam.Remoting.pendingCalls.get(callId);
    Seam.Remoting.pendingCalls.remove(callId);
    if (call && call.asyncReq)
    {
      if (Seam.Remoting.pendingCalls.isEmpty())
        Seam.Remoting.hideLoadingMessage();
      call.asyncReq.onreadystatechange = function() {};
      call.asyncReq.abort();
    }
  }
  
  Seam.Remoting.execute = function(component, methodName, params, callback)
  {
    var call = Seam.Remoting.createCall(component, methodName, params, callback);
  
    if (Seam.Remoting.inBatch)
    {
      Seam.Remoting.batchedCalls[Seam.Remoting.batchedCalls.length] = call;
    }
    else
    {
      // Marshal the request
      var envelope = Seam.Remoting.createEnvelope(Seam.Remoting.createHeader(), call.data);
      Seam.Remoting.pendingCalls.put(call.id, call);
      call.asyncReq = Seam.Remoting.sendAjaxRequest(envelope, Seam.Remoting.PATH_EXECUTE, Seam.Remoting.processResponse, false);
    }
  
    return call;
  }
  
  Seam.Remoting.sendAjaxRequest = function(envelope, path, callback, silent)
  {
    Seam.Remoting.log("Request packet:\n" + envelope);
  
    if (!silent)
      Seam.Remoting.displayLoadingMessage();
  
    var asyncReq;
  
    if (window.XMLHttpRequest)
    {
      asyncReq = new XMLHttpRequest();
      if (asyncReq.overrideMimeType)
        asyncReq.overrideMimeType('text/xml');
    }
    else
      asyncReq = new ActiveXObject("Microsoft.XMLHTTP");
  
    asyncReq.onreadystatechange = function() {Seam.Remoting.requestCallback(asyncReq, callback); }
    asyncReq.open("POST", Seam.Remoting.resourcePath + path, true);
    asyncReq.send(envelope);
    return asyncReq;
  }
  
  Seam.Remoting.setCallback = function(component, methodName, callback)
  {
    component.__callback[methodName] = callback;
  }
  
  Seam.Remoting.requestCallback = function(req, callback)
  {
    if (req.readyState == 4)
    {
      Seam.Remoting.hideLoadingMessage();
  
      if (req.status == 200)
      {
        Seam.Remoting.log("Response packet:\n" + req.responseText);
  
        if (callback)
          callback(req.responseXML);
      }
      else
        alert("There was an error processing your request.  Error code: " + req.status);
    }
  }
  
  Seam.Remoting.processResponse = function(doc)
  {
    var headerNode;
    var bodyNode;
    var context = new Seam.Remoting.__Context;
  
    for (var i = 0; i < doc.documentElement.childNodes.length; i++)
    {
      var node = doc.documentElement.childNodes.item(i);
      if (node.tagName == "header")
        headerNode = node;
      else if (node.tagName == "body")
        bodyNode = node;
    }
  
    if (headerNode)
    {
      var contextNode;
      for (var i = 0; i < headerNode.childNodes.length; i++)
      {
        var node = headerNode.childNodes.item(i);
        if (node.tagName == "context")
        {
          contextNode = node;
          break;
        }
      }
      if (contextNode)
      {
        Seam.Remoting.unmarshalContext(contextNode, context);
        if (context.getConversationId() && Seam.Remoting.getContext().getConversationId() == null)
          Seam.Remoting.getContext().setConversationId(context.getConversationId());
      }
    }
  
    if (bodyNode)
    {
      for (var i = 0; i < bodyNode.childNodes.length; i++)
      {
        var node = bodyNode.childNodes.item(i);
        if (node.tagName == "result")
          Seam.Remoting.processResult(node, context);
      }
    }
  }
  
  Seam.Remoting.processResult = function(result, context)
  {
    var callId = result.getAttribute("id");
    var call = Seam.Remoting.pendingCalls.get(callId);
    Seam.Remoting.pendingCalls.remove(callId);
  
    if (call && call.callback)
    {
      var valueNode = null;
      var refsNode = null;
  
      var children = result.childNodes;
      for (var i = 0; i < children.length; i++)
      {
        var tag = children.item(i).tagName;
        if (tag == "value")
          valueNode = children.item(i);
        else if (tag == "refs")
          refsNode = children.item(i);
      }
  
      var refs = new Array();
      if (refsNode)
        Seam.Remoting.unmarshalRefs(refsNode, refs);
  
      var value = Seam.Remoting.unmarshalValue(valueNode.firstChild, refs);
  
      call.callback(value, context);
    }
  }
  
  Seam.Remoting.unmarshalContext = function(ctxNode, context)
  {
    for (var i = 0; i < ctxNode.childNodes.length; i++)
    {
      var tag = ctxNode.childNodes.item(i).tagName;
      if (tag == "conversationId")
        context.setConversationId(ctxNode.childNodes.item(i).firstChild.nodeValue);
    }
  }
  
  Seam.Remoting.unmarshalRefs = function(refsNode, refs)
  {
    var objs = new Array();
  
    // Pass 1 - create the reference objects
    for (var i = 0; i < refsNode.childNodes.length; i++)
    {
      if (refsNode.childNodes.item(i).tagName == "ref")
      {
        var refNode = refsNode.childNodes.item(i);
        var refId = parseInt(refNode.getAttribute("id"));
  
        var valueNode = refNode.firstChild;
        if (valueNode.tagName == "bean")
        {
          var obj = null;
          var typeName = valueNode.getAttribute("type");
          if (Seam.Component.isRegistered(typeName))
            obj = Seam.Component.newInstance(typeName);
          else
            obj = Seam.Remoting.createType(typeName);
          if (obj)
          {
            refs[refId] = obj;
            objs[objs.length] = {obj: obj, node: valueNode};
          }
        }
      }
    }
  
    // Pass 2 - populate the object members
    for (var i = 0; i < objs.length; i++)
    {
      for (var j = 0; j < objs[i].node.childNodes.length; j++)
      {
        var child = objs[i].node.childNodes.item(j);
        if (child.tagName == "member")
        {
          var name = child.getAttribute("name");
          objs[i].obj[name] = Seam.Remoting.unmarshalValue(child.firstChild, refs);
        }
      }
    }
  }
  
  Seam.Remoting.unmarshalValue = function(element, refs)
  {
    var tag = element.tagName;
  
    switch (tag)
    {
      case "bool": return element.firstChild.nodeValue == "true";
      case "number":
        if (element.firstChild.nodeValue.indexOf(".") == -1)
          return parseInt(element.firstChild.nodeValue);
        else
          return parseFloat(element.firstChild.nodeValue);
      case "str":
        var data = "";
        for (var i = 0; i < element.childNodes.length; i++)
        {
          if (element.childNodes[i].nodeType == 3) // NODE_TEXT
            data += element.childNodes[i].nodeValue;
        }
        return decodeURIComponent(data);
      case "ref": return refs[parseInt(element.getAttribute("id"))];
      case "bag":
        var value = new Array();
        for (var i = 0; i < element.childNodes.length; i++)
        {
          if (element.childNodes.item(i).tagName == "element")
            value[value.length] = Seam.Remoting.unmarshalValue(element.childNodes.item(i).firstChild, refs);
        }
        return value;
      case "map":
        var map = new Seam.Remoting.Map();
        for (var i = 0; i < element.childNodes.length; i++)
        {
          var childNode = element.childNodes.item(i);
          if (childNode.tagName == "element")
          {
            var key = null
            var value = null;
  
            for (var j = 0; j < childNode.childNodes.length; j++)
            {
              if (key == null && childNode.childNodes.item(j).tagName == "k")
                key = Seam.Remoting.unmarshalValue(childNode.childNodes.item(j).firstChild, refs);
              else if (value == null && childNode.childNodes.item(j).tagName == "v")
                value = Seam.Remoting.unmarshalValue(childNode.childNodes.item(j).firstChild, refs);
            }
  
            if (key != null)
              map.put(key, value);
          }
        }
        return map;
      case "date": return Seam.Remoting.deserializeDate(element.firstChild.nodeValue);
      default: return null;
    }
  }
  
  Seam.Remoting.deserializeDate = function(val)
  {
    var dte = new Date();
    dte.setFullYear(parseInt(val.substring(0,4), 10));
    dte.setMonth(parseInt(val.substring(4,6), 10) - 1);
    dte.setDate(parseInt(val.substring(6,8), 10));
    dte.setHours(parseInt(val.substring(8,10), 10));
    dte.setMinutes(parseInt(val.substring(10,12), 10));
    dte.setSeconds(parseInt(val.substring(12,14), 10));
    dte.setMilliseconds(parseInt(val.substring(14,17), 10));
    return dte;
  }
  
  Seam.Remoting.loadingMsgDiv = null;
  Seam.Remoting.loadingMessage = "Please wait...";
  Seam.Remoting.displayLoadingMessage = function()
  {
    if (!Seam.Remoting.loadingMsgDiv)
    {
      Seam.Remoting.loadingMsgDiv = document.createElement('div');
      var msgDiv = Seam.Remoting.loadingMsgDiv;
      msgDiv.setAttribute('id', 'loadingMsg');
  
      msgDiv.style.position = "absolute";
      msgDiv.style.top = "0px";
      msgDiv.style.right = "0px";
      msgDiv.style.background = "red";
      msgDiv.style.color = "white";
      msgDiv.style.fontFamily = "Verdana,Helvetica,Arial";
      msgDiv.style.fontSize = "small";
      msgDiv.style.padding = "2px";
      msgDiv.style.border = "1px solid black";
  
      document.body.appendChild(msgDiv);
  
      var text = document.createTextNode(Seam.Remoting.loadingMessage);
      msgDiv.appendChild(text);
    }
    else
    {
      Seam.Remoting.loadingMsgDiv.innerHTML = Seam.Remoting.loadingMessage;
      Seam.Remoting.loadingMsgDiv.style.visibility = 'visible';
    }
  }
  
  Seam.Remoting.hideLoadingMessage = function()
  {
    if (Seam.Remoting.loadingMsgDiv)
      Seam.Remoting.loadingMsgDiv.style.visibility = 'hidden';
  }
  
  /* Messaging API */
  
  Seam.Remoting.pollInterval = 10; // Default poll interval of 10 seconds
  Seam.Remoting.pollTimeout = 0; // Default timeout of 0 seconds
  Seam.Remoting.polling = false;
  
  Seam.Remoting.setPollInterval = function(interval)
  {
    Seam.Remoting.pollInterval = interval;
  }
  
  Seam.Remoting.setPollTimeout = function(timeout)
  {
    Seam.Remoting.pollTimeout = timeout;
  }
  
  Seam.Remoting.subscriptionRegistry = new Array();
  
  Seam.Remoting.subscribe = function(topicName, callback)
  {
    for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
    {
      if (Seam.Remoting.subscriptionRegistry[i].topic == topicName)
        return;
    }
  
    var body = "<subscribe topic=\"" + topicName + "\"/>";
    var env = Seam.Remoting.createEnvelope(null, body);
    Seam.Remoting.subscriptionRegistry.push({topic:topicName, callback:callback});
    Seam.Remoting.sendAjaxRequest(env, Seam.Remoting.PATH_SUBSCRIPTION, Seam.Remoting.subscriptionCallback, false);
  }
  
  Seam.Remoting.unsubscribe = function(topicName)
  {
    var token = null;
  
    for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
    {
      if (Seam.Remoting.subscriptionRegistry[i].topic == topicName)
      {
        token = Seam.Remoting.subscriptionRegistry[i].token;
        Seam.Remoting.subscriptionRegistry.splice(i, 1);
      }
    }
  
    if (token)
    {
      var body = "<unsubscribe token=\"" + token + "\"/>";
      var env = Seam.Remoting.createEnvelope(null, body);
      Seam.Remoting.sendAjaxRequest(env, Seam.Remoting.PATH_SUBSCRIPTION, null, false);
    }
  }
  
  Seam.Remoting.subscriptionCallback = function(doc)
  {
    var body = doc.documentElement.firstChild;
    for (var i = 0; i < body.childNodes.length; i++)
    {
      var node = body.childNodes.item(i);
      if (node.tagName == "subscription")
      {
        var topic = node.getAttribute("topic");
        var token = node.getAttribute("token");
        for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
        {
          if (Seam.Remoting.subscriptionRegistry[i].topic == topic)
          {
            Seam.Remoting.subscriptionRegistry[i].token = token;
            Seam.Remoting.poll();
            break;
          }
        }
      }
    }
  }
  
  Seam.Remoting.pollTimeoutFunction = null;
  
  Seam.Remoting.poll = function()
  {
    if (Seam.Remoting.polling)
      return;
  
    Seam.Remoting.polling = true;
    clearTimeout(Seam.Remoting.pollTimeoutFunction);
  
    var body = "";
  
    if (Seam.Remoting.subscriptionRegistry.length == 0)
    {
      Seam.Remoting.polling = false;
      return;
    }
  
    for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
    {
      body += "<poll token=\"" + Seam.Remoting.subscriptionRegistry[i].token + "\" ";
      body += "timeout=\"" + Seam.Remoting.pollTimeout + "\"/>";
    }
  
    var env = Seam.Remoting.createEnvelope(null, body);
    Seam.Remoting.sendAjaxRequest(env, Seam.Remoting.PATH_POLL, Seam.Remoting.pollCallback, true);
  }
  
  Seam.Remoting.pollCallback = function(doc)
  {
    Seam.Remoting.polling = false;
  
    var body = doc.documentElement.firstChild;
    for (var i = 0; i < body.childNodes.length; i++)
    {
      var node = body.childNodes.item(i);
      if (node.tagName == "messages")
        Seam.Remoting.processMessages(node);
      else if (node.tagName == "errors")
        Seam.Remoting.processPollErrors(node);
    }
  
    Seam.Remoting.pollTimeoutFunction = setTimeout("Seam.Remoting.poll()", Math.max(Seam.Remoting.pollInterval * 1000, 1000));
  }
  
  Seam.Remoting.processMessages = function(messages)
  {
    var token = messages.getAttribute("token");
  
    var callback = null;
    for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
    {
      if (Seam.Remoting.subscriptionRegistry[i].token == token)
      {
        callback = Seam.Remoting.subscriptionRegistry[i].callback;
        break;
      }
    }
  
    if (callback != null)
    {
      var messageNode = null;
  
      var children = messages.childNodes;
      for (var i = 0; i < children.length; i++)
      {
        if (children.item(i).tagName == "message")
        {
          messageNode = children.item(i);
          var messageType = messageNode.getAttribute("type");
  
          var valueNode = null;
          var refsNode = null;
          for (var j = 0; j < messageNode.childNodes.length; j++)
          {
            var node = messageNode.childNodes.item(j);
            if (node.tagName == "value")
              valueNode = node;
            else if (node.tagName == "refs")
              refsNode = node;
          }
  
          var refs = new Array();
          if (refsNode)
            Seam.Remoting.unmarshalRefs(refsNode, refs);
  
          var value = Seam.Remoting.unmarshalValue(valueNode.firstChild, refs);
  
          callback(Seam.Remoting.createMessage(messageType, value));
        }
      }
    }
  }
  
  Seam.Remoting.processErrors = function(errors)
  {
    var token = errors.getAttribute("token");
  
    // Unsubscribe to the topic
    for (var i = 0; i < Seam.Remoting.subscriptionRegistry.length; i++)
    {
      if (Seam.Remoting.subscriptionRegistry[i].token == token)
      {
        Seam.Remoting.subscriptionRegistry.splice(i, 1);
        break;
      }
    }
  
    for (var i = 0; i < errors.childNodes.length; i++)
    {
      if (errors.childNodes.item(i).tagName == "error")
      {
        var errorNode = errors.childNodes.item(i);
        var code = errorNode.getAttribute("code");
        var message = errorNode.firstChild.nodeValue;
  
        if (Seam.Remoting.onPollError)
          Seam.Remoting.onPollError(code, message);
        else
          alert("A polling error occurred: " + code + " " + message);
      }
    }
  }
  
  Seam.Remoting.ObjectMessage = function()
  {
    this.value = null;
  
    Seam.Remoting.ObjectMessage.prototype.getValue = function()
    {
      return this.value;
    }
  
    Seam.Remoting.ObjectMessage.prototype.setValue = function(value)
    {
      this.value = value;
    }
  }
  
  Seam.Remoting.TextMessage = function()
  {
    this.text = null;
  
    Seam.Remoting.TextMessage.prototype.getText = function()
    {
      return this.text;
    }
  
    Seam.Remoting.TextMessage.prototype.setText = function(text)
    {
      this.text = text;
    }
  }
  
  Seam.Remoting.createMessage = function(messageType, value)
  {
    switch (messageType)
    {
      case "object":
        var msg = new Seam.Remoting.ObjectMessage();
        msg.setValue(value);
        return msg;
      case "text":
        var msg = new Seam.Remoting.TextMessage();
        msg.setText(value);
        return msg;
    }
    return null;
  }
  
  
  



More information about the jboss-cvs-commits mailing list