View Javadoc

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