forked from p81075629/datagear
SQL工作台:引入cometd技术,用于支持SQL执行反馈
This commit is contained in:
parent
86cd42ad01
commit
6d23f52be9
|
@ -147,6 +147,11 @@
|
|||
<artifactId>freemarker</artifactId>
|
||||
<version>2.3.28</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cometd.java</groupId>
|
||||
<artifactId>cometd-java-server</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -4,18 +4,25 @@
|
|||
|
||||
package org.datagear.web.controller;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.cometd.bayeux.server.BayeuxServer;
|
||||
import org.cometd.bayeux.server.ServerChannel;
|
||||
import org.cometd.bayeux.server.ServerSession;
|
||||
import org.datagear.connection.ConnectionSource;
|
||||
import org.datagear.management.domain.Schema;
|
||||
import org.datagear.management.service.SchemaService;
|
||||
import org.datagear.web.convert.ClassDataConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* SQL工作台控制器。
|
||||
|
@ -27,6 +34,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
@RequestMapping("/sqlpad")
|
||||
public class SqlpadController extends AbstractSchemaConnController
|
||||
{
|
||||
@Autowired
|
||||
private BayeuxServer bayeuxServer;
|
||||
|
||||
public SqlpadController()
|
||||
{
|
||||
super();
|
||||
|
@ -38,6 +48,16 @@ public class SqlpadController extends AbstractSchemaConnController
|
|||
super(messageSource, classDataConverter, schemaService, connectionSource);
|
||||
}
|
||||
|
||||
public BayeuxServer getBayeuxServer()
|
||||
{
|
||||
return bayeuxServer;
|
||||
}
|
||||
|
||||
public void setBayeuxServer(BayeuxServer bayeuxServer)
|
||||
{
|
||||
this.bayeuxServer = bayeuxServer;
|
||||
}
|
||||
|
||||
@RequestMapping("/{schemaId}")
|
||||
public String index(HttpServletRequest request, HttpServletResponse response,
|
||||
org.springframework.ui.Model springModel, @PathVariable("schemaId") String schemaId) throws Throwable
|
||||
|
@ -53,4 +73,21 @@ public class SqlpadController extends AbstractSchemaConnController
|
|||
|
||||
return "/sqlpad/sqlpad";
|
||||
}
|
||||
|
||||
@RequestMapping("/message")
|
||||
@ResponseBody
|
||||
public String message(HttpServletRequest request, HttpServletResponse response) throws Throwable
|
||||
{
|
||||
String channelName = "/sqlpad";
|
||||
|
||||
this.bayeuxServer.createIfAbsent(channelName);
|
||||
|
||||
ServerChannel channel = this.bayeuxServer.getChannel(channelName);
|
||||
Set<ServerSession> sessions = channel.getSubscribers();
|
||||
|
||||
for (ServerSession serverSession : sessions)
|
||||
channel.publish(serverSession, "hello");
|
||||
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2018 datagear.org. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package org.datagear.web.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.cometd.bayeux.server.BayeuxServer;
|
||||
import org.cometd.bayeux.server.BayeuxServer.Extension;
|
||||
import org.cometd.server.BayeuxServerImpl;
|
||||
|
||||
/**
|
||||
* {@linkplain BayeuxServer}工厂类。
|
||||
*
|
||||
* @author datagear@163.com
|
||||
*
|
||||
*/
|
||||
public class BayeuxServerFactory
|
||||
{
|
||||
private Map<String, Object> options;
|
||||
|
||||
private List<Extension> extensions;
|
||||
|
||||
public BayeuxServerFactory()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public Map<String, Object> getOptions()
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions(Map<String, Object> options)
|
||||
{
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public List<Extension> getExtensions()
|
||||
{
|
||||
return extensions;
|
||||
}
|
||||
|
||||
public void setExtensions(List<Extension> extensions)
|
||||
{
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public BayeuxServer getBayeuxServer()
|
||||
{
|
||||
BayeuxServerImpl bayeuxServerImpl = new BayeuxServerImpl();
|
||||
|
||||
if (this.options != null)
|
||||
bayeuxServerImpl.setOptions(this.options);
|
||||
|
||||
if (this.extensions != null)
|
||||
{
|
||||
for (Extension extension : this.extensions)
|
||||
bayeuxServerImpl.addExtension(extension);
|
||||
}
|
||||
|
||||
return bayeuxServerImpl;
|
||||
}
|
||||
}
|
|
@ -331,6 +331,32 @@
|
|||
|
||||
<bean id="changelogResolver" class="org.datagear.web.util.ChangelogResolver" />
|
||||
|
||||
<!-- cometd配置开始 -->
|
||||
<bean id="bayeuxServerFactory" class="org.datagear.web.util.BayeuxServerFactory">
|
||||
<property name="options">
|
||||
<map>
|
||||
<entry key="logLevel" value="3" />
|
||||
<entry key="timeout" value="15000" />
|
||||
</map>
|
||||
</property>
|
||||
<property name="extensions">
|
||||
<list>
|
||||
<bean class="org.cometd.server.ext.AcknowledgedMessagesExtension" />
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="bayeuxServer" factory-bean="bayeuxServerFactory" factory-method="getBayeuxServer" init-method="start" destroy-method="stop" />
|
||||
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
|
||||
<property name="attributes">
|
||||
<map>
|
||||
<entry key="org.cometd.bayeux">
|
||||
<ref local="bayeuxServer" />
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
<!-- cometd配置结束 -->
|
||||
|
||||
<!-- 定时任务开始 -->
|
||||
|
||||
<!-- 删除过期的BLOB文件 -->
|
||||
|
|
|
@ -31,6 +31,12 @@ var contextPath="${contextPath}";
|
|||
<script src="${contextPath}/static/script/textarea-helper-0.3.1/textarea-helper.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/ace-21.02.19/ace.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/ace-21.02.19/mode-sql.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/cometd.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/AckExtension.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/ReloadExtension.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/jquery.cometd.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/jquery.cometd-ack.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/cometd-2.9.1/jquery.cometd-reload.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/datagear-pagination.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/datagear-model.js" type="text/javascript"></script>
|
||||
<script src="${contextPath}/static/script/datagear-modelcache.js" type="text/javascript"></script>
|
||||
|
|
|
@ -98,6 +98,19 @@ Schema schema 数据库,不允许为null
|
|||
editor.setValue("");
|
||||
editor.focus();
|
||||
});
|
||||
|
||||
var cometd = $.cometd;
|
||||
|
||||
if(!$.isCometdInit)
|
||||
{
|
||||
$.isCometdInit = true;
|
||||
cometd.init("${contextPath}/cometd");
|
||||
}
|
||||
|
||||
cometd.subscribe("/sqlpad", function(message)
|
||||
{
|
||||
$("<p />").html(message).appendTo(po.element(".content-result"));
|
||||
});
|
||||
})
|
||||
(${pageId});
|
||||
</script>
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
<param-name>forceEncoding</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
<async-supported>true</async-supported>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>encodingFilter</filter-name>
|
||||
|
@ -49,22 +50,9 @@
|
|||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<servlet-name>dispatcherServlet</servlet-name>
|
||||
</filter-mapping>
|
||||
|
||||
<!--
|
||||
<filter>
|
||||
<filter-name>userFilter</filter-name>
|
||||
<filter-class>org.datagear.web.filter.UserFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>userFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
</filter-mapping>
|
||||
-->
|
||||
|
||||
<servlet>
|
||||
<servlet-name>dispatcherServlet</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet
|
||||
|
@ -98,6 +86,16 @@
|
|||
<url-pattern>/notification/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>cometd</servlet-name>
|
||||
<servlet-class>org.cometd.server.CometdServlet</servlet-class>
|
||||
<async-supported>true</async-supported>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>cometd</servlet-name>
|
||||
<url-pattern>/cometd/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
(function()
|
||||
{
|
||||
function bind(org_cometd)
|
||||
{
|
||||
/**
|
||||
* This client-side extension enables the client to acknowledge to the server
|
||||
* the messages that the client has received.
|
||||
* For the acknowledgement to work, the server must be configured with the
|
||||
* correspondent server-side ack extension. If both client and server support
|
||||
* the ack extension, then the ack functionality will take place automatically.
|
||||
* By enabling this extension, all messages arriving from the server will arrive
|
||||
* via the long poll, so the comet communication will be slightly chattier.
|
||||
* The fact that all messages will return via long poll means also that the
|
||||
* messages will arrive with total order, which is not guaranteed if messages
|
||||
* can arrive via both long poll and normal response.
|
||||
* Messages are not acknowledged one by one, but instead a group of messages is
|
||||
* acknowledged when long poll returns.
|
||||
*/
|
||||
return org_cometd.AckExtension = function()
|
||||
{
|
||||
var _cometd;
|
||||
var _serverSupportsAcks = false;
|
||||
var _ackId = -1;
|
||||
|
||||
function _debug(text, args)
|
||||
{
|
||||
_cometd._debug(text, args);
|
||||
}
|
||||
|
||||
this.registered = function(name, cometd)
|
||||
{
|
||||
_cometd = cometd;
|
||||
_debug('AckExtension: executing registration callback');
|
||||
};
|
||||
|
||||
this.unregistered = function()
|
||||
{
|
||||
_debug('AckExtension: executing unregistration callback');
|
||||
_cometd = null;
|
||||
};
|
||||
|
||||
this.incoming = function(message)
|
||||
{
|
||||
var channel = message.channel;
|
||||
if (channel == '/meta/handshake')
|
||||
{
|
||||
_serverSupportsAcks = message.ext && message.ext.ack;
|
||||
_debug('AckExtension: server supports acks', _serverSupportsAcks);
|
||||
}
|
||||
else if (channel == '/meta/connect' && message.successful && _serverSupportsAcks)
|
||||
{
|
||||
var ext = message.ext;
|
||||
if (ext && typeof ext.ack === 'number')
|
||||
{
|
||||
_ackId = ext.ack;
|
||||
_debug('AckExtension: server sent ack id', _ackId);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
this.outgoing = function(message)
|
||||
{
|
||||
var channel = message.channel;
|
||||
if (channel == '/meta/handshake')
|
||||
{
|
||||
if (!message.ext)
|
||||
{
|
||||
message.ext = {};
|
||||
}
|
||||
message.ext.ack = _cometd && _cometd.ackEnabled !== false;
|
||||
_ackId = -1;
|
||||
}
|
||||
else if (channel == '/meta/connect' && _serverSupportsAcks)
|
||||
{
|
||||
if (!message.ext)
|
||||
{
|
||||
message.ext = {};
|
||||
}
|
||||
message.ext.ack = _ackId;
|
||||
_debug('AckExtension: client sending ack id', _ackId);
|
||||
}
|
||||
return message;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
(function()
|
||||
{
|
||||
function bind(org_cometd)
|
||||
{
|
||||
if (!org_cometd.COOKIE)
|
||||
{
|
||||
org_cometd.COOKIE = {};
|
||||
org_cometd.COOKIE.set = function(name, value, options)
|
||||
{
|
||||
throw 'Abstract';
|
||||
};
|
||||
org_cometd.COOKIE.get = function(name)
|
||||
{
|
||||
throw 'Abstract';
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The reload extension allows a page to be loaded (or reloaded)
|
||||
* without having to re-handshake in the new (or reloaded) page,
|
||||
* therefore resuming the existing cometd connection.
|
||||
*
|
||||
* When the reload() method is called, the state of the cometd
|
||||
* connection and of the cometd subscriptions is stored in a cookie
|
||||
* with a short max-age.
|
||||
* The reload() method must therefore be called by page unload
|
||||
* handlers, often provided by JavaScript toolkits.
|
||||
*
|
||||
* When the page is (re)loaded, this extension checks the cookie
|
||||
* and restores the cometd connection and the cometd subscriptions.
|
||||
*/
|
||||
return org_cometd.ReloadExtension = function(configuration)
|
||||
{
|
||||
var _cometd;
|
||||
var _debug;
|
||||
var _state = null;
|
||||
var _cookieName = 'org.cometd.reload';
|
||||
var _cookiePath = '/';
|
||||
var _cookieMaxAge = 5;
|
||||
var _batch = false;
|
||||
|
||||
function _reload(config)
|
||||
{
|
||||
if (_state && _state.handshakeResponse !== null)
|
||||
{
|
||||
_configure(config);
|
||||
_state.cookiePath = _cookiePath;
|
||||
var cookie = org_cometd.JSON.toJSON(_state);
|
||||
_debug('Reload extension saving cookie value', cookie);
|
||||
org_cometd.COOKIE.set(_cookieName, cookie, {
|
||||
'max-age': _cookieMaxAge,
|
||||
path: _cookiePath,
|
||||
expires: new Date(new Date().getTime() + _cookieMaxAge * 1000)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _similarState(oldState)
|
||||
{
|
||||
// We want to check here that the CometD object
|
||||
// did not change much between reloads.
|
||||
// We just check the URL for now, but in future
|
||||
// further checks may involve the transport type
|
||||
// and other configuration parameters.
|
||||
return _state.url == oldState.url;
|
||||
}
|
||||
|
||||
function _configure(config)
|
||||
{
|
||||
if (config)
|
||||
{
|
||||
if (typeof config.cookieMaxAge === 'number')
|
||||
{
|
||||
_cookieMaxAge = config.cookieMaxAge;
|
||||
}
|
||||
if (typeof config.cookieName === 'string')
|
||||
{
|
||||
_cookieName = config.cookieName;
|
||||
}
|
||||
if (typeof config.cookiePath === 'string')
|
||||
{
|
||||
_cookiePath = config.cookiePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.configure = _configure;
|
||||
|
||||
this.registered = function(name, cometd)
|
||||
{
|
||||
_cometd = cometd;
|
||||
_cometd.reload = _reload;
|
||||
_debug = _cometd._debug;
|
||||
};
|
||||
|
||||
this.unregistered = function()
|
||||
{
|
||||
delete _cometd.reload;
|
||||
_cometd = null;
|
||||
};
|
||||
|
||||
this.outgoing = function(message)
|
||||
{
|
||||
switch (message.channel)
|
||||
{
|
||||
case '/meta/handshake':
|
||||
{
|
||||
_state = {};
|
||||
_state.url = _cometd.getURL();
|
||||
|
||||
var cookie = org_cometd.COOKIE.get(_cookieName);
|
||||
_debug('Reload extension found cookie value', cookie);
|
||||
// Is there a saved handshake response from a prior load ?
|
||||
if (cookie)
|
||||
{
|
||||
try
|
||||
{
|
||||
var oldState = org_cometd.JSON.fromJSON(cookie);
|
||||
|
||||
// Remove the cookie, not needed anymore
|
||||
org_cometd.COOKIE.set(_cookieName, '', {
|
||||
'max-age': -1,
|
||||
path: oldState.cookiePath,
|
||||
expires: -1
|
||||
});
|
||||
|
||||
if (oldState.handshakeResponse && _similarState(oldState))
|
||||
{
|
||||
_debug('Reload extension restoring state', oldState);
|
||||
setTimeout(function ()
|
||||
{
|
||||
_debug('Reload extension replaying handshake response', oldState.handshakeResponse);
|
||||
_state.handshakeResponse = oldState.handshakeResponse;
|
||||
_state.transportType = oldState.transportType;
|
||||
_state.reloading = true;
|
||||
var response = _cometd._mixin(true, {}, _state.handshakeResponse, {ext: {reload: true}});
|
||||
response.supportedConnectionTypes = [_state.transportType];
|
||||
_cometd.receive(response);
|
||||
_debug('Reload extension replayed handshake response', response);
|
||||
}, 0);
|
||||
|
||||
// delay any sends until first connect is complete.
|
||||
if (!_batch)
|
||||
{
|
||||
_batch = true;
|
||||
_cometd.startBatch();
|
||||
}
|
||||
// This handshake is aborted, as we will replay the prior handshake response
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_debug('Reload extension could not restore state', oldState);
|
||||
}
|
||||
}
|
||||
catch (x)
|
||||
{
|
||||
_debug('Reload extension error while trying to restore cookie', x);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '/meta/connect':
|
||||
{
|
||||
if (!_state.transportType)
|
||||
{
|
||||
_state.transportType = message.connectionType;
|
||||
_debug('Reload extension tracked transport type', _state.transportType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '/meta/disconnect':
|
||||
{
|
||||
_state = null;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
this.incoming = function(message)
|
||||
{
|
||||
if (message.successful)
|
||||
{
|
||||
switch (message.channel)
|
||||
{
|
||||
case '/meta/handshake':
|
||||
{
|
||||
// If the handshake response is already present, then we're replaying it.
|
||||
// Since the replay may have modified the handshake response, do not record it here.
|
||||
if (!_state.handshakeResponse)
|
||||
{
|
||||
// Save successful handshake response
|
||||
_state.handshakeResponse = message;
|
||||
_debug('Reload extension tracked handshake response', message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '/meta/disconnect':
|
||||
{
|
||||
_state = null;
|
||||
break;
|
||||
}
|
||||
case '/meta/connect':
|
||||
{
|
||||
if (_batch)
|
||||
{
|
||||
_cometd.endBatch();
|
||||
_batch = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
_configure(configuration);
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
(function()
|
||||
{
|
||||
function bind(org_cometd)
|
||||
{
|
||||
/**
|
||||
* The timestamp extension adds the optional timestamp field to all outgoing messages.
|
||||
*/
|
||||
return org_cometd.TimeStampExtension = function()
|
||||
{
|
||||
this.outgoing = function(message)
|
||||
{
|
||||
message.timestamp = new Date().toUTCString();
|
||||
return message;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd);
|
||||
}
|
||||
}());
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright (c) 2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
(function()
|
||||
{
|
||||
function bind(org_cometd)
|
||||
{
|
||||
/**
|
||||
* With each handshake or connect, the extension sends timestamps within the
|
||||
* ext field like: <code>{ext:{timesync:{tc:12345567890,l:23,o:4567},...},...}</code>
|
||||
* where:<ul>
|
||||
* <li>tc is the client timestamp in ms since 1970 of when the message was sent.
|
||||
* <li>l is the network lag that the client has calculated.
|
||||
* <li>o is the clock offset that the client has calculated.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* A cometd server that supports timesync, can respond with an ext
|
||||
* field like: <code>{ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...}</code>
|
||||
* where:<ul>
|
||||
* <li>tc is the client timestamp of when the message was sent,
|
||||
* <li>ts is the server timestamp of when the message was received
|
||||
* <li>p is the poll duration in ms - ie the time the server took before sending the response.
|
||||
* <li>a is the measured accuracy of the calculated offset and lag sent by the client
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* The relationship between tc, ts & l is given by <code>ts=tc+o+l</code> (the
|
||||
* time the server received the messsage is the client time plus the offset plus the
|
||||
* network lag). Thus the accuracy of the o and l settings can be determined with
|
||||
* <code>a=(tc+o+l)-ts</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* When the client has received the response, it can make a more accurate estimate
|
||||
* of the lag as <code>l2=(now-tc-p)/2</code> (assuming symmetric lag).
|
||||
* A new offset can then be calculated with the relationship on the client
|
||||
* that <code>ts=tc+o2+l2</code>, thus <code>o2=ts-tc-l2</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* Since the client also receives the a value calculated on the server, it
|
||||
* should be possible to analyse this and compensate for some asymmetry
|
||||
* in the lag. But the current client does not do this.
|
||||
* </p>
|
||||
*
|
||||
* @param configuration
|
||||
*/
|
||||
return org_cometd.TimeSyncExtension = function(configuration)
|
||||
{
|
||||
var _cometd;
|
||||
var _maxSamples = configuration && configuration.maxSamples || 10;
|
||||
var _lags = [];
|
||||
var _offsets = [];
|
||||
var _lag = 0;
|
||||
var _offset = 0;
|
||||
|
||||
function _debug(text, args)
|
||||
{
|
||||
_cometd._debug(text, args);
|
||||
}
|
||||
|
||||
this.registered = function(name, cometd)
|
||||
{
|
||||
_cometd = cometd;
|
||||
_debug('TimeSyncExtension: executing registration callback');
|
||||
};
|
||||
|
||||
this.unregistered = function()
|
||||
{
|
||||
_debug('TimeSyncExtension: executing unregistration callback');
|
||||
_cometd = null;
|
||||
_lags = [];
|
||||
_offsets = [];
|
||||
};
|
||||
|
||||
this.incoming = function(message)
|
||||
{
|
||||
var channel = message.channel;
|
||||
if (channel && channel.indexOf('/meta/') === 0)
|
||||
{
|
||||
if (message.ext && message.ext.timesync)
|
||||
{
|
||||
var timesync = message.ext.timesync;
|
||||
_debug('TimeSyncExtension: server sent timesync', timesync);
|
||||
|
||||
var now = new Date().getTime();
|
||||
var l2 = (now - timesync.tc - timesync.p) / 2;
|
||||
var o2 = timesync.ts - timesync.tc - l2;
|
||||
|
||||
_lags.push(l2);
|
||||
_offsets.push(o2);
|
||||
if (_offsets.length > _maxSamples)
|
||||
{
|
||||
_offsets.shift();
|
||||
_lags.shift();
|
||||
}
|
||||
|
||||
var samples = _offsets.length;
|
||||
var lagsSum = 0;
|
||||
var offsetsSum = 0;
|
||||
for (var i = 0; i < samples; ++i)
|
||||
{
|
||||
lagsSum += _lags[i];
|
||||
offsetsSum += _offsets[i];
|
||||
}
|
||||
_lag = parseInt((lagsSum / samples).toFixed());
|
||||
_offset = parseInt((offsetsSum / samples).toFixed());
|
||||
_debug('TimeSyncExtension: network lag', _lag, 'ms, time offset with server', _offset, 'ms', _lag, _offset);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
this.outgoing = function(message)
|
||||
{
|
||||
var channel = message.channel;
|
||||
if (channel && channel.indexOf('/meta/') === 0)
|
||||
{
|
||||
if (!message.ext)
|
||||
{
|
||||
message.ext = {};
|
||||
}
|
||||
message.ext.timesync = {
|
||||
tc: new Date().getTime(),
|
||||
l: _lag,
|
||||
o: _offset
|
||||
};
|
||||
_debug('TimeSyncExtension: client sending timesync', org_cometd.JSON.toJSON(message.ext.timesync));
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the estimated offset in ms from the clients clock to the
|
||||
* servers clock. The server time is the client time plus the offset.
|
||||
*/
|
||||
this.getTimeOffset = function()
|
||||
{
|
||||
return _offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an array of multiple offset samples used to calculate
|
||||
* the offset.
|
||||
*/
|
||||
this.getTimeOffsetSamples = function()
|
||||
{
|
||||
return _offsets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the estimated network lag in ms from the client to the server.
|
||||
*/
|
||||
this.getNetworkLag = function()
|
||||
{
|
||||
return _lag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the estimated server time in ms since the epoch.
|
||||
*/
|
||||
this.getServerTime = function()
|
||||
{
|
||||
return new Date().getTime() + _offset;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Get the estimated server time as a Date object
|
||||
*/
|
||||
this.getServerDate = function()
|
||||
{
|
||||
return new Date(this.getServerTime());
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a timeout to expire at given time on the server.
|
||||
* @param callback The function to call when the timer expires
|
||||
* @param atServerTimeOrDate a js Time or Date object representing the
|
||||
* server time at which the timeout should expire
|
||||
*/
|
||||
this.setTimeout = function(callback, atServerTimeOrDate)
|
||||
{
|
||||
var ts = (atServerTimeOrDate instanceof Date) ? atServerTimeOrDate.getTime() : (0 + atServerTimeOrDate);
|
||||
var tc = ts - _offset;
|
||||
var interval = tc - new Date().getTime();
|
||||
if (interval <= 0)
|
||||
{
|
||||
interval = 1;
|
||||
}
|
||||
return org_cometd.Utils.setTimeout(_cometd, callback, interval);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd);
|
||||
}
|
||||
})();
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
function bind(AckExtension, cometd)
|
||||
{
|
||||
var result = new AckExtension();
|
||||
cometd.registerExtension('ack', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd/AckExtension', 'jquery.cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd.AckExtension, jQuery.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
function bind(org_cometd, cookie, ReloadExtension, cometd)
|
||||
{
|
||||
// Remap cometd COOKIE functions to jquery cookie functions
|
||||
// Avoid to set to undefined if the jquery cookie plugin is not present
|
||||
if (cookie)
|
||||
{
|
||||
org_cometd.COOKIE.set = cookie;
|
||||
org_cometd.COOKIE.get = cookie;
|
||||
}
|
||||
|
||||
var result = new ReloadExtension();
|
||||
cometd.registerExtension('reload', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd', 'jquery.cookie', 'org/cometd/ReloadExtension', 'jquery.cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd, jQuery.cookie, org.cometd.ReloadExtension, jQuery.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
function bind(TimeStampExtension, cometd)
|
||||
{
|
||||
var result = new TimeStampExtension();
|
||||
cometd.registerExtension('timestamp', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd/TimeStampExtension', 'jquery.cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd.TimeStampExtension, jQuery.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
function bind(TimeSyncExtension, cometd)
|
||||
{
|
||||
var result = new TimeSyncExtension();
|
||||
cometd.registerExtension('timesync', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['org/cometd/TimeSyncExtension', 'jquery.cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(org.cometd.TimeSyncExtension, jQuery.cometd);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
function bind($, org_cometd)
|
||||
{
|
||||
// Remap cometd JSON functions to jquery JSON functions
|
||||
org_cometd.JSON.toJSON = JSON.stringify;
|
||||
org_cometd.JSON.fromJSON = JSON.parse;
|
||||
|
||||
function _setHeaders(xhr, headers)
|
||||
{
|
||||
if (headers)
|
||||
{
|
||||
for (var headerName in headers)
|
||||
{
|
||||
if (headerName.toLowerCase() === 'content-type')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
xhr.setRequestHeader(headerName, headers[headerName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remap toolkit-specific transport calls
|
||||
function LongPollingTransport()
|
||||
{
|
||||
var _super = new org_cometd.LongPollingTransport();
|
||||
var that = org_cometd.Transport.derive(_super);
|
||||
|
||||
that.xhrSend = function(packet)
|
||||
{
|
||||
return $.ajax({
|
||||
url: packet.url,
|
||||
async: packet.sync !== true,
|
||||
type: 'POST',
|
||||
contentType: 'application/json;charset=UTF-8',
|
||||
data: packet.body,
|
||||
beforeSend: function(xhr)
|
||||
{
|
||||
// Has no effect if the request is not cross domain
|
||||
// but if it is, allows cookies to be sent to the server.
|
||||
xhr.withCredentials = true;
|
||||
_setHeaders(xhr, packet.headers);
|
||||
// Returning false will abort the XHR send
|
||||
return true;
|
||||
},
|
||||
success: packet.onSuccess,
|
||||
error: function(xhr, reason, exception)
|
||||
{
|
||||
packet.onError(reason, exception);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
function CallbackPollingTransport()
|
||||
{
|
||||
var _super = new org_cometd.CallbackPollingTransport();
|
||||
var that = org_cometd.Transport.derive(_super);
|
||||
|
||||
that.jsonpSend = function(packet)
|
||||
{
|
||||
$.ajax({
|
||||
url: packet.url,
|
||||
async: packet.sync !== true,
|
||||
type: 'GET',
|
||||
dataType: 'jsonp',
|
||||
jsonp: 'jsonp',
|
||||
data: {
|
||||
// In callback-polling, the content must be sent via the 'message' parameter
|
||||
message: packet.body
|
||||
},
|
||||
beforeSend: function(xhr)
|
||||
{
|
||||
_setHeaders(xhr, packet.headers);
|
||||
// Returning false will abort the XHR send
|
||||
return true;
|
||||
},
|
||||
success: packet.onSuccess,
|
||||
error: function(xhr, reason, exception)
|
||||
{
|
||||
packet.onError(reason, exception);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
$.Cometd = function(name)
|
||||
{
|
||||
var cometd = new org_cometd.Cometd(name);
|
||||
|
||||
// Registration order is important
|
||||
if (org_cometd.WebSocket)
|
||||
{
|
||||
cometd.registerTransport('websocket', new org_cometd.WebSocketTransport());
|
||||
}
|
||||
cometd.registerTransport('long-polling', new LongPollingTransport());
|
||||
cometd.registerTransport('callback-polling', new CallbackPollingTransport());
|
||||
|
||||
return cometd;
|
||||
};
|
||||
|
||||
// The default cometd instance
|
||||
$.cometd = new $.Cometd();
|
||||
|
||||
return $.cometd;
|
||||
}
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
{
|
||||
define(['jquery', 'org/cometd'], bind);
|
||||
}
|
||||
else
|
||||
{
|
||||
bind(jQuery, org.cometd);
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue