Java tutorial
/* * Copyright (c) 2011 PonySDK * Owners: * Luciano Broussal <luciano.broussal AT gmail.com> * Mathieu Barbier <mathieu.barbier AT gmail.com> * Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com> * * WebSite: * http://code.google.com/p/pony-sdk/ * * 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. */ package com.ponysdk.ui.server.basic; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ponysdk.core.UIContext; import com.ponysdk.core.socket.ConnectionListener; import com.ponysdk.core.socket.WebSocket; import com.ponysdk.core.stm.Txn; import com.ponysdk.core.stm.TxnSocketContext; import com.ponysdk.core.tools.ListenerCollection; import com.ponysdk.ui.terminal.Dictionnary.PROPERTY; import com.ponysdk.ui.terminal.WidgetType; /** * Push data to clients using WebSocket. */ public class PPusher extends PObject implements ConnectionListener { private static final Logger log = LoggerFactory.getLogger(PPusher.class); public static final String PUSHER = "com.ponysdk.ui.server.basic.PPusher"; private WebSocket websocket; private final UIContext uiContext; private final List<ConnectionListener> connectionListeners = new ArrayList<ConnectionListener>(); private final ListenerCollection<DataListener> listenerCollection = new ListenerCollection<DataListener>(); private PusherState pusherState = PusherState.STOPPED; private final TxnSocketContext txnContext; public enum PusherState { STOPPED, INITIALIZING, STARTED } private PPusher(final int pollingDelay, final int ping) { super(); this.txnContext = new TxnSocketContext(); create.put(PROPERTY.FIXDELAY, pollingDelay); create.put(PROPERTY.PINGDELAY, ping); this.pusherState = PusherState.INITIALIZING; this.uiContext = UIContext.get(); } public void initialize(final WebSocket websocket) { this.websocket = websocket; this.websocket.addConnectionListener(this); this.txnContext.setSocket(websocket); } public static PPusher initialize(final int pollingDelay, final int ping) { if (UIContext.get() == null) throw new RuntimeException("It's not possible to instanciate a pusher in a new Thread."); PPusher pusher = UIContext.get().getAttribute(PUSHER); if (pusher != null) return pusher; pusher = new PPusher(pollingDelay, ping); UIContext.get().setAttribute(PUSHER, pusher); return pusher; } public static PPusher initialize() { return initialize(1000, 5 * 1000); } public static PPusher get() { if (UIContext.get() == null) throw new RuntimeException("It's not possible to instanciate a pusher in a new Thread."); final PPusher pusher = UIContext.get().getAttribute(PUSHER); if (pusher == null) { throw new RuntimeException("The pusher must be initialize before using it"); } return pusher; } public void close() { if (websocket == null) return; websocket.close(); } @Override protected WidgetType getWidgetType() { return WidgetType.PUSHER; } public UIContext getUiContext() { return uiContext; } public TxnSocketContext getTxContext() { return txnContext; } public void begin() { uiContext.acquire(); UIContext.setCurrent(uiContext); } public void end() { UIContext.remove(); uiContext.release(); } @Override public void onClientData(final JSONObject event) throws JSONException { if (event.has(PROPERTY.ERROR_MSG)) { log.warn("Failed to open websocket connection. Falling back to polling."); txnContext.switchToPollingMode(); doOpen(); } else if (event.has(PROPERTY.POLL)) { txnContext.flushNow(); } } public PusherState getPusherState() { return pusherState; } public void addConnectionListener(final ConnectionListener listener) { connectionListeners.add(listener); } public void removeConnectionListener(final ConnectionListener listener) { connectionListeners.remove(listener); } public void addDataListener(final DataListener listener) { listenerCollection.register(listener); } public void removeDataListener(final DataListener listener) { listenerCollection.unregister(listener); } public void pushBatchToClient(final Collection<Object> collection) { if (pusherState != PusherState.STARTED) { if (log.isDebugEnabled()) log.debug("Pusher not started. Skipping message #" + collection); return; } if (UIContext.get() == null) { begin(); try { if (listenerCollection.isEmpty()) return; final Txn txn = Txn.get(); txn.begin(txnContext); try { for (final Object data : collection) { for (final DataListener listener : listenerCollection) { listener.onData(data); } } txn.commit(); } catch (final Throwable e) { log.error("Cannot process open socket", e); txn.rollback(); } } finally { end(); } } else { if (listenerCollection.isEmpty()) return; for (final Object data : collection) { for (final DataListener listener : listenerCollection) { listener.onData(data); } } } } public void pushToClient(final Object data) { if (pusherState != PusherState.STARTED) { if (log.isDebugEnabled()) log.debug("Pusher not started. Skipping message #" + data); return; } if (UIContext.get() == null) { begin(); try { if (listenerCollection.isEmpty()) return; final Txn txn = Txn.get(); txn.begin(txnContext); try { for (final DataListener listener : listenerCollection) { listener.onData(data); } txn.commit(); } catch (final Throwable e) { log.error("Cannot process open socket", e); txn.rollback(); } } finally { end(); } } else { if (listenerCollection.isEmpty()) return; for (final DataListener listener : listenerCollection) { listener.onData(data); } } } @Override public void onClose() { begin(); try { final Txn txn = Txn.get(); txn.begin(txnContext); try { doClose(); txn.commit(); } catch (final Throwable e) { log.error("Cannot process open socket", e); txn.rollback(); } } finally { end(); } } @Override public void onOpen() { begin(); try { final Txn txn = Txn.get(); txn.begin(txnContext); try { doOpen(); txn.commit(); } catch (final Throwable e) { log.error("Cannot process open socket", e); txn.rollback(); } } finally { end(); } } public void doOpen() { pusherState = PusherState.STARTED; for (final ConnectionListener listener : connectionListeners) { listener.onOpen(); } } public void doClose() { pusherState = PusherState.STOPPED; for (final ConnectionListener listener : connectionListeners) { listener.onClose(); } } public boolean execute(final Runnable runnable) { try { if (UIContext.get() == null) { begin(); try { final Txn txn = Txn.get(); txn.begin(txnContext); try { runnable.run(); txn.commit(); } catch (final Throwable e) { log.error("Cannot process commmand", e); txn.rollback(); return false; } } finally { end(); } } else { runnable.run(); } } catch (final Throwable e) { log.error("Cannot execute command : " + runnable, e); return false; } return true; } }