/** * Copyright 2015-2017 Linagora, Université Joseph Fourier, Floralis * * The present code is developed in the scope of the joint LINAGORA - * Université Joseph Fourier - Floralis research program and is designated * as a "Result" pursuant to the terms and conditions of the LINAGORA * - Université Joseph Fourier - Floralis research program. Each copyright * holder of Results enumerated here above fully & independently holds complete * ownership of the complete Intellectual Property rights applicable to the whole * of said Results, and may freely exploit it in any manner which does not infringe * the moral rights of the other copyright holders. * * 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 net.roboconf.messaging.http.internal.clients; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.websocket.api.Session; import net.roboconf.messaging.api.extensions.AbstractRoutingClient; import net.roboconf.messaging.api.extensions.MessagingContext; import net.roboconf.messaging.api.extensions.MessagingContext.RecipientKind; import net.roboconf.messaging.api.jmx.RoboconfMessageQueue; import net.roboconf.messaging.api.messages.Message; import net.roboconf.messaging.http.HttpConstants; import net.roboconf.messaging.http.internal.HttpClientFactory.HttpRoutingContext; import net.roboconf.messaging.http.internal.HttpUtils; import net.roboconf.messaging.http.internal.messages.HttpMessage; import net.roboconf.messaging.http.internal.messages.SubscriptionMessage; /** * @author Vincent Zurczak - Linagora */ public class HttpDmClient extends AbstractRoutingClient<Session> { // Internal field (for a convenient access). private static final String DM_OWNER_ID = AbstractRoutingClient.buildOwnerId( RecipientKind.DM, null, null ); private final Map<String,Session> ctxToSession; private RoboconfMessageQueue messageQueue; private final AtomicInteger openConnections = new AtomicInteger( 0 ); private String httpServerIp; private int httpPort; /** * Constructor. */ public HttpDmClient( HttpRoutingContext routingContext ) { super( routingContext, RecipientKind.DM ); this.connectionIsRequired = false; this.ctxToSession = routingContext.ctxToSession; } @Override public void openConnection() throws IOException { // There is only one instance per Http Factory. // So, we do not want to close the connection someone is still using it. this.openConnections.incrementAndGet(); super.openConnection(); } @Override public void closeConnection() throws IOException { // There is only one instance per Http Factory. // So, we do not want to close the connection someone is still using it. if( this.openConnections.decrementAndGet() == 0 ) super.closeConnection(); } @Override public void setMessageQueue( RoboconfMessageQueue messageQueue ) { this.messageQueue = messageQueue; } @Override protected Map<String,Session> getStaticContextToObject() { return this.ctxToSession; } @Override public String getMessagingType() { return HttpConstants.FACTORY_HTTP; } @Override public Map<String,String> getConfiguration() { return HttpUtils.httpMessagingConfiguration( this.httpServerIp, this.httpPort ); } @Override protected void process( Session session, Message message ) throws IOException { if( session.isOpen()) { HttpUtils.sendAsynchronously( message, session.getRemote()); } else { this.logger.finer( "Session is not available anymore. No message can be published." ); } } @Override public void publish( MessagingContext ctx, Message msg ) throws IOException { this.logger.fine( "The DM's HTTP client is about to publish a message (" + msg + ") to " + ctx ); // The DM has no session. // So, we intercept messages for the DM and determine whether the // message should be enqueued or ignored. This decision is based on subscriptions and the connection. if( ctx.getKind() == RecipientKind.DM && this.connected.get()) { Set<MessagingContext> subs = this.routingContext.subscriptions.get( DM_OWNER_ID ); if( subs != null && subs.contains( ctx )) this.messageQueue.add( msg ); } // Agents => use the standard publish action. else { super.publish( ctx, msg ); } } /** * Processes a message received from a web socket (i.e. sent by agents). * @param message a message * @throws IOException */ public void processReceivedMessage( Message message, Session session ) throws IOException { this.logger.fine( "The DM's HTTP client is about to process a message (" + message + ") received through a web socket." ); // HttpMessage if( message instanceof HttpMessage ) { HttpMessage httpMsg = (HttpMessage) message; // Store the session registerSession( httpMsg.getOwnerId(), session ); // Publish the message publish( httpMsg.getCtx(), httpMsg.getMessage()); } // Subscription message else if( message instanceof SubscriptionMessage ) { SubscriptionMessage sub = (SubscriptionMessage) message; // Store the session registerSession( sub.getOwnerId(), session ); // Update the subscriptions if( sub.isSubscribe()) subscribe( sub.getOwnerId(), sub.getCtx()); else unsubscribe( sub.getOwnerId(), sub.getCtx()); } } /** * Indicates a message was received but could not be decoded. */ public void errorWhileReceivingMessage() { this.messageQueue.errorWhileReceivingMessage(); } /** * Sets the DM's IP address (to propagate it through its configuration). * @param httpServerIp the DM's IP address (must be visible/reachable from agents) */ public void setHttpServerIp( String httpServerIp ) { this.httpServerIp = httpServerIp; this.logger.info( "The DM's IP address was changed to " + httpServerIp ); } /** * Sets the DM's port (to propagate it through its configuration). * @param httpPort the DM's port */ public void setHttpPort( int httpPort ) { this.httpPort = httpPort; this.logger.info( "The DM's port was changed to " + httpPort ); } private void registerSession( String ownerId, Session session ) { if( session != null ) this.ctxToSession.put( ownerId, session ); } }