/*
 * 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;

import org.apache.log4j.Logger;
import org.jboss.msc.service.ServiceController;
import org.restcomm.cluster.MobicentsCluster;
import org.mobicents.slee.connector.local.SleeConnectionService;
import org.mobicents.slee.container.activity.ActivityContextFactory;
import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.classloading.ReplicationClassLoader;
import org.mobicents.slee.container.component.du.DeployableUnitManagement;
import org.mobicents.slee.container.congestion.CongestionControl;
import org.mobicents.slee.container.deployment.SleeContainerDeployer;
import org.mobicents.slee.container.event.EventContextFactory;
import org.mobicents.slee.container.eventrouter.EventRouter;
import org.mobicents.slee.container.facilities.ActivityContextNamingFacility;
import org.mobicents.slee.container.facilities.TimerFacility;
import org.mobicents.slee.container.facilities.nullactivity.NullActivityContextInterfaceFactory;
import org.mobicents.slee.container.facilities.nullactivity.NullActivityFactory;
import org.mobicents.slee.container.jndi.JndiManagement;
import org.mobicents.slee.container.management.*;
import org.mobicents.slee.container.management.jmx.editors.SleePropertyEditorRegistrator;
import org.mobicents.slee.container.rmi.RmiServerInterface;
import org.mobicents.slee.container.sbbentity.SbbEntityFactory;
import org.mobicents.slee.container.transaction.SleeTransactionManager;

import javax.management.MBeanServer;
import javax.slee.InvalidStateException;
import javax.slee.management.SleeState;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;

/**
 * Implements the SleeContainer. The SleeContainer is the anchor for the SLEE.
 * It is the central location from where all container modules are accessible.
 *
 * @author F.Moggia
 * @author M. Ranganathan
 * @author Ivelin Ivanov
 * @author Emil Ivov
 * @author Tim Fox
 * @author eduardomartins
 */
public class SleeContainer {

	private final static Logger logger = Logger.getLogger(SleeContainer.class);

	static {
		// Force this property to allow invocation of getters.
		// http://code.google.com/p/restcomm/issues/detail?id=63
		System.setProperty("jmx.invoke.getters", "true");
		// establish the location of restcomm in JBoss AS deploy folder
		/*
        try {
			java.net.URL url = VFSUtils.getCompatibleURL(VFS
					.getRoot(SleeContainer.class.getClassLoader().getResource(
							"..")));
			java.net.URI uri = new java.net.URI(url.toExternalForm()
					.replaceAll(" ", "%20"));
			deployPath = new File(new File(uri).getParent()).getAbsolutePath();
		} catch (Exception e) {
			logger
					.error(
							"Failed to establish path to Restcomm root deployment directory",
							e);
			deployPath = null;
		}
		*/
		// Config JUL logger to use Log4J filter
		Handler[] handlers = java.util.logging.Logger.getLogger("")
				.getHandlers();
		for (Handler handler : handlers)
			if (handler instanceof ConsoleHandler)
				handler.setFilter(new MobicentsLogFilter());
	}

	private final String deployPath;

	private static SleeContainer sleeContainer;

	/**
	 *
	 * @return the full file system path where restcomm.sar is located
	 */
	public String getDeployPath() {
		return deployPath;
	}

	private LinkedList<SleeContainerModule> modules = new LinkedList<SleeContainerModule>();

	private final SleeTransactionManager sleeTransactionManager;
	private final MobicentsCluster cluster;
	// mbean server where the container's mbeans are registred
	private final MBeanServer mbeanServer;
	/** The lifecycle state of the SLEE */
	private SleeState sleeState;

	// the class that actually posts events to the SBBs.
	// This should be made into a facility and registered with jmx and jndi
	// so it can be independently controlled.
	private final EventRouter router;
	// monitor object for sync on management operations
	private final Object managementMonitor = new Object();
	// for external access to slee
	@SuppressWarnings("unused")
	private RmiServerInterface rmiServerInterfaceMBean;
	// component managers
	private final ComponentManagement componentManagement;
	private final ServiceManagement serviceManagement;

	private final ProfileManagement sleeProfileTableManager;
	private final ResourceManagement resourceManagement;
	private final SbbManagement sbbManagement;
	// object pool management
	// non clustered scheduler
	private final ScheduledExecutorService nonClusteredScheduler;
	private static final int NON_CLUSTERED_SCHEDULER_THREADS = 4;
	// slee factories
	private final ActivityContextFactory activityContextFactory;
	private final NullActivityContextInterfaceFactory nullActivityContextInterfaceFactory;

	private final NullActivityFactory nullActivityFactory;

	// slee facilities
	private final ActivityContextNamingFacility activityContextNamingFacility;

	private final AlarmManagement alarmMBeanImpl;

	private final TraceManagement traceMBeanImpl;

	private final TimerFacility timerFacility;

	private final MobicentsUUIDGenerator uuidGenerator;

	private final UsageParametersManagement usageParametersManagement;

	private ReplicationClassLoader replicationClassLoader;

	private final SbbEntityFactory sbbEntityFactory;

	private final EventContextFactory eventContextFactory;

	private final CongestionControl congestionControl;

	private final SleeConnectionService sleeConnectionService;

	private final SleeContainerDeployer deployer;

	private JndiManagement jndiManagement;

	private ServiceController serviceController;

	/**
	 * Creates a new instance of SleeContainer -- This is called from the
	 * SleeManagementMBean to get the whole thing running.
	 *
	 */
	public SleeContainer(
			String deployPath, ServiceController serviceController, MBeanServer mBeanServer,
			ComponentManagement componentManagement,
			SbbManagement sbbManagement,
			ServiceManagement serviceManagement,
			ResourceManagement resourceManagement,
			ProfileManagement profileManagement,
			EventContextFactory eventContextFactory,
			EventRouter eventRouter,
			TimerFacility timerFacility,
			ActivityContextFactory activityContextFactory,
			ActivityContextNamingFacility activityContextNamingFacility,
			NullActivityContextInterfaceFactory nullActivityContextInterfaceFactory,
			NullActivityFactory nullActivityFactory,
			RmiServerInterface rmiServerInterface,
			SleeTransactionManager sleeTransactionManager,
			MobicentsCluster cluster,
			ReplicationClassLoader replicationClassLoader,
			AlarmManagement alarmMBeanImpl,
			TraceManagement traceMBeanImpl,
			UsageParametersManagement usageParametersManagement,
			SbbEntityFactory sbbEntityFactory, CongestionControl congestionControl,
			SleeConnectionService sleeConnectionService, SleeContainerDeployer sleeContainerDeployer) throws Exception {
        this.deployPath = deployPath;

		this.serviceController = serviceController;

        this.mbeanServer = mBeanServer;

		this.sleeTransactionManager = sleeTransactionManager;
		addModule(sleeTransactionManager);

		this.componentManagement = componentManagement;
		addModule(componentManagement);

		this.replicationClassLoader = replicationClassLoader;

		this.cluster = cluster;

		this.uuidGenerator = new MobicentsUUIDGenerator(cluster
				.getMobicentsCache().isLocalMode());

		this.alarmMBeanImpl = alarmMBeanImpl;
		addModule(alarmMBeanImpl);

		this.traceMBeanImpl = traceMBeanImpl;
		addModule(traceMBeanImpl);

		this.usageParametersManagement = usageParametersManagement;
		addModule(usageParametersManagement);

		this.activityContextFactory = activityContextFactory;
		addModule(activityContextFactory);

		this.activityContextNamingFacility = activityContextNamingFacility;
		addModule(activityContextNamingFacility);

		this.nullActivityFactory = nullActivityFactory;
		addModule(nullActivityFactory);

		this.nullActivityContextInterfaceFactory = nullActivityContextInterfaceFactory;
		addModule(nullActivityContextInterfaceFactory);

		this.timerFacility = timerFacility;
		addModule(timerFacility);

		this.eventContextFactory = eventContextFactory;
		addModule(eventContextFactory);

		this.router = eventRouter;
		addModule(router);

		this.rmiServerInterfaceMBean = rmiServerInterface;
		addModule(rmiServerInterface);

		this.nonClusteredScheduler = new ScheduledThreadPoolExecutor(
				NON_CLUSTERED_SCHEDULER_THREADS);

		this.sbbEntityFactory = sbbEntityFactory;
		addModule(sbbEntityFactory);

		this.congestionControl = congestionControl;
		addModule(congestionControl);

		this.sleeConnectionService = sleeConnectionService;
		addModule(sleeConnectionService);

		// these must be the last ones to be notified of startup, and in this
		// order
		this.resourceManagement = resourceManagement;
		addModule(resourceManagement);

		this.sbbManagement = sbbManagement;
		addModule(sbbManagement);

		this.sleeProfileTableManager = profileManagement;
		addModule(sleeProfileTableManager);

		this.serviceManagement = serviceManagement;
		addModule(serviceManagement);

		this.deployer = sleeContainerDeployer;
		addModule(sleeContainerDeployer);

	}

	private void addModule(SleeContainerModule module) {
		if(module != null) {
			modules.add(module);
			module.setSleeContainer(this);
		}
	}

	// GETTERS -- managers

	public JndiManagement getJndiManagement() {
		return this.jndiManagement;
	}

	public void setJndiManagement(JndiManagement jndiManagement) {
		this.jndiManagement = jndiManagement;
	}

	public ServiceController getServiceController() {
		return this.serviceController;
	}

	/**
	 * dumps the container state as a string, useful for debug/profiling
	 *
	 * @return
	 */
	public String dumpState() {
		return componentManagement + "\n" + resourceManagement + "\n"
				+ timerFacility + "\n" + traceMBeanImpl + "\n"
				+ sleeProfileTableManager + "\n" + activityContextFactory
				+ "\n" + activityContextNamingFacility + "\n"
				+ nullActivityFactory + "\n"
				+ getEventRouter() + "\n"
				+ getEventContextFactory() + "\n"
				+ getTransactionManager() + "\n";
				//+ cluster.getMobicentsCache().getCacheContent();
	}

	public ActivityContextFactory getActivityContextFactory() {
		return this.activityContextFactory;
	}

	public ActivityContextNamingFacility getActivityContextNamingFacility() {
		return activityContextNamingFacility;
	}

	/**
	 *
	 * @return
	 */
	public AlarmManagement getAlarmManagement() {
		return alarmMBeanImpl;
	}

	/**
	 * The cache which manages the container's HA and FT data
	 *
	 * @return
	 */
	public MobicentsCluster getCluster() {
		return cluster;
	}

	/**
	 *
	 * @return
	 */
	public SleeContainerDeployer getDeployer() {
		return deployer;
	}

	/**
	 * @return the componentManagement
	 */
	public ComponentManagement getComponentManagement() {
		return componentManagement;
	}

	/**
	 * retrieves the container's component repository implementation
	 *
	 * @return
	 */
	public ComponentRepository getComponentRepository() {
		return componentManagement.getComponentRepository();
	}

	/**
	 *
	 * @return
	 */
	public CongestionControl getCongestionControl() {
		return congestionControl;
	}

	/**
	 * Retrieves the deployable unit manager
	 *
	 * @return
	 */
	public DeployableUnitManagement getDeployableUnitManagement() {
		return componentManagement.getDeployableUnitManagement();
	}

	/**
	 * @return the eventContextFactory
	 */
	public EventContextFactory getEventContextFactory() {
		return eventContextFactory;
	}

	/**
	 * the container's event router
	 */
	public EventRouter getEventRouter() {
		return this.router;
	}

	/**
	 * object for synchronization on management operations that (un)install
	 * components
	 */
	public Object getManagementMonitor() {
		return managementMonitor;
	}

	// GETTERS -- slee factories

	/**
	 * Return the MBeanServer that the SLEEE is registers with in the current
	 * JVM
	 */
	public MBeanServer getMBeanServer() {
		return mbeanServer;
	}

	/**
	 * Retrieves the container's non clustered scheduler.
	 *
	 * @return the nonClusteredScheduler
	 */
	public ScheduledExecutorService getNonClusteredScheduler() {
		return nonClusteredScheduler;
	}

	public NullActivityContextInterfaceFactory getNullActivityContextInterfaceFactory() {
		return this.nullActivityContextInterfaceFactory;
	}

	/**
	 *
	 * @return
	 */
	public SleeConnectionService getSleeConnectionService() {
		return this.sleeConnectionService;
	}

	// GETTERS -- slee facilities

	public NullActivityFactory getNullActivityFactory() {
		return this.nullActivityFactory;
	}

	/**
	 * Retrieves the class loader used in data replication.
	 *
	 * @return
	 */
	public ReplicationClassLoader getReplicationClassLoader() {
		return replicationClassLoader;
	}

	/**
	 * manages (un)install of resource adaptors
	 *
	 * @return
	 */
	public ResourceManagement getResourceManagement() {
		return resourceManagement;
	}

	/**
	 * @return the sbbEntityFactory
	 */
	public SbbEntityFactory getSbbEntityFactory() {
		return sbbEntityFactory;
	}

	// GETTERS -- slee runtime

	/**
	 * manages (un)install of sbbs
	 *
	 * @return
	 */
	public SbbManagement getSbbManagement() {
		return sbbManagement;
	}

	/**
	 * manages (un)install of services
	 *
	 * @return
	 */
	public ServiceManagement getServiceManagement() {
		return this.serviceManagement;
	}

	public ProfileManagement getSleeProfileTableManager() {
		return this.sleeProfileTableManager;
	}

	/**
	 * Get the current state of the Slee Container
	 *
	 * @return SleeState
	 */
	public SleeState getSleeState() {
		return this.sleeState;
	}

	/**
	 *
	 * @return
	 */
	public TimerFacility getTimerFacility() {
		return timerFacility;
	}

	/**
	 *
	 * @return
	 */
	public TraceManagement getTraceManagement() {
		return traceMBeanImpl;
	}

	/**
	 * Get the transaction manager
	 *
	 * @throws
	 */
	public SleeTransactionManager getTransactionManager() {
		return sleeTransactionManager;
	}

	// JNDI RELATED

	/**
	 *
	 * @return
	 */
	public UsageParametersManagement getUsageParametersManagement() {
		return usageParametersManagement;
	}

	/**
	 * a UUID generator for the container
	 */
	public MobicentsUUIDGenerator getUuidGenerator() {
		return uuidGenerator;
	}

	/**
	 * Initiates the SLEE container
	 */
	public void initSlee() throws InvalidStateException {
		if (sleeState != null) {
			throw new InvalidStateException("slee in "+sleeState+" state");
		}
		// slee init
		beforeModulesInitialization();
		for (Iterator<SleeContainerModule> i = modules.iterator(); i
			.hasNext();) {
			i.next().sleeInitialization();
		}
		afterModulesInitialization();
		sleeState = SleeState.STOPPED;
	}

	/**
	 * Shutdown of the SLEE container
	 * @throws InvalidStateException
	 */
	public void shutdownSlee() throws InvalidStateException {
		if (sleeState != SleeState.STOPPED) {
			throw new InvalidStateException("slee in "+sleeState+" state");
		}
		// slee shutdown
		beforeModulesShutdown();
		for (Iterator<SleeContainerModule> i = modules
				.descendingIterator(); i.hasNext();) {
			i.next().sleeShutdown();
		}
		afterModulesShutdown();
		sleeState = null;
	}

    /**
     *
     * @param request
     * @throws InvalidStateException
     */
	public void setSleeState(final SleeStateChangeRequest request) throws InvalidStateException {

		final SleeState newState = request.getNewState();

		if (logger.isDebugEnabled()) {
			logger.debug("Changing state: " + sleeState + " -> " + newState);
		}

		validateStateTransition(sleeState, newState);

		// change state
		SleeState oldState = this.sleeState;
		this.sleeState = newState;
		request.stateChanged(oldState);

		// notify modules and complete request
		final Runnable task = new Runnable() {
			public void run() {
				try {
					if (newState == SleeState.STARTING) {
						for (Iterator<SleeContainerModule> i = modules.iterator(); i.hasNext();) {
							i.next().sleeStarting();
						}
					}
					else if (newState == SleeState.RUNNING) {
						for (Iterator<SleeContainerModule> i = modules.iterator(); i.hasNext();) {
							i.next().sleeRunning();
						}
					}
					else if (newState == SleeState.STOPPING) {
						for (Iterator<SleeContainerModule> i = modules.descendingIterator(); i.hasNext();) {
							i.next().sleeStopping();
						}
					}
					else if (newState == SleeState.STOPPED) {
						if(logger.isInfoEnabled()) {
							logger.info(dumpState());
						}
						for (Iterator<SleeContainerModule> i = modules.descendingIterator(); i.hasNext();) {
							i.next().sleeStopped();
						}
					}
				}
				catch (Throwable e) {
					logger.error(e.getMessage(),e);
				}
				request.requestCompleted();
			}
		};
		if (request.isBlockingRequest()) {
			task.run();
		}
		else {
			final ExecutorService executorService = Executors.newSingleThreadExecutor();
			try {
				executorService.submit(task);
			}
			catch (Throwable e) {
				logger.error(e.getMessage(),e);
			}
			executorService.shutdown();
		}
	}

	/**
	 * Ensures the standard SLEE lifecycle.
	 *
	 * @param oldState
	 * @param newState
	 * @throws InvalidStateException
	 */
	private void validateStateTransition(SleeState oldState, SleeState newState)
			throws InvalidStateException {
		if (oldState == SleeState.STOPPED) {
			if (newState == SleeState.STARTING) {
				return;
			}
		} else if (oldState == SleeState.STARTING) {
			if (newState == SleeState.RUNNING || newState == SleeState.STOPPING) {
				return;
			}
		} else if (oldState == SleeState.RUNNING) {
			if (newState == SleeState.STOPPING) {
				return;
			}
		} else if (oldState == SleeState.STOPPING) {
			if (newState == SleeState.STOPPED) {
				return;
			}
		}
		throw new InvalidStateException("illegal slee state transition: " + oldState + " -> "+ newState);
	}

	public void beforeModulesInitialization() {
		sleeContainer = this;
		// Register property editors for the composite SLEE types so
		// jmx console can pass it as an argument.
		// TODO: ensure this is of any use for all containers, i.e. standard jdk jmx console or our web console takes advantage from it, if this was just a jboss as5 feature then let's move it to "build" modules
		new SleePropertyEditorRegistrator().register();
	}

	public void afterModulesInitialization() {
		// start cluster
		cluster.startCluster();
	}

	public void beforeModulesShutdown() {
		// stop the cluster
		cluster.stopCluster();
	}

	public void afterModulesShutdown() {
		/*
		try {
			unregisterWithJndi();
			Context ctx = new InitialContext();
			Util.unbind(ctx, JVM_ENV + CTX_SLEE);
		}
		catch (Exception e) {
			throw new RuntimeException(e.getMessage(),e);
		}*/
	}

	/**
	 * Return the SleeContainer instance registered in the JVM scope of JNDI
	 */
	public static SleeContainer lookupFromJndi() {
		return sleeContainer;
	}

}


/*
 * 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 org.apache.log4j.Logger;

/**
 * @author martins
 *
 */
public class EventRouterConfiguration implements
		EventRouterConfigurationMBean {

	private static final Logger logger = Logger
			.getLogger(EventRouterConfiguration.class);

	private Integer eventRouterThreads;
	private String executorMapperClassName;
	private Boolean collectStats;
	private boolean confirmSbbEntityAttachement;

	@Override
	public boolean isConfirmSbbEntityAttachement() {
		return confirmSbbEntityAttachement;
	}

	@Override
	public void setConfirmSbbEntityAttachement(boolean value) {
		this.confirmSbbEntityAttachement = value;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#getEventRouterThreads()
	 */
	public int getEventRouterThreads() {
		return eventRouterThreads;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#getExecutorMapperClassName()
	 */
	public String getExecutorMapperClassName() {
		return executorMapperClassName;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#isCollectStats()
	 */
	public boolean isCollectStats() {
		return collectStats;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#setCollectStats(boolean)
	 */
	public void setCollectStats(boolean collectStats) {
		if (this.collectStats != null) {
			logger.warn("Setting collectStats property to "
				+ collectStats
				+ ". If called with server running a stop and start is need to apply changes.");
		}
		this.collectStats = collectStats;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#setEventRouterThreads(int)
	 */
	public void setEventRouterThreads(int value) {
		if (this.eventRouterThreads != null) {
			logger.warn("Setting eventRouterThreads property to "
				+ value
				+ ". If called with server running a stop and start is need to apply changes.");
		}
		this.eventRouterThreads = value;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.management.jmx.EventRouterConfigurationMBean#setExecutorMapperClassName(java.lang.String)
	 */
	public void setExecutorMapperClassName(String className)
			throws ClassNotFoundException {
		Class.forName(className);
		if (this.executorMapperClassName != null) {
			logger
			.warn("Setting executorMapperClassName property to "
					+ className
					+ ". If called with server running a stop and start is need to apply changes.");
		}
		this.executorMapperClassName = className;

	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Event Router Executors: "
			+ eventRouterThreads+", Collect Stats: "+collectStats+", Executor<->Activity Mapper Class: "
			+ executorMapperClassName;
	}
}


/*
 * 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.profile;

import javax.slee.InvalidArgumentException;
import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.facilities.AlarmFacility;
import javax.slee.facilities.Tracer;
import javax.slee.profile.ProfileLocalObject;
import javax.slee.profile.ProfileTable;
import javax.slee.profile.UnrecognizedProfileTableNameException;
import javax.transaction.SystemException;

import org.mobicents.slee.ProfileContextExt;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.runtime.facilities.TracerImpl;

/**
 * Start time:17:11:23 2009-03-13

 * Project: restcomm-jainslee-server-core

 *
 * This class represents ProfileContext. Note that profile context object MUST
 * belong to one profile table during its life time, profile(object) can change.

 * The ProfileContext interface provides a Profile object with access to
 * SLEE-managed state that is dependent on the Profile objects's currently
 * executing context.
 *
 * A ProfileContext object is given to a Profile object after the Profile object
 * is created via the setProfileContext method. The ProfileContext object
 * remains associated with the Profile object for the lifetime of that Profile
 * object. Note that the information that the Profile object obtains from the
 * ProfileContext object may change as the SLEE assigns the Profile object to
 * different profiles during the Profile object's lifecycle.
 *
 * @author  Bartosz Baranowski
 * @author  Alexandre Mendonca
 */
public class ProfileContextImpl implements ProfileContextExt {

	private ProfileTableImpl profileTable = null;
	private ProfileObjectImpl profileObject = null;

	public ProfileContextImpl(ProfileTableImpl profileTable) {
		if (profileTable == null) {
			throw new NullPointerException("Parameters must not be null");
		}

		this.profileTable = profileTable;

	}

	public void setProfileObject(ProfileObjectImpl profileObject) {
		this.profileObject = profileObject;
	}

	/*
	 * (non-Javadoc)
	 * @see javax.slee.profile.ProfileContext#getProfileLocalObject()
	 */
	public ProfileLocalObject getProfileLocalObject()
			throws IllegalStateException, SLEEException {
		// check state
		if (profileObject == null
				|| profileObject.getState() == ProfileObjectState.PROFILE_INITIALIZATION
				|| profileObject.getProfileEntity() == null) {
			throw new IllegalStateException();
		}
		// check if it is default profile
		if (profileObject.getProfileEntity().getProfileName() == null) {
			throw new IllegalStateException();
		}
		return profileObject.getProfileLocalObject();
	}

	/*
	 * (non-Javadoc)
	 * @see javax.slee.profile.ProfileContext#getProfileName()
	 */
	public String getProfileName() throws IllegalStateException, SLEEException {
		doGeneralChecks();

		if (profileObject == null
				|| profileObject.getState() == ProfileObjectState.PROFILE_INITIALIZATION
				|| profileObject.getProfileEntity() == null) {
			throw new IllegalStateException();
		}

		return this.profileObject.getProfileEntity().getProfileName();
	}

	/*
	 * (non-Javadoc)
	 * @see javax.slee.profile.ProfileContext#getProfileTable()
	 */
	public ProfileTable getProfileTable() throws SLEEException {
		doGeneralChecks();
		return this.profileTable;
	}

	/*
	 * (non-Javadoc)
	 * @see javax.slee.profile.ProfileContext#getProfileTable(java.lang.String)
	 */
	public ProfileTable getProfileTable(String profileTableName)
			throws NullPointerException, UnrecognizedProfileTableNameException,
			SLEEException {

		return this.profileTable.getSleeContainer()
				.getSleeProfileTableManager().getProfileTable(profileTableName);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.profile.ProfileContext#getProfileTableName()
	 */
	public String getProfileTableName() throws SLEEException {
		doGeneralChecks();
		try {
			return this.profileTable.getProfileTableName();
		} catch (Exception e) {
			throw new SLEEException("Operaion failed.", e);
		}

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.profile.ProfileContext#getRollbackOnly()
	 */
	public boolean getRollbackOnly() throws TransactionRequiredLocalException,
			SLEEException {
		doGeneralChecks();
		final SleeTransactionManager txMgr = profileTable.getSleeContainer()
				.getTransactionManager();
		txMgr.mandateTransaction();
		try {
			return txMgr.getRollbackOnly();
		} catch (SystemException e) {
			throw new SLEEException("Problem with the tx manager!");
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.profile.ProfileContext#setRollbackOnly()
	 */
	public void setRollbackOnly() throws TransactionRequiredLocalException,
			SLEEException {
		doGeneralChecks();
		final SleeTransactionManager txMgr = profileTable.getSleeContainer()
				.getTransactionManager();
		txMgr.mandateTransaction();
		try {
			txMgr.setRollbackOnly();
		} catch (SystemException e) {
			throw new SLEEException("Problem with the tx manager!");
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.profile.ProfileContext#getTracer(java.lang.String)
	 */
	public Tracer getTracer(String tracerName) throws NullPointerException,
			IllegalArgumentException, SLEEException {
		doGeneralChecks();
		try {
			TracerImpl.checkTracerName(tracerName, this.profileTable
					.getProfileTableNotification().getNotificationSource());
		} catch (InvalidArgumentException e1) {
			throw new IllegalArgumentException(e1);
		}
		try {
			return profileTable.getSleeContainer().getTraceManagement()
					.createTracer(
							this.profileTable.getProfileTableNotification()
									.getNotificationSource(), tracerName, true);
		} catch (Exception e) {
			throw new SLEEException("Failed to obtain tracer", e);
		}
	}

	private void doGeneralChecks() {
		if (this.profileTable == null)
			throw new SLEEException("Profile table has not been set.");
		if (this.profileTable.getProfileTableNotification() == null)
			throw new SLEEException("Profile table has no notification source.");
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.ProfileContextExt#getAlarmFacility()
	 */
	public AlarmFacility getAlarmFacility() {
		return profileTable.getProfileSpecificationComponent().getAlarmFacility();
	}

}


/*
 * 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.component.deployment;

import java.util.Set;

import javax.slee.ComponentID;
import javax.slee.EventTypeID;
import javax.slee.SbbID;
import javax.slee.ServiceID;
import javax.slee.UnrecognizedComponentException;
import javax.slee.management.LibraryID;
import javax.slee.profile.ProfileSpecificationID;
import javax.slee.resource.ResourceAdaptorID;
import javax.slee.resource.ResourceAdaptorTypeID;

import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.SleeComponent;
import org.mobicents.slee.container.component.du.DeployableUnitRepository;
import org.mobicents.slee.container.component.event.EventTypeComponent;
import org.mobicents.slee.container.component.library.LibraryComponent;
import org.mobicents.slee.container.component.profile.ProfileSpecificationComponent;
import org.mobicents.slee.container.component.ra.ResourceAdaptorComponent;
import org.mobicents.slee.container.component.ratype.ResourceAdaptorTypeComponent;
import org.mobicents.slee.container.component.sbb.SbbComponent;
import org.mobicents.slee.container.component.service.ServiceComponent;

/**
 * Extends the component repository, providing access also to the components in a DeployableUnit, those components are not installed yet
 * @author martins
 *
 */
public class DeployableUnitRepositoryImpl implements DeployableUnitRepository {

	private final DeployableUnitImpl deployableUnit;
	private final ComponentRepository componentRepository;

	public DeployableUnitRepositoryImpl(DeployableUnitImpl deployableUnit,ComponentRepository componentRepository) {
		if (deployableUnit == null) {
			throw new NullPointerException("null deployableUnit");
		}
		if (componentRepository == null) {
			throw new NullPointerException("null componentRepository");
		}
		this.deployableUnit = deployableUnit;
		this.componentRepository = componentRepository;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.EventTypeID)
	 */
	public EventTypeComponent getComponentByID(EventTypeID id) {
		// get from repository
		EventTypeComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getEventTypeComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.profile.ProfileSpecificationID)
	 */
	public ProfileSpecificationComponent getComponentByID(
			ProfileSpecificationID id) {
		// get from repository
		ProfileSpecificationComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getProfileSpecificationComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.management.LibraryID)
	 */
	public LibraryComponent getComponentByID(LibraryID id) {
		// get from repository
		LibraryComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getLibraryComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.resource.ResourceAdaptorID)
	 */
	public ResourceAdaptorComponent getComponentByID(ResourceAdaptorID id) {
		// get from repository
		ResourceAdaptorComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getResourceAdaptorComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.resource.ResourceAdaptorTypeID)
	 */
	public ResourceAdaptorTypeComponent getComponentByID(
			ResourceAdaptorTypeID id) {
		// get from repository
		ResourceAdaptorTypeComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getResourceAdaptorTypeComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.SbbID)
	 */
	public SbbComponent getComponentByID(SbbID id) {
		// get from repository
		SbbComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getSbbComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getComponentByID(javax.slee.ServiceID)
	 */
	public ServiceComponent getComponentByID(ServiceID id) {
		// get from repository
		ServiceComponent component = componentRepository.getComponentByID(id);
		if (component == null) {
			// not found in repository, get it from deployable unit
			component = deployableUnit.getServiceComponents().get(id);
		}
		return component;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#isInstalled(javax.slee.ComponentID)
	 */
	public boolean isInstalled(ComponentID componentID) {
		return componentRepository.isInstalled(componentID);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getEventComponentIDs()
	 */
	public Set<EventTypeID> getEventComponentIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getLibraryIDs()
	 */
	public Set<LibraryID> getLibraryIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getProfileSpecificationIDs()
	 */
	public Set<ProfileSpecificationID> getProfileSpecificationIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getReferringComponents(org.mobicents.slee.core.component.SleeComponent)
	 */
	public Set<SleeComponent> getReferringComponents(
			SleeComponent component) {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getResourceAdaptorIDs()
	 */
	public Set<ResourceAdaptorID> getResourceAdaptorIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getResourceAdaptorTypeIDs()
	 */
	public Set<ResourceAdaptorTypeID> getResourceAdaptorTypeIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getSbbIDs()
	 */
	public Set<SbbID> getSbbIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getServiceIDs()
	 */
	public Set<ServiceID> getServiceIDs() {
		throw new UnsupportedOperationException("not implemented yet");
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#getReferringComponents(javax.slee.ComponentID)
	 */
	public ComponentID[] getReferringComponents(ComponentID componentID)
			throws NullPointerException, UnrecognizedComponentException {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.event.EventTypeComponent)
	 */
	public boolean putComponent(EventTypeComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.library.LibraryComponent)
	 */
	public boolean putComponent(LibraryComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.profile.ProfileSpecificationComponent)
	 */
	public boolean putComponent(ProfileSpecificationComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.ra.ResourceAdaptorComponent)
	 */
	public boolean putComponent(ResourceAdaptorComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.ratype.ResourceAdaptorTypeComponent)
	 */
	public boolean putComponent(ResourceAdaptorTypeComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.sbb.SbbComponent)
	 */
	public boolean putComponent(SbbComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#putComponent(org.mobicents.slee.core.component.service.ServiceComponent)
	 */
	public boolean putComponent(ServiceComponent component) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.EventTypeID)
	 */
	public void removeComponent(EventTypeID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.management.LibraryID)
	 */
	public void removeComponent(LibraryID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.profile.ProfileSpecificationID)
	 */
	public void removeComponent(ProfileSpecificationID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.resource.ResourceAdaptorID)
	 */
	public void removeComponent(ResourceAdaptorID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.resource.ResourceAdaptorTypeID)
	 */
	public void removeComponent(ResourceAdaptorTypeID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.SbbID)
	 */
	public void removeComponent(SbbID componentID) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.component.ComponentRepository#removeComponent(javax.slee.ServiceID)
	 */
	public void removeComponent(ServiceID componentID) {
		throw new UnsupportedOperationException();
	}

}


/*
 * 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.deployment.profile.jpa;

import java.beans.Introspector;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.CtPrimitiveType;
import javassist.NotFoundException;

import javax.slee.Address;
import javax.slee.SLEEException;
import javax.slee.profile.Profile;
import javax.slee.profile.ProfileManagement;

import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.ClassPool;
import org.mobicents.slee.container.component.profile.ProfileAttribute;
import org.mobicents.slee.container.component.profile.ProfileConcreteClassInfo;
import org.mobicents.slee.container.component.profile.ProfileSpecificationComponent;
import org.mobicents.slee.container.component.profile.ProfileSpecificationDescriptor;
import org.mobicents.slee.container.component.profile.cmp.ProfileCMPInterfaceDescriptor;
import org.mobicents.slee.container.deployment.ClassUtils;
import org.mobicents.slee.container.deployment.profile.ClassGeneratorUtils;
import org.mobicents.slee.container.deployment.profile.SleeProfileClassCodeGenerator;
import org.mobicents.slee.container.profile.ProfileCmpHandler;
import org.mobicents.slee.container.profile.ProfileConcrete;
import org.mobicents.slee.container.profile.ProfileObjectImpl;
import org.mobicents.slee.container.util.ObjectCloner;

/**
 *
 * Generates the ProfileConcrete impl for a specific Profile Specification.
 *
 *

 * Project: restcomm

 * 11:16:57 AM Mar 23, 2009

 *
 * @author  Alexandre Mendonca
 * @author  Bartosz Baranowski
 */
@SuppressWarnings("deprecation")
public class ConcreteProfileGenerator {

	private static final Logger logger = Logger
			.getLogger(ConcreteProfileGenerator.class);

	private final ProfileSpecificationComponent profileComponent;

	private final int profileCombination;

	public ConcreteProfileGenerator(
			ProfileSpecificationComponent profileComponent) {
		this.profileComponent = profileComponent;
		this.profileCombination = SleeProfileClassCodeGenerator
				.checkCombination(profileComponent);
		ClassGeneratorUtils.setClassPool(this.profileComponent.getClassPool()
				.getClassPool());
	}

	@SuppressWarnings("unchecked")
	public Class<?> generateConcreteProfile() {

		Class<?> clazz = null;

		try {
			/*
			 * 10.12 Profile concrete class A Profile concrete class is
			 * implemented by the SLEE when a Profile Specification is deployed.
			 * The Profile concrete class extends the Profile abstract class and
			 * implements the Profile CMP methods.
			 *
			 * The following rules apply to the Profile concrete class
			 * implemented by the SLEE: - If a Profile abstract class is
			 * defined, then the SLEE implemented Profile concrete class extends
			 * the Profile abstract class and implements the Profile CMP
			 * methods.
			 *
			 * - If a Profile abstract class is not defined, then the SLEE
			 * implemented Profile concrete class provides an implementation of
			 * the Profile CMP interface.
			 */

			ProfileSpecificationDescriptor profileDescriptor = profileComponent
					.getDescriptor();

			if (logger.isTraceEnabled())
				logger.trace("Profile combination for "
					+ profileComponent.getProfileSpecificationID() + " = "
					+ this.profileCombination);

			String deployDir = profileComponent.getDeploymentDir()
					.getAbsolutePath();

			ProfileCMPInterfaceDescriptor cmpInterface = profileDescriptor
					.getProfileCMPInterface();

			String concreteClassName = cmpInterface
					.getProfileCmpInterfaceName()
					+ "Impl";

			// Create the Impl class
			CtClass profileConcreteClass = ClassGeneratorUtils.createClass(
					concreteClassName, new String[] {
							cmpInterface.getProfileCmpInterfaceName(),
							ProfileConcrete.class.getName() });

			// If this is combination 3 or 4, the the concrete class extends the
			// Concrete Profile Management Abstract Class
			if (profileCombination >= 3) {
				if (profileDescriptor.getProfileAbstractClass() != null) {
					ClassGeneratorUtils.createInheritanceLink(
							profileConcreteClass, profileDescriptor
									.getProfileAbstractClass()
									.getProfileAbstractClassName());
				}

				if (profileDescriptor.getProfileManagementInterface() != null) {
					ClassGeneratorUtils.createInterfaceLinks(
							profileConcreteClass,
							new String[] { profileDescriptor
									.getProfileManagementInterface() });
				}
			}

			generateProfileConcreteClassInfo(profileConcreteClass);

			// add profile object field and getter/setter
			CtField fProfileObject = ClassGeneratorUtils
					.addField(ClassGeneratorUtils.getClass(ProfileObjectImpl.class
							.getName()), "profileObject", profileConcreteClass);
			ClassGeneratorUtils.generateGetterAndSetter(fProfileObject, null);

			// CMP fields getters and setters
			generateCMPAccessors(profileConcreteClass);

			generateConstructors(profileConcreteClass);

			// Profile Management methods for JAIN SLEE 1.1
			Map<String, CtMethod> profileManagementMethods = new HashMap<String, CtMethod>();

			// only add Profile Management methods for JAIN SLEE 1.0 that are implemented by SLEE
			for (Object object : ClassUtils
					.getInterfaceMethodsFromInterface(ClassGeneratorUtils
							.getClass(ProfileManagement.class.getName())).entrySet()) {
				Entry entry = (Entry) object;
				CtMethod ctMethod = (CtMethod) entry.getValue();
				if (ctMethod.getName().equals("markProfileDirty") || ctMethod.getName().equals("isProfileDirty") || ctMethod.getName().equals("isProfileValid")) {
					profileManagementMethods.put((String) entry.getKey(),ctMethod);
				}
			}

			// Check for a Profile Management Interface
			Class<?> profileManagementInterface = this.profileComponent
					.getProfileManagementInterfaceClass();

			if (profileManagementInterface != null) {
				profileManagementMethods
						.putAll(ClassUtils
								.getInterfaceMethodsFromInterface(ClassGeneratorUtils
										.getClass(profileManagementInterface
												.getName())));
			}

			Class<?> profileLocalInterface = this.profileComponent
					.getProfileLocalInterfaceClass();

			if (profileLocalInterface != null) {
				profileManagementMethods.putAll(ClassUtils
						.getAbstractMethodsFromClass((ClassGeneratorUtils
								.getClass(profileLocalInterface.getName()))));
			}

			Map<String, CtMethod> cmpInterfaceMethods = ClassUtils
					.getInterfaceMethodsFromInterface(ClassGeneratorUtils
							.getClass(this.profileComponent
									.getProfileCmpInterfaceClass().getName()));
			generateManagementHandlerDelegationMethods(profileConcreteClass,
					profileManagementMethods, cmpInterfaceMethods);

			if (profileComponent.getUsageParametersInterface() != null) {
				generateDefaultUsageParameterGetter(profileConcreteClass);
				generateNamedUsageParameterGetter(profileConcreteClass);
			}

			profileConcreteClass.getClassFile().setVersionToJava5();

			if (logger.isTraceEnabled())
				logger.trace("Writing PROFILE CONCRETE CLASS to: " + deployDir);

			profileConcreteClass.writeFile(deployDir);

			clazz = Thread.currentThread().getContextClassLoader().loadClass(
					profileConcreteClass.getName());

			profileConcreteClass.defrost();

		} catch (Exception e) {
			throw new SLEEException(e.getMessage(), e);
		}

		return clazz;
	}

	private void generateConstructors(CtClass profileConcreteClass) {
		ClassGeneratorUtils.generateDefaultConstructor(profileConcreteClass);
	}

	/**
	 * Create a named usage parameter getter.
	 *
	 * @param profileConcreteClass
	 * @throws SLEEException
	 */
	private void generateNamedUsageParameterGetter(CtClass profileConcreteClass) {
		String methodName = "getUsageParameterSet";
		for (CtMethod ctMethod : profileConcreteClass.getMethods()) {
			if (ctMethod.getName().equals(methodName)) {
				try {
					// copy method, we can't just add body becase it is in super
					// class and does not sees profileObject field
					CtMethod ctMethodCopy =  CtNewMethod.copy(ctMethod, profileConcreteClass, null);
					// create the method body
					String methodBody = "{ return ($r)"
						+ ClassGeneratorUtils.MANAGEMENT_HANDLER
						+ ".getUsageParameterSet(profileObject,$1); }";
					if (logger.isTraceEnabled())
						logger.trace("Implemented method " + methodName
								+ " , body = " + methodBody);
					ctMethodCopy.setBody(methodBody);
					profileConcreteClass.addMethod(ctMethodCopy);
				} catch (CannotCompileException e) {
					throw new SLEEException(e.getMessage(), e);
				}
			}
		}
	}

	private void generateDefaultUsageParameterGetter(
			CtClass profileConcreteClass) {
		String methodName = "getDefaultUsageParameterSet";
		for (CtMethod ctMethod : profileConcreteClass.getMethods()) {
			if (ctMethod.getName().equals(methodName)) {
				try {
					// copy method, we can't just add body becase it is in super
					// class and does not sees profileObject field
					CtMethod ctMethodCopy =  CtNewMethod.copy(ctMethod, profileConcreteClass, null);
					// create the method body
					String methodBody = "{ return ($r)"
						+ ClassGeneratorUtils.MANAGEMENT_HANDLER
						+ ".getDefaultUsageParameterSet(profileObject); }";
					if (logger.isTraceEnabled())
						logger.trace("Implemented method " + methodName
								+ " , body = " + methodBody);
					ctMethodCopy.setBody(methodBody);
					profileConcreteClass.addMethod(ctMethodCopy);
				} catch (CannotCompileException e) {
					throw new SLEEException(e.getMessage(), e);
				}
			}
		}
	}

	private void generateManagementHandlerDelegationMethods(CtClass profileConcreteClass,
			Map<String, CtMethod> methods,
			Map<String, CtMethod> cmpInterfaceMethods) {
		// boolean useInterceptor = true;
		Class<?> abstractClass = this.profileComponent
				.getProfileAbstractClass();

		Iterator<Map.Entry<String, CtMethod>> mm = methods.entrySet()
				.iterator();

		Set<String> implementedMethods = new HashSet<String>();
		implementedMethods.addAll(cmpInterfaceMethods.keySet());

		while (mm.hasNext()) {
			String interceptor = ClassGeneratorUtils.MANAGEMENT_HANDLER;

			Map.Entry<String, CtMethod> entry = mm.next();

			CtMethod method = entry.getValue();

			// We should use key, but ClassUtils has different behaviors... go
			// safe!
			String methodKey = method.getName() + method.getSignature();

			if (!implementedMethods.add(methodKey)) {
				// This was already implemented
				continue;
			}

			if (abstractClass != null) {
				try {
					int i = 0;
					Class<?>[] pTypes = new Class[method.getParameterTypes().length];

					for (CtClass pType : method.getParameterTypes()) {
						if (pType.isPrimitive())
							pTypes[i++] = ((Class<?>) Class.forName(
									((CtPrimitiveType) pType).getWrapperName())
									.getField("TYPE").get(null));
						else if (pType.isArray())
							pTypes[i++] = org.apache.commons.lang.ClassUtils.getClass(pType.getName());
						else
							pTypes[i++] = Class.forName(pType.getClassFile()
									.getName());
					}

					Method m = abstractClass
							.getMethod(method.getName(), pTypes);
					interceptor = Modifier.isAbstract(m.getModifiers()) ? interceptor
							: "super";
				} catch (Exception e) {
					if (!(e instanceof NoSuchMethodException))
						throw new SLEEException(
								"Problem with Business method generation: "
										+ method.getName(), e);

					// else ignore... we are using default interceptor.
				}
			}

			try {
				ClassGeneratorUtils.generateDelegateMethod(
						profileConcreteClass,method, interceptor,
						true);
			} catch (Exception e) {
				throw new SLEEException(e.getMessage(), e);
			}
		}
	}

	private void generateCMPAccessors(CtClass profileConcreteClass)
			throws Exception {

		// Get the CMP interface to generate the getters/setters
		ProfileCMPInterfaceDescriptor cmpInterface = profileComponent.getDescriptor()
				.getProfileCMPInterface();

		ClassPool pool = profileComponent.getClassPool();

		CtClass cmpInterfaceClass = pool.get(cmpInterface
				.getProfileCmpInterfaceName());
		CtClass objectClass = pool.get(Object.class.getName());

		for (CtMethod method : cmpInterfaceClass.getMethods()) {

			if (!method.getDeclaringClass().equals(objectClass)) {
				// ignoring methods from Object class

				if (method.getName().startsWith("get")) {
					generateCMPGetter(method, profileConcreteClass);
				} else if (method.getName().startsWith("set")) {
					generateCMPSetter(method, profileConcreteClass);
				} else {
					throw new SLEEException(
							"unexpected method name in cmp interface "
									+ method.getName());
				}
			}
		}
	}

	private boolean isPrimitiveOrPrimitiveArray(CtClass ctClass) {
		if (ctClass.isArray()) {
			try {
				return ctClass.getComponentType().isPrimitive();
			} catch (NotFoundException e) {
				throw new SLEEException(e.getMessage(), e);
			}
		} else {
			return ctClass.isPrimitive();
		}
	}

	public boolean isNumericOrStringArray(CtClass ctClass) {
		boolean result = false;
		if (ctClass.isArray()) {
			try {
				String typeName = ctClass.getComponentType().getName();
				result = typeName.equals(Boolean.class.getName()) ||
						typeName.equals(Character.class.getName()) ||
						typeName.equals(Byte.class.getName()) ||
						typeName.equals(Short.class.getName()) ||
						typeName.equals(Integer.class.getName()) ||
						typeName.equals(Long.class.getName()) ||
						typeName.equals(Float.class.getName()) ||
						typeName.equals(Double.class.getName()) ||
						typeName.equals(String.class.getName());
			} catch (NotFoundException e) {
			}
		}

		return result;
	}

	private void generateCMPGetter(CtMethod method,
			CtClass classToBeInstrumented) throws Exception {

		// see issue #23: [Profiles] getting correct fieldName from method of ProfileCMP interface
		String fieldName = ClassGeneratorUtils.decapitalize(method.getName()
				.replaceFirst("get", ""));

		boolean isPrimitive = isPrimitiveOrPrimitiveArray(method
				.getReturnType());

		// 1. invoke profile entity getter
		String profileEntityGetterInvocationBody = "(("
				+ profileComponent.getProfileEntityFramework()
						.getProfileEntityClass().getName()
				+ ")profileObject.getProfileEntity()).get"
				+ ClassGeneratorUtils.getPojoCmpAccessorSufix(fieldName) + "()";

		// 2. create the open and close "return" code - do a deep copy on the
		// result if the return type is not a primitive, this ensures no refs
		// with original value
		String returnOpenBody = "return ($r) "
				+ (!isPrimitive ? ObjectCloner.class.getName()
						+ ".makeDeepCopy(" : "");
		String returnCloseBody = !isPrimitive ? ");" : ";";

		// 3. lets add the code between the profile entity getter invocation and
		// the return clause - if the return type is an array then we need to
		// convert it from the attr array value list
		String methodBody = null;
		if (method.getReturnType().isArray()) {
			if (isPrimitive || isNumericOrStringArray(method.getReturnType())) {
				methodBody = returnOpenBody
						+ ProfileAttributeArrayValueUtils.class.getName()
						+ ".to"
						+ method.getReturnType().getComponentType()
								.getSimpleName() + "Array("
						+ profileEntityGetterInvocationBody + ")"
						+ returnCloseBody;
			} else {
				methodBody = List.class.getName() + " list = "
						+ profileEntityGetterInvocationBody + ";"
						+ returnOpenBody
						+ ProfileAttributeArrayValueUtils.class.getName()
						+ ".toSerializableArray( new "
						+ method.getReturnType().getComponentType().getName()
						+ "[list.size()] , list)" + returnCloseBody;
			}
		} else {
			methodBody = returnOpenBody + profileEntityGetterInvocationBody
					+ returnCloseBody;
		}
		;
		// add final method wrappers
		methodBody = "{" + ProfileCmpHandler.class.getName()
				+ ".beforeGetCmpField(profileObject);" + "	try { " + methodBody
				+ " } finally { " + ProfileCmpHandler.class.getName()
				+ ".afterGetCmpField(profileObject);	}" + "}";

		if (logger.isTraceEnabled()) {
			logger.trace("Adding method named " + method.getName()
					+ ", with source : " + methodBody + ", into: "
					+ classToBeInstrumented);
		}

		CtMethod methodCopy = CtNewMethod.copy(method, classToBeInstrumented,
				null);
		methodCopy.setBody(methodBody);
		classToBeInstrumented.addMethod(methodCopy);

	}

	private void generateCMPSetter(CtMethod method,
			CtClass classToBeInstrumented) throws Exception {

		// see issue #23: [Profiles] getting correct fieldName from method of ProfileCMP interface
		String fieldName = ClassGeneratorUtils.decapitalize(method.getName()
				.replaceFirst("set", ""));

		ProfileAttribute profileAttribute = profileComponent
				.getProfileAttributes().get(fieldName);

		JPAProfileEntityFramework profileEntityFramework = (JPAProfileEntityFramework) profileComponent
				.getProfileEntityFramework();

		String pojoCmpAccessorSufix = ClassGeneratorUtils
				.getPojoCmpAccessorSufix(fieldName);

		// define the string of the object to store in the profile entity
		// if it is not a primitive do a deep copy of the object
		String objectToStore = !profileAttribute.isPrimitive() ? "("
				+ method.getParameterTypes()[0].getName() + ")"
				+ ObjectCloner.class.getName() + ".makeDeepCopy($1)" : "$1";
		// if it is an array convert it to a list
		if (profileAttribute.getType().isArray()) {
			objectToStore = ProfileAttributeArrayValueUtils.class.getName()
					+ ".toProfileAttributeArrayValueList( "
					+ profileEntityFramework
							.getProfileEntityArrayAttrValueClassMap().get(
									profileAttribute.getName()).getName()
					+ ".class , profileEntity, profileEntity.get"
					+ pojoCmpAccessorSufix + "() , "
					+ (profileAttribute.isUnique() ? true : false) + " , "
					+ objectToStore + ")";
		}

		String methodBody = "{" + ProfileCmpHandler.class.getName()
				+ ".beforeSetCmpField(profileObject);" + "	try {" + " "
				+ profileEntityFramework.getProfileEntityClass().getName()
				+ " profileEntity = ("
				+ profileEntityFramework.getProfileEntityClass().getName()
				+ ")profileObject.getProfileEntity();"
				+ "	    profileEntity.set" + pojoCmpAccessorSufix + "("
				+ objectToStore + ");" + "	}" + "	finally {"
				+ ProfileCmpHandler.class.getName()
				+ ".afterSetCmpField(profileObject);" + "	};" + "}";

		if (logger.isTraceEnabled()) {
			logger.trace("Adding method named " + method.getName()
					+ ", with source : " + methodBody + ", into: "
					+ classToBeInstrumented);
		}

		CtMethod methodCopy = CtNewMethod.copy(method, classToBeInstrumented,
				null);
		methodCopy.setBody(methodBody);
		classToBeInstrumented.addMethod(methodCopy);

	}

	/**
	 * Generates info that indicates if a method from {@link Profile} interface
	 * should be invoked or not, in runtime. Note that all methods from {@link ProfileManagement}
	 * that we are interested are all contained in {@link Profile} interface.
	 */
	private void generateProfileConcreteClassInfo(CtClass profileConcreteClass) {

		final ClassPool pool = profileComponent.getClassPool();

		CtClass profileClass = null;
		try {
			profileClass = pool.get(Profile.class.getName());
		} catch (NotFoundException e) {
			throw new SLEEException(e.getMessage(), e);
		}

		ProfileConcreteClassInfo profileConcreteClassInfo = profileComponent.getProfileConcreteClassInfo();

		for (CtMethod method : profileClass.getDeclaredMethods()) {
			for (CtMethod profileConcreteMethod : profileConcreteClass
					.getMethods()) {
				if (profileConcreteMethod.getName().equals(
						method.getName())
						&& profileConcreteMethod.getSignature().equals(
								method.getSignature())) {
					// match, save info
					profileConcreteClassInfo.setInvokeInfo(profileConcreteMethod
							.getMethodInfo().getName(), !profileConcreteMethod
							.isEmpty());
					break;
				}
			}
		}
	}
}


/*
 * 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.runtime.facilities.nullactivity;

import javax.slee.ActivityContextInterface;
import javax.slee.FactoryException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.UnrecognizedActivityException;
import javax.slee.nullactivity.NullActivity;

import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.activity.ActivityContext;
import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.facilities.nullactivity.NullActivityContextInterfaceFactory;

/**
 *Implementation of null activity context interface.
 *
 *@author M. Ranganathan
 *@author martins
 */
public class NullActivityContextInterfaceFactoryImpl extends AbstractSleeContainerModule implements
        NullActivityContextInterfaceFactory {

	@Override
	public void sleeInitialization() {
	}

    /* (non-Javadoc)
     * @see javax.slee.nullactivity.NullActivityContextInterfaceFactory#getActivityContextInterface(javax.slee.nullactivity.NullActivity)
     */
    public ActivityContextInterface getActivityContextInterface(
            NullActivity nullActivity) throws NullPointerException,
            TransactionRequiredLocalException, UnrecognizedActivityException,
            FactoryException {

    	if (nullActivity == null )
             throw new NullPointerException ("null NullActivity ! huh!!");

        if (! (nullActivity instanceof NullActivityImpl))
            throw new UnrecognizedActivityException ("unrecognized activity");

        NullActivityImpl nullActivityImpl = (NullActivityImpl) nullActivity;
        ActivityContextHandle ach = new NullActivityContextHandle(nullActivityImpl.getHandle());
        ActivityContext ac = sleeContainer.getActivityContextFactory().getActivityContext(ach);
        if (ac == null) {
        	throw new UnrecognizedActivityException(nullActivity);
        }
        return ac.getActivityContextInterface();

    }

}


/*
 * Copyright 2013 Adam Dubiel, Przemek Hertel.
 *
 * 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 org.polyjdbc.core.query;

import java.util.List;
import java.util.Set;
import org.polyjdbc.core.query.mapper.ObjectMapper;

/**
 * Runs simple query in one-time transaction, can be used multiple times, resources are always freed.
 * Internally uses {@link TransactionRunner}. Each time a query is made, new transaction is
 * opened, closed after query execution.
 *
 * @author Adam Dubiel
 */
public class SimpleQueryRunner {

    private final TransactionRunner runner;

    /**
     * Create new runner that will use given query runner factory to create query runners.
     */
    public SimpleQueryRunner(QueryRunnerFactory queryRunnerFactory) {
        runner = new TransactionRunner(queryRunnerFactory);
    }

    /**
     * Find unique entry, uses {@link QueryRunner#queryUnique(org.polyjdbc.core.query.SelectQuery, org.polyjdbc.core.query.mapper.ObjectMapper) }.
     */
    public <T> T queryUnique(final SelectQuery query, final ObjectMapper<T> mapper) {
        return runner.run(new TransactionWrapper<T>() {
            public T perform(QueryRunner queryRunner) {
                return queryRunner.queryUnique(query, mapper);
            }
        });
    }

    /**
     * Retrieve list, uses {@link QueryRunner#queryList(org.polyjdbc.core.query.SelectQuery, org.polyjdbc.core.query.mapper.ObjectMapper) }.
     */
    public <T> List<T> queryList(final SelectQuery query, final ObjectMapper<T> mapper) {
        return runner.run(new TransactionWrapper<List<T>>() {
            public List<T> perform(QueryRunner queryRunner) {
                return queryRunner.queryList(query, mapper);
            }
        });
    }

    /**
     * Retrieve set, uses {@link QueryRunner#querySet(org.polyjdbc.core.query.SelectQuery, org.polyjdbc.core.query.mapper.ObjectMapper) }.
     */
    public <T> Set<T> querySet(final SelectQuery query, final ObjectMapper<T> mapper) {
        return runner.run(new TransactionWrapper<Set<T>>() {
            public Set<T> perform(QueryRunner queryRunner) {
                return queryRunner.querySet(query, mapper);
            }
        });
    }

    /**
     * Check if any result exists for query, uses {@link QueryRunner#queryExistence(org.polyjdbc.core.query.SelectQuery) }.
     */
    public boolean queryExistence(final SelectQuery query) {
        return runner.run(new TransactionWrapper<Boolean>() {
            public Boolean perform(QueryRunner queryRunner) {
                return queryRunner.queryExistence(query);
            }
        });
    }

    /**
     * Run insert query, uses {@link QueryRunner#insert(org.polyjdbc.core.query.InsertQuery) }.
     */
    public long insert(final InsertQuery query) {
        return runner.run(new TransactionWrapper<Long>() {
            public Long perform(QueryRunner queryRunner) {
                return queryRunner.insert(query);
            }
        });
    }

    /**
     * Run update query, uses {@link QueryRunner#update(org.polyjdbc.core.query.UpdateQuery) }.
     */
    public int update(final UpdateQuery query) {
        return runner.run(new TransactionWrapper<Integer>() {
            public Integer perform(QueryRunner queryRunner) {
                return queryRunner.update(query);
            }
        });
    }
}


/*
 * Copyright 2013 Adam Dubiel, Przemek Hertel.
 *
 * 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 org.polyjdbc.core.query;

import org.polyjdbc.core.type.ColumnTypeMapper;
import org.polyjdbc.core.util.StringBuilderUtil;

/**
 * Builds update query, use {@link QueryFactory#update() } to create new instance.
 *
 *

 * QueryFactory.update("test")
 *      .value("columnA", "A")
 *      .value("columnB", 2)
 *      .value("columnC", BigDecimal.valueOf(3.1415))
 *      .where("columnA > :maxId").withArgument("maxId", 10000);
 *

 *
 * @author Adam Dubiel
 */
public class UpdateQuery {

    private static final int VALUES_LENGTH = 50;

    private Query query;

    private StringBuilder values = new StringBuilder(VALUES_LENGTH);

    private StringBuilder where = new StringBuilder(VALUES_LENGTH);

    UpdateQuery(ColumnTypeMapper typeMapper, String what) {
        this.query = new Query(typeMapper);
        this.query.append("UPDATE ").append(what).append(" SET ");
    }

    Query build() {
        StringBuilderUtil.deleteLastCharacters(values, 2);
        query.append(values.toString()).append(" ").append(where.toString());
        query.compile();

        return query;
    }

    /**
     * Set column to update. Object is automatically translated
     * onto matching JDBC type.
     *
     * @see org.polyjdbc.core.type.ColumnTypeMapper
     */
    public UpdateQuery set(String fieldName, Object value) {
        String updatedFieldName = "update_" + fieldName;
        values.append(fieldName).append(" = :").append(updatedFieldName).append(", ");
        query.setArgument(updatedFieldName, value);
        return this;
    }

    /**
     * Adds WHERE clause with given conditions. Words prefixed with
     * : are replaced into value placeholders, which can be populated
     * using {@link #withArgument(java.lang.String, java.lang.Object) } method.
     *

     * .where("value > :value").withArgument("value", 10);
     *

     */
    public UpdateQuery where(String conditions) {
        where.append("WHERE ").append(conditions);
        return this;
    }

    /**
     * Sets value for placeholder defined in query. Placeholder name should
     * not start with :, it is stripped off. Based on passed object type,
     * appropriate JDBC type is chosen.
     *
     * @see org.polyjdbc.core.type.ColumnTypeMapper
     *
     */
    public UpdateQuery withArgument(String argumentName, Object object) {
        query.setArgument(argumentName, object);
        return this;
    }
}


/*
 * Copyright 2013 Adam Dubiel, Przemek Hertel.
 *
 * 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 org.polyjdbc.core.query;

import org.polyjdbc.core.type.ColumnTypeMapper;

/**
 * Builds insert query, use {@link QueryFactory#delete() } to create new instance.
 *
 *

 * QueryFactory.delete().from("test").where("id > :maxId").withArgument("maxId", 1000);
 *

 *
 * @author Adam Dubiel
 */
public class DeleteQuery {

    private final Query query;

    DeleteQuery(ColumnTypeMapper typeMapper) {
        query = new Query(typeMapper);
    }

    Query build() {
        query.compile();
        return query;
    }

    /**
     * Creates FROM clause with given table name.
     */
    public DeleteQuery from(String tableName) {
        query.append("DELETE FROM ").append(tableName);
        return this;
    }

    /**
     * Adds WHERE clause with given conditions. Words prefixed with
     * : are replaced into value placeholders, which can be populated
     * using {@link #withArgument(java.lang.String, java.lang.Object) } method.
     *

     * .where("value > :value").withArgument("value", 10);
     *

     */
    public DeleteQuery where(String conditions) {
        query.append(" WHERE ").append(conditions);
        return this;
    }

    /**
     * Sets value for placeholder defined in query. Placeholder name should
     * not start with :, it is stripped off. Based on passed object type,
     * appropriate JDBC type is chosen.
     *
     * @see org.polyjdbc.core.type.ColumnTypeMapper
     *
     */
    public DeleteQuery withArgument(String argumentName, Object object) {
        query.setArgument(argumentName, object);
        return this;
    }
}


package org.polyjdbc.core;

import org.polyjdbc.core.dialect.Dialect;
import org.polyjdbc.core.transaction.ConnectionProvider;
import org.polyjdbc.core.transaction.DataSourceTransactionManager;
import org.polyjdbc.core.transaction.ExternalTransactionManager;
import org.polyjdbc.core.transaction.TransactionManager;
import org.polyjdbc.core.type.ColumnTypeMapper;
import org.polyjdbc.core.type.SqlType;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

public final class PolyJDBCBuilder {

    private final Dialect dialect;

    private String schemaName;

    private final Map<Class<?>, SqlType> customMappings = new HashMap<Class<?>, SqlType>();

    private DataSource dataSource;

    private ConnectionProvider connectionProvider;

    private PolyJDBCBuilder(Dialect dialect) {
        this(dialect, null);
    }

    private PolyJDBCBuilder(Dialect dialect, String schemaName) {
        this.dialect = dialect;
        this.schemaName = schemaName;
    }

    /**
     * Start Creating new PolyJDBC instance speaking given dialect.
     */
    public static PolyJDBCBuilder polyJDBC(Dialect dialect) {
        return new PolyJDBCBuilder(dialect);
    }

    /**
     * Create new PolyJDBC instance with a given dialect and schema.
     */
    public static PolyJDBCBuilder polyJDBC(Dialect dialect, String schemaName) {
        return new PolyJDBCBuilder(dialect, schemaName);
    }

    /**
     * Return new PolyJDBC instance.
     */
    public PolyJDBC build() {
        TransactionManager manager;
        if (dataSource != null) {
            manager = new DataSourceTransactionManager(dataSource);
        } else {
            manager = new ExternalTransactionManager(connectionProvider);
        }
        return new DefaultPolyJDBC(dialect, schemaName, new ColumnTypeMapper(customMappings), manager);
    }

    /**
     * Specify source of connections in transactional mode. PolyJDCB will take care of commits, rollbacks and
     * connection closing.
     */
    public PolyJDBCBuilder connectingToDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        return this;
    }

    /**
     *  Specify source of connections in unmanaged (passive) mode. PolyJDBC expects provided connections to be managed
     *  by some other entity (ie framework), and so won't attempt to commit, rollback nor close them.
     */
    public PolyJDBCBuilder usingManagedConnections(ConnectionProvider connectionProvider) {
        this.connectionProvider = connectionProvider;
        return this;
    }

    /**
     * Register custom mapping from clazz to SQL type. SqlType should be one of {@link java.sql.Types}. If you need to
     * add some transformations, register custom implementation of {@link org.polyjdbc.core.type.TypeWrapper}.
     *
     * @param clazz class to register
     * @param sqlType type compatible with {@link java.sql.Types}
     */
    public PolyJDBCBuilder withCustomMapping(Class<?> clazz, SqlType sqlType) {
        this.customMappings.put(clazz, sqlType);
        return this;
    }
}


/*
 * Copyright 2013 Adam Dubiel, Przemek Hertel.
 *
 * 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 org.polyjdbc.core.query;

import org.polyjdbc.core.dialect.Dialect;
import org.polyjdbc.core.dialect.Limit;
import org.polyjdbc.core.type.ColumnTypeMapper;
import org.polyjdbc.core.util.StringBuilderUtil;

/**
 * Builds select query, use {@link QueryFactory } to create new instance.
 * DSL method should be called according to order of clauses in query. Otherwise
 * query will be malformed.
 *
 *

 * QueryFactory.selectAll().from("test").where("id > :maxId").withArgument("maxId", 1000);
 * QueryFactory.select().query("select * from test where id > :maxId").withArgument("maxId", 1000);
 *

 *
 * @author Adam Dubiel
 */
public class SelectQuery {

    private static final int ORDER_BY_LENGTH = 20;

    private final Dialect dialect;

    private final Query query;

    private StringBuilder orderBy;

    private Limit limit;

    SelectQuery(Dialect dialect, ColumnTypeMapper typeMapper, String what) {
        this(dialect, typeMapper);
        this.query.append("SELECT ").append(what).append(" ");
    }

    SelectQuery(Dialect dialect, ColumnTypeMapper typeMapper) {
        this.dialect = dialect;
        this.query = new Query(typeMapper);
    }

    Query build() {
        if (orderBy != null) {
            StringBuilderUtil.deleteLastCharacters(orderBy, 2);
            query.append(orderBy.toString());
        }
        if (limit != null) {
            dialect.queries().limit(query, limit, orderBy != null);
        }

        query.compile();
        return query;
    }

    /**
     * Fill FROM clause.
     */
    public SelectQuery from(String from) {
        query.append("FROM ").append(from).append(" ");
        return this;
    }

    /**
     * Fill WHERE clause.
     */
    public SelectQuery where(String where) {
        query.append("WHERE ").append(where).append(" ");
        return this;
    }

    /**
     * Replaces contents of select query with given query string. Query has to
     * contain all clauses - nothing is generated. Only {@link Select#orderBy(java.lang.String, java.lang.String)} and
     * {@link SelectQuery#limit(int, int)} can be used with this method.
     */
    public SelectQuery query(String queryText) {
        query.overwrite(queryText);
        return this;
    }

    /**
     * Append any text to query, creation method does not matter. Useful when
     * additional WHERE clause conditions are needed after initial creation.
     */
    public SelectQuery append(String queryText) {
        query.append(queryText);
        return this;
    }

    /**
     * Create ORDER BY clause for given column, can be used multiple
     * times with multiple columns.
     */
    public SelectQuery orderBy(String name, Order order) {
        if (orderBy == null) {
            orderBy = new StringBuilder(ORDER_BY_LENGTH);
            orderBy.append(" ORDER BY ");
        }
        orderBy.append(name).append(" ").append(order.getStringCode()).append(", ");

        return this;
    }

    /**
     * Create LIMIT clause.
     */
    public SelectQuery limit(int limit, int offset) {
        this.limit = new Limit(limit, offset);
        return this;
    }

    /**
     * Create LIMIT clause without specifying offset.
     */
    public SelectQuery limit(int limit) {
        return limit(limit, 0);
    }

    /**
     * Sets value for placeholder defined in query. Placeholder name should
     * not start with :, it is stripped off. Based on passed object type,
     * appropriate JDBC type is chosen.
     *
     * @see org.polyjdbc.core.type.ColumnTypeMapper
     *
     */
    public SelectQuery withArgument(String argumentName, Object object) {
        query.setArgument(argumentName, object);
        return this;
    }
}


/*
 * Copyright 2013 Adam Dubiel, Przemek Hertel.
 *
 * 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 org.polyjdbc.core.query;

import org.polyjdbc.core.key.KeyGenerator;
import org.polyjdbc.core.transaction.Transaction;
import org.polyjdbc.core.type.ColumnTypeMapper;
import org.polyjdbc.core.util.StringBuilderUtil;

import java.sql.SQLException;

/**
 * Builds insert query, use {@link QueryFactory#insert() } to create new instance.
 *
 *

 * QueryFactory.insert().into("test").sequence("id", "seq_test")
 *      .value("columnA", "A")
 *      .value("columnB", 2)
 *      .value("columnC", BigDecimal.valueOf(3.1415));
 *

 *
 * @author Adam Dubiel
 */
public abstract class InsertQuery {

    private static final int VALUES_LENGTH = 50;

    private final Query query;

    private final StringBuilder valueNames = new StringBuilder(VALUES_LENGTH);

    private final StringBuilder values = new StringBuilder(VALUES_LENGTH);

    InsertQuery(ColumnTypeMapper typeMapper) {
        this.query = new Query(typeMapper);
    }

    Query build() {
        StringBuilderUtil.deleteLastCharacters(valueNames, 2);
        StringBuilderUtil.deleteLastCharacters(values, 2);

        query.append("(").append(valueNames.toString()).append(")")
                .append(" VALUES(").append(values.toString()).append(")");
        query.compile();

        return query;
    }

    /**
     * Creates INSERT INTO clause for given table name.
     */
    public InsertQuery into(String tableName) {
        query.append("INSERT INTO ").append(tableName);
        return this;
    }

    /**
     * Insert next sequence value into column of given name. Only one sequenced
     * column per table is supported so far.
     */
    public abstract InsertQuery sequence(String sequenceField, String sequenceName);

    abstract boolean isIdInserted();

    abstract long generateSequenceValue(KeyGenerator keyGenerator, Transaction transaction) throws SQLException;

    /**
     * Insert value into column of given name. Object is automatically translated
     * onto matching JDBC type.
     *
     * @see org.polyjdbc.core.type.ColumnTypeMapper
     */
    public InsertQuery value(String fieldName, Object value) {
        valueNames.append(fieldName).append(", ");
        values.append(":").append(fieldName).append(", ");
        setArgument(fieldName, value);
        return this;
    }

    void setArgument(String fieldName, Object value) {
        query.setArgument(fieldName, value);
    }
}


/*
 * Copyright 2013 Adam Dubiel.
 *
 * 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 org.polyjdbc.core.query;

import org.polyjdbc.core.exception.TransactionInterruptedException;
import org.polyjdbc.core.util.TheCloser;

/**
 * Run any number of operations in single transaction without the need to wrap code in try-finally block as TransactionRunner takes care of
 * resource freeing even when exception is thrown. TransactionRunner is reusable, creates transaction per {@link #run(org.polyjdbc.core.query.TransactionWrapper)
 * } method usage.
 *
 * <pre>
 * Test test = transactionRunner.run(new TransactionWrapper<Test>() {
 *   @Override
 *   public Test perform(QueryRunner queryRunner) {
 *       SelectQuery query = QueryFactory.select().query("select * from test where name = :name")
 *           .withArgument("name", "test");
 *       return queryRunner.queryUnique(query, new TestMapper());
 *   }
 * });
 * </pre>
 *
 * @see TransactionWrapper
 * @see VoidTransactionWrapper
 *
 * @author Adam Dubiel
 */
public class TransactionRunner {

    private final QueryRunnerFactory queryRunnerFactory;

    public TransactionRunner(QueryRunnerFactory queryRunnerFactory) {
        this.queryRunnerFactory = queryRunnerFactory;
    }

    /**
     * Run specified operations in safe transaction block.
     */
    public <T> T run(TransactionWrapper<T> operation) {
        QueryRunner runner = null;
        try {
            runner = queryRunnerFactory.create();
            T result = operation.perform(runner);
            runner.commit();

            return result;
        } catch (Throwable throwable) {
            TheCloser.rollback(runner);
            throw new TransactionInterruptedException(throwable);
        } finally {
            TheCloser.close(runner);
        }
    }
}


package de.digitalcollections.iiif.model.search;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.collect.Lists;
import de.digitalcollections.iiif.model.sharedcanvas.AnnotationList;
import de.digitalcollections.iiif.model.sharedcanvas.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * An AnnotationList that contains hits for a given search query.
 *
 * See http://iiif.io/api/search/1.0/#simple-lists
 */
public class SearchResult extends AnnotationList {

  public static final String CONTEXT = "http://iiif.io/api/search/1/context.json";

  private List<SearchHit> hits;

  @JsonCreator
  public SearchResult(@JsonProperty("@id") String identifier) {
    super(identifier);
  }

  @JsonProperty("@context")
  public List<String> getContext() {
    return Arrays.asList(Resource.CONTEXT, CONTEXT);
  }

  private void setContext(List<String> contexts) {
    // NOP, is just here for jackson
  }

  public List<SearchHit> getHits() {
    return hits;
  }

  public void setHits(List<SearchHit> hits) {
    this.hits = hits;
  }

  public SearchResult addHit(SearchHit first, SearchHit... rest) {
    if (this.hits == null) {
      this.hits = new ArrayList<>();
    }
    this.hits.addAll(Lists.asList(first, rest));
    return this;
  }

  /**
   * Set the list of containing resources. Must all be instances of {@link SearchLayer}
   *
   * @throws IllegalArgumentException if at least one of the resources is not a {@link SearchLayer}
   */
  @JsonDeserialize(contentAs = SearchLayer.class)
  @Override
  public void setWithin(List<Resource> within) throws IllegalArgumentException {
    if (within.stream().anyMatch(r -> !(r instanceof SearchLayer))) {
      throw new IllegalArgumentException("SearchResult can only be within a SearchLayer.");
    }
    super.setWithin(within);
  }

  /**
   * Add a new containing resource. Must be an instance of {@link SearchLayer}
   *
   * @throws IllegalArgumentException if the resource is not a {@link SearchLayer}
   */
  @Override
  public SearchResult addWithin(Resource first, Resource... rest) throws IllegalArgumentException {
    if (!(first instanceof SearchLayer) || Arrays.stream(rest).anyMatch(r -> !(r instanceof SearchLayer))) {
      throw new IllegalArgumentException("SearchResult can only be within a SearchLayer.");
    }
    super.addWithin(first, rest);
    return this;
  }

  @Override
  public SearchResult getNext() {
    return (SearchResult) super.getNext();
  }

  public void setNext(SearchResult next) {
    super.setNext(next);
  }

  @Override
  public SearchResult getPrevious() {
    return (SearchResult) super.getPrevious();
  }

  public void setPrevious(SearchResult previous) {
    super.setPrevious(previous);
  }
}