/* * 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.vysper.xmpp.server; import java.util.HashMap; import java.util.Map; import org.apache.vysper.xmpp.addressing.Entity; import org.apache.vysper.xmpp.protocol.ProtocolException; import org.apache.vysper.xmpp.protocol.SessionStateHolder; import org.apache.vysper.xmpp.protocol.StanzaHandler; import org.apache.vysper.xmpp.stanza.Stanza; import org.apache.vysper.xmpp.stanza.StanzaBuilder; import org.apache.vysper.xmpp.state.resourcebinding.BindException; import org.apache.vysper.xmpp.uuid.JVMBuiltinUUIDGenerator; import org.apache.vysper.xmpp.uuid.UUIDGenerator; import org.apache.vysper.xmpp.writer.StanzaWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * provides default session context behavior * * @author The Apache MINA Project (dev@mina.apache.org) */ public abstract class AbstractSessionContext implements SessionContext { private static final Logger logger = LoggerFactory.getLogger(AbstractSessionContext.class); protected ServerRuntimeContext serverRuntimeContext; protected String sessionId; protected String xmlLang; protected UUIDGenerator sequence = new JVMBuiltinUUIDGenerator(); protected SessionStateHolder sessionStateHolder; // be secure: do not provide this via a getter or other means protected Entity serverEntity; private Entity initiatingEntity; private boolean serverToServer = false; private Map<String, Object> attributeMap = new HashMap<String, Object>(); public AbstractSessionContext(ServerRuntimeContext serverRuntimeContext, SessionStateHolder sessionStateHolder) { this.serverRuntimeContext = serverRuntimeContext; sessionId = serverRuntimeContext.getNextSessionId(); serverEntity = serverRuntimeContext.getServerEnitity(); xmlLang = serverRuntimeContext.getDefaultXMLLang(); this.sessionStateHolder = sessionStateHolder; } @Override public String toString() { return sessionId; } public ServerRuntimeContext getServerRuntimeContext() { return serverRuntimeContext; } public boolean isRemotelyInitiatedSession() { return true; } public Entity getInitiatingEntity() { return initiatingEntity; } public void setInitiatingEntity(Entity entity) { if (entity == null) throw new IllegalArgumentException("initiating entity must not be set to NULL"); if (entity.isResourceSet()) throw new IllegalArgumentException("initiating entity must be bare JID"); this.initiatingEntity = entity; } public boolean isServerToServer() { return serverToServer; } public void setServerToServer() { serverToServer = true; } public void setClientToServer() { serverToServer = false; } public SessionState getState() { return sessionStateHolder.getState(); } public String getSessionId() { return sessionId; } public void setSessionId(String sessionId) { this.sessionId = sessionId; } public String getXMLLang() { return xmlLang; } public void setXMLLang(String languageCode) { // TODO think about disallow changing the xmlLang a second time after from default to client value xmlLang = languageCode; } public void endSession(SessionTerminationCause terminationCause) { StanzaWriter stanzaWriter = getResponseWriter(); stanzaWriter.close(); if (terminationCause == null) { throw new RuntimeException("no termination cause given"); } if (terminationCause == SessionTerminationCause.CLIENT_BYEBYE || terminationCause == SessionTerminationCause.CONNECTION_ABORT) { if(getState().equals(SessionState.AUTHENTICATED)) { Stanza unavailableStanza = StanzaBuilder.createUnavailablePresenceStanza(null, terminationCause); StanzaHandler handler = serverRuntimeContext.getHandler(unavailableStanza); try { handler.execute(unavailableStanza, serverRuntimeContext, true, this, sessionStateHolder); } catch (ProtocolException e) { logger.error("Failed to send unavailable stanza on connection close", e); } } } else if (terminationCause == SessionTerminationCause.SERVER_SHUTDOWN) { // do nothing } else if (terminationCause == SessionTerminationCause.STREAM_ERROR) { // TODO find a solution for informing the contacts without breaking test cases // but do nothing for now } else { throw new IllegalArgumentException("endSession() not implemented for termination cause"); } // unbind session and remove from registry serverRuntimeContext.getResourceRegistry().unbindSession(this); sessionStateHolder.setState(SessionState.CLOSED); // no more traffic // TODO close underlying transport (TCP socket) } public Entity getServerJID() { return serverEntity; } public String bindResource() throws BindException { // TODO we should impose a hard limit on the number of bound resources per session (in ServerConfiguration) // TODO to avoid DoS attacks based on resource binding and to shield against clients running berserk return getServerRuntimeContext().getResourceRegistry().bindSession(this); } /** * creates a unique ID, possibly a UUID, mostly for use as an IQ id. * @return unique sequence ID */ public String nextSequenceValue() { return sequence.create(); } public Object putAttribute(String key, Object value) { return attributeMap.put(key, value); } public Object getAttribute(String key) { return attributeMap.get(key); } }