- Configure CometD servlet in portlet web.xml
- Implement Data Producer to produce data
- Implement Service to publish data on Channels Using Bayeux
- Use CometD java script in client side to subscribe channel and show data.
|
<servlet>
<servlet-name>
cometd</servlet-name>
<servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
<init-param>
<param-name>transports</param-name>
<param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
</init-param>
<init-param>
<param-name>services</param-name>
<param-value>com.meera.commetd.StockPriceService</param-value>
</init-param>
<init-param>
<param-name>maxLazyTimeout</param-name>
<param-value>2000</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>
cometd</servlet-name>
<url-pattern>/
cometd/*</url-pattern>
</servlet-mapping>
|
|
bayeuxServer.createChannelIfAbsent(channelName,
newConfigurableServerChannel.Initializer()
{
public
void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(
true);
channel.setLazy(
true);
}
});
|
|
ServerChannel channel = bayeuxServer.getChannel(channelName);
channel.publish(sender, data);
|
|
var cometURL = location.protocol + "//" + location.host + config.contextPath + "/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
|
|
cometd.subscribe('/stock/*',
function(message)
{
var data = message.data;
});
|
|
<scriptsrc=
"http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/cometd-namespace.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/cometd-json.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/AckExtension.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/TransportRegistry.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Transport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/RequestTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/WebSocketTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath()%>
/js/comet/CallbackPollingTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/LongPollingTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Utils.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Cometd.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/jquery.cometd.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/application.js"></script>
|
|
<web-app id=
"WebApp_ID" version=
"2.4"xmlns=
"http://java.sun.com/xml/ns/j2ee"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>LiferayCommetDRevserseAjax-
portlet</display-name>
<servlet>
<servlet-name>
cometd</servlet-name>
<servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
<init-param>
<param-name>transports</param-name>
<param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
</init-param>
<init-param>
<param-name>services</param-name>
<param-value>com.meera.commetd.StockPriceService</param-value>
</init-param>
<init-param>
<param-name>maxLazyTimeout</param-name>
<param-value>2000</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>
cometd</servlet-name>
<url-pattern>/
cometd/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/
cometd/*</url-pattern>
</filter-mapping>
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/portlet_2_0</taglib-uri>
<taglib-location>
/WEB-INF/
tld/
liferay-portlet.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://liferay.com/tld/aui</taglib-uri>
<taglib-location>/WEB-INF/
tld/aui.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
|
|
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import org.cometd.annotation.Service;
import org.cometd.annotation.Session;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.LocalSession;
import org.cometd.bayeux.server.ServerChannel;
@Service
public
class StockPriceService
implements StockPriceEmitter.Listener
{
@Inject
private BayeuxServer bayeuxServer;
@Session
private LocalSession sender;
public
void onUpdates(List<StockPriceEmitter.Update> updates)
{
for (StockPriceEmitter.Update update : updates)
{
// Create the channel name using the stock symbol
String channelName = "/stock/" + update.getSymbol().toLowerCase(Locale.
ENGLISH);
// Initialize the channel, making it persistent and lazy
bayeuxServer.createChannelIfAbsent(channelName,
newConfigurableServerChannel.Initializer()
{
public
void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(
true);
channel.setLazy(
true);
}
});
// Convert the Update business object to a CometD-friendly format
Map<String, Object> data =
new HashMap<String, Object>(4);
data.put("symbol", update.getSymbol());
data.put("oldValue", update.getOldValue());
data.put("newValue", update.getNewValue());
System.
out.println("===========================");
// Publish to all subscribers
ServerChannel channel =bayeuxServer.getChannel(channelName);
channel.publish(sender, data);
}
}
}
|
|
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public
class StockPriceEmitter
implements Runnable {
private
final ScheduledExecutorService scheduler = Executors
.
newSingleThreadScheduledExecutor();
private
final List<String> symbols =
new ArrayList<String>();
private
final Map<String, Float> values =
new HashMap<String, Float>();
private
final List<Listener> listeners =
newCopyOnWriteArrayList<Listener>();
public StockPriceEmitter() {
symbols.addAll(Arrays.
asList("ORCL", "MSFT", "GOOG","YHOO", "FB"));
values.put("ORCL", 29.94f);
values.put("MSFT", 27.10f);
values.put("GOOG", 655.37f);
values.put("YHOO", 17.82f);
values.put("FB", 21.33f);
}
public List<Listener> getListeners() {
return listeners;
}
public
void start() {
run();
}
public
void stop() {
scheduler.shutdownNow();
}
public
void run() {
Random random =
new Random();
List<Update> updates =
new ArrayList<Update>();
// Randomly choose how many stocks to update
int howMany = random.nextInt(symbols.size()) + 1;
for (
int i = 0; i < howMany; ++i) {
// Randomly choose which one to update
int which = random.nextInt(symbols.size());
String symbol = symbols.get(which);
float oldValue = values.get(symbol);
// Randomly choose how much to update
boolean sign = random.nextBoolean();
float howMuch = random.nextFloat();
float newValue = oldValue + (sign ? howMuch : -howMuch);
// Store the new value
values.put(symbol, newValue);
updates.add(
new Update(symbol, oldValue, newValue));
}
// Notify the listeners
for (Listener listener : listeners) {
listener.onUpdates(updates);
}
// Randomly choose how long for the next update
// We use a max delay of 1 second to simulate a high rate of updates
long howLong = random.nextInt(1000);
scheduler.schedule(
this, howLong, TimeUnit.
MILLISECONDS);
}
public
static
class Update {
private
final String symbol;
private
final
float oldValue;
private
final
float newValue;
public Update(String symbol,
float oldValue,
floatnewValue) {
this.symbol = symbol;
this.oldValue = oldValue;
this.newValue = newValue;
}
public String getSymbol() {
return symbol;
}
public
float getOldValue() {
return oldValue;
}
public
float getNewValue() {
return newValue;
}
}
public
interface Listener
extends EventListener {
void onUpdates(List<Update> updates);
}
}
|
|
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import com.liferay.util.bridges.mvc.MVCPortlet;
public
class LiferayCommetDReverseAjax
extends MVCPortlet {
private StockPriceEmitter emitter;
public
void startStockUpdates(
ActionRequest actionRequest, ActionResponse actionResponse)
throws IOException, PortletException {
emitter =
new StockPriceEmitter();
StockPriceService service(StockPriceService)getPortletContext().
getAttribute(StockPriceService. class.getName());
// Register the service as a listener of the
emitter
emitter.getListeners().add(service);
// Start the
emitter
emitter.start();
}
public
void stopStockUpdates(
ActionRequest actionRequest, ActionResponse actionResponse)
throws IOException, PortletException {
//
emitter = new StockPriceEmitter();
emitter.stop();
}
}
|
|
(
function($)
{
var cometd = $.cometd;
//alert(
cometd);
$(document).ready(
function()
{
function _connectionEstablished()
{
$('#body').append('<div>CometD Connection Established</div>');
}
function _connectionBroken()
{
$('#body').append('<div>CometD Connection Broken</div>');
}
function _connectionClosed()
{
$('#body').append('<div>CometD Connection Closed</div>');
}
// Function that manages the connection status with the
Bayeuxserver
var _connected =
false;
function _metaConnect(message)
{
if (cometd.isDisconnected())
{
_connected =
false;
_connectionClosed();
return;
}
var wasConnected = _connected;
_connected = message.successful ===
true;
if (!wasConnected && _connected)
{
_connectionEstablished();
}
else
if (wasConnected && !_connected)
{
_connectionBroken();
}
}
// Function invoked when first contacting the server and
// when the server has lost the state of this client
function _metaHandshake(handshake)
{
if (handshake.successful ===
true)
{
cometd.batch(
function()
{
cometd.subscribe('/stock/*',
function(message)
{
var data = message.data;
var symbol = data.symbol;
var value = data.newValue;
// alert(symbol);
var id = 'stock_'+ symbol;
var symbolDiv=document.getElementById(id);
if (!symbolDiv)
{
symbolDiv = document.createElement('div');
symbolDiv.id =id;
document.getElementById('stocks').appendChild(symbolDiv);
}
symbolDiv.innerHTML = '<span class="symbol">' + symbol + ': <b>' + value + '</b></span>';
});
// Publish on a service channel since the message is for the server only
//cometd.publish('/stock/*', { name: 'World' });
});
}
}
// Disconnect when the page unloads
$(window).unload(
function()
{
cometd.disconnect(
true);
});
var cometURL = location.protocol + "//" + location.host + config.contextPath + "/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
});
})(jQuery);
|
|
<%@ include file=
"init.jsp"%>
<scriptsrc=
"http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/cometd-namespace.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/cometd-json.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/AckExtension.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/TransportRegistry.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Transport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/RequestTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/WebSocketTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath()%>
/js/comet/CallbackPollingTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/LongPollingTransport.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Utils.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/Cometd.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/jquery.cometd.js"></script>
<script type=
"text/javascript" src=
"<%=renderRequest.getContextPath() %>
/js/comet/application.js"></script>
<%--
The reason to use a JSP is that it is very easy to obtain server-side configuration
information (such as the contextPath) and pass it to the JavaScript environment on the client.
--%>
<style>
#stocks
.symbol
b{
color:
red;
}
#controler-table
td{
padding-left:
30px;
}
</style>
<script type=
"text/javascript">
var config = {
contextPath: '<%=renderRequest.getContextPath()%>'
};
</script>
<portlet:actionURL var=
"startStockUpdates" name=
"startStockUpdates">
</portlet:actionURL>
<portlet:actionURL var=
"stopStockUpdates" name=
"stopStockUpdates">
</portlet:actionURL>
<h2>
Liferay CometD
Ajax Push
Portlet</h2>
<br/>
<table id=
"controler-table">
<tr>
<td><a href=
"<%=startStockUpdates.toString()%>
">Start</a></td>
<td><a href=
"<%=stopStockUpdates.toString()%>
">Stop</a></td>
</tr>
</table>
<br/>
<div id=
"stocks"></div>
|


