View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.ajax.container;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.config.MuleProperties;
12  import org.mule.api.transport.Connector;
13  import org.mule.module.json.transformers.ObjectToJson;
14  import org.mule.transport.ajax.BayeuxAware;
15  import org.mule.transport.ajax.i18n.AjaxMessages;
16  import org.mule.transport.service.TransportFactory;
17  import org.mule.transport.servlet.MuleServletContextListener;
18  import org.mule.util.annotation.AnnotationUtils;
19  
20  import java.io.IOException;
21  import java.util.HashSet;
22  import java.util.Set;
23  
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import org.cometd.Message;
29  import org.mortbay.cometd.AbstractBayeux;
30  import org.mortbay.cometd.MessageImpl;
31  import org.mortbay.cometd.MessagePool;
32  import org.mortbay.cometd.continuation.ContinuationBayeux;
33  import org.mortbay.cometd.continuation.ContinuationCometdServlet;
34  
35  /**
36   * Wraps the {@link ContinuationCometdServlet} servlet and binds the Bayeux object to
37   * the Mule {@link AjaxServletConnector}.
38   */
39  public class MuleAjaxServlet extends ContinuationCometdServlet
40  {
41      protected Connector connector = null;
42  
43      private ObjectToJson jsonTransformer;
44  
45      private Set<Class> ignoreClasses = new HashSet<Class>();
46      private Set<Class> jsonBindings = new HashSet<Class>();
47  
48      @Override
49      public void init() throws ServletException
50      {
51          super.init();
52          MuleContext muleContext = (MuleContext)getServletContext().getAttribute(MuleProperties.MULE_CONTEXT_PROPERTY);
53          if(muleContext==null)
54          {
55              throw new ServletException("Attribute " + MuleProperties.MULE_CONTEXT_PROPERTY + " not set on ServletContext");
56          }
57          String servletConnectorName = getServletConfig().getInitParameter(MuleServletContextListener.CONNECTOR_NAME);
58          if (servletConnectorName == null)
59          {
60              servletConnectorName = (String)getServletContext().getAttribute(MuleServletContextListener.CONNECTOR_NAME);
61          }
62          
63          if (servletConnectorName == null)
64          {
65              connector = new TransportFactory(muleContext).getConnectorByProtocol(getConnectorProtocol());
66              if (connector == null)
67              {
68                  connector = new AjaxServletConnector(muleContext);
69                  connector.setName("ajax.servlet." + getServletContext().getServerInfo());
70                  try
71                  {
72                      muleContext.getRegistry().registerConnector(connector);
73                  }
74                  catch (MuleException e)
75                  {
76                      throw new ServletException("Failed to register the AjaxServletConnector", e);
77                  }
78              }
79          }
80          else
81          {
82              connector = muleContext.getRegistry().lookupConnector(servletConnectorName);
83              if (connector == null)
84              {
85                  throw new ServletException(AjaxMessages.noAjaxConnectorWithName(servletConnectorName, MuleServletContextListener.CONNECTOR_NAME).toString());
86              }
87          }
88  
89          try
90          {
91              ((BayeuxAware)connector).setBayeux(getBayeux());
92              jsonTransformer = new ObjectToJson();
93              connector.getMuleContext().getRegistry().applyProcessorsAndLifecycle(jsonTransformer);
94          }
95          catch (MuleException e)
96          {
97              throw new ServletException(e);
98          }
99      }
100 
101     @Override
102     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
103     {
104         super.service(request, response);
105     }
106 
107     protected String getConnectorProtocol()
108     {
109         return AjaxServletConnector.PROTOCOL;
110     }
111 
112     @Override
113     protected AbstractBayeux newBayeux()
114     {
115         return new MuleContinuationBayeux();
116     }
117 
118     /**
119      * We subclass the {@link org.mortbay.cometd.continuation.ContinuationBayeux} so tat we can insert a different
120      * message implementation that allows us to have better control over the message going across the wire. Right now this
121      * means that we use Jackson for Json serialization.
122      */
123     protected class MuleContinuationBayeux extends ContinuationBayeux
124     {
125         @Override
126         public MessageImpl newMessage()
127         {
128             //TODO no access to the message pool, need to have a fork of ContinuationBayeux to mimic exact behaviour
129             MessageImpl message;//_messagePool.poll();
130 //            if (message == null)
131 //            {
132                 message=new MuleMessageImpl(this);
133            // }
134             message.incRef();
135             return message;
136         }
137 
138         @Override
139         public MessageImpl newMessage(Message associated)
140         {
141             //TODO no access to the message pool, need to have a fork of ContinuationBayeux to mimic exact behaviour
142             MessageImpl message;//_messagePool.poll();
143 //            if (message == null)
144 //            {
145                 message=new MuleMessageImpl(this);
146             //}
147             message.incRef();
148             if (associated != null)
149                 message.setAssociated(associated);
150             return message;
151         }
152     }
153 
154 
155     public class MuleMessageImpl extends MessageImpl
156     {
157         public MuleMessageImpl(MessagePool bayeux)
158         {
159             super(bayeux);
160         }
161 
162         @Override
163         public String getJSON()
164         {
165             Object data = getData();
166             try
167             {
168                 if(data!=null && !ignoreClasses.contains(data.getClass()))
169                 {
170                     if(jsonBindings.contains(data.getClass()))
171                     {
172                         return (String) jsonTransformer.transform(this);
173                     }
174                     else if(AnnotationUtils.hasAnnotationWithPackage("org.codehaus.jackson", data.getClass()))
175                     {
176                         //Tell the transformer to accept this type next time
177                         jsonBindings.add(data.getClass());
178                         return (String) jsonTransformer.transform(this);
179                     }
180                     else
181                     {
182                         //We can ignore objects of this type and delegate to the super class
183                         ignoreClasses.add(data.getClass());
184                     }
185                 }
186                 return super.getJSON();
187             }
188             catch (Exception e)
189             {
190                 throw new RuntimeException("Failed to convert message to JSON", e);
191 
192             }
193         }
194     }
195 
196 }