Java tutorial
package com.conwet.xjsp.session; /* * #%L * eXtensible JSON Streaming Protocol * %% * Copyright (C) 2011 - 2014 CoNWeT Lab., Universidad Politcnica de Madrid * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import com.conwet.xjsp.errors.ConnectionException; import com.conwet.xjsp.features.FeatureMap; import com.conwet.xjsp.features.ImmutableMessage; import com.conwet.xjsp.features.FeatureHandler; import com.conwet.xjsp.features.Session; import com.conwet.xjsp.XJSPConstants; import com.conwet.xjsp.errors.ConnError; import com.conwet.xjsp.features.SessionListener; import com.conwet.xjsp.json.JSONUtil; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Normal connection state in which messages are interchanged. * * @apiviz.stereotype State * @apiviz.uses com.conwet.silbops.xjsp.session.ClosingState * @author sortega */ public class SessionState implements ConnectionState { private static final Logger logger = LoggerFactory.getLogger(SessionState.class); private final FeatureMap features; private Session session; private AtomicBoolean closed; public SessionState(FeatureMap features) { this.features = features; this.session = null; // Lazy initialization this.closed = new AtomicBoolean(); } @Override public void start(final ConnectionContext context) throws ConnectionException { this.session = new AbstractSession(context.getChannel(), features) { @Override public void close() { SessionState.this.close(context); } }; // register the features used so each SessionListener knows // if it has to register itself or not. this.session.setAttribute("features", features); for (SessionListener listener : context.getSessionListeners()) { listener.sessionCreated(this.session); } } @Override public void newData(ConnectionContext context) throws ConnectionException { String stanza; JSONParser parser = new JSONParser(); try { while ((stanza = JSONUtil.extractStanza(context.getBuffer())) != null) { if ("]}".equals(stanza)) { try { context.getChannel().sendFragment("]}"); } catch (IOException ex) { throw new ConnectionException(ConnError.ApplicationError, ex.getMessage()); } finally { closed.set(Boolean.TRUE); context.dispose(); removeListeners(context); } } else { try { JSONObject json = (JSONObject) parser.parse(stanza); handleMessage(json, context); } catch (ParseException ex) { throw new ConnectionException(ConnError.ProtocolSyntaxError, "Invalid stanza: " + ex.getMessage()); } } } } catch (ParseException ex) { throw new ConnectionException(ConnError.ProtocolSyntaxError, ex.getMessage()); } } @Override public long getTimeoutPeriod(ConnectionContext context) { if (context.getBuffer().toString().isEmpty()) { return 0; } return XJSPConstants.RESPONSE_TIMEOUT; } @Override public void timeout(ConnectionContext context) throws ConnectionException { if (!context.getBuffer().toString().isEmpty()) { context.getBuffer().setLength(0); throw new ConnectionException(ConnError.MessageTimeout); } } @Override public void abort(ConnectionContext context) { context.setState(new AbortedState(session)); } private void handleMessage(JSONObject json, final ConnectionContext context) throws ConnectionException { hasFields(json, new String[] { "type", "id", "message" }); String[] type = ((String) json.get("type")).split(":"); if (type.length != 2) { throw new ConnectionException(ConnError.MessageFieldError, "type"); } FeatureHandler feature = features.resolveFeature(type[0]); if (feature == null) { throw new ConnectionException(ConnError.UnknownPrefix, type[0]); } ImmutableMessage message = new ImmutableMessage(feature.getFeatureName(), type[1], json.get("message"), (String) json.get("id")); try { feature.handleMessage(message, this.session); } catch (Exception ex) { logger.warn("Application error when handling message {}", message, ex); throw new ConnectionException(ConnError.ApplicationError, ex.getMessage()); } } private synchronized void close(ConnectionContext context) { if (!closed.getAndSet(Boolean.TRUE)) { context.setState(new ClosingState()); removeListeners(context); } } private void removeListeners(ConnectionContext context) { for (SessionListener listener : context.getSessionListeners()) { listener.sessionDestroyed(session); } } private static void hasFields(JSONObject json, String[] keys) throws ConnectionException { for (String key : keys) { if (!json.containsKey(key)) { throw new ConnectionException(ConnError.MessageFieldError, key); } } } }