/** * Copyright 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.api.internal.jmx; import java.io.IOException; import java.util.Date; import java.util.Dictionary; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Logger; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import net.roboconf.core.model.beans.Application; import net.roboconf.core.utils.Utils; import net.roboconf.messaging.api.extensions.IMessagingClient; import net.roboconf.messaging.api.extensions.MessagingContext; import net.roboconf.messaging.api.extensions.MessagingContext.RecipientKind; import net.roboconf.messaging.api.internal.client.dismiss.DismissClient; import net.roboconf.messaging.api.jmx.MessagingApiMBean; import net.roboconf.messaging.api.jmx.RoboconfMessageQueue; import net.roboconf.messaging.api.messages.Message; import net.roboconf.messaging.api.utils.MessagingUtils; import net.roboconf.messaging.api.utils.OsgiHelper; /** * A class that wraps a real messaging client and deals with JMX stuff. * <p> * There is no need to make this class exposed outside this bundle. * </p> * * @author Vincent Zurczak - Linagora */ public class JmxWrapperForMessagingClient implements IMessagingClient, MessagingApiMBean { // Sent messages private final AtomicLong sentMessagesCount = new AtomicLong(); private final AtomicLong failedSendingCount = new AtomicLong(); private final AtomicLong timestampOfLastSendingFailure = new AtomicLong(); private final AtomicLong timestampOfLastSentMessage = new AtomicLong(); // The wrapped elements private final IMessagingClient messagingClient; private RoboconfMessageQueue messageQueue; private String id; // Logger private final Logger logger = Logger.getLogger( getClass().getName()); // OSGi stuff ServiceRegistration<MessagingApiMBean> serviceReg; /** * Constructor. * @param messagingClient */ public JmxWrapperForMessagingClient( IMessagingClient messagingClient ) { this( messagingClient, new OsgiHelper()); } /** * Constructor for tests only. * @param messagingClient * @param osgiHelper */ JmxWrapperForMessagingClient( IMessagingClient messagingClient, OsgiHelper osgiHelper ) { this.messagingClient = messagingClient != null ? messagingClient : new DismissClient(); // Register the object as service in the OSGi registry. // Apache Aries should then map it to a MBean if the JMX management // is available. It is a little bit dirty. BundleContext bundleCtx = osgiHelper.findBundleContext(); if( bundleCtx != null ) { this.logger.fine( "Running in an OSGi environment. Trying to register a MBean for the messaging." ); Dictionary<String,String> properties = new Hashtable<> (); properties.put( "jmx.objectname", "net.roboconf:type=messaging" ); try { this.serviceReg = bundleCtx.registerService( MessagingApiMBean.class, this, properties ); this.logger.fine( "A MBean was successfully registered for the messaging." ); } catch( Exception e ) { this.logger.severe( "A MBean could not be registered for the messaging." ); Utils.logException( this.logger, e ); } } } /** * Unregisters the service that was created from this class instance. * <p> * This method MUST be invoked every time a client is not necessary anymore. * </p> */ public void unregisterService() { try { if( this.serviceReg != null ) { this.logger.finer( "Running in an OSGi environment. Trying to unregister a MBean for the messaging." ); this.serviceReg.unregister(); this.logger.finer( "Running in an OSGi environment. The MBean was unregistered." ); } } catch( Exception e ) { this.logger.severe( "A MBean could not be unregistered for the messaging." ); Utils.logException( this.logger, e ); } finally { this.serviceReg = null; } } /** * @return true if the wrapped messaging client was null and thus replaced by a DismissClient */ public boolean isDismissClient() { return this.messagingClient instanceof DismissClient; } @Override public synchronized void reset() { this.sentMessagesCount.set( 0 ); this.failedSendingCount.set( 0 ); this.timestampOfLastSendingFailure.set( 0 ); this.timestampOfLastSentMessage.set( 0 ); if( this.messageQueue != null ) this.messageQueue.reset(); } @Override public String getId() { return this.id; } // Sent messages @Override public synchronized long getFailedSendingCount() { return this.failedSendingCount.get(); } @Override public synchronized long getSentMessagesCount() { return this.sentMessagesCount.get(); } @Override public synchronized long getTimestampOfLastSendingFailure() { return this.timestampOfLastSendingFailure.get(); } @Override public synchronized long getTimestampOfLastSentMessage() { return this.timestampOfLastSentMessage.get(); } // Received messages @Override public synchronized long getFailedReceptionCount() { return this.messageQueue == null ? 0 : this.messageQueue.getFailedReceptionCount(); } @Override public synchronized long getReceivedMessagesCount() { return this.messageQueue == null ? 0 : this.messageQueue.getReceivedMessagesCount(); } @Override public synchronized long getTimestampOfLastReceptionFailure() { return this.messageQueue == null ? 0 : this.messageQueue.getTimestampOfLastReceptionFailure(); } @Override public synchronized long getTimestampOfLastReceivedMessage() { return this.messageQueue == null ? 0 : this.messageQueue.getTimestampOfLastReceivedMessage(); } // Simple wrapped methods @Override public void closeConnection() throws IOException { this.messagingClient.closeConnection(); } @Override public void openConnection() throws IOException { this.messagingClient.openConnection(); } @Override public void deleteMessagingServerArtifacts( Application application ) throws IOException { this.messagingClient.deleteMessagingServerArtifacts( application ); } @Override public Map<String,String> getConfiguration() { return this.messagingClient.getConfiguration(); } @Override public String getMessagingType() { return this.messagingClient.getMessagingType(); } @Override public boolean isConnected() { return this.messagingClient.isConnected(); } @Override public void subscribe( MessagingContext ctx ) throws IOException { this.messagingClient.subscribe( ctx ); } @Override public void unsubscribe( MessagingContext ctx ) throws IOException { this.messagingClient.unsubscribe( ctx ); } // Complex wrapped methods @Override public void setMessageQueue( RoboconfMessageQueue messageQueue ) { synchronized( this ) { this.messageQueue = messageQueue; } this.messagingClient.setMessageQueue( messageQueue ); } @Override public void publish( MessagingContext ctx, Message msg ) throws IOException { try { this.messagingClient.publish( ctx, msg ); this.sentMessagesCount.incrementAndGet(); this.timestampOfLastSentMessage.set( new Date().getTime()); } catch( IOException e ) { this.failedSendingCount.incrementAndGet(); this.timestampOfLastSendingFailure.set( new Date().getTime()); throw e; } } @Override public void setOwnerProperties( RecipientKind ownerKind, String domain, String applicationName, String scopedInstancePath ) { this.id = MessagingUtils.buildId( ownerKind, domain, applicationName, scopedInstancePath ); this.messagingClient.setOwnerProperties( ownerKind, domain, applicationName, scopedInstancePath ); } }