/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/**
 *
 */
package org.mobicents.slee.resource;

import javax.slee.Address;
import javax.slee.resource.ActivityHandle;
import javax.slee.resource.EventFlags;
import javax.slee.resource.FailureReason;
import javax.slee.resource.FireableEventType;
import javax.slee.resource.ReceivableService;

import org.mobicents.slee.container.event.EventProcessingFailedCallback;
import org.mobicents.slee.container.event.EventProcessingSucceedCallback;
import org.mobicents.slee.container.event.EventUnreferencedCallback;
import org.mobicents.slee.container.resource.ResourceAdaptorEntity;

/**
 * @author martins
 *
 */
public class EventProcessingCallbacks implements EventProcessingFailedCallback,
		EventProcessingSucceedCallback, EventUnreferencedCallback {

	private final int eventFlags;
	private final FireableEventType fireableEventType;
	private final ReceivableService receivableService;
	private final ResourceAdaptorEntity raEntity;
	private final Object event;
	private final Address address;
	private final ActivityHandle activityHandle;

	/**
	 *
	 * @param activityHandle
	 * @param fireableEventType
	 * @param event
	 * @param address
	 * @param receivableService
	 * @param eventFlags
	 * @param raEntity
	 */
	public EventProcessingCallbacks(ActivityHandle activityHandle,
			FireableEventType fireableEventType, Object event, Address address,
			ReceivableService receivableService, int eventFlags,
			ResourceAdaptorEntity raEntity) {
		this.eventFlags = eventFlags;
		this.fireableEventType = fireableEventType;
		this.receivableService = receivableService;
		this.raEntity = raEntity;
		this.event = event;
		this.address = address;
		this.activityHandle = activityHandle;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @seeorg.mobicents.slee.core.event.SleeEventProcessingFailedCallback#
	 * eventProcessingFailed(javax.slee.resource.FailureReason)
	 */
	public void eventProcessingFailed(FailureReason failureReason) {
		raEntity.getResourceAdaptorObject().eventProcessingFailed(activityHandle, fireableEventType,
				failureReason, address, receivableService, eventFlags,
				failureReason);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @seeorg.mobicents.slee.core.event.SleeEventProcessingSucceedCallback#
	 * eventProcessingSucceed()
	 */
	public void eventProcessingSucceed(boolean sbbProcessedEvent) {
		int flags = sbbProcessedEvent ? EventFlags.setSbbProcessedEvent(eventFlags) : eventFlags;
		raEntity.getResourceAdaptorObject().eventProcessingSuccessful(activityHandle, fireableEventType,
				event, address, receivableService, flags);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.event.EventUnreferencedCallback#eventUnreferenced()
	 */
	public void eventUnreferenced() {
		raEntity.getResourceAdaptorObject().eventUnreferenced(activityHandle, fireableEventType,
				event, address, receivableService, eventFlags);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.event.EventUnreferencedCallback#acceptsTransaction()
	 */
	public boolean requiresTransaction() {
		return false;
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright XXXX, Red Hat Middleware LLC, and individual contributors as indicated
 * by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * 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 General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */
package org.mobicents.tools.twiddle.jslee;

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;

import java.io.PrintWriter;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.slee.ComponentID;
import javax.slee.ServiceID;
import javax.slee.management.DeployableUnitID;

import org.jboss.console.twiddle.command.CommandContext;
import org.jboss.console.twiddle.command.CommandException;
import org.jboss.logging.Logger;
import org.mobicents.tools.twiddle.AbstractSleeCommand;
import org.mobicents.tools.twiddle.Utils;
import org.mobicents.tools.twiddle.op.AbstractOperation;

/**
 * @author baranowb
 *
 */
public class DeployCommand extends AbstractSleeCommand {

	public DeployCommand() {
		super("deploy", "This command performs operations on JSLEE DeploymentMBean." );

	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.tools.twiddle.AbstractSleeCommand#displayHelp()
	 */
	@Override
	public void displayHelp() {
		PrintWriter out = context.getWriter();

		out.println(desc);
		out.println();
		out.println("usage: " + name + " <-operation[[arg] | [--option[=arg]]*]>");
		out.println();
		out.println("operation:");
		out.println("    -l, --list                     Lists deployed components based on passed option:");
		out.println("            --sbbs                 Lists sbbs, if ServiceID is present as argument,");
		out.println("                                   sbbs are listed for given service.");
		out.println("            --services             Lists services, does not take argument.");
		out.println("            --libraries            Lists libraries, does not take argument.");
		out.println("            --events               Lists event types, does not take argument.");
		out.println("            --ra-types             Lists RA types, does not take argument.");
		out.println("            --ras                  Lists RAs, does not take argument.");
		out.println("            --dus                  Lists DUs, does not take argument.");
		out.println("            --profile-spec         Lists profile specifications, does not take argument.");
		out.println("    -y, --installed                Checks if SLEE component is installed. It accepts following options:");
		out.println("            --duid                 Indicates check based on DeployableUnit ID. ");
		out.println("                                   It expects DeployableUnit ID as argument. It excludes \"--cid\".");
		out.println("            --cid                  Indicates check based on Component ID. ");
		out.println("                                   It expects Component ID as argument. It excludes \"--duid\".");
		out.println("    -i, --install                  Install DU which is identified by given path. ");
		out.println("                                   It expects path as argument.");
		out.println("    -u, --un-install               Un-install DU which is identified by given DeployableUnit ID. ");
		out.println("                                   It expects DeployableUnit ID as argument.");
		//out.println("    -d, --duid                     Fetches Deployable Unit ID for given path.");
		//out.println("                                   Requires path as argument.");
		out.println("    -d, --desc                     Fetches descriptors for given SLEE component. It supports following options:");
		out.println("            --duid                 Operation fetches descriptors based on DeployableUnit ID passed as arg. Accepts array argument.");
		out.println("                                   It expects DeployableUnit ID(single or array) as argument.");
		out.println("            --cid                  Operation fetches descriptors based on Component ID passed as arg.");
		out.println("    -r, --ref                      Fetches IDs of referring components. Expects ComponentID as argument. Accepts array argument.");

		out.println("arg:");
		out.println("");
		out.println("    ComponentID:             Is any valid component id, for instance ServiceID[name=xxx,vendor=uuu,version=123.0.00]");
		out.println("    ComponentID Array:       ServiceID[name=xxx,vendor=uuu,version=123.0.00];ServiceID[name=xxx,vendor=uuu,version=123.0.00]");
		out.println("");
		out.println("Examples: ");
		out.println("");
		out.println("     1. List all installed SBBs:");
		out.println("" + name + " -l --sbbs");
		out.println("");
		out.println("     2. List intalled SBBs that are part of specific service:");
		out.println("" + name + " -l --sbbs=ServiceID[name=xxx,vendor=uuu,version=123.0.00]");
		out.println("");
		out.println("     3. Get DeployableUnitID based on deploy path:");
		out.println("" + name + " -d/core/dev/container/deploy/xxx/ServiceDU.jar-1351q616/");
		out.println("");
		out.println("     4. List all referencing components:");
		out.println("" + name + " -rProfileSpecificationID[name=ResourceInfoProfileSpec,vendor=javax.slee,version=1.0]");
		out.println("");
		out.println("     5. ");
		out.println("" + name + " --install=g:/workspace/jslee/resource/SecretLab-DU.jar");
		out.println("");
		out.println("     6. Get descriptors of components:");
		out.println("" + name + " -s --cid=ProfileSpecificationID[name=ResourceInfoProfileSpec,vendor=javax.slee,version=1.0];SbbID[name=SipRegistrarSbb,vendor=org.mobicents,version=1.2]");

		out.flush();

	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.tools.twiddle.AbstractSleeCommand#processArguments(java.lang.String[])
	 */
	@Override
	protected void processArguments(String[] args) throws CommandException {
		//String sopts = ":lyi:u:d:sr:";
		String sopts = ":lyi:u:dr:";
		LongOpt[] lopts = {
				new LongOpt("list", LongOpt.NO_ARGUMENT, null, 'l'),
					//options
					new LongOpt("sbbs", LongOpt.OPTIONAL_ARGUMENT, null, ListOperation.sbbs),
					new LongOpt("services", LongOpt.NO_ARGUMENT, null, ListOperation.services),
					new LongOpt("libraries", LongOpt.NO_ARGUMENT, null, ListOperation.libraries),
					new LongOpt("events", LongOpt.NO_ARGUMENT, null, ListOperation.events),
					new LongOpt("ra-types", LongOpt.NO_ARGUMENT, null, ListOperation.ra_types),
					new LongOpt("ras", LongOpt.NO_ARGUMENT, null, ListOperation.ras),
					new LongOpt("dus", LongOpt.NO_ARGUMENT, null, ListOperation.dus),
					new LongOpt("profile-spec", LongOpt.NO_ARGUMENT, null, ListOperation.profile_specs),
				new LongOpt("installed", LongOpt.NO_ARGUMENT, null, 'y'),
					new LongOpt("cid", LongOpt.REQUIRED_ARGUMENT, null, IsInstalledOperation.cid),
					new LongOpt("duid", LongOpt.REQUIRED_ARGUMENT, null, IsInstalledOperation.duid),
				new LongOpt("install", LongOpt.REQUIRED_ARGUMENT, null, 'i'),
				new LongOpt("un-install", LongOpt.REQUIRED_ARGUMENT, null, 'u'),
				//new LongOpt("duid", LongOpt.REQUIRED_ARGUMENT, null, 'd'),
				new LongOpt("desc", LongOpt.NO_ARGUMENT, null, 'd'),
					//new LongOpt("cid", LongOpt.REQUIRED_ARGUMENT, null, GetDescriptorsOperation.cid),
					//new LongOpt("duid", LongOpt.REQUIRED_ARGUMENT, null, GetDescriptorsOperation.duid),
				new LongOpt("ref", LongOpt.REQUIRED_ARGUMENT, null, 'r'),

		};

		Getopt getopt = new Getopt(null, args, sopts, lopts);
		getopt.setOpterr(false);

		int code;
		while ((code = getopt.getopt()) != -1) {
			switch (code) {
			case ':':
				throw new CommandException("Option requires an argument: " + args[getopt.getOptind() - 1]);

			case '?':
				throw new CommandException("Invalid (or ambiguous) option: " + args[getopt.getOptind() - 1]);

			case 'l':

				super.operation = new ListOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;
			case 'y':

				super.operation = new IsInstalledOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;
			case 'i':

				super.operation = new InstallOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;
			case 'u':

				super.operation = new UninstallOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;
			case 'd':

				//super.operation = new DeployableUnitIDOperation(super.context, super.log, this);
				//super.operation.buildOperation(getopt, args);

				//break;
			//case 's':

				super.operation = new GetDescriptorsOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;
			case 'r':

				super.operation = new GetReferringComponentsOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);

				break;

			default:
				throw new CommandException("Command: \"" + getName() + "\", found unexpected opt: " + args[getopt.getOptind() - 1]);

			}
		}
	}
	/* (non-Javadoc)
	 * @see org.mobicents.slee.tools.twiddle.AbstractSleeCommand#getBeanOName()
	 */
	@Override
	public ObjectName getBeanOName() throws MalformedObjectNameException, NullPointerException {
		return new ObjectName(Utils.SLEE_DEPLOYMENT);
	}

	private class GetReferringComponentsOperation extends AbstractOperation
	{
		public static final String OPERATION_getReferringComponents = "getReferringComponents";
		public GetReferringComponentsOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_getReferringComponents;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			String optArg = opts.getOptarg();
			if (optArg.contains(";")) {
				// arrays for ServiceID

				throw new CommandException("Array parameter is not supported by: "+args[opts.getOptind()-1]);

			}
			try{
				addArg(optArg, ComponentID.class, true);
			}catch(Exception e)
			{
				throw new CommandException("Failed to parse ComponentID: \""+optArg+"\"",e);
			}
		}

	}

	private class GetDescriptorsOperation extends AbstractOperation
	{
		private static final String OPERATION_getDescriptors = "getDescriptors";
		private static final String OPERATION_getDescriptor = "getDescriptor";

		private String stringDUID;
		private String stringCID;
		public GetDescriptorsOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			//op name not set, depends on args
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {

			int code;
			while ((code = opts.getopt()) != -1) {
				switch (code) {
				case ':':
					throw new CommandException("Option requires an argument: " + args[opts.getOptind()-1]);

				case '?':
					throw new CommandException("Invalid (or ambiguous) option: " + args[opts.getOptind()-1]+" --> "+opts.getOptopt());

				case IsInstalledOperation.duid:

					stringDUID = opts.getOptarg();
					break;
				case IsInstalledOperation.cid:
					stringCID = opts.getOptarg();
					break;

				default:
					throw new CommandException("Operation \""+this.operationName+"\" for command: \""+sleeCommand.getName()+"\", found unexpected opt: "+args[opts.getOptind()-1]);

				}
			}

			if( (stringCID == null && stringDUID == null) ||  (stringDUID != null && stringCID != null) )
			{
				throw new CommandException("Operation \""+this.operationName+"\" for command: \""+sleeCommand.getName()+"\", expects either \"--duid\" or \"--cid\" to be present");
			}

			if(stringCID!=null)
			{

				if (stringCID.contains(";")) {
					// arrays for ServiceID

					super.operationName=OPERATION_getDescriptors;

				}else
				{
					super.operationName=OPERATION_getDescriptor;
				}
				try{
					addArg(stringCID, ComponentID.class, true);
				}catch(Exception e)
				{
					throw new CommandException("Failed to parse ComponentID: \""+stringCID+"\"",e);
				}

			}else
			{
				if (stringDUID.contains(";")) {
					// arrays for ServiceID

					super.operationName=OPERATION_getDescriptors;

				}else
				{
					super.operationName=OPERATION_getDescriptor;
				}
				try{
					addArg(stringDUID, DeployableUnitID.class, true);
				}catch(Exception e)
				{
					throw new CommandException("Failed to parse DeployableUnitID: \""+stringDUID+"\"",e);
				}

			}

		}

	}

	private class DeployableUnitIDOperation extends AbstractOperation
	{
		private static final String OPERATION_getDeployableUnit = "getDeployableUnit";
		public DeployableUnitIDOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_getDeployableUnit;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			addArg(opts.getOptarg(), String.class, false);
		}

	}

	private class UninstallOperation extends AbstractOperation
	{
		private static final String OPERATION_uninstall = "uninstall";
		public UninstallOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_uninstall;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			String optArg = opts.getOptarg();
			if (optArg.contains(";")) {
				// arrays for ServiceID

				throw new CommandException("Array parameter is not supported by: "+args[opts.getOptind()-1]);

			}
			try{
				addArg(optArg, DeployableUnitID.class, true);
			}catch(Exception e)
			{
				throw new CommandException("Failed to parse ComponentID: \""+optArg+"\"",e);
			}
		}

	}

	private class InstallOperation extends AbstractOperation
	{
		private static final String OPERATION_install = "install";
		public InstallOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_install;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			//TODO: add path resolution, check if its ./ and expand ?
			addArg(opts.getOptarg(), String.class, false);
		}

	}

	private class IsInstalledOperation extends AbstractOperation {
		public static final char duid = 'n';
		public static final char cid = 'm';

		private static final String OPERATION_isInstalled = "isInstalled";

		//TODO: remove this, remove options and make String check for DUID prefix ?
		private String stringDUID;
		private String stringCID;

		public IsInstalledOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_isInstalled;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			int code;
			while ((code = opts.getopt()) != -1) {
				switch (code) {
				case ':':
					throw new CommandException("Option requires an argument: " + args[opts.getOptind() - 1]);

				case '?':
					throw new CommandException("Invalid (or ambiguous) option: " + args[opts.getOptind() - 1] );

				case duid:

					stringDUID = opts.getOptarg();

					break;
				case cid:
					stringCID = opts.getOptarg();

					break;

				default:
					throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName()
							+ "\", found unexpected opt: " + args[opts.getOptind() - 1]);

				}
			}

			if ((stringCID == null && stringDUID == null) || (stringDUID != null && stringCID != null)) {
				throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName()
						+ "\", expects either \"--duid\" or \"--cid\" to be present");
			}

			if (stringCID != null) {

				if (stringCID.contains(";")) {
					throw new CommandException("Array parameter is not supported by: " + args[opts.getOptind() - 1]);

				}
				try {
					addArg(stringCID, ComponentID.class, true);
				} catch (Exception e) {
					throw new CommandException("Failed to parse ComponentID: \"" + stringCID + "\"", e);
				}

			} else {
				if (stringDUID.contains(";")) {
					// arrays for ServiceID

					throw new CommandException("Array parameter is not supported by: " + args[opts.getOptind() - 1]);

				}
				try {
					addArg(stringDUID, DeployableUnitID.class, true);
				} catch (Exception e) {
					throw new CommandException("Failed to parse DeployableUnitID: \"" + stringDUID + "\"", e);
				}

			}

		}

		@Override
		protected String prepareResultText() {
			Boolean b = (Boolean) super.operationResult;
			if(b)
			{
				return "Is installed.";
			}else
			{
				return "Is not installed.";
			}
		}

	}

	private class ListOperation extends AbstractOperation {

		public static final char sbbs = 'q';
		public static final char ra_types = 'w';
		public static final char ras = 'e';
		public static final char services = 'c';
		public static final char libraries = 't';
		public static final char events = 'a';
		public static final char dus = 'h';
		public static final char profile_specs = 'g';

		private static final String OPERATION_getSbbs = "getSbbs";
		private static final String OPERATION_getResourceAdaptorTypes ="getResourceAdaptorTypes";
		private static final String OPERATION_getResourceAdaptors ="getResourceAdaptors";
		private static final String OPERATION_getServices ="getServices";
		private static final String OPERATION_getLibraries ="getLibraries";
		private static final String OPERATION_getEventTypes ="getEventTypes";
		private static final String OPERATION_getDeployableUnits ="getDeployableUnits";
		private static final String OPERATION_getProfileSpecifications ="getProfileSpecifications";

		//service id which can be presen with 'q';
		private String stringServiceID;

		public ListOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);

		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {

			// not perfect, it will swallow everything that matches. but its ok.
			int code;
			while ((code = opts.getopt()) != -1) {
				switch (code) {
				case ':':
					throw new CommandException("Option requires an argument: " + args[opts.getOptind() - 1]);

				case '?':
					throw new CommandException("Invalid (or ambiguous) option: " + args[opts.getOptind() - 1]);

				case sbbs:
					super.operationName = OPERATION_getSbbs;
					stringServiceID = opts.getOptarg();
					if (stringServiceID != null) {
						// we have service id;
						try {
							addArg(stringServiceID, ServiceID.class, true);
						} catch (Exception e) {
							throw new CommandException("Failed to parse ServiceID: \"" + stringServiceID + "\"", e);
						}
					}
					break;
				case ra_types:
					super.operationName = OPERATION_getResourceAdaptorTypes;
					break;
				case ras:
					super.operationName = OPERATION_getResourceAdaptors;
					break;
				case services:
					super.operationName = OPERATION_getServices;
					break;
				case libraries:
					super.operationName = OPERATION_getLibraries;
					break;
				case events:
					super.operationName = OPERATION_getEventTypes;
					break;

				case dus:
					super.operationName = OPERATION_getDeployableUnits;// FIXME:
																		// add
																		// editor
																		// for
																		// that
					break;
				case profile_specs:
					super.operationName = OPERATION_getProfileSpecifications;
					break;
				default:
					throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName() + "\", found unexpected opt: "
							+ args[opts.getOptind() - 1]);

				}

			}

			if (this.operationName == null) {
				throw new CommandException(sleeCommand.getName() + " command requires option to be passed.");
			}

		}

	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.management.jmx;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.slee.InvalidArgumentException;
import javax.slee.SLEEException;
import javax.slee.SbbID;
import javax.slee.ServiceID;
import javax.slee.UnrecognizedSbbException;
import javax.slee.management.ManagementException;
import javax.slee.management.NotificationSource;
import javax.slee.management.SbbNotification;
import javax.slee.management.UsageParameterSetNameAlreadyExistsException;
import javax.slee.usage.SbbUsageMBean;
import javax.slee.usage.UnrecognizedUsageParameterSetNameException;
import javax.slee.usage.UsageMBean;
import javax.slee.usage.UsageNotificationManagerMBean;

import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.SleeComponentWithUsageParametersInterface;
import org.mobicents.slee.container.component.sbb.SbbComponent;
import org.mobicents.slee.container.component.service.ServiceComponent;
import org.mobicents.slee.runtime.usage.AbstractUsageParameterSet;

/**
 *
 *
 * The ServiceUsageMBean interface defines the management interface used to
 * interact with SBB usage parameter sets for SBBs in a Service. It defines the
 * methods to create, lookup, and remove SBB usage parameter sets from SBBs in
 * the Service.
 *
 * @author M. Ranganathan
 * @author Ivelin Ivanov
 * @author martins
 */
@SuppressWarnings("deprecation")
public class ServiceUsageMBeanImpl extends StandardMBean implements
		ServiceUsageMBean, UsageMBeanImplParent,
		Serializable {

	private static final long serialVersionUID = 2670146310843436229L;

	private static transient Logger logger = Logger
			.getLogger(ServiceUsageMBeanImpl.class);

	/**
	 * This is the service ID for this service usage mbean.
	 *
	 */
	private final ServiceID serviceID;

	/**
	 * the sbb usage mbeans registred by this service usage mbean
	 */
	private ConcurrentHashMap<SbbUsageMBeanMapKey, UsageMBeanImpl> usageMBeans = new ConcurrentHashMap<SbbUsageMBeanMapKey, UsageMBeanImpl>();

	/**
	 * the usage notification manager mbeans per sbb id
	 */
	private ConcurrentHashMap<SbbID, UsageNotificationManagerMBeanImpl> notificationManagers = new ConcurrentHashMap<SbbID, UsageNotificationManagerMBeanImpl>();

	/**
	 * Container we run in.
	 */
	private final SleeContainer sleeContainer;

	private final ObjectName objectName;

	public ServiceUsageMBeanImpl(ServiceComponent serviceComponent, SleeContainer sleeContainer)
			throws NotCompliantMBeanException, MalformedObjectNameException,
			NullPointerException {
		super(ServiceUsageMBean.class);
		this.serviceID = serviceComponent.getServiceID();
		this.sleeContainer = sleeContainer;
		try {
			this.objectName = new ObjectName(ServiceUsageMBean.BASE_OBJECT_NAME + ','
					+ ServiceUsageMBean.SERVICE_NAME_KEY + '='
					+ ObjectName.quote(serviceID.getName()) + ','
					+ ServiceUsageMBean.SERVICE_VENDOR_KEY + '='
					+ ObjectName.quote(serviceID.getVendor()) + ','
					+ ServiceUsageMBean.SERVICE_VERSION_KEY + '='
					+ ObjectName.quote(serviceID.getVersion()));
			sleeContainer.getMBeanServer().registerMBean(this, objectName);
		} catch (Throwable e) {
			throw new SLEEException(
					"unable to register service usage mbean for " + serviceID,
					e);
		}
		try {
			// install all the default usage parameters
			for (SbbID sbbID : serviceComponent.getSbbIDs(sleeContainer
					.getComponentRepository())) {
				createUsageParameterSet(sbbID);
			}
		} catch (Throwable e) {
			throw new SLEEException(
					"unable to create default usage param sets for "
							+ serviceID, e);
		}
		serviceComponent.setServiceUsageMBean(this);
	}

	@Override
	public String toString() {
		return serviceID + " Usage MBean : "
			+ "\n+-- Notification Managers: "	+ notificationManagers.keySet()
			+ "\n+-- Usage MBeans: "	+ usageMBeans.keySet();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#getService()
	 */
	public ServiceID getService() throws ManagementException {
		return this.serviceID;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#createUsageParameterSet(javax.slee.SbbID,
	 *      java.lang.String)
	 */
	public void createUsageParameterSet(SbbID sbbId, String name)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UsageParameterSetNameAlreadyExistsException, ManagementException {

		if (name == null)
			throw new NullPointerException("Sbb usage param set is null");
		if (name.length() == 0)
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");
		if (!isValidUsageParameterName(name))
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");

		_createUsageParameterSet(sbbId, name, true);

	}

	/*
	 * creates the default usage parameter set
	 */
	public void createUsageParameterSet(SbbID sbbId)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UsageParameterSetNameAlreadyExistsException, ManagementException {
		_createUsageParameterSet(sbbId, null, false);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#createUsageParameterSet(javax.slee.SbbID,
	 *      java.lang.String)
	 */
	private synchronized void _createUsageParameterSet(SbbID sbbId,
			String name, boolean failIfSbbHasNoUsageParamSet)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UsageParameterSetNameAlreadyExistsException, ManagementException {

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}
		// get sbb usage parameter class
		Class<?> usageParameterClass = sbbComponent
				.getUsageParametersConcreteClass();
		if (usageParameterClass == null) {
			if (failIfSbbHasNoUsageParamSet) {
				throw new InvalidArgumentException(sbbId.toString()
						+ " does not define a usage parameters interface");
			} else {
				return;
			}
		}
		// check if the usage parameter name set already exists
		SbbUsageMBeanMapKey mapKey = new SbbUsageMBeanMapKey(sbbId, name);
		if (this.usageMBeans.containsKey(mapKey)) {
			throw new UsageParameterSetNameAlreadyExistsException("name "
					+ name + " already exists for service " + serviceComponent
					+ " and sbb " + sbbComponent);
		}

		UsageMBeanImpl usageMbean = null;
		UsageNotificationManagerMBeanImpl usageNotificationManagerMBean = null;
		Thread currentThread = Thread.currentThread();
		ClassLoader currentThreadClassLoader = currentThread
				.getContextClassLoader();
		try {
			// change class loader
			currentThread.setContextClassLoader(sbbComponent.getClassLoader());
			// create the actual usage parameter instance and map it in the
			// mbean
			SbbNotification sbbNotification = new SbbNotification(serviceID,
					sbbId);
			AbstractUsageParameterSet installedUsageParameterSet = (AbstractUsageParameterSet) AbstractUsageParameterSet.newInstance(usageParameterClass, sbbNotification, name, sleeContainer);
			// create and register the usage mbean
			Class<?> usageParameterMBeanClass = sbbComponent
					.getUsageParametersMBeanImplConcreteClass();

			Constructor<?> constructor = null;
			if (sbbComponent.isSlee11()) {
				constructor = usageParameterMBeanClass
						.getConstructor(new Class[] { Class.class,
								NotificationSource.class });
			} else {
				constructor = usageParameterMBeanClass
						.getConstructor(new Class[] { Class.class,
								SbbNotification.class });
			}
			ObjectName usageParameterMBeanObjectName = generateUsageParametersMBeanObjectName(
					name, sbbId, sbbComponent.isSlee11());
			usageMbean = (UsageMBeanImpl) constructor.newInstance(new Object[] {
					sbbComponent.getUsageParametersMBeanConcreteInterface(),
					sbbNotification });
			usageMbean.setObjectName(usageParameterMBeanObjectName);
			usageMbean.setParent(this);
			sleeContainer.getMBeanServer().registerMBean(usageMbean,
					usageParameterMBeanObjectName);
			// set the usage param data related with the mbean

			installedUsageParameterSet.setUsageMBean(usageMbean);
			usageMbean.setUsageParameter(installedUsageParameterSet);
			// store the mbean
			this.usageMBeans.put(mapKey, usageMbean);
			// if it's the default usage param set and it's an slee 1.1. sbb
			// then we have to create the notification manager too
			if (sbbComponent.isSlee11() && name == null) {
				Class<?> usageNotificationManagerMBeanClass = sbbComponent
						.getUsageNotificationManagerMBeanImplConcreteClass();
				constructor = usageNotificationManagerMBeanClass
						.getConstructor(new Class[] { Class.class,
								NotificationSource.class,SleeComponentWithUsageParametersInterface.class });
				usageNotificationManagerMBean = (UsageNotificationManagerMBeanImpl) constructor
						.newInstance(new Object[] {
								sbbComponent
										.getUsageNotificationManagerMBeanConcreteInterface(),
								sbbNotification ,sbbComponent});
				ObjectName usageNotificationManagerMBeanObjectName = generateUsageNotificationManagerMBeanObjectName(sbbId);
				usageNotificationManagerMBean
						.setObjectName(usageNotificationManagerMBeanObjectName);
				sleeContainer.getMBeanServer().registerMBean(
						usageNotificationManagerMBean,
						usageNotificationManagerMBeanObjectName);
				this.notificationManagers.put(sbbId,
						usageNotificationManagerMBean);
			}

		} catch (Throwable e) {
			if (mapKey != null && usageMbean != null) {
				this.usageMBeans.remove(mapKey);
				try {
					sleeContainer.getMBeanServer().unregisterMBean(
							usageMbean.getObjectName());
				} catch (Throwable f) {
					logger.error("failed to unregister usage parameter mbean "
							+ usageMbean.getObjectName());
				}
			}
			if (usageNotificationManagerMBean != null) {
				this.notificationManagers.remove(sbbId);
				try {
					sleeContainer.getMBeanServer().unregisterMBean(
							usageNotificationManagerMBean.getObjectName());
				} catch (Throwable f) {
					logger
							.error("failed to unregister usage notification manager mbean "
									+ usageNotificationManagerMBean
											.getObjectName());
				}
			}
			throw new ManagementException(e.getMessage(), e);
		} finally {
			currentThread.setContextClassLoader(currentThreadClassLoader);
		}
	}

	private boolean isValidUsageParameterName(String str) {
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (!(Character.isDigit(c) || Character.isLetter(c) || (c <= '\u007e' && c >= '\u0020'))) {
				return false;
			}
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#removeUsageParameterSet(javax.slee.SbbID,
	 *      java.lang.String)
	 */
	public void removeUsageParameterSet(SbbID sbbId, String name)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UnrecognizedUsageParameterSetNameException, ManagementException {

		if (name == null)
			throw new NullPointerException("Sbb usage param set is null");
		if (name.length() == 0)
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");
		if (!isValidUsageParameterName(name))
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");

		_removeUsageParameterSet(sbbId, name);
	}

	public void removeAllUsageParameterSet() {
		for (SbbUsageMBeanMapKey mapKey : usageMBeans.keySet()) {
			try {
				_removeUsageParameterSet(mapKey.sbbID, mapKey.paramName);
			} catch (Throwable e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	private synchronized void _removeUsageParameterSet(SbbID sbbId, String name)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UnrecognizedUsageParameterSetNameException, ManagementException {

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}
		// get sbb usage parameter class
		Class<?> usageParameterClass = sbbComponent
				.getUsageParametersConcreteClass();
		if (usageParameterClass == null) {
			throw new InvalidArgumentException(sbbId.toString()
					+ " does not define a usage parameters interface");
		}

		SbbUsageMBeanMapKey mapKey = new SbbUsageMBeanMapKey(sbbId, name);
		UsageMBeanImpl usageMbean = null;
		try {
			// remove from this mbean map
			usageMbean = this.usageMBeans.remove(mapKey);
			if (usageMbean == null) {
				throw new UnrecognizedUsageParameterSetNameException(name);
			}
			sleeContainer.getMBeanServer().unregisterMBean(
					usageMbean.getObjectName());
			if (name == null && sbbComponent.isSlee11()) {
				removeNotificationManager(sbbId);
			}
			boolean clustered = !sleeContainer.getCluster().isSingleMember();
			if( !clustered)
			{
				//we are last, lets clear cache
				usageMbean.getUsageParameter().remove();
			}
		} catch (Throwable e) {
			// rollback changes
			if(usageMbean!=null)
			{
				if (mapKey != null && usageMbean != null) {
					this.usageMBeans.put(mapKey, usageMbean);
				}
				try {
					sleeContainer.getMBeanServer().registerMBean(usageMbean,
							usageMbean.getObjectName());
				} catch (Throwable f) {
					logger.error("failed to re-register usage parameter mbean "
							+ usageMbean.getObjectName());
				}
			}
			// note: removal rollback of notification manager is done by the
			// removeNotificationManager() method
			if(e instanceof UnrecognizedUsageParameterSetNameException)
			{
				throw (UnrecognizedUsageParameterSetNameException)e;
			}else
			{
				throw new ManagementException(e.getMessage(), e);
			}
		}
	}

	/**
	 * This method returns a list containing the names of the named SBB usage
	 * parameter sets that belong to the SBB specified by the sbbID argument and
	 * the Service represented by the ServiceUsageMBean object.
	 *
	 * @see javax.slee.management.ServiceUsageMBean#getUsageParameterSets(javax.slee.SbbID)
	 */
	public synchronized String[] getUsageParameterSets(SbbID sbbId)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException, ManagementException {

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		} else {
			if (sbbComponent.getUsageParametersInterface() == null) {
				throw new InvalidArgumentException(
						"no usage parameter interface for " + sbbId);
			}
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}
		Set<String> resultSet = new HashSet<String>();
		for (UsageMBeanImpl usageMBeanImpl : usageMBeans.values()) {
			if (((SbbNotification) usageMBeanImpl.getNotificationSource())
					.getSbb().equals(sbbId)) {
				String name = usageMBeanImpl.getUsageParameterSet();
				if (name != null) {
					resultSet.add(name);
				}
			}
		}
		return resultSet.toArray(new String[resultSet.size()]);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#getSbbUsageMBean(javax.slee.SbbID)
	 */
	public ObjectName getSbbUsageMBean(SbbID sbbId)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException, ManagementException {
		try {
			return _getSbbUsageMBean(sbbId, null);
		} catch (UnrecognizedUsageParameterSetNameException e) {
			throw new ManagementException(
					"default usage parameter name not found", e);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#getSbbUsageMBean(javax.slee.SbbID,
	 *      java.lang.String)
	 */
	public ObjectName getSbbUsageMBean(SbbID sbbId, String name)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException,
			UnrecognizedUsageParameterSetNameException, ManagementException {
		if (name == null)
			throw new NullPointerException("Sbb usage param set is null");
		if (name.length() == 0)
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");
		if (!isValidUsageParameterName(name))
			throw new InvalidArgumentException(
					"The lenght of the Usage Parameter Set Name is zero!");
		return _getSbbUsageMBean(sbbId, name);
	}

	private synchronized ObjectName _getSbbUsageMBean(SbbID sbbId, String name)
			throws UnrecognizedSbbException, InvalidArgumentException,
			ManagementException, UnrecognizedUsageParameterSetNameException {

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		} else {
			if (sbbComponent.getUsageParametersInterface() == null) {
				throw new InvalidArgumentException(
						"no usage parameter interface for " + sbbId);
			}
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}

		UsageMBeanImpl bean = null;
		try {
			SbbUsageMBeanMapKey mapKey = new SbbUsageMBeanMapKey(sbbId, name);
			bean=  usageMBeans.get(mapKey);

		} catch (Throwable e) {
			throw new ManagementException(e.getMessage(), e);
		}

		if (bean == null || bean.getObjectName() == null) {
			throw new UnrecognizedUsageParameterSetNameException(name);
		} else {
			return bean.getObjectName();
		}
	}

	/**
	 * Resets the usage parameters of only the SBB specified by the sbbID
	 * argument argument (within the Service represented by the
	 * ServiceUsageMBean object).
	 *
	 * @see javax.slee.management.ServiceUsageMBean#resetAllUsageParameters(javax.slee.SbbID)
	 */
	public synchronized void resetAllUsageParameters(SbbID sbbId)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException, ManagementException {

		if (logger.isDebugEnabled()) {
			logger.debug("resetAllUsageParameters: " + sbbId);
		}

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		} else {
			if (sbbComponent.getUsageParametersInterface() == null) {
				throw new InvalidArgumentException(
						"no usage parameter interface for " + sbbId);
			}
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}

		for (UsageMBeanImpl usageMBeanImpl : usageMBeans.values()) {
			SbbNotification sbbNotification = (SbbNotification) usageMBeanImpl
					.getNotificationSource();
			if (sbbNotification.getSbb().equals(sbbId)) {
				usageMBeanImpl.resetAllUsageParameters();
			}
		}
	}

	/**
	 * Resets the usage parameters of all SBBs within the Service represented by
	 * the ServiceUsageMBean object. The SLEE sets counter-type usage parameters
	 * to zero and removes all samples from sample-type usage parameters.
	 *
	 * @see javax.slee.management.ServiceUsageMBean#resetAllUsageParameters()
	 */
	public synchronized void resetAllUsageParameters()
			throws ManagementException {
		try {
			//FIXME: hmm, how to check here for clustered... ghmp
			for (UsageMBeanImpl usageMBeanImpl : usageMBeans.values()) {
				usageMBeanImpl.resetAllUsageParameters();
			}
		} catch (Throwable e) {
			throw new ManagementException(e.getMessage(), e);
		}
	}

	public void remove() {
		if (logger.isDebugEnabled()) {
			logger.debug("Unregistring Usage MBean of service " + serviceID);
		}
		final MBeanServer mbeanServer = sleeContainer.getMBeanServer();
		try {
			mbeanServer.unregisterMBean(getObjectName());
		} catch (Exception e) {
			logger.error("failed to remove service usage mbean "
					+ getObjectName(), e);
		}
		// remove all service usage param
		if (logger.isDebugEnabled()) {
			logger.debug("Removing all usage parameters of service "
					+ serviceID);
		}
		removeAllUsageParameterSet();
		// remove usage mbean from service component
		final ServiceComponent serviceComponent = sleeContainer.getComponentRepository().getComponentByID(serviceID);
		if (serviceComponent != null) {
			serviceComponent.setServiceUsageMBean(null);
		}

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceUsageMBean#close()
	 */
	public void close() throws ManagementException {
		// ignore
	}

	public ObjectName getSbbUsageNotificationManagerMBean(SbbID sbbId)
			throws NullPointerException, UnrecognizedSbbException,
			InvalidArgumentException, ManagementException {

		if (sbbId == null)
			throw new NullPointerException("Sbb ID is null!");

		// get the sbb component
		SbbComponent sbbComponent = sleeContainer.getComponentRepository()
				.getComponentByID(sbbId);
		if (sbbComponent == null) {
			throw new UnrecognizedSbbException(sbbId.toString());
		} else {
			if (sbbComponent
					.getUsageNotificationManagerMBeanConcreteInterface() == null) {
				throw new InvalidArgumentException(
						"no notification manager defined for " + sbbId);
			}
		}
		// get service component and check if the sbb belongs to the service
		ServiceComponent serviceComponent = sleeContainer
				.getComponentRepository().getComponentByID(getService());
		if (!serviceComponent.getSbbIDs(
				sleeContainer.getComponentRepository()).contains(sbbId)) {
			throw new UnrecognizedSbbException(sbbId.toString()
					+ " is not part of " + getService());
		}

		try {
			return generateUsageNotificationManagerMBeanObjectName(sbbId);
		} catch (Throwable e) {
			throw new ManagementException(e.getMessage(), e);
		}
	}

	public ConcurrentHashMap<SbbUsageMBeanMapKey, UsageMBeanImpl> getUsageParameterSets() {
		return this.usageMBeans;
	}

	public ObjectName getObjectName() {
		return objectName;
	}

	private ObjectName generateUsageParametersMBeanObjectName(String name,
			SbbID sbbID, boolean isSlee11) throws MalformedObjectNameException,
			NullPointerException {
		String objectNameAsString = UsageMBean.BASE_OBJECT_NAME
				+ (name != null ? "," + UsageMBean.USAGE_PARAMETER_SET_NAME_KEY
						+ '=' + ObjectName.quote(name) : "")
				+ ','
				+ UsageMBean.NOTIFICATION_SOURCE_KEY
				+ '='
				+ (isSlee11 ? SbbNotification.USAGE_NOTIFICATION_TYPE
						: SbbUsageMBean.USAGE_NOTIFICATION_TYPE) + ','
				+ SbbNotification.SERVICE_NAME_KEY + '='
				+ ObjectName.quote(serviceID.getName()) + ','
				+ SbbNotification.SERVICE_VENDOR_KEY + '='
				+ ObjectName.quote(serviceID.getVendor()) + ','
				+ SbbNotification.SERVICE_VERSION_KEY + '='
				+ ObjectName.quote(serviceID.getVersion()) + ','
				+ SbbNotification.SBB_NAME_KEY + '='
				+ ObjectName.quote(sbbID.getName()) + ','
				+ SbbNotification.SBB_VENDOR_KEY + '='
				+ ObjectName.quote(sbbID.getVendor()) + ','
				+ SbbNotification.SBB_VERSION_KEY + '='
				+ ObjectName.quote(sbbID.getVersion());
		return new ObjectName(objectNameAsString);
	}

	private ObjectName generateUsageNotificationManagerMBeanObjectName(
			SbbID sbbID) throws MalformedObjectNameException,
			NullPointerException {
		String objectNameAsString = UsageNotificationManagerMBean.BASE_OBJECT_NAME
				+ ','
				+ UsageMBean.NOTIFICATION_SOURCE_KEY
				+ '='
				+ SbbNotification.USAGE_NOTIFICATION_TYPE
				+ ','
				+ SbbNotification.SERVICE_NAME_KEY
				+ '='
				+ ObjectName.quote(serviceID.getName())
				+ ','
				+ SbbNotification.SERVICE_VENDOR_KEY
				+ '='
				+ ObjectName.quote(serviceID.getVendor())
				+ ','
				+ SbbNotification.SERVICE_VERSION_KEY
				+ '='
				+ ObjectName.quote(serviceID.getVersion())
				+ ','
				+ SbbNotification.SBB_NAME_KEY
				+ '='
				+ ObjectName.quote(sbbID.getName())
				+ ','
				+ SbbNotification.SBB_VENDOR_KEY
				+ '='
				+ ObjectName.quote(sbbID.getVendor())
				+ ','
				+ SbbNotification.SBB_VERSION_KEY
				+ '='
				+ ObjectName.quote(sbbID.getVersion());
		return new ObjectName(objectNameAsString);
	}

	/**
	 * Convenience method to retrieve the default
	 * {@link AbstractUsageParameterSet} for the specified sbb
	 *
	 * @param sbbID
	 * @return
	 * @throws UnrecognizedUsageParameterSetNameException
	 */
	public AbstractUsageParameterSet getDefaultInstalledUsageParameterSet(
			SbbID sbbID) {
		return _getInstalledUsageParameterSet(sbbID, null);
	}

	/**
	 * Convenience method to retrieve the {@link AbstractUsageParameterSet} for
	 * the specified sbb and name
	 *
	 * @param sbbID
	 * @param name
	 * @return
	 * @throws UnrecognizedUsageParameterSetNameException
	 */
	public AbstractUsageParameterSet getInstalledUsageParameterSet(
			SbbID sbbID, String name)
			throws UnrecognizedUsageParameterSetNameException {
		if (name == null) {
			throw new NullPointerException("null name");
		}
		AbstractUsageParameterSet installedUsageParameterSet = _getInstalledUsageParameterSet(
				sbbID, name);
		if (installedUsageParameterSet == null) {
			throw new UnrecognizedUsageParameterSetNameException(name);
		}
		return installedUsageParameterSet;
	}

	private AbstractUsageParameterSet _getInstalledUsageParameterSet(
			SbbID sbbID, String name) {
		if (sbbID == null) {
			throw new NullPointerException("null sbb id");
		}
		SbbUsageMBeanMapKey mapKey = new SbbUsageMBeanMapKey(sbbID, name);
		UsageMBeanImpl usageMBean = usageMBeans.get(mapKey);
		if (usageMBean == null) {
			return null;
		} else {
			return usageMBean.getUsageParameter();
		}
	}

	public UsageNotificationManagerMBeanImpl getUsageNotificationManagerMBean(
			NotificationSource notificationSource) {
		return notificationManagers.get(((SbbNotification) notificationSource)
				.getSbb());
	}

	public void removeChild(UsageMBeanImpl usageMBeanImpl) {
		try {
			removeUsageParameterSet(((SbbNotification) usageMBeanImpl
					.getNotificationSource()).getSbb(), usageMBeanImpl
					.getUsageParameterSet());
		} catch (Throwable e) {
			logger.error(e.getMessage(), e);
		}
	}

	public void removeChild(UsageNotificationManagerMBeanImpl child) {
		try {
			removeNotificationManager(((SbbNotification) child
					.getNotificationSource()).getSbb());
		} catch (Throwable e) {
			logger.error(e.getMessage(), e);
		}
	}

	private void removeNotificationManager(SbbID sbbId)
			throws ManagementException {

		UsageNotificationManagerMBeanImpl usageMbean = null;
		try {
			// remove from this mbean map
			usageMbean = this.notificationManagers.remove(sbbId);
			if (usageMbean != null) {
				sleeContainer.getMBeanServer().unregisterMBean(
						usageMbean.getObjectName());
			}
		} catch (Throwable e) {
			// rollback changes
			if (usageMbean != null) {
				this.notificationManagers.put(sbbId, usageMbean);
			}
			try {
				sleeContainer.getMBeanServer().registerMBean(usageMbean,
						usageMbean.getObjectName());
			} catch (Throwable f) {
				logger.error("failed to re-register usage parameter mbean "
						+ usageMbean.getObjectName());
			}
			throw new ManagementException(e.getMessage(), e);
		}
	}

	public static class SbbUsageMBeanMapKey {

		private final SbbID sbbID;
		private final String paramName;

		public SbbUsageMBeanMapKey(SbbID sbbID, String paramName) {
			this.sbbID = sbbID;
			this.paramName = paramName;
		}

		@Override
		public int hashCode() {
			return sbbID.hashCode() * 31
					+ (paramName != null ? paramName.hashCode() : 0);
		}

		@Override
		public boolean equals(Object obj) {
			if (obj != null && obj.getClass() == this.getClass()) {
				SbbUsageMBeanMapKey other = (SbbUsageMBeanMapKey) obj;
				// check sbb id
				if (!this.sbbID.equals(other.sbbID)) {
					return false;
				}
				// check param name, may be null
				if (other.paramName == null) {
					if (this.paramName != null) {
						return false;
					}
				} else {
					if (!other.paramName.equals(this.paramName)) {
						return false;
					}
				}
				return true;
			} else {
				return false;
			}
		}
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.restcomm.slee.resource.statistics;

import javax.management.ObjectName;
import javax.slee.Address;
import javax.slee.InvalidArgumentException;
import javax.slee.facilities.EventLookupFacility;
import javax.slee.facilities.Tracer;
import javax.slee.management.UnrecognizedResourceAdaptorEntityException;
import javax.slee.resource.ActivityHandle;
import javax.slee.resource.ConfigProperties;
import javax.slee.resource.FailureReason;
import javax.slee.resource.FireableEventType;
import javax.slee.resource.Marshaler;
import javax.slee.resource.ReceivableService;
import javax.slee.resource.ResourceAdaptor;
import javax.slee.resource.ResourceAdaptorContext;
import javax.slee.resource.SleeEndpoint;

import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.management.ResourceManagement;

import org.restcomm.commons.statistics.reporter.RestcommStatsReporter;
import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;

import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 *
 */
public class StatisticsResourceAdaptor implements ResourceAdaptor {
	private transient Tracer tracer;

	private ResourceAdaptorContext raContext;
	private SleeContainer sleeContainer;
	private ResourceManagement resourceManagement;

	public StatisticsResourceAdaptor() { }

	public ResourceAdaptorContext getResourceAdaptorContext() {
		return raContext;
	}

	// Restcomm Statistics
	protected static final String STATISTICS_SERVER = "statistics.server";
	protected static final String DEFAULT_STATISTICS_SERVER = "https://statistics.restcomm.com/rest/";

	private RestcommStatsReporter statsReporter = new RestcommStatsReporter();
	private MetricRegistry metrics = RestcommStatsReporter.getMetricRegistry();
	// define metric name
	private Counter counterCalls = metrics.counter("calls");
	private Counter counterSeconds = metrics.counter("seconds");
	private Counter counterMessages = metrics.counter("messages");

	// lifecycle methods

	public void setResourceAdaptorContext(ResourceAdaptorContext raContext) {
		this.raContext = raContext;
		this.tracer = raContext.getTracer(StatisticsResourceAdaptor.class.getSimpleName());

		this.sleeContainer = SleeContainer.lookupFromJndi();
		if (this.sleeContainer != null) {
			this.resourceManagement = sleeContainer.getResourceManagement();
		}
	}

	private class StatisticsTimerTask extends TimerTask {

		@Override
		public void run() {

			if (resourceManagement == null) {
				return;
			}

			for (String raEntity: resourceManagement.getResourceAdaptorEntities()) {
				if (tracer.isFineEnabled()) {
					tracer.fine("RA Entity: " + raEntity);
				}

				try {
					ObjectName usageMBeanName = resourceManagement.getResourceUsageMBean(raEntity);

					// null for default set
					String usageParameterSetName = null; // "statisitcs";
					Object usageParameterSet = ManagementFactory.getPlatformMBeanServer()
							.invoke(usageMBeanName, "getInstalledUsageParameterSet",
									new Object[] {usageParameterSetName},
									new String[] {String.class.getName()});

					if (usageParameterSet != null) {
						try {
							Method method = usageParameterSet.getClass()
									.getMethod("getParameter", String.class, boolean.class);

							// get and reset
							Long calls = (Long) method.invoke(usageParameterSet, "calls", true);
							if (tracer.isFineEnabled()) {
								tracer.fine("calls: " + calls);
							}
							counterCalls.inc(calls);

							Long messages = (Long) method.invoke(usageParameterSet, "messages", true);
							if (tracer.isFineEnabled()) {
								tracer.fine("messages: " + messages);
							}
							counterMessages.inc(messages);

							Long seconds = (Long) method.invoke(usageParameterSet, "seconds", true);
							if (tracer.isFineEnabled()) {
								tracer.fine("seconds: " + seconds);
							}
							counterSeconds.inc(seconds);

						} catch (Exception e) {
							if (tracer.isWarningEnabled()) {
								tracer.warning("Cant get Usage parameter value", e);
							}
						}
					}

					//ManagementFactory.getPlatformMBeanServer()
					//		.invoke(usageMBeanName, "resetAllUsageParameters",
					//				null, null);

				} catch (UnrecognizedResourceAdaptorEntityException e) {
					// TODO
				} catch (InvalidArgumentException e) {
					// TODO
				} catch (Exception e) {
					// TODO
				}
			} // end of for

			// TODO: check counters?
			//if (calls != 0 || messages != 0 || seconds != 0) {
			if (statsReporter != null) {
				statsReporter.report();
			}
			//}
		}
	}

	public void raConfigure(ConfigProperties properties) {
	}

	public void raActive() {
		if (statsReporter == null) {
			statsReporter = new RestcommStatsReporter();
		}

		String statisticsServer = Version.getVersionProperty(STATISTICS_SERVER);
		if (statisticsServer == null || !statisticsServer.contains("http")) {
			statisticsServer = DEFAULT_STATISTICS_SERVER;
		}

		if (tracer.isFineEnabled()) {
			tracer.fine("statisticsServer: " + statisticsServer);
		}

		//define remote server address (optionally)
		statsReporter.setRemoteServer(statisticsServer);
		String projectName = System.getProperty("RestcommProjectName", "jainslee");
		String projectType = System.getProperty("RestcommProjectType", "community");
		String projectVersion = System.getProperty("RestcommProjectVersion",
				Version.getVersionProperty(Version.RELEASE_VERSION));

		if (tracer.isFineEnabled()) {
			tracer.fine("Restcomm Stats " + projectName + " " + projectType + " " + projectVersion);
		}

		statsReporter.setProjectName(projectName);
		statsReporter.setProjectType(projectType);
		statsReporter.setVersion(projectVersion);

		Version.printVersion();

		// TODO: define periodicity - now to once a minute (for testing)
		//define periodicity - default to once a day
		//statsReporter.start(86400, TimeUnit.SECONDS);
		raContext.getTimer().schedule(new StatisticsTimerTask(), 0, 30 * 1000);
	}

	public void raStopping() { }

	public void raInactive() {
		statsReporter.stop();
		statsReporter = null;
	}

	public void raUnconfigure() { }

	public void unsetResourceAdaptorContext() {
		raContext = null;
		tracer = null;

		sleeContainer = null;
		resourceManagement = null;
	}

	// config management methods
	public void raVerifyConfiguration(ConfigProperties properties)
			throws javax.slee.resource.InvalidConfigurationException {
	}

	public void raConfigurationUpdate(ConfigProperties properties) {
		throw new UnsupportedOperationException();
	}

	// event filtering methods
	public void serviceActive(ReceivableService service) {
	}

	public void serviceStopping(ReceivableService service) {
	}

	public void serviceInactive(ReceivableService service) {
	}

	// mandatory callbacks
	public void administrativeRemove(ActivityHandle handle) {
	}

	public Object getActivity(ActivityHandle activityHandle) {
		return null;
	}

	public ActivityHandle getActivityHandle(Object activity) {
		return null;
	}

	// optional call-backs
	public void activityEnded(ActivityHandle handle) {
	}

	public void activityUnreferenced(ActivityHandle activityHandle) {
	}

	public void eventProcessingFailed(ActivityHandle arg0,
			FireableEventType arg1, Object arg2, Address arg3,
			ReceivableService arg4, int arg5, FailureReason arg6) {
	}

	public void eventProcessingSuccessful(ActivityHandle arg0,
			FireableEventType arg1, Object arg2, Address arg3,
			ReceivableService arg4, int arg5) {
	}

	public void eventUnreferenced(ActivityHandle arg0, FireableEventType arg1,
			Object event, Address arg3, ReceivableService arg4, int arg5) {
	}

	public void queryLiveness(ActivityHandle activityHandle) {
	}

	// interface accessors
	public Object getResourceAdaptorInterface(String arg0) {
		return null;
	}

	public Marshaler getMarshaler() {
		return null;
	}

	// ra logic
}