blob: f2ccfb11b6ece03de915c20eb4a564af34c35dc2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.bayeux;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.Date;
import java.text.SimpleDateFormat;
import javax.servlet.ServletException;
import org.apache.catalina.comet.CometEvent;
import org.apache.tomcat.bayeux.HttpError;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.apache.cometd.bayeux.Bayeux;
import org.apache.cometd.bayeux.Message;
/**
* Common functionality and member variables for all Bayeux requests.
*
* @author Guy A. Molinari
* @version 0.9
*
*/
public abstract class RequestBase implements BayeuxRequest {
protected static final SimpleDateFormat timestampFmt =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
static {
timestampFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
}
//message properties, combined for all messages
protected TomcatBayeux tomcatBayeux;
protected String channel;
protected String id;
protected String clientId;
protected String version = null;
protected String[] suppConnTypes = null;
protected int suppConnTypesFlag = 0;
protected int desiredConnTypeFlag = 0;
protected String minVersion = null;
protected String subscription = null;
protected String data = null;
protected String conType = null;
protected LinkedHashMap<String, Object> ext = new LinkedHashMap<String, Object> ();
protected CometEvent event;
protected HashMap<String, Object> response = null;
private static final Log log = LogFactory.getLog(RequestBase.class);
protected int reconnectInterval = 1000;
protected RequestBase(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
this.tomcatBayeux = tb;
this.event = event;
channel = jsReq.optString(Bayeux.CHANNEL_FIELD);
id = jsReq.optString(Bayeux.ID_FIELD);
clientId = jsReq.optString(Bayeux.CLIENT_FIELD);
version = jsReq.optString(Bayeux.VERSION_FIELD);
minVersion = jsReq.optString(Bayeux.MIN_VERSION_FIELD);
conType = jsReq.optString(Bayeux.CONNECTION_TYPE_FIELD);
subscription = jsReq.optString(Bayeux.SUBSCRIPTION_FIELD);
data = jsReq.optString(Bayeux.DATA_FIELD);
reconnectInterval = tb.getReconnectInterval();
if (jsReq.has(Bayeux.EXT_FIELD)) {
JSONObject jext = jsReq.getJSONObject(Bayeux.EXT_FIELD);
for (Iterator<String> i = jext.keys(); i.hasNext(); ) {
String key = i.next();
ext.put(key, jext.get(key));
}//for
}//end if
if (jsReq.has(Bayeux.SUPP_CONNECTION_TYPE_FIELD)) {
JSONArray types = jsReq.getJSONArray(Bayeux.SUPP_CONNECTION_TYPE_FIELD);
suppConnTypes = new String[types.length()];
for (int i = 0; i < types.length(); i++) {
suppConnTypes[i] = types.getString(i);
if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(suppConnTypes[i]))
suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_CALLBACK_POLL;
else if (Bayeux.TRANSPORT_LONG_POLL.equals(suppConnTypes[i]))
suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_LONG_POLL;
}//for
}//end if
if (conType!=null) {
if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType))
desiredConnTypeFlag = ClientImpl.SUPPORT_CALLBACK_POLL;
else if (Bayeux.TRANSPORT_LONG_POLL.equals(conType))
desiredConnTypeFlag = ClientImpl.SUPPORT_LONG_POLL;
}//end if
//due to the fact that the javascript doesn't send up a required field
//we have to fake it
suppConnTypesFlag = ClientImpl.SUPPORT_CALLBACK_POLL | ClientImpl.SUPPORT_LONG_POLL;
}
public HttpError validate() {
HttpError result = null;
// if (clientId == null) {
// result = new HttpError(401,"No Client ID.", null);
// }
return result;
}
public TomcatBayeux getTomcatBayeux() {
return tomcatBayeux;
}
public String getChannel() {
return channel;
}
public String getId() {
return id;
}
public String getClientId() {
return clientId;
}
public LinkedHashMap getExt() {
return ext;
}
public CometEvent getEvent() {
return event;
}
protected static void deliver(CometEvent event, ClientImpl to) throws IOException, ServletException, BayeuxException {
JSONArray jarray = getJSONArray(event,true);
if ( jarray == null ) throw new BayeuxException("No message to send!");
String jsonstring = jarray.toString();
if (log.isDebugEnabled()) {
log.debug("["+Thread.currentThread().getName()+"] Delivering message to[" + to + "] message:" + jsonstring);
}
if (to!=null) {
if (to.useJsonFiltered()) {
if (!event.getHttpServletResponse().isCommitted()) event.getHttpServletResponse().setContentType("text/json-comment-filtered");
}else {
if (!event.getHttpServletResponse().isCommitted()) event.getHttpServletResponse().setContentType("text/json");
}
}
PrintWriter out = event.getHttpServletResponse().getWriter();
if (to==null) {
//do nothing
}else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {
if (to.useJsonFiltered())
out.print("/*");
} else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {
String jsonp = event.getHttpServletRequest().getParameter(Bayeux.JSONP_PARAMETER);
if (jsonp == null)
jsonp = Bayeux.JSONP_DEFAULT_NAME;
out.print(jsonp);
out.print('(');
} else {
throw new BayeuxException("Client doesn't support any appropriate connection type.");
}
out.print(jsonstring);
if ( to == null ) {
//do nothing
} else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {
if (to.useJsonFiltered())
out.print("*/");
} else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {
out.print(");");
}
out.flush();
event.getHttpServletResponse().flushBuffer();
}
protected static JSONArray getJSONArray(CometEvent event, boolean nullok) {
synchronized(event) {
JSONArray jarray = (JSONArray) event.getHttpServletRequest().getAttribute(JSON_MSG_ARRAY);
if (jarray == null && (!nullok)) {
jarray = new JSONArray();
event.getHttpServletRequest().setAttribute(JSON_MSG_ARRAY, jarray);
}
return jarray;
}
}
protected JSONArray getJSONArray() {
return getJSONArray(event,false);
}
protected void addToDeliveryQueue(ClientImpl to, JSONObject msg) throws IOException, ServletException, BayeuxException {
synchronized (event) {
getJSONArray().put(msg);
}
}
protected void flushMessages(ClientImpl client) throws BayeuxException {
List<Message> msgs = client.takeMessages();
synchronized (event) {
try {
for (Iterator<Message> it = msgs.iterator(); it.hasNext(); ){
MessageImpl msg = (MessageImpl)it.next();
Map map = new HashMap();
map.put(Bayeux.CHANNEL_FIELD,msg.getChannel().getId());
if (msg.getClient()!=null) map.put(Bayeux.CLIENT_FIELD,msg.getClient().getId());
map.put(Bayeux.DATA_FIELD,msg);
JSONObject obj = new JSONObject(map);
addToDeliveryQueue(client, obj);
}
} catch (ServletException x) {
throw new BayeuxException(x);
} catch (IOException x) {
throw new BayeuxException(x);
}
}
}
public int process(int prevops) throws BayeuxException {
event.getHttpServletRequest().setAttribute(CURRENT_REQ_ATTR,this);
return prevops;
}
public int getReconnectInterval() {
return reconnectInterval;
}
public String getTimeStamp() {
return timestampFmt.format(new Date(System.currentTimeMillis()));
}
}