/* * @@COPYRIGHT@@ */ package com.cosylab.acs.maci.manager; import java.io.IOException; import java.io.ObjectStreamField; import java.io.Serializable; import java.lang.reflect.Constructor; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.NameAlreadyBoundException; import javax.naming.NameNotFoundException; import javax.naming.NameParser; import javax.naming.NamingException; import org.omg.CORBA.IntHolder; import org.prevayler.Command; import org.prevayler.Prevayler; import org.prevayler.implementation.AbstractPrevalentSystem; import org.prevayler.implementation.SnapshotPrevayler; import si.ijs.maci.ClientOperations; import alma.ACSErrTypeCommon.wrappers.AcsJBadParameterEx; import alma.ACSErrTypeCommon.wrappers.AcsJNullPointerEx; import alma.ACSErrTypeCommon.wrappers.AcsJUnexpectedExceptionEx; import alma.acs.alarmsystem.source.AlarmSource; import alma.acs.alarmsystem.source.AlarmSourceImpl; import alma.acs.concurrent.DaemonThreadFactory; import alma.acs.exceptions.AcsJException; import alma.acs.logging.AcsLogLevel; import alma.acs.util.ACSPorts; import alma.acs.util.IsoDateFormat; import alma.jmanagerErrType.wrappers.AcsJCyclicDependencyDetectedEx; import alma.jmanagerErrType.wrappers.AcsJSyncLockFailedEx; import alma.maciErrType.wrappers.AcsJCannotGetComponentEx; import alma.maciErrType.wrappers.AcsJComponentSpecIncompatibleWithActiveComponentEx; import alma.maciErrType.wrappers.AcsJIncompleteComponentSpecEx; import alma.maciErrType.wrappers.AcsJInvalidComponentSpecEx; import alma.maciErrType.wrappers.AcsJNoPermissionEx; import com.cosylab.acs.maci.AccessRights; import com.cosylab.acs.maci.Administrator; import com.cosylab.acs.maci.AuthenticationData; import com.cosylab.acs.maci.BadParametersException; import com.cosylab.acs.maci.Client; import com.cosylab.acs.maci.ClientInfo; import com.cosylab.acs.maci.Component; import com.cosylab.acs.maci.ComponentInfo; import com.cosylab.acs.maci.ComponentSpec; import com.cosylab.acs.maci.ComponentStatus; import com.cosylab.acs.maci.Container; import com.cosylab.acs.maci.Container.ComponentInfoCompletionCallback; import com.cosylab.acs.maci.ContainerInfo; import com.cosylab.acs.maci.CoreException; import com.cosylab.acs.maci.Daemon; import com.cosylab.acs.maci.HandleConstants; import com.cosylab.acs.maci.HandleHelper; import com.cosylab.acs.maci.ImplLang; import com.cosylab.acs.maci.IntArray; import com.cosylab.acs.maci.Manager; import com.cosylab.acs.maci.MessageType; import com.cosylab.acs.maci.NoDefaultComponentException; import com.cosylab.acs.maci.NoResourcesException; import com.cosylab.acs.maci.RemoteException; import com.cosylab.acs.maci.RemoteTransientException; import com.cosylab.acs.maci.ServiceDaemon; import com.cosylab.acs.maci.StatusHolder; import com.cosylab.acs.maci.SynchronousAdministrator; import com.cosylab.acs.maci.TimeoutRemoteException; import com.cosylab.acs.maci.Transport; import com.cosylab.acs.maci.loadbalancing.LoadBalancingStrategy; import com.cosylab.acs.maci.manager.app.ManagerContainerServices; import com.cosylab.acs.maci.manager.recovery.AdministratorCommandAllocate; import com.cosylab.acs.maci.manager.recovery.AdministratorCommandDeallocate; import com.cosylab.acs.maci.manager.recovery.AdministratorCommandSet; import com.cosylab.acs.maci.manager.recovery.AlarmCleared; import com.cosylab.acs.maci.manager.recovery.AlarmRaised; import com.cosylab.acs.maci.manager.recovery.ClientCommandAllocate; import com.cosylab.acs.maci.manager.recovery.ClientCommandDeallocate; import com.cosylab.acs.maci.manager.recovery.ClientCommandSet; import com.cosylab.acs.maci.manager.recovery.ClientInfoCommandComponentAdd; import com.cosylab.acs.maci.manager.recovery.ClientInfoCommandComponentRemove; import com.cosylab.acs.maci.manager.recovery.ComponentCommandAckAlloc; import com.cosylab.acs.maci.manager.recovery.ComponentCommandAllocate; import com.cosylab.acs.maci.manager.recovery.ComponentCommandAllocateHandle; import com.cosylab.acs.maci.manager.recovery.ComponentCommandClientAdd; import com.cosylab.acs.maci.manager.recovery.ComponentCommandClientRemove; import com.cosylab.acs.maci.manager.recovery.ComponentCommandDeallocate; import com.cosylab.acs.maci.manager.recovery.ComponentCommandPreallocate; import com.cosylab.acs.maci.manager.recovery.ComponentCommandSet; import com.cosylab.acs.maci.manager.recovery.ComponentInfoCommandComponentAdd; import com.cosylab.acs.maci.manager.recovery.ComponentInfoCommandComponentRemove; import com.cosylab.acs.maci.manager.recovery.ContainerCommandAllocate; import com.cosylab.acs.maci.manager.recovery.ContainerCommandDeallocate; import com.cosylab.acs.maci.manager.recovery.ContainerCommandSet; import com.cosylab.acs.maci.manager.recovery.ContainerCommandUpdate; import com.cosylab.acs.maci.manager.recovery.ContainerInfoCommandComponentAdd; import com.cosylab.acs.maci.manager.recovery.ContainerInfoCommandComponentRemove; import com.cosylab.acs.maci.manager.recovery.DefaultComponentCommandPut; import com.cosylab.acs.maci.manager.recovery.UnavailableComponentCommandPut; import com.cosylab.acs.maci.manager.recovery.UnavailableComponentCommandRemove; import com.cosylab.acs.maci.plug.ManagerProxy; import com.cosylab.cdb.client.CDBAccess; import com.cosylab.cdb.client.DAOProxy; import com.cosylab.cdb.client.DAOProxyConnectionListener; import com.cosylab.util.WildcharMatcher; /** * This class is an implementation of MACI com.cosylab.acs.maci.Manager. * It provides the actual internal implementation of the Manager * functionality in a way independent from the ACS maci Manager IDL interface. * * @author Matej Sekoranja (matej.sekoranja@cosylab.com) * @version @@VERSION@@ * @see Manager */ @SuppressWarnings("unchecked") public class ManagerImpl extends AbstractPrevalentSystem implements Manager, HandleConstants { /** * Serial versionUID */ private static final long serialVersionUID = 1372046383416324709L; // used to report exceptions that cannot be propagated, etc. private void reportException(Throwable th) { logger.log(Level.SEVERE, th.getMessage(), th); } /** * Synchronization helper class used for activation/deactivation synchronization. * This class enforces <code>attempt</code> method of acquiring the locks to prevent deadlocks. * Class also offers reference counting. * (NOTE: automatic lock counting was not implemented due to imperfect usage.) * * Example of usage: * <code> * ReferenceCountingLock lock; * if (lock.acquire(3*Sync.ONE_MINUTE)) * { * try * { * // critical section here * } * finally * { * lock.release(); * } * } * else * { * throw new TimoutException("Deadlock detected..."); * } * * </code> */ class ReferenceCountingLock { /** * Number of current locks. */ private final AtomicInteger references = new AtomicInteger(1); /** * Synchronization mutex. */ private final Lock lock = new ReentrantLock(); /** * User data (must be sync by the user). */ private Object userData = null; /** * Constructor of <code>ReferenceCountingLock</code>. * After construction lock is free and reference count equals <code>1</code>. */ public ReferenceCountingLock() { // no-op. } /** * Attempt to acquire lock. * * @param msecs the number of milliseconds to wait. * An argument less than or equal to zero means not to wait at all. * @return <code>true</code> if acquired, <code>false</code> othwerwise. */ public boolean acquire(long msecs) { try { return lock.tryLock(msecs, TimeUnit.MILLISECONDS); } catch (InterruptedException ie) { return false; } } /** * Release previously acquired lock. */ public void release() { lock.unlock(); } /** * Get number of references. * * @return number of references. */ public int referenceCount() { return references.get(); } /** * Increment number of references. * * @return number of references. */ public int increment() { return references.incrementAndGet(); } /** * Decrement number of references. * * @return number of references. */ public int decrement() { return references.decrementAndGet(); } /** * @return the userData */ public Object getUserData() { return userData; } /** * @param userData the userData to set */ public void setUserData(Object userData) { this.userData = userData; } } /** * Task thats invokes <code>internalRequestComponent</code> method. */ class RequestComponentTask implements Runnable { private final int h; private final URI[] curls; public RequestComponentTask(int h, URI curl) { assert (curl != null); this.h = h; this.curls = new URI[] { curl }; } public RequestComponentTask(int h, URI[] curls) { assert (curls != null); this.h = h; this.curls = curls; } public void run() { StatusHolder status = new StatusHolder(); for (int i = 0; i < curls.length; i++) { try { internalRequestComponent(h, curls[i], status); if (status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) logger.log(Level.FINE,"Failed to activate requested component '"+curls[i]+"', reason: '"+status.getStatus()+"'."); } catch (Throwable ex) { CoreException ce = new CoreException("Failed to request component '"+curls[i]+"'.", ex); reportException(ce); } } } } /** * Task thats invokes <code>internalReleaseComponent</code> method. */ class ReleaseComponentTask implements Runnable { private final int h; private final int[] handles; public ReleaseComponentTask(int h, int handle) { this.h = h; this.handles = new int[] { handle }; } public ReleaseComponentTask(int h, int[] handles) { assert (handles != null); this.h = h; this.handles = handles; } public void run() { for (int i = 0; i < handles.length; i++) { try { internalReleaseComponent(h, handles[i], false); } catch (Throwable ex) { CoreException ce = new CoreException("Failed to release component with handle '"+handles[i]+"'.", ex); reportException(ce); } } } } private transient Map<Object, GroupedNotifyTask> groupedNotifyTaskMap; interface GroupedRunnable extends Runnable { public void cancelAll(); public boolean isCancelAll(); } abstract class DefaultGroupedRunnable implements GroupedRunnable { private boolean cancelAll = false; public void cancelAll() { cancelAll = true; } public boolean isCancelAll() { return cancelAll; } } protected void registerGroupedNotifyTaks(Object key, GroupedRunnable runnable) { synchronized (groupedNotifyTaskMap) { GroupedNotifyTask gnt = groupedNotifyTaskMap.get(key); if (gnt == null) { gnt = new GroupedNotifyTask(key, runnable); groupedNotifyTaskMap.put(key, gnt); threadPool.execute(gnt); } else gnt.addTask(runnable); } } /** * Task thats invokes <code>Runnable</code> method. */ private class GroupedNotifyTask implements Runnable { private final Object key; // synced to groupedNotifyTaskMap private final Deque<GroupedRunnable> tasks = new LinkedList<GroupedRunnable>(); public GroupedNotifyTask(Object key, GroupedRunnable task) { this.key = key; addTask(task); } // must be synced outside public void addTask(GroupedRunnable task) { tasks.addLast(task); } public void run() { while (true) { GroupedRunnable taskToRun; synchronized (groupedNotifyTaskMap) { taskToRun = tasks.pollFirst(); if (taskToRun == null) { groupedNotifyTaskMap.remove(key); break; } } // run try { taskToRun.run(); } catch (Throwable th) { logger.log(Level.SEVERE, "Unhandeled exception caught.", th); } // checked after if (taskToRun.isCancelAll()) { synchronized (groupedNotifyTaskMap) { tasks.clear(); groupedNotifyTaskMap.remove(key); break; } } } } } /** * Task thats invokes <code>internalDeactivateComponent</code> method. */ class DeactivateComponentTask extends TimerTask { private final String name; public DeactivateComponentTask(String name) { super(); this.name = name; } public void run() { try { internalDeactivateComponent(name); } catch (Throwable th) { CoreException ce = new CoreException("Failed to deactivate component '"+name+"'.", th); reportException(ce); } } } /** * Task thats invokes <code>shutdownContainer</code> method. */ class ShutdownContainerTask extends TimerTask { private final String containerName; public ShutdownContainerTask(String containerName) { super(); this.containerName = containerName; } public void run() { try { // shutdown only if component does not host any component ContainerInfo containerInfo = getContainerInfo(containerName); if (containerInfo == null) return; else { synchronized (containerInfo.getComponents()) { if (containerInfo.getComponents().size() > 0) return; } } final int SHUTDOWN_CONTAINER_ACTION = 2 << 8; shutdownContainer(ManagerImpl.this.getHandle(), containerName, SHUTDOWN_CONTAINER_ACTION); } catch (Throwable th) { CoreException ce = new CoreException("Failed to shutdown container '"+containerName+"'.", th); reportException(ce); } } } /** * Implementation of <code>Component</code> interface for services. */ class ServiceComponent implements Component { /** * Service representing this Component. */ private final Object object; /** * Construct service Component. * @param object service itself, non-<code>null</code>. */ public ServiceComponent(Object object) { assert (object != null); this.object = object; } /** * @see com.cosylab.acs.maci.Component#construct() */ public void construct() throws RemoteException { } /** * @see com.cosylab.acs.maci.Component#destruct() */ public void destruct() throws RemoteException { } /** * @see com.cosylab.acs.maci.Component#getObject() */ public Object getObject() { return object; } /** * @see com.cosylab.acs.maci.Component#implementedInterfaces() */ public String[] implementedInterfaces() { return new String[] { object.getClass().getName() }; } /** * @see com.cosylab.acs.maci.Component#doesImplement(String) */ public boolean doesImplement(String type) { return false; } } /** * All fields that represent state of the Manager must be listed here. * Those fields will be written to and read from persistence storage */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("domain", String.class), new ObjectStreamField("handle", Integer.TYPE), new ObjectStreamField("clients", HandleDataStore.class), new ObjectStreamField("administrators", HandleDataStore.class), new ObjectStreamField("containers", HandleDataStore.class), new ObjectStreamField("components", HandleDataStore.class), new ObjectStreamField("releasedHandles", Map.class), new ObjectStreamField("unavailableComponents", Map.class), new ObjectStreamField("defaultComponents", Map.class), new ObjectStreamField("domains", HashSet.class), new ObjectStreamField("activeAlarms", HashSet.class), new ObjectStreamField("statePersitenceFlag", AtomicBoolean.class)}; /** * Interdomain manager handle mask. */ // TODO MF tmp private static final int INTERDOMAIN_MANAGER_HANDLE = 0x05555555; /** * Manager domain name. * @serial */ private String domain = DEFAULT_DOMAIN; /** * Manager handle. * @serial */ private int handle = MANAGER_MASK; /** * Clients data. * @serial */ private HandleDataStore clients = new HandleDataStore(16, HANDLE_MASK); /** * Administrators data. * @serial */ private HandleDataStore administrators = new HandleDataStore(16, HANDLE_MASK); /** * Containers data. * @serial */ private HandleDataStore containers = new HandleDataStore(32, HANDLE_MASK); /** * Components data. * Access must be protected using {@link #componentsLock}. * @serial */ private HandleDataStore components = new HandleDataStore(128, HANDLE_MASK); /** * This lock was introduced to debug thread contention for http://jira.alma.cl/browse/COMP-6488, * replacing "synchronized (components)". * Once that problem is resolved, we can either hardcode the ReentrantLock here, or leave in this choice and simply * not define property <code>acs.enableReentrantLockProfiling</code> which would lead to using the ProfilingReentrantLock. */ private transient Lock componentsLock = null; public enum WhyUnloadedReason { REMOVED, TIMEOUT, DISAPPEARED, REPLACED }; /** * Monitor entry generated at every handle removal. */ static class HandleMonitorEntry implements Serializable { private static final long serialVersionUID = -5661590007096077942L; public final long timestamp; public final WhyUnloadedReason reason; /** * @param timestamp * @param reason */ public HandleMonitorEntry(long timestamp, WhyUnloadedReason reason) { super(); this.timestamp = timestamp; this.reason = reason; } } /** * Handle data store to monitor released handles. * @serial */ private Map<Integer, HandleMonitorEntry> releasedHandles = new HashMap<Integer, HandleMonitorEntry>(); /** * List of all unavailable components. * @serial */ private Map<String, ComponentInfo> unavailableComponents = new LinkedHashMap<String, ComponentInfo>(); /** * List of all pending activations. * Needed for cyclic dependency checks, since non-fully-activated components * are not accessible via HandleDataStore iterator. */ private transient Map<String, ComponentInfo> pendingActivations = null; /** * List of all pending container shutdowns. */ private transient Set<String> pendingContainerShutdown = null; /** * List of all pending container async. requests (activations). */ private transient Map<String, Deque<ComponentInfoCompletionCallback>> pendingContainerAsyncRequests = null; /** * New container logged in notification. */ private transient Object containerLoggedInMonitor = null; /** * Map of default components (set via getDynamicComponent) overriding CDB state. * Entry is: (String type, String name). * @serial */ private Map<String, ComponentInfo> defaultComponents = new HashMap<String, ComponentInfo>(); /** * Manager domain name. */ private transient Random random = null; /** * Heartbeat timer. */ private transient Timer heartbeatTask = null; /** * Delayed release timer. */ private transient Timer delayedDeactivationTask = null; /** * Root context of the remote directory. */ private transient Context remoteDirectory = null; /** * Manager Component reference. */ private transient Object managerComponentReference = null; /** * Remote Directory Component reference. */ private transient Object remoteDirectoryComponentReference = null; /** * Activation/deactivation synchronization mechanism. */ private transient Map<String, ReferenceCountingLock> activationSynchronization; /** * Activation/deactivation synchronization mechanism. * Reader lock is acquired when activation/deactivation * is pending (i.e. until completed) and writer lock by * processes which require not to be run until * activation/deactivation is in progress. */ private transient ReaderPreferenceReadWriteLock activationPendingRWLock; // private transient ReadWriteLock activationPendingRWLock; /** * Shutdown status. */ private transient AtomicBoolean shutdown; /** * Thread pool (guarantees order of execution). */ private transient ThreadPoolExecutor threadPool; /** * Default manager domain. */ private static final String DEFAULT_DOMAIN = ""; /** * Number of threads in thread pool (guarantees order of execution). */ private transient int poolThreads; /** * Lock timeout (deadlock detection time) in ms. */ private transient long lockTimeout; /** * Client ping interval. */ private transient int clientPingInterval; /** * Administrator ping interval. */ private transient int administratorPingInterval; /** * Container ping interval. */ private transient int containerPingInterval; /** * Container rights. */ private static final int CONTAINER_RIGHTS = AccessRights.NONE; /** * Shutdown implementation. */ private transient ManagerShutdown shutdownImplementation = null; /** * CDB access. */ private transient CDBAccess cdbAccess = null; /** * Manager DAO dao (access to the CDB). */ private transient DAOProxy managerDAO = null; /** * Components DAO dao (access to the CDB). */ private transient DAOProxy componentsDAO = null; /** * Containers DAO dao (access to the CDB). */ private transient DAOProxy containersDAO = null; /** * Cached list of all component entries in the CDB. */ private transient String[] componentListCache = null; /** * CDB component specification system property name. */ private static final String NAME_CDB_COMPONENTSPEC = "ACS.CDBComponentSpec"; /** * CDB component specification. * If non-<code>null</code> CDB will be automatically activated on container login. */ private transient ComponentSpec cdbActivation = null; /** * Load balancing strategy system property name. */ private static final String NAME_LOAD_BALANCING_STRATEGY = "ACS.LoadBalancingStrategy"; /** * Load balancing strategy. */ private transient LoadBalancingStrategy loadBalancingStrategy = null; /** * CDB disable system property name. */ private static final String NAME_CDB_DISABLE = "ACS.disableCDB"; /** * Enable handle monitoring property name. */ private static final String NAME_HANDLE_MONITORING = "manager.debug.rememberOldHandles"; /** * Enable handle monitoring store duration property name. */ private static final String NAME_HANDLE_MONITORING_TIME = "manager.debug.rememberOldHandlesStoreDurationMin"; /** * Default andle monitoring store duration. */ private static final int HANDLE_MONITORING_TIME_MIN = 120; /** * Handle monitoring flag. */ private transient boolean enableHandleMonitoring; /** * Handle monitoring duration in minutes. */ private transient int enableHandleMonitoringDurationMins = HANDLE_MONITORING_TIME_MIN; /** * CURL URI schema */ private static final String CURL_URI_SCHEMA = "curl://"; /** * Manager federation domain list property name. */ private static final String NAME_DOMAIN_LIST = "ACS.domains"; /** * Manager federation central naming service. */ private static final String NAME_DOMAIN_DIRECTORY = "ACS.federationDirectory"; /** * Federation enabled flag. */ private transient boolean federationEnabled = false; /** * Root context of the federation directory. */ private transient Context federationDirectory = null; /** * List of domains to manage. */ private HashSet domains = new HashSet(); /** * Cache of non-local (federated) managers. */ private transient Map<String, Manager> managerCache = null; /** * Implementation of prevayler system. */ private transient Prevayler prevayler = null; /** * Implementation of transport helper. */ private transient Transport transport = null; /** * Component info topology sort manager. */ private transient ComponentInfoTopologicalSortManager topologySortManager; /** * Logger. */ private transient Logger logger; /** * Alarm System Interface. */ private transient AlarmSource alarmSource; /** * Persistent set of raised (timer task) alarms. */ private HashSet activeAlarms = new HashSet(); /** * Last execution id; */ private transient long lastExecutionId = 0; /** * Queue (per client) for its messages. */ protected transient Map<Client, LinkedList<ClientMessageTask>> clientMessageQueue; /** * Free threads percentage. The manager will reject client logins when this value gets too large * (current threshold is 90). * @see #setConnectionThreadUsage(int) */ private transient AtomicInteger threadsUsedPercentage; /** * Use sync. instead of async. activation of components. */ private static final String NAME_SYNC_ACTIVATE = "manager.sync_activate"; /** * Allows setting the current percentage of used connection threads, * which would typically be updated by an ORB profiler. * @param percentage * @see #threadsUsedPercentage */ public void setConnectionThreadUsage(int percentage) { threadsUsedPercentage.set(percentage); } public static final String STATE_PERSISTENCE_DEFAULT = "manager.statePersistance"; /** * Prevayler enabled/disabled (remotely) flag. */ private AtomicBoolean statePersitenceFlag = new AtomicBoolean(Boolean.getBoolean(STATE_PERSISTENCE_DEFAULT)); /** * Initializes Manager. * @param prevayler implementation of prevayler system * @param context remote directory implementation */ public void initialize(Prevayler prevayler, CDBAccess cdbAccess, Context context, final Logger logger, ManagerContainerServices managerContainerServices) { this.prevayler = prevayler; this.remoteDirectory = context; this.logger = logger; // needs to be done here, since deserialization is used initializeDefaultConfiguration(); if (cdbAccess != null) setCDBAccess(cdbAccess); readManagerConfiguration(); componentsLock = (ProfilingReentrantLock.isProfilingEnabled ? new ProfilingReentrantLock("componentsLock") : new ReentrantLock() ); random = new Random(); heartbeatTask = new Timer(true); delayedDeactivationTask = new Timer(true); containerLoggedInMonitor = new Object(); activationSynchronization = new HashMap<String, ReferenceCountingLock>(); activationPendingRWLock = new ReaderPreferenceReadWriteLock(); shutdown = new AtomicBoolean(false); threadPool = new ThreadPoolExecutor(poolThreads, poolThreads, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue(), new DaemonThreadFactory("managerThreadPool")); managerCache = new HashMap<String, Manager>(); pendingActivations = new HashMap<String, ComponentInfo>(); pendingContainerShutdown = Collections.synchronizedSet(new HashSet<String>()); pendingContainerAsyncRequests = new HashMap<String, Deque<ComponentInfoCompletionCallback>>(); clientMessageQueue = new HashMap<Client, LinkedList<ClientMessageTask>>(); groupedNotifyTaskMap = new HashMap<Object, GroupedNotifyTask>(); threadsUsedPercentage = new AtomicInteger(0); // create threads threadPool.prestartAllCoreThreads(); // read CDB startup try { String componentSpec = System.getProperty(NAME_CDB_COMPONENTSPEC); if (componentSpec != null) { cdbActivation = new ComponentSpec(componentSpec); logger.log(Level.INFO,"Using CDB component specification: '" + cdbActivation + "'."); } } catch (Throwable t) { logger.log(Level.WARNING, "Failed to parse '" + NAME_CDB_COMPONENTSPEC + "' variable, " + t.getMessage(), t); } // check load balancing strategy checkLoadBalancingStrategy(); // establish connect to the alarm system try { alarmSource = new AlarmSourceImpl(managerContainerServices); alarmSource.start(); } catch (Throwable ex) { logger.log(Level.SEVERE, "Failed to initialize Alarm System Interface " + ex.getMessage(), ex); alarmSource = null; } // register ping tasks initializePingTasks(); // handle monitoring removal task final long timeInMs = enableHandleMonitoringDurationMins*60L*1000; if (enableHandleMonitoring && enableHandleMonitoringDurationMins > 0) { heartbeatTask.schedule(new TimerTask() { @Override public void run() { try { logHandleCleanup(timeInMs); } catch (Throwable th) { logger.log(Level.SEVERE, "Unexpected exception in handle log cleanup task.", th); } } }, 0, timeInMs); } // start topology sort manager topologySortManager = new ComponentInfoTopologicalSortManager( components, containers, activationPendingRWLock, pendingContainerShutdown, threadPool, logger); if (prevayler == null) statePersitenceFlag.set(false); String enDis = statePersitenceFlag.get() ? "enabled" : "disabled"; logger.info("Manager initialized with state persistence " + enDis + "."); } /** * Initialize manager default configuration. */ private void initializeDefaultConfiguration() { poolThreads = 10; lockTimeout = 10 * 60000L; // 10 minutes clientPingInterval = 60000; // 60 secs administratorPingInterval = 45000; // 45 secs containerPingInterval = 30000; // 30 secs } /** * Initialized (registers) all ping tasks (to completely recover). */ private void initializePingTasks() { // go through all the containers, clients, administrators and register ping tasks, // reference of classes are already in TimerTaskContainerInfo/TimerTaskClientInfo // TODO some admin references can be null !!! // containers TimerTaskContainerInfo containerInfo = null; int h = containers.first(); while (h != 0) { containerInfo = (TimerTaskContainerInfo)containers.get(h); h = containers.next(h); // if deserialization failed, logout container ClientInfo clientInfo = containerInfo.createClientInfo(); // register container to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, clientInfo, alarmSource); containerInfo.setTask(task); heartbeatTask.schedule(task, 0, containerInfo.getPingInterval()); } // administrators TimerTaskClientInfo adminInfo = null; h = administrators.first(); while (h != 0) { adminInfo = (TimerTaskClientInfo)administrators.get(h); h = administrators.next(h); // register administrator to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, adminInfo, null); adminInfo.setTask(task); heartbeatTask.schedule(task, 0, administratorPingInterval); } // clients TimerTaskClientInfo clientInfo = null; h = clients.first(); while (h != 0) { clientInfo = (TimerTaskClientInfo)clients.get(h); h = clients.next(h); // register client to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, clientInfo, null); clientInfo.setTask(task); heartbeatTask.schedule(task, 0, clientPingInterval); } } /** * Checks and registers load balancing strategy. * Load balancing strategy is defined as Java JVM system property named * <code>NAME_LOAD_BALANCING_STRATEGY</code> containing class name of the * <code>LoadBalancingStrategy</code> implementation. */ private void checkLoadBalancingStrategy() { try { String loadBalancingStrategyClassName = System.getProperty(NAME_LOAD_BALANCING_STRATEGY); if (loadBalancingStrategyClassName != null) { // load class Class strategyClass = Class.forName(loadBalancingStrategyClassName); // create component implementation Constructor constructor = strategyClass.getConstructor((Class[])null); if (constructor == null) throw new IllegalArgumentException("Class '" + strategyClass.getName() + "' does have required default constructor."); Object strategyObject = constructor.newInstance((Object[])null); if (!(strategyObject instanceof LoadBalancingStrategy)) throw new IllegalArgumentException("Class '" + strategyClass.getName() + "' does not implement '" + LoadBalancingStrategy.class.getName() + "' interface."); loadBalancingStrategy = (LoadBalancingStrategy)strategyObject; logger.log(Level.INFO,"Using load balancing strategy: '" + strategyClass.getName() + "'."); } } catch (Throwable t) { logger.log(Level.WARNING, "Failed to register '" + NAME_LOAD_BALANCING_STRATEGY + "' load balancing strategy: " + t.getMessage(), t); } } /** * Called from client code after all manager initialization is done. */ public void initializationDone() { threadPool.execute(new Runnable() { public void run() { initializeServiceDaemons(); autoStartComponents(); } }); } /** * Returns the handle of the Manager. * @return int handle of the Manager. */ public int getHandle() { return handle; } /** * Set name of the domain, which this manager will handle. * * @param domain name of the domain, which this manager will handle, non-<code>null</code> * @see #getDomain */ public void setDomain(String domain) { assert (domain != null); this.domain = domain; } /** * @see com.cosylab.acs.maci.Manager#getDomain() */ public String getDomain() { return domain; } protected synchronized long generateExecutionId() { final long id = Math.max(System.currentTimeMillis(), lastExecutionId + 1); lastExecutionId = id; return id; } /** * @see com.cosylab.acs.maci.Manager#getContainerInfo(int, int[], String) */ public ContainerInfo[] getContainerInfo(int id, int[] handles, String name_wc) throws AcsJNoPermissionEx { if (handles == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'handles' sequence expected."); throw af; } else if (handles.length == 0 && name_wc == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'names_wc' sequence expected."); throw af; } /* Pattern pattern = null; if (handles.length == 0 && name_wc != null) { // test wildcard patten (try to compile it) try { pattern = Pattern.compile(name_wc); } catch (Exception ex) { // BAD_PARAM BadParametersException af = new BadParametersException("Failed to compile 'names_wc' reqular expression string '"+name_wc+"'."); af.caughtIn(this, "getContainerInfo"); af.putValue("name_wc", name_wc); throw af; } } */ /****************************************************************/ // info to be returned ContainerInfo[] info = null; // requesting info. about itself if (handles.length == 1 && handles[0] == id) { // check handle, no special rights for own info securityCheck(id, 0); info = new ContainerInfo[1]; info[0] = getContainerInfo(id); } // get info of requested handles else if (handles.length > 0) { // check handle, INTROSPECT_MANAGER rights needed securityCheck(id, AccessRights.INTROSPECT_MANAGER); info = new ContainerInfo[handles.length]; for (int i = 0; i < handles.length; i++) info[i] = getContainerInfo(handles[i]); } // get info requested using name wildcard else { // check handle, INTROSPECT_MANAGER rights needed securityCheck(id, AccessRights.INTROSPECT_MANAGER); // list of client matching search pattern ArrayList<ContainerInfo> list = new ArrayList<ContainerInfo>(); // check clients synchronized (containers) { int h = containers.first(); while (h != 0) { ContainerInfo containerInfo = (ContainerInfo)containers.get(h); /*Matcher m = pattern.matcher(containerInfo.getName()); if (m.matches())*/ if (WildcharMatcher.match(name_wc, containerInfo.getName())) list.add(containerInfo); h = containers.next(h); } } // copy to array info = new ContainerInfo[list.size()]; list.toArray(info); } /****************************************************************/ return info; } /** * @see com.cosylab.acs.maci.Manager#getClientInfo(int, int[], String) */ public ClientInfo[] getClientInfo(int id, int[] handles, String name_wc) throws AcsJNoPermissionEx { if (handles == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'handles' sequence expected."); throw af; } else if (handles.length == 0 && name_wc == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'names_wc' sequence expected."); throw af; } /* Pattern pattern = null; if (handles.length == 0 && name_wc != null) { // test wildcard patten (try to compile it) try { pattern = Pattern.compile(name_wc); } catch (Exception ex) { // BAD_PARAM BadParametersException af = new BadParametersException("Failed to compile 'names_wc' reqular expression string '"+name_wc+"'."); af.caughtIn(this, "getClientInfo"); af.putValue("name_wc", name_wc); throw af; } } */ /****************************************************************/ // info to be returned ClientInfo[] info = null; // requesting info. about itself if (handles.length == 1 && handles[0] == id) { // check handle, no special rights for own info securityCheck(id, 0); info = new ClientInfo[1]; info[0] = getClientInfo(id); } // get info of requested handles else if (handles.length > 0) { // check handle, INTROSPECT_MANAGER rights needed securityCheck(id, AccessRights.INTROSPECT_MANAGER); info = new ClientInfo[handles.length]; for (int i = 0; i < handles.length; i++) info[i] = getClientInfo(handles[i]); } // get info requested using name wildcard else { // check handle, INTROSPECT_MANAGER rights needed securityCheck(id, AccessRights.INTROSPECT_MANAGER); // list of clients matching search pattern ArrayList<ClientInfo> list = new ArrayList<ClientInfo>(); // check clients synchronized (clients) { int h = clients.first(); while (h != 0) { ClientInfo clientInfo = (ClientInfo)clients.get(h); /* Matcher m = pattern.matcher(clientInfo.getName()); if (m.matches()) */ if (WildcharMatcher.match(name_wc, clientInfo.getName())) list.add(clientInfo); h = clients.next(h); } } // check administrators synchronized (administrators) { int h = administrators.first(); while (h != 0) { ClientInfo clientInfo = (ClientInfo)administrators.get(h); /* Matcher m = pattern.matcher(clientInfo.getName()); if (m.matches()) */ if (WildcharMatcher.match(name_wc, clientInfo.getName())) list.add(clientInfo); h = administrators.next(h); } } // copy to array info = new ClientInfo[list.size()]; list.toArray(info); } /****************************************************************/ return info; } /** * @see com.cosylab.acs.maci.Manager#getComponentInfo(int, int[], String, String, boolean) */ // TODO MF all (using wildchars match for domain names) interdomain queries public ComponentInfo[] getComponentInfo(int id, int[] handles, String name_wc, String type_wc, boolean activeOnly) throws AcsJNoPermissionEx { if (handles == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'handles' sequence expected."); throw af; } else if (handles.length == 0 && (name_wc == null || type_wc == null)) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'names_wc' or' type_wc' sequence expected."); throw af; } /****************************************************************/ // the caller must have INTROSPECT_MANAGER access rights, // or it must have adequate privileges to access the component (the same as with the get_component method). securityCheck(id, AccessRights.NONE); // info to be returned ComponentInfo[] info = null; // get info of requested handles if (handles.length > 0) { info = new ComponentInfo[handles.length]; for (int i = 0; i < handles.length; i++) { // access rights to be checked here... info[i] = getComponentInfo(handles[i]); // filter out unavailable if (info[i] != null && info[i].getComponent() == null) info[i] = null; } } // use name_wc and type_wc as search criteria else { // check for inter-domain search if (name_wc.startsWith(CURL_URI_SCHEMA)) { URI curl = null; try { curl = CURLHelper.createURI(name_wc); if (curl.getAuthority() != null && curl.getAuthority().indexOf('*') >= 0) throw new IllegalArgumentException("Wildchars not supported in domain names."); } catch (URISyntaxException e) { // BAD_PARAM BadParametersException af = new BadParametersException("Invalid CURL syntax in 'names_wc'."); throw af; } name_wc = extractName(curl); Manager remoteManager = null; // if not local do inter-domain query if (name_wc.startsWith(CURL_URI_SCHEMA)) { // TODO MF do the login? try { String domainName = curl.getAuthority(); remoteManager = getManagerForDomain(domainName); if (remoteManager == null) throw new CoreException("Failed to obtain manager for domain '" + domainName + "'."); } catch (Throwable th) { logger.log(Level.WARNING, "Failed to obtain non-local manager required by CURL '"+curl+"'.", th); return null; } } try { // local name to be used String localName = curl.getPath(); if (localName.charAt(0) == '/') localName = localName.substring(1); ComponentInfo[] infos = remoteManager.getComponentInfo(INTERDOMAIN_MANAGER_HANDLE, handles, localName, type_wc, false); if (infos != null) { // prefix names final String prefix = CURL_URI_SCHEMA + curl.getAuthority() + "/"; for (int i = 0; i < infos.length; i++) { if (!infos[i].getName().startsWith(CURL_URI_SCHEMA)) infos[i].setName(prefix + infos[i].getName()); String containerName = infos[i].getContainerName(); if (containerName != null && !containerName.startsWith(CURL_URI_SCHEMA)) infos[i].setContainerName(prefix + containerName); } } return infos; } catch (Exception ex) { RemoteException re = new RemoteException("Failed to obtain component infos for CURL '"+curl+"' from remote manager.", ex); reportException(re); return null; } } // map of components to be returned Map<String, ComponentInfo> map = new HashMap<String, ComponentInfo>(); // read active/registered components componentsLock.lock(); try { int h = components.first(); while (h != 0) { ComponentInfo componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getComponent() != null && WildcharMatcher.match(name_wc, componentInfo.getName()) && WildcharMatcher.match(type_wc, componentInfo.getType())) { // access rights to be checked here... // found the match, add existing info to list map.put(componentInfo.getName(), componentInfo); } h = components.next(h); } } finally { componentsLock.unlock(); } // add also non-active, if requested if (!activeOnly) { DAOProxy componentsDAO = getComponentsDAOProxy(); if (componentsDAO != null) { try { // get names of all components /*String[] ids =*/ componentsDAO.get_field_data(""); /// @TODO here to check if CDB is available String[] ids = getComponentsList(); // test names for (int i = 0; i < ids.length; i++) { // read name String name = ids[i]; //readStringCharacteristics(componentsDAO, ids[i]+"/Name"); if (name == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no name of component '"+ids[i]+"' defined."); continue; } // add if not already added and matches criteria if (!map.containsKey(name) && //!name.equals(ComponentSpec.COMPSPEC_ANY) && name.indexOf(ComponentSpec.COMPSPEC_ANY) != 0 && WildcharMatcher.match(name_wc, name)) { // read type String type = readStringCharacteristics(componentsDAO, ids[i]+"/Type"); if (type == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no type of component '"+name+"' defined."); continue; } // test type if (!type.equals(ComponentSpec.COMPSPEC_ANY) && WildcharMatcher.match(type_wc, type)) { // read code String code = readStringCharacteristics(componentsDAO, ids[i]+"/Code"); if (code == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no code of component '"+name+"' defined."); continue; } // test code if (code.equals(ComponentSpec.COMPSPEC_ANY)) continue; // read container String container = readStringCharacteristics(componentsDAO, ids[i]+"/Container"); if (container == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no container name of component '"+name+"' defined."); continue; } // test container if (container.equals(ComponentSpec.COMPSPEC_ANY)) continue; // got the match // access rights to be checked here... // create info and put it into list ComponentInfo retInfo = new ComponentInfo(0, name, type, code, null); retInfo.setContainerName(container); map.put(name, retInfo); } } } } catch (Exception ex) { CoreException ce = new CoreException("Failed to obtain component data from the CDB.", ex); reportException(ce); } } } // copy to array info = new ComponentInfo[map.size()]; map.values().toArray(info); } /****************************************************************/ return info; } /** * @see com.cosylab.acs.maci.Manager#getService(int, java.net.URI, boolean, StatusHolder) */ public Component getService(int id, URI curl, boolean activate, StatusHolder status) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx { return getComponent(id, curl, activate, status, true); } /** * @see com.cosylab.acs.maci.Manager#getComponent(int, URI, boolean, StatusHolder) */ public Component getComponent(int id, URI curl, boolean activate, StatusHolder status) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx { return getComponent(id, curl, activate, status, false); } /** * @see #getComponent */ private Component getComponent(int id, URI curl, boolean activate, StatusHolder status, boolean allowServices) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx { AcsJCannotGetComponentEx ex2 = null; // extract name String name = extractName(curl); // check if null try { checkCURL(curl); } catch (AcsJBadParameterEx e) { e.setParameter("curl"); ex2 = new AcsJCannotGetComponentEx(e); ex2.setCURL(name); throw ex2; } if (status == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("status"); ex2 = new AcsJCannotGetComponentEx(ex); ex2.setCURL(name); throw ex2; } /****************************************************************/ // log info String requestorName = null; if (id != 0) { requestorName = getRequestorName(id); logger.log(Level.INFO,"'" + requestorName + "' requested component '" + curl + "'."); } else logger.log(Level.INFO,"Request for component '" + curl + "' issued."); // no login required for predefined objects (services) Component component = null; // "Manager" is a special service Component if (allowServices && name.equals("Manager")) { if (managerComponentReference != null) status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); else status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); component = new ServiceComponent(managerComponentReference); } // "NameService" is also a special service Component else if (allowServices && name.equals("NameService")) { if (remoteDirectoryComponentReference != null) status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); else status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); component = new ServiceComponent(remoteDirectoryComponentReference); } else if (allowServices && !name.startsWith(CURL_URI_SCHEMA) && isServiceComponent(name)) { Object obj = lookup(name, null); // set status if (obj != null) status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); else status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); component = new ServiceComponent(obj); } else { // check handle and NONE permissions securityCheck(id, AccessRights.NONE); try { component = internalRequestComponent(id, curl, status, activate); } catch (Throwable ce) { ex2 = new AcsJCannotGetComponentEx(ce); } } // log info if (component != null && component.getObject() != null) { if (requestorName != null) logger.log(Level.INFO, "Component '" + curl + "' provided to '" + requestorName + "'."); else logger.log(Level.INFO,"Component '" + curl + "' provided."); } else if (ex2 == null && !activate && status.getStatus() == ComponentStatus.COMPONENT_NOT_ACTIVATED) { if (requestorName != null) logger.log(Level.INFO,"Request from '" + requestorName + "' for component '" + curl + "' completed sucessfully, but component not activated."); else logger.log(Level.INFO,"Request for component '" + curl + "' completed sucessfully, but component not activated."); } /** * @todo GCH 2006.09.25 * This last case should never happen, because * there should be and exception thrown instead. */ else { if (ex2 == null) ex2 = new AcsJCannotGetComponentEx(); // we log the exceptions only if level is low enough (to allow diagnostic on manager size) // it's clients responibility to handle the exception if (requestorName != null) { if (logger.isLoggable(Level.FINE)) logger.log(Level.WARNING,"Failed to provide component '" + curl + "' to '" + requestorName + "'.", ex2); else logger.log(Level.WARNING,"Failed to provide component '" + curl + "' to '" + requestorName + "'."); } else { if (logger.isLoggable(Level.FINE)) logger.log(Level.WARNING,"Failed to provide component '" + curl + "'.", ex2); else logger.log(Level.FINE,"Failed to provide component '" + curl + "'."); } } /****************************************************************/ if(ex2 != null) { ex2.setCURL(name); throw ex2; } return component; } /** * @see com.cosylab.acs.maci.Manager#getComponentNonSticky(int id, URI curl) */ public Component getComponentNonSticky(int id, URI curl) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx { // extract name String name = extractName(curl); // check if null try { checkCURL(curl); } catch (AcsJBadParameterEx e) { AcsJCannotGetComponentEx ex2 = new AcsJCannotGetComponentEx(e); ex2.setCURL(name); throw ex2; } /****************************************************************/ // check handle and NONE permissions securityCheck(id, AccessRights.NONE); // log info String requestorName = getRequestorName(id); logger.log(Level.FINE,"'" + requestorName + "' requested non-sticky component '" + curl + "'."); Component component = null; componentsLock.lock(); try { int h = components.first(); while (h != 0) { ComponentInfo ci = (ComponentInfo)components.get(h); if (ci.getName().equals(name)) { component = ci.getComponent(); break; } h = components.next(h); } } finally { componentsLock.unlock(); } // log info if (component != null && component.getObject() != null) logger.log(Level.FINE,"Non-sticky component '" + curl + "' provided to '" + requestorName + "'."); else logger.log(Level.INFO,"Failed to provide non-sticky component '" + curl + "' to '" + requestorName + "'."); /****************************************************************/ return component; } /** * @see com.cosylab.acs.maci.Manager#makeComponentImmortal(int, java.net.URI, boolean) */ public void makeComponentImmortal(int id, URI curl, boolean immortalState) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx, AcsJBadParameterEx { // extract name String name = extractName(curl); // check if null // let same exception flying up try { checkCURL(curl); } catch (AcsJBadParameterEx e) { throw e; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ int h; ComponentInfo componentInfo = null; componentsLock.lock(); try { h = components.first(); while (h != 0) { componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(name)) { h = componentInfo.getHandle(); break; } h = components.next(h); } // component not yet activated check if (h == 0) { NoResourcesException af = new NoResourcesException("Component not activated."); throw af; } // if not an owner of the component, check administrator rights if (!componentInfo.getClients().contains(id)) { securityCheck(id, AccessRights.INTROSPECT_MANAGER); } if (immortalState) { // finally, add manager as an owner if (!componentInfo.getClients().contains(this.getHandle())) { // ACID - !!! executeCommand(new ComponentCommandClientAdd(componentInfo.getHandle() & HANDLE_MASK, this.getHandle())); //componentInfo.getClients().add(this.getHandle()); } logger.log(Level.INFO,"Component " + name + " was made immortal."); } } finally { componentsLock.unlock(); } // this must be done outside component sync. block if (!immortalState) { logger.log(Level.INFO,"Component " + name + " was made mortal."); // finally, can happen that the manager is the only owner // so release could be necessary internalReleaseComponent(this.getHandle(), h, false); } /****************************************************************/ } /** * @see com.cosylab.acs.maci.Manager#login(Client) */ public ClientInfo login(Client reference) throws AcsJNoPermissionEx { // check if already shutdown if (shutdown.get()) { // already shutdown AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Manager in shutdown state."); throw npe; } if (reference == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'reference' expected."); throw af; } /****************************************************************/ ClientInfo info = null; try { long executionId = generateExecutionId(); AuthenticationData reply = reference.authenticate(executionId, "Identify yourself"); if (reply == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Invalid response to 'Client::authenticate()' method - non-null structure expected."); throw af; } else if (reply.getClientType() == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Invalid response to 'Client::authenticate()' method - non-null client type expected."); throw af; } else if (reply.getImplLang() == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Invalid response to 'Client::authenticate()' method - no-null implementation language expected."); throw af; } // get client's name String name = reference.name(); if (name == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Invalid response to 'Client::name()' method - non-null string expected."); throw af; } logger.log(Level.FINE,"'"+name+"' is logging in."); final long timeStamp = reply.getTimeStamp() > 0 ? reply.getTimeStamp() : System.currentTimeMillis(); if (reply.getExecutionId() != 0) executionId = generateExecutionId(); // delegate switch (reply.getClientType()) { // container case CONTAINER: if (reference instanceof Container) { info = containerLogin(name, reply, (Container)reference, timeStamp, executionId); } else { // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Given reply to 'Client::authenticate()' method indicated container login, but given reference does not implement 'maci::Container' interface."); npe.setID(name); throw npe; } break; // client case CLIENT: info = clientLogin(name, reply, reference, timeStamp, executionId); break; // supervisor (administrator) case ADMINISTRATOR: if (reference instanceof Administrator) { info = administratorLogin(name, reply, (Administrator)reference, timeStamp, executionId); } else { // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Given reply to 'Client::authenticate()' method indicated administrator login, but given reference does not implement 'maci::Administrator' interface."); npe.setID(name); throw npe; } break; default: assert(false); } } catch (AcsJNoPermissionEx npe) { throw npe; } catch (BadParametersException bpe) { throw bpe; } catch (NoResourcesException nre) { throw nre; } catch (RemoteException re) { // TODO @todo exception RuntimeException rt = new RuntimeException("Exception caught while examining the client. Login rejected.", re); throw rt; } catch (Throwable ex) { // TODO @todo exception RuntimeException rt = new RuntimeException("Unexpected exception during login. Login rejected.", ex); throw rt; } /****************************************************************/ logger.log(Level.FINE,"Client with handle '" + HandleHelper.toString(info.getHandle()) + "' has logged in."); return info; } /** * @see com.cosylab.acs.maci.Manager#logout(int) */ public void logout(int id) throws AcsJNoPermissionEx { if (isShuttingDown()) { // Skip because the manager is shutting down logger.log(Level.FINE,"Logout "+HandleHelper.toString(id)+" skipped because the Manager is shutting down"); return; } logout(id, false); } /** * Logout client. * @param id client handle. * @param pingFailed flag indicating that ping has failed (i.e. reason for logout). * @see com.cosylab.acs.maci.Manager#logout(int) */ public void logout(int id, boolean pingFailed) throws AcsJNoPermissionEx { if (pingFailed) logger.log(Level.FINE,"Client with handle '" + HandleHelper.toString(id) + "' is being forcefully logged out due to its unresponsiveness."); else logger.log(Level.FINE,"Client with handle '" + HandleHelper.toString(id) + "' is logging out."); // check handle, no special rights needed for logout // AcsJNoPermissionEx flies directly up from securityCheck() securityCheck(id, 0); /****************************************************************/ switch (id & TYPE_MASK) { case CONTAINER_MASK: containerLogout(id, pingFailed); break; case CLIENT_MASK: clientLogout(id, pingFailed); break; case ADMINISTRATOR_MASK: administratorLogout(id, pingFailed); break; } /****************************************************************/ } /** * @see com.cosylab.acs.maci.Manager#registerComponent(int, URI, String, Component) */ public int registerComponent(int id, URI curl, String type, Component component) throws AcsJNoPermissionEx, AcsJBadParameterEx { // check for null if (curl == null ) { AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("curl"); af.setParameterValue("null"); throw af; } if (type == null) { AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("type"); af.setParameterValue("null"); throw af; } if (component == null) { AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("component"); af.setParameterValue("null"); throw af; } // checks CURL, reject non-local domain curls // Just rethrow the exception try { checkCURL(curl, false); } catch (AcsJBadParameterEx e) { throw e; } // check handle and REGISTER_COMPONENT permissions securityCheck(id, AccessRights.REGISTER_COMPONENT); /****************************************************************/ // extract name String name = extractName(curl); int h = 0; componentsLock.lock(); try { // check if Component is already registred // if it is, return existing info h = components.first(); while (h != 0) { ComponentInfo registeredComponentInfo = (ComponentInfo)components.get(h); if (registeredComponentInfo.getName().equals(name)) { if (registeredComponentInfo.getType().equals(type)) { // it is already activated, add manager as an owner and return handle if (!registeredComponentInfo.getClients().contains(this.getHandle())) { // ACID - !!! executeCommand(new ComponentCommandClientAdd(registeredComponentInfo.getHandle() & HANDLE_MASK, this.getHandle())); //registredComponentInfo.getClients().add(this.getHandle()); } return registeredComponentInfo.getHandle(); } else { AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Component with name '"+name+"' but different type already registered."); npe.setID(HandleHelper.toString(id)); npe.setProtectedResource(name); throw npe; } } h = components.next(h); } // allocate new handle // !!! ACID 2 Integer objHandle = (Integer)executeCommand(new ComponentCommandAllocate()); int handle; //int handle = components.allocate(); if (objHandle == null || (handle = objHandle.intValue()) == 0) { NoResourcesException af = new NoResourcesException("Generation of new handle failed, too many components registred."); throw af; } // generate external handle h = handle | COMPONENT_MASK; // add generated key h |= (random.nextInt(0x100)) << 16; // create new component info ComponentInfo componentInfo = new ComponentInfo(h, name, type, null, component); // no container componentInfo.setContainer(0); componentInfo.setContainerName(null); // components can register other components componentInfo.setAccessRights(AccessRights.REGISTER_COMPONENT); // set Manager as client of the Component (to keep it immortal) componentInfo.getClients().add(this.getHandle()); // set interfaces // NOTE: this could block since it is a remote call componentInfo.setInterfaces(component.implementedInterfaces()); // !!! ACID - register AddComponentCommand executeCommand(new ComponentCommandSet(handle, componentInfo)); // store info //components.set(handle, componentInfo); } finally { componentsLock.unlock(); } // bind to remote directory // NOTE: this could block since it is a remote call //bind(convertToHiearachical(name), "O", component); logger.log(Level.INFO,"Component '"+name+"' registered."); /****************************************************************/ return h; } /** * @see com.cosylab.acs.maci.Manager#unregisterComponent(int, int) */ public void unregisterComponent(int id, int h) throws AcsJNoPermissionEx, AcsJBadParameterEx { // check handle and REGISTER_COMPONENT permissions securityCheck(id, AccessRights.REGISTER_COMPONENT); /****************************************************************/ internalReleaseComponent(this.getHandle(), h, false); logger.log(Level.INFO,"Component with handle '"+HandleHelper.toString(h)+"' unregistered."); /****************************************************************/ } /** * @see com.cosylab.acs.maci.Manager#restartComponent(int, URI) */ public Component restartComponent(int id, URI curl) throws AcsJNoPermissionEx, AcsJBadParameterEx { // checks CURL // let same exception fly up // TODO MF tmp, reject non-local domains try { checkCURL(curl, false); } catch (AcsJBadParameterEx e) { throw e; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ Component component = internalRestartComponent(id, curl); if (component != null) logger.log(Level.INFO,"Component '"+curl+"' restarted."); else logger.log(Level.INFO,"Failed to restart component '"+curl+"'."); /****************************************************************/ return component; } /** * @see com.cosylab.acs.maci.Manager#releaseComponent(int, URI) */ public int releaseComponent(int id, URI curl) throws AcsJNoPermissionEx, AcsJBadParameterEx { // checks CURL // throw up same exceptions try { checkCURL(curl); } catch (AcsJBadParameterEx e) { throw e; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ // log info String requestorName = getRequestorName(id); logger.log(Level.INFO,"'" + requestorName + "' requested release of component '" + curl + "'."); int owners = internalReleaseComponent(id, curl, id == this.getHandle()).owners; logger.log(Level.INFO,"Component '" + curl + "' released by '" + requestorName + "'."); /****************************************************************/ return owners; } /** * @see com.cosylab.acs.maci.Manager#releaseComponentAsync(int, java.net.URI, com.cosylab.acs.maci.Manager.LongCompletionCallback) */ public void releaseComponentAsync(int id, URI curl, LongCompletionCallback callback) throws AcsJNoPermissionEx, AcsJBadParameterEx { // checks CURL // throw up same exceptions try { checkCURL(curl); } catch (AcsJBadParameterEx e) { throw e; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ // log info final String requestorName = getRequestorName(id); logger.log(Level.INFO,"'" + requestorName + "' requested async release of component '" + curl + "'."); final int fid = id; final URI fcurl = curl; final boolean force = (id == this.getHandle()); final LongCompletionCallback fcallback = callback; threadPool.execute(new Runnable() { public void run() { try { ReleaseComponentResult rcr = internalReleaseComponent(fid, fcurl, force); if (fcallback != null) { if (rcr.exception == null) { logger.log(Level.INFO,"Component '" + fcurl + "' async released by '" + requestorName + "'."); fcallback.done(rcr.owners); } else { logger.log(Level.INFO,"Component '" + fcurl + "' async released by '" + requestorName + " with failure'."); fcallback.failed(rcr.owners, rcr.exception); } } } catch (Throwable th) { if (fcallback != null) fcallback.failed(-1, th); } } }); } /** * @see com.cosylab.acs.maci.Manager#forceReleaseComponent(int, URI) */ public int forceReleaseComponent(int id, URI curl) throws AcsJNoPermissionEx, AcsJBadParameterEx { // checks CURL // let same exception fly up try { checkCURL(curl); } catch (AcsJBadParameterEx e) { throw e; } // check handle and INTROSPECT_MANAGER permissions // TODO not clean !!! admin permissions required //securityCheck(id, AccessRights.INTROSPECT_MANAGER); // hack by HSO 2006-07-19: ACS 5.0.4 temporarily offers the forceReleaseComponent in the AdvancedContainerServices, // which will be removed when we have implemented the concept of non-sticky clients whose references must not prevent // the unloading of a component. // This feature will be used by some master components, which just cannot be admin/supervisors for the manager, // therefore we temporarily have to grant access to just any kind of client. securityCheck(id, AccessRights.NONE); /****************************************************************/ // log info String requestorName = getRequestorName(id); logger.log(Level.INFO,"'" + requestorName + "' requested forceful release of component '" + curl + "'."); int owners = internalReleaseComponent(id, curl, true).owners; logger.log(Level.INFO,"Component '" + curl + "' forcefully released by '" + requestorName + "'."); /****************************************************************/ return owners; } /** * @see com.cosylab.acs.maci.Manager#releaseComponents(int, URI[]) */ public void releaseComponents(int id, URI[] curls) throws AcsJNoPermissionEx { // check if null if (curls == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null CURLs expected."); throw af; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ int released = 0; for (int i = 0; i < curls.length; i++) { try { releaseComponent(id, curls[i]); released++; } catch (Exception ex) { CoreException ce = new CoreException("Failed to release component '"+curls[i]+"'.", ex); reportException(ce); } } logger.log(Level.INFO,released + " of " + curls.length +" components released."); /****************************************************************/ } /** * Checks if container's state (e.g. not in shutdown state). * @param containerInfo container's info to be checked. */ private void checkContainerShutdownState(ContainerInfo containerInfo) throws NoResourcesException { String containerName = containerInfo.getName(); if (pendingContainerShutdown.contains(containerName)) { // NO_RESOURCES NoResourcesException nre = new NoResourcesException("Container '" + containerName + "' is being shutdown."); throw nre; } } /** * @see com.cosylab.acs.maci.Manager#shutdownContainer(int, java.lang.String, int) */ public void shutdownContainer(int id, String containerName, int action) throws AcsJNoPermissionEx { // check if null if (containerName == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'containerName' expected."); throw af; } /****************************************************************/ // the caller must have SHUTDOWN_SYSTEM access rights, securityCheck(id, AccessRights.SHUTDOWN_SYSTEM); Container container; ContainerInfo containerInfo = getContainerInfo(containerName); if (containerInfo == null || (container = containerInfo.getContainer()) == null) { // NO_RESOURCES NoResourcesException nre = new NoResourcesException("Container '" + containerName + "' not logged in."); throw nre; } pendingContainerShutdown.add(containerInfo.getName()); try { // release components try { // get shutdown order ComponentInfo[] infos = topologySortManager.getComponentShutdownOrder(containerInfo); releaseComponents(infos); } catch (Throwable th) { CoreException ce = new CoreException("Failed to release components on container '" + containerName + "'.", th); reportException(ce); } // shutdown (or disconnect) try { if (action == 0) container.disconnect(); else container.shutdown(action); } catch (Throwable th) { // NO_RESOURCES NoResourcesException nre = new NoResourcesException("Failed to shutdown container '" + containerName + "'.", th); throw nre; } } finally { pendingContainerShutdown.remove(containerInfo.getName()); } /****************************************************************/ } /** * Release components (using manager handle) * @param infos components to release */ private void releaseComponents(ComponentInfo[] infos) throws AcsJNoPermissionEx { if (infos.length > 0) { // map strings to CURLs URI[] uris = new URI[infos.length]; for (int i = 0; i < infos.length; i++) { try { uris[i] = CURLHelper.createURI(infos[i].getName()); } catch (URISyntaxException usi) { BadParametersException hbpe = new BadParametersException(usi.getMessage(), usi); reportException(hbpe); } } // release components releaseComponents(getHandle(), uris); } } /* * Used to store original values and then restored after called via shutdownImplementation. * Default values are values when called via shutdownImplemention. */ private volatile int originalId = MANAGER_MASK; private volatile int originalContainers = 0; /** * @see com.cosylab.acs.maci.Manager#shutdown(int, int) */ public void shutdown(int id, int containers) throws AcsJNoPermissionEx { // check handle and SHUTDOWN_SYSTEM permissions securityCheck(id, AccessRights.SHUTDOWN_SYSTEM); if (id == MANAGER_MASK) id = originalId; else originalId = id; if (containers == 0) containers = originalContainers; else originalContainers = containers; /****************************************************************/ // always destroy through destroy implementation // if application is not destroying already, destroy it if (shutdownImplementation != null && !shutdownImplementation.isShutdownInProgress()) { // spawn another thread new Thread(new Runnable() { public void run() { // fire destroy application if (!shutdownImplementation.isShutdownInProgress()) shutdownImplementation.shutdown(false); } } , "ManagerApplicationShutdown").start(); return; } /****************************************************************/ // check if already shutdown if (shutdown.getAndSet(true)) { // already shutdown AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Manager already in shutdown state."); npe.setID(HandleHelper.toString(id)); throw npe; } /****************************************************************/ logger.log(Level.INFO,"Manager is shutting down."); logger.log(Level.FINER,"Canceling heartbeat task."); // cancel hertbeat task heartbeatTask.cancel(); topologySortManager.destroy(); /* * Are those actions OK? don't we want to have silent (unnoticable) restarts of the manager?! * There probably should be several shutdown modes - from silent to whole system shutdown... * For the time being sileny mode is implemented. * logger.finer("Releasing immortal components."); /// !!! TBD logger.finer("Notifying clients and administrators to disconnect."); notifyClientDisconnectShutdown(); */ // if not "silent" shutdown if (containers != 0) { logger.log(Level.FINER,"Releasing all components in the system."); try { topologySortManager.requestTopologicalSort(); releaseComponents(topologySortManager.getComponentShutdownOrder(null)); } catch (Throwable th) { CoreException ce = new CoreException("Failed to release all components in the system.", th); reportException(ce); } } logger.log(Level.FINER,"Notifying containers to disconnect or shutdown."); notifyContainerDisconnectShutdown(containers); // finalizeFearation finalizeFederation(); // process tasks in thread pool // !!! NOTE: this could block (for a long time) logger.log(Level.FINER,"Waiting for tasks in thread pool to complete..."); threadPool.shutdown(); try { if (!threadPool.awaitTermination(3, TimeUnit.SECONDS)) threadPool.shutdownNow(); } catch (InterruptedException ie) { /* noop */ } if (alarmSource != null) { alarmSource.tearDown(); } // unbind Manager unbind("Manager", null); setCDBAccess(null); // release CDB DAO daos destroyComponetsDAOProxy(); destroyContainersDAOProxy(); destroyManagerDAOProxy(); logger.log(Level.INFO,"Manager shutdown completed."); /****************************************************************/ } /*****************************************************************************/ /**************************** [ Login methods ] ******************************/ /*****************************************************************************/ /** * Container specific login method. * @param name name of the container, non-<code>null</code>. * @param reply reply to authenticate method, non-<code>null</code>. * @param container container that is logging in, non-<code>null</code>. * @return ClientInfo client info. of newly logged container */ private ClientInfo containerLogin(String name, AuthenticationData reply, Container container, long timeStamp, long executionId) throws AcsJNoPermissionEx { assert (name != null); assert (reply != null); assert (container != null); TimerTaskContainerInfo containerInfo = null; ClientInfo clientInfo = null; boolean existingLogin = false; synchronized (containers) { // check if container is already logged in, // if it is, return existing info int h = containers.first(); while (h != 0) { ContainerInfo loggedContainerInfo = (ContainerInfo)containers.get(h); // containers are persistent and this will not work //if (container.equals(loggedContainerInfo.getContainer())) if (name.equals(loggedContainerInfo.getName())) { Container loggedContainer = loggedContainerInfo.getContainer(); if (loggedContainer != null) { // if same instance simply recover, if not... if (!loggedContainer.equals(container)) { // check if logged container is alive, if it is reject him boolean alive = false; try { int lh = loggedContainer.get_handle(); if (lh != 0 && lh == loggedContainerInfo.getHandle()) alive = true; } catch (Throwable th) { // noop } if (alive) { AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); String address = ""; try { address = " " + loggedContainer.getRemoteLocation(); } catch (Throwable th) { /* noop */ } npe.setReason("Rejecting container login, container with name '" + name + "'" + address + " already logged in."); npe.setID(HandleHelper.toString(loggedContainerInfo.getHandle())); npe.setProtectedResource(name); throw npe; } else logger.log(Level.FINER, "Container '" + name + "' is no longer functional, new container is taking over."); } } // !!! ACID 2 // new reference is considered as better executeCommand(new ContainerCommandUpdate(h, container)); //loggedContainerInfo.setContainer(container); existingLogin = true; // generate ClientInfo containerInfo = (TimerTaskContainerInfo)loggedContainerInfo; clientInfo = containerInfo.createClientInfo(); break; } h = containers.next(h); } // new container if (h == 0) { long pingInterval = 0; DAOProxy dao = getContainersDAOProxy(); if (dao != null) { String impLang = readStringCharacteristics(dao, name + "/ImplLang", true); ImplLang configuredImplLang = ImplLang.fromString(impLang); if (configuredImplLang != ImplLang.not_specified && configuredImplLang != reply.getImplLang()) { AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Rejecting container login, container reported '" + reply.getImplLang() + "' implementation language, but configured '" + configuredImplLang + "'."); npe.setProtectedResource(name); throw npe; } pingInterval = readLongCharacteristics(dao, name + "/PingInterval", -1, true)*1000; } // allocate new handle // !!! ACID 2 Integer objHandle = (Integer)executeCommand(new ContainerCommandAllocate()); int handle; //int handle = containers.allocate(); if (objHandle == null || (handle = objHandle.intValue()) == 0) { NoResourcesException af = new NoResourcesException("Generation of new handle failed, too many containers logged in."); throw af; } // generate external handle h = handle | CONTAINER_MASK; // add generated key h |= (random.nextInt(0x100)) << 16; // create new container info containerInfo = new TimerTaskContainerInfo(h, name, container, containerPingInterval); containerInfo.setImplLang(reply.getImplLang()); if (pingInterval >= 1000) // safety limit containerInfo.setPingInterval(pingInterval); clientInfo = containerInfo.createClientInfo(); // register container to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, clientInfo, alarmSource); containerInfo.setTask(task); heartbeatTask.schedule(task, containerInfo.getPingInterval(), containerInfo.getPingInterval()); // !!! ACID - register AddContainerCommand executeCommand(new ContainerCommandSet(handle, containerInfo)); // store info //containers.set(handle, containerInfo); } } // cancel all "old" container async request AcsJCannotGetComponentEx acgcex = new AcsJCannotGetComponentEx(); acgcex.setReason("Request canceled due to container re-login."); cancelPendingContainerAsyncRequestWithException(containerInfo.getName(), acgcex); final boolean recoverContainer = reply.isRecover(); if (existingLogin) { // merge container's and manager's internal state containerInternalStateMerge(containerInfo, recoverContainer); } // notify administrators about the login notifyContainerLogin(containerInfo, timeStamp, executionId); // do container post login activation in separate thread final ContainerInfo finalInfo = containerInfo; threadPool.execute( new Runnable() { public void run() { containerPostLoginActivation(finalInfo, recoverContainer); } }); logger.log(Level.INFO,"Container '" + name + "' logged in."); return clientInfo; } /** * Container post login activation, activate startup and unavailable components. * NOTE: to be run in separate thread. * * @param containerInfo container info for which to perform post login activation. * @param recoverContainer recovery mode flag. */ private void containerPostLoginActivation(final ContainerInfo containerInfo, boolean recoverContainer) { // give containers some time to fully initialize if(containerInfo.getImplLang() == ImplLang.cpp || containerInfo.getImplLang() == ImplLang.not_specified){ try { Thread.sleep(3000); } catch (InterruptedException ie) { } } // shutdown check if (isShuttingDown()) return; // TODO what if it has already logged out? // CDB startup if (cdbActivation != null && containerInfo.getName().equals(cdbActivation.getContainer())) { try { StatusHolder status = new StatusHolder(); ComponentInfo cdbInfo = internalRequestComponent(this.getHandle(), cdbActivation.getName(), cdbActivation.getType(), cdbActivation.getCode(), cdbActivation.getContainer(), RELEASE_IMMEDIATELY, status, true); if (status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) logger.log(Level.SEVERE,"Failed to activate CDB, reason: '"+status.getStatus()+"'."); else if (cdbInfo == null || cdbInfo.getHandle() == 0 || cdbInfo.getComponent() == null) logger.log(Level.SEVERE,"Failed to activate CDB, invalid ComponentInfo returned: '"+cdbInfo+"'."); else { logger.log(Level.INFO,"CDB activated on container '" + containerInfo.getName() + "'."); } } catch (Throwable ex) { logger.log(Level.SEVERE, "Failed to activate CDB on container '" + containerInfo.getName() + "'.", ex); } } // used for fast lookups Map<String, Integer> activationRequests = new HashMap<String, Integer>(); // order is important, preserve it ArrayList<URI> activationRequestsList = new ArrayList<URI>(); // get CDB access daos DAOProxy dao = getManagerDAOProxy(); DAOProxy componentsDAO = getComponentsDAOProxy(); // // autostart components (Manager.Startup array) - TODO to be removed (left for backward compatibility) // if (dao != null && componentsDAO != null) { try { // query startup components String[] startup = dao.get_string_seq("Startup"); final Integer managerHandle = new Integer(this.getHandle()); for (int i = 0; i < startup.length; i++) { // TODO simulator test workaround if (startup[i].length() == 0) continue; // read container field String containerName = readStringCharacteristics(componentsDAO, startup[i]+"/Container"); if (containerName == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no container of component '"+startup[i]+"' defined."); continue; } // if container name matches, add activation request if (containerInfo.getName().equals(containerName)) { try { URI curl = CURLHelper.createURI(startup[i]); // check CURL try { checkCURL(curl); } catch (RuntimeException e) { // @todo Auto-generated catch block e.printStackTrace(); } activationRequestsList.add(curl); activationRequests.put(startup[i], managerHandle); } catch (URISyntaxException usi) { logger.log(Level.WARNING, "Failed to create URI from component name '"+startup[i]+"'.", usi); } } } } catch (Throwable th) { logger.log(Level.WARNING, "Failed to retrieve list of startup components.", th); } } // // autostart components (<component>.Autostart attribute) // final String TRUE_STRING = "true"; if (componentsDAO != null) { try { final Integer managerHandle = new Integer(this.getHandle()); // get names of all components /*String[] ids =*/ componentsDAO.get_field_data(""); // TODO here to check if CDB is available String[] ids = getComponentsList(); // test names for (int i = 0; i < ids.length; i++) { // read name String name = ids[i]; //readStringCharacteristics(componentsDAO, ids[i]+"/Name"); if (name == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no name of component '"+ids[i]+"' defined."); continue; } // read autostart silently String autostart = readStringCharacteristics(componentsDAO, ids[i]+"/Autostart", true); if (autostart == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no autostart attribute of component '"+ids[i]+"' defined."); continue; } else if (autostart.equalsIgnoreCase(TRUE_STRING) && !activationRequests.containsKey(name) /* TODO to be removed */ ) { // read container silently String componentContainer = readStringCharacteristics(componentsDAO, ids[i]+"/Container", true); if (componentContainer == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no container attribute of component '"+ids[i]+"' defined."); continue; } else if (!containerInfo.getName().equals(componentContainer)) continue; try { URI curl = CURLHelper.createURI(name); // check CURL, no non-local curls try { checkCURL(curl, false); } catch (RuntimeException e) { reportException(e); } activationRequestsList.add(curl); activationRequests.put(name, managerHandle); } catch (URISyntaxException usi) { logger.log(Level.WARNING, "Failed to create URI from component name '"+name+"'.", usi); } } } } catch (Throwable ex) { logger.log(Level.WARNING, "Failed to retrieve list of components.", ex); } } // list of componentInfo to be cleaned up (cannot be immediately, due to lock) ArrayList<ComponentInfo> cleanupList = new ArrayList<ComponentInfo>(); // // check unavailable components if (unavailableComponents.size() > 0) { if (componentsDAO != null) { try { final Integer reactivateHandle = new Integer(0); synchronized (unavailableComponents) { String[] names = unavailableComponents.keySet().toArray(new String[unavailableComponents.size()]); // reverse for (int i = names.length - 1; i >= 0; i--) { String name = names[i]; boolean reactivate = false; // dynamic component check ComponentInfo componentInfo = unavailableComponents.get(name); if (componentInfo != null && componentInfo.isDynamic() && componentInfo.getDynamicContainerName() != null && componentInfo.getDynamicContainerName().equals(containerInfo.getName())) { // reactivate dynamic component reactivate = true; } else { // read container field String containerName = readStringCharacteristics(componentsDAO, name+"/Container"); if (containerName == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no container of component '"+name+"' defined."); //continue; } // if container name matches, add (override) activation request else if (containerInfo.getName().equals(containerName)) { reactivate = true; } } if (reactivate) { // clean up if in non-recovery mode if (!recoverContainer) { // discard all component information // and if not already in activation list (startup component) if (!activationRequests.containsKey(name)) { cleanupList.add(componentInfo); continue; } } // this Component needs reactivation if (activationRequests.containsKey(name)) { activationRequests.put(name, reactivateHandle); } // put to activation list else { try { activationRequestsList.add(CURLHelper.createURI(name)); activationRequests.put(name, reactivateHandle); } catch (URISyntaxException usi) { logger.log(Level.WARNING, "Failed to create URI from component name '"+name+"'.", usi); } } } } } } catch (Throwable ex) { CoreException ce = new CoreException("Failed to obtain component data from the CDB.", ex); reportException(ce); } } } if (cleanupList.size() > 0) { Iterator<ComponentInfo> iter = cleanupList.iterator(); while (iter.hasNext()) { ComponentInfo componentInfo = iter.next(); componentsLock.lock(); try { // remove from its owners list ... int[] owners = componentInfo.getClients().toArray(); for (int j = 0; j < owners.length; j++) removeComponentOwner(componentInfo.getHandle(), owners[j]); // ... and deallocate executeCommand(new ComponentCommandDeallocate(componentInfo.getHandle() & HANDLE_MASK, componentInfo.getHandle(), WhyUnloadedReason.REMOVED)); executeCommand(new UnavailableComponentCommandRemove(componentInfo.getName())); // remove component from container component list synchronized (containerInfo.getComponents()) { if (containerInfo.getComponents().contains(componentInfo.getHandle())) executeCommand(new ContainerInfoCommandComponentRemove(containerInfo.getHandle() & HANDLE_MASK, componentInfo.getHandle())); } } finally { componentsLock.unlock(); } // unbind from remote directory //unbind(convertToHiearachical(componentInfo.getName()), "O"); } } logger.log(Level.INFO,"Container '"+containerInfo.getName()+"' startup statistics: " + activationRequestsList.size() + " components queued to be activated."); // send message to the container sendMessage(containerInfo.getContainer(), "Startup statistics: " + activationRequestsList.size() + " components queued to be activated.", MessageType.MSG_INFORMATION, ClientOperations.MSGID_AUTOLOAD_START); // activate startup components int activated = 0; StatusHolder status = new StatusHolder(); Iterator<URI> iterator = activationRequestsList.iterator(); while (iterator.hasNext()) { URI uri = iterator.next(); try { String name = extractName(uri); int requestor = activationRequests.get(name); internalRequestComponent(requestor, uri, status); if (status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) logger.log(Level.WARNING,"Failed to activate autostart component '"+uri+"', reason: '"+status.getStatus()+"'."); else activated++; } catch (Throwable ex) { logger.log(Level.WARNING,"Failed to activate autostart component '"+uri+"'.", ex); } } logger.log(Level.INFO,"Container '"+containerInfo.getName()+"' startup statistics: " + activated + " of " + activationRequestsList.size() + " components activated."); // send message to the container sendMessage(containerInfo.getContainer(), "Startup statistics: " + activated + " of " + activationRequestsList.size() + " components activated.", MessageType.MSG_INFORMATION, ClientOperations.MSGID_AUTOLOAD_END); // notify about new container login synchronized (containerLoggedInMonitor) { containerLoggedInMonitor.notifyAll(); } } /** * Retrieve container's internal state and merge it with manager's. * NOTE: this method should not be run in separate thread since states * should be merged synchronously. * Merge is split to two parts: * * <h2>Container -> Manager</h2> * * If container component handle is also allocated in manager state and components information match, * then we have a perfect fit and no action is required. If they do not match, container is rejected. * If container component handle is not allocated in the manager state and no component with * same name is found in manager state, component information is transferred to the manager, * owtherwise container is rejected. * <p> * NOTE: The second option allows components without owners to be activated. * </p> * <p> * NOTE: Container is rejected due to its state inconsistency which will probably cause * problems in the future. Container has to be restarted.<br/> * (A more sophisticated algorithm could give manager "the power to shape up" container state.) * </p> * <p> * NOTE: Container -> Manager has to work in the way transactions do, i.e. in case of rejection * manager state should no be affected. * </p> * * <h2>Manager -> Container</h2> * * If <code>recoverContainer</code> is <code>true</code>, all components known to the manager to * be activated on this particular container and listed as activated by the container * will be marked as unavailable to be reactivated later by * <code>containerPostLoginActivation</code> method. * If <code>recoverContainer</code> is <code>false</code>, all there information will be discared * (components removed from container component list, deallocated, and removed from their owners list). * * @param containerInfo container info for which to perform merge, non-<code>null</code>. * @param recoverContainer if <code>true</code> manager state will be 'transferred' to container. */ private void containerInternalStateMerge(ContainerInfo containerInfo, boolean recoverContainer) throws AcsJNoPermissionEx { assert(containerInfo != null); // firstly, query containers state ComponentInfo[] infos = null; try { infos = containerInfo.getContainer().get_component_info(new int[0]); } catch (Throwable ex) { logger.log(Level.SEVERE, "Failed to query state of container '"+containerInfo.getName()+"'.", ex); } boolean requireTopologySort = false; // copy elements IntArray managerContainersComponents; synchronized (containerInfo.getComponents()) { managerContainersComponents = new IntArray(containerInfo.getComponents().toArray()); } // // Container -> Manager // if (infos != null && infos.length > 0) { componentsLock.lock(); try { // iterate through all handles // phase 1: check for state consistency for (int i = 0; i < infos.length; i++) { // check if info is valid if (infos[i].getHandle() == 0 || (infos[i].getHandle() & COMPONENT_MASK) != COMPONENT_MASK || infos[i].getName() == null || infos[i].getType() == null || infos[i].getCode() == null || infos[i].getComponent() == null || infos[i].getContainer() == 0 || infos[i].getInterfaces() == null) { // bad info // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Inconsistent container state - component information is not valid, rejecting container."); npe.setID(containerInfo.getName()); npe.setProtectedResource(infos[i].getName()); throw npe; } int handle = infos[i].getHandle() & HANDLE_MASK; if (components.isAllocated(handle)) { // handle is allocated // check if components information match ComponentInfo componentInfo = (ComponentInfo)components.get(handle); if (componentInfo == null || componentInfo.getHandle() != infos[i].getHandle() || !componentInfo.getName().equals(infos[i].getName()) || (componentInfo.getContainer() != 0 && componentInfo.getContainer() != infos[i].getContainer())) { // information does not match, reject container // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Inconsistent container state - components information do not match, rejecting container."); npe.setID(containerInfo.getName()); if (componentInfo != null) { npe.setProtectedResource(componentInfo.getName()); } throw npe; } } else { // handle is not allocated // check if component with same name is already registered int h = components.first(); while (h != 0) { ComponentInfo componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(infos[i].getName())) { // yes it does, reject container // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Inconsistent container state - component with name '" + componentInfo.getName() + "' already registered with different handle, rejecting container."); npe.setID(containerInfo.getName()); npe.setProtectedResource(componentInfo.getName()); throw npe; } h = components.next(h); } } } // states are consistent // phase 2: do the transfer for (int i = 0; i < infos.length; i++) { int handle = infos[i].getHandle() & HANDLE_MASK; if (components.isAllocated(handle)) { // handle is allocated and info match // just in case component is not persistent, update the reference // is always non-null - checked already above ComponentInfo componentInfo = (ComponentInfo)components.get(handle); componentInfo.setComponent(infos[i].getComponent()); componentInfo.setContainer(infos[i].getContainer()); componentInfo.setContainerName(infos[i].getContainerName()); // remove if unavailable and notify synchronized (unavailableComponents) { // !!! ACID 3 executeCommand(new UnavailableComponentCommandRemove(componentInfo.getName())); //unavailableComponents.remove(componentInfo.getName()); } int clients[] = componentInfo.getClients().toArray(); notifyComponentAvailable(0, clients, new ComponentInfo[] { componentInfo }); } else { // handle is not allocated // transfer component // create new ComponentInfo (we do not trust containers) ComponentInfo info = new ComponentInfo(infos[i].getHandle(), infos[i].getName(), infos[i].getType(), infos[i].getCode(), infos[i].getComponent()); info.setContainer(infos[i].getContainer()); info.setContainerName(infos[i].getContainerName()); info.setInterfaces(infos[i].getInterfaces()); info.setAccessRights(0); // determine if component is dynamic // if it has a CDB entry or if there is no CDB available if (!hasCDBEntry(info)) { info.setDynamic(true); info.setDynamicContainerName(containerInfo.getName()); } // !!! ACID 3 // allocate and store executeCommand(new ComponentCommandAllocateHandle(handle)); //components.allocate(handle); executeCommand(new ComponentCommandSet(handle, info)); //components.set(handle, info); requireTopologySort = true; } // we handled this component managerContainersComponents.remove(infos[i].getHandle()); // add to container component list synchronized (containerInfo.getComponents()) { // !!! ACID 3 if (!containerInfo.getComponents().contains(infos[i].getHandle())) executeCommand(new ContainerInfoCommandComponentAdd(containerInfo.getHandle() & HANDLE_MASK, infos[i].getHandle())); //containerInfo.getComponents().add(infos[i].getHandle()); } } } finally { componentsLock.unlock(); } } // // Manager -> Container // // managerContainersComponents contains component handles not handled yet for (int i = 0; i < managerContainersComponents.size(); i++) { int componentHandle = managerContainersComponents.get(i); // remove component handle from container component list // will be added when actually activated // !!! ACID 3 executeCommand(new ContainerInfoCommandComponentRemove(containerInfo.getHandle() & HANDLE_MASK, componentHandle)); //containerInfo.getComponents().remove(componentHandle); // marked as unavailable to be reactivated later (or discarded) componentsLock.lock(); try { int handle = componentHandle & HANDLE_MASK; if (components.isAllocated(handle)) { ComponentInfo componentInfo = (ComponentInfo)components.get(handle); // what if null (very not likely to happen, but possible) if (componentInfo == null) { // internal error, this should not happen logger.log(Level.SEVERE,"Internal state is not consistent (no ComponentInfo)."); continue; } // notify component owners about Component deactivation makeUnavailable(componentInfo); if (!recoverContainer) { // discard all Component information // remove from its owners list ... int[] owners = componentInfo.getClients().toArray(); for (int j = 0; j < owners.length; j++) removeComponentOwner(componentHandle, owners[j]); // !!! ACID 3 // ... and deallocate executeCommand(new ComponentCommandDeallocate(handle, componentInfo.getHandle(), WhyUnloadedReason.REMOVED)); //components.deallocate(handle); requireTopologySort = true; } } else { // internal error, this should not happen logger.log(Level.SEVERE,"Internal state is not consistent."); } } finally { componentsLock.unlock(); } } if (requireTopologySort) topologySortManager.notifyTopologyChange(containerInfo.getHandle()); } /** * Provides its own address to daemons. * <p> * @TODO: The manager should call this method regularly (e.g. once per minute), * so that also restarted service daemons get the manager address. * Or the daemons should resolve the naming service themselves and get it from there, * with possible problems when using different subnets. */ private void initializeServiceDaemons() { // get CDB access daos DAOProxy dao = getManagerDAOProxy(); // no data if (dao == null) return; String[] daemons; try { // query service daemon array daemons = dao.get_string_seq("ServiceDaemons"); } catch (Throwable th) { // parameter is optional logger.log(Level.WARNING, "No list of services daemons available in the CDB. " + "In an operational environment using ACS daemons, this is a severe error!! " + "It is OK only if you run the system without using these daemons. "); return; } for (int i = 0; i < daemons.length; i++) { try { ServiceDaemon daemon = transport.getServiceDaemon(daemons[i]); if (daemon != null) daemon.setManagerReference(transport.getManagerReference()); else throw new RuntimeException("Failed to resolve service daemon reference '" + daemons[i] + "'."); } catch (Throwable th) { // do not make scary logs... logger.config("Failed to set manager reference on service daemon on host '"+daemons[i]+"'."); //logger.log(Level.CONFIG,"Failed to set manager reference on service daemon on host '"+daemons[i]+"'.", th); } } } /** * Checks for autostart components that are to be hosed by autostart containers. */ private void autoStartComponents() { // order is important, preserve it LinkedHashSet<String> activationRequestsList = new LinkedHashSet<String>(); // get CDB access daos DAOProxy dao = getManagerDAOProxy(); DAOProxy componentsDAO = getComponentsDAOProxy(); DAOProxy containersDAO = getContainersDAOProxy(); // no data if (componentsDAO == null || containersDAO == null) return; // // autostart components (Manager.Startup array) - TODO to be removed (left for backward compatibility) // if (dao != null) { try { // query startup components and add them to a list of candidates String[] startup = dao.get_string_seq("Startup"); for (int i = 0; i < startup.length; i++) { // TODO simulator test workaround if (startup[i].length() == 0) continue; activationRequestsList.add(startup[i]); } } catch (Throwable th) { logger.log(Level.WARNING, "Failed to retrieve list of startup components.", th); } } // // autostart components (<component>.Autostart attribute) // final String TRUE_STRING = "true"; { try { // get names of all components /*String[] ids =*/ componentsDAO.get_field_data(""); // TODO here to check if CDB is available String[] ids = getComponentsList(); // test names for (int i = 0; i < ids.length; i++) { // read name String name = ids[i]; //readStringCharacteristics(componentsDAO, ids[i]+"/Name"); if (name == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no name of component '"+ids[i]+"' defined."); continue; } // read autostart silently String autostart = readStringCharacteristics(componentsDAO, ids[i]+"/Autostart", true); if (autostart == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no autostart attribute of component '"+ids[i]+"' defined."); continue; } else if (autostart.equalsIgnoreCase(TRUE_STRING)) { activationRequestsList.add(name); } } } catch (Throwable ex) { logger.log(Level.WARNING, "Failed to retrieve list of components.", ex); } } // now gather list of all containers to be started up (they will autostart components by default) // by filtering out all components that are not hosted by auto-start containers // leave only components that have "*" listed as container LinkedHashSet startupContainers = new LinkedHashSet(); Iterator<String> iterator = activationRequestsList.iterator(); while (iterator.hasNext()) { String name = iterator.next(); try { // get container String containerName = readStringCharacteristics(componentsDAO, name+"/Container", true); if (containerName == null) { iterator.remove(); continue; } else if (containerName.equals(ComponentSpec.COMPSPEC_ANY)) { // leave it in the list continue; } // get its deploy info String host = readStringCharacteristics(containersDAO, containerName + "/DeployInfo/Host", true); String startOnDemand = readStringCharacteristics(containersDAO, containerName + "/DeployInfo/StartOnDemand", true); if (host != null && startOnDemand != null && startOnDemand.equalsIgnoreCase("TRUE")) startupContainers.add(containerName); // remove (or is it auto-started by starting a container or is it not hosted by auto-start container) iterator.remove(); } catch (Throwable ex) { logger.log(Level.WARNING, "Failed to retrieve list of components.", ex); } } int activated = 0; // autostart containers iterator = startupContainers.iterator(); while (iterator.hasNext()) { String containerName = (String)iterator.next(); try { startUpContainer(containerName); } catch (Throwable ex) { logger.log(Level.WARNING, "Failed to auto-start container to auto-start components.", ex); } } // activate startup components (no container specified) StatusHolder status = new StatusHolder(); iterator = activationRequestsList.iterator(); while (iterator.hasNext()) { String name = iterator.next(); try { URI uri = CURLHelper.createURI(name); internalRequestComponent(this.getHandle(), uri, status); if (status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) logger.log(Level.FINE, "Failed to auto-activate requested component '"+name+"', reason: '"+status.getStatus()+"'."); else activated++; } catch (Throwable ex) { CoreException ce = new CoreException("Failed to request component '"+name+"'.", ex); reportException(ce); } } } /** * Search for CDB entry of given component. * @param componentInfo component info of CDB entry to be found, non-<code>null</code>. * @return if CDB entry of has been found <code>true</code>, or if it does not exist or * there is no CDB available <code>false</code>. */ private boolean hasCDBEntry(ComponentInfo componentInfo) { assert (componentInfo != null); try { // check component entry DAOProxy dao = getComponentsDAOProxy(); if (dao == null || readStringCharacteristics(dao, componentInfo.getName(), true) == null) return false; // check type String type = readStringCharacteristics(dao, componentInfo.getName()+"/Type", true); if (type == null || !type.equals(componentInfo.getType())) return false; // check code String code = readStringCharacteristics(dao, componentInfo.getName()+"/Code", true); if (code == null || !code.equals(componentInfo.getCode())) return false; // check container String container = readStringCharacteristics(dao, componentInfo.getName()+"/Container", true); if (container == null) return false; else { // dynamic component case if (componentInfo.isDynamic()) { if (componentInfo.getDynamicContainerName() != null && !container.equals(componentInfo.getDynamicContainerName())) return false; } // static component case else { ContainerInfo containerInfo = (ContainerInfo)getContainerInfo(componentInfo.getContainer()); if (containerInfo != null && !containerInfo.getName().equals(container)) return false; } } // there is an entry in CDB return true; } catch (Throwable t) { return false; } } /** * Administrator specific login method. * @param name name of the administrator * @param reply reply to authenticate method * @param administrator administrator that is logging in * @return ClientInfo client info. of newly logged administrator */ private ClientInfo administratorLogin(String name, AuthenticationData reply, Administrator administrator, long timeStamp, long executionId) throws AcsJNoPermissionEx { assert (name != null); assert (administrator != null); TimerTaskClientInfo clientInfo = null; synchronized (administrators) { // check if administrator is already logged in, // if it is, return existing info int h = administrators.first(); while (h != 0) { ClientInfo loggedAdministratorInfo = (ClientInfo)administrators.get(h); if (administrator.equals(loggedAdministratorInfo.getClient())) return loggedAdministratorInfo; h = administrators.next(h); } // allocate new handle // !!! ACID 2 Integer objHandle = (Integer)executeCommand(new AdministratorCommandAllocate()); int handle; //int handle = administrators.allocate(); if (objHandle == null || (handle = objHandle.intValue()) == 0) { NoResourcesException af = new NoResourcesException("Generation of new handle failed, too many administrators logged in."); throw af; } // generate external handle h = handle | ADMINISTRATOR_MASK; // add generated key h |= (random.nextInt(0x100)) << 16; // create new client info clientInfo = new TimerTaskClientInfo(h, name, administrator); clientInfo.setAccessRights(AccessRights.REGISTER_COMPONENT | AccessRights.INTROSPECT_MANAGER | AccessRights.SHUTDOWN_SYSTEM); // register administrator to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, clientInfo, null); clientInfo.setTask(task); heartbeatTask.schedule(task, administratorPingInterval, administratorPingInterval); // !!! ACID - register AddAdministratorCommand executeCommand(new AdministratorCommandSet(handle, clientInfo)); // store info //administrators.set(handle, clientInfo); } // notify administrators about the login notifyClientLogin(clientInfo, timeStamp, executionId); logger.log(Level.INFO,"Administrator '" + name + "' logged in."); return clientInfo; } /** * Client specific login method. * @param name name of the client * @param reply reply to authenticate method * @param client client that is logging in * @return ClientInfo client info. of newly logged client */ private ClientInfo clientLogin(String name, AuthenticationData reply, Client client, long timeStamp, long executionId) throws AcsJNoPermissionEx { assert (name != null); assert (client != null); TimerTaskClientInfo clientInfo = null; synchronized (clients) { // check if client is already logged in, // if it is, return existing info int h = clients.first(); while (h != 0) { ClientInfo loggedClientInfo = (ClientInfo)clients.get(h); if (client.equals(loggedClientInfo.getClient())) return loggedClientInfo; h = clients.next(h); } // check thread resources int usage = threadsUsedPercentage.get(); if (usage > 90) { throw new NoResourcesException("Thread usage too high (%" + usage + "), rejecting login."); } // allocate new handle // !!! ACID 2 Integer objHandle = (Integer)executeCommand(new ClientCommandAllocate()); int handle; //int handle = clients.allocate(); if (objHandle == null || (handle = objHandle.intValue()) == 0) { NoResourcesException af = new NoResourcesException("Generation of new handle failed, too many clients logged in."); throw af; } // generate external handle h = handle | CLIENT_MASK; // add generated key h |= (random.nextInt(0x100)) << 16; // create new client info clientInfo = new TimerTaskClientInfo(h, name, client); clientInfo.setAccessRights(AccessRights.REGISTER_COMPONENT); // register client to the heartbeat manager PingTimerTask task = new PingTimerTask(this, logger, clientInfo, null); clientInfo.setTask(task); heartbeatTask.schedule(task, clientPingInterval, clientPingInterval); // !!! ACID - register AddClientCommand executeCommand(new ClientCommandSet(handle, clientInfo)); // store info //clients.set(handle, clientInfo); } // notify administrators about the login notifyClientLogin(clientInfo, timeStamp, executionId); logger.log(Level.INFO,"Client '" + name + "' logged in."); return clientInfo; } /** * Container specific logout method * @param id handle of the container. * @param pingFailed flag indicating that ping has failed (i.e. is the reason of this logout). */ private void containerLogout(int id, boolean pingFailed) { TimerTaskContainerInfo containerInfo = null; synchronized (containers) { int handle = id & HANDLE_MASK; // already logged out if (!containers.isAllocated(handle)) return; containerInfo = (TimerTaskContainerInfo)containers.get(handle); // cancel all "old" container async request AcsJCannotGetComponentEx acgcex = new AcsJCannotGetComponentEx(); acgcex.setReason("Request canceled due to container logout."); cancelPendingContainerAsyncRequestWithException(containerInfo.getName(), acgcex); // !!! ACID - RemoveContainerCommand executeCommand(new ContainerCommandDeallocate(handle, id, pingFailed ? WhyUnloadedReason.DISAPPEARED : WhyUnloadedReason.REMOVED)); // remove //containers.deallocate(handle); } // deregister container from the heartbeat manager containerInfo.getTask().cancel(); // make all container components unavailable markContainersComponentsUnavailable(containerInfo); /// TODO !!!!!!!!!!!!!! no more handle -> componentInfo data // notify administrators about the logout notifyContainerLogout(containerInfo, System.currentTimeMillis()); Container container = containerInfo.getContainer(); if (container != null) container.release(); logger.log(Level.INFO,"Container '" + containerInfo.getName() + "' logged out."); } private void markContainersComponentsUnavailable( TimerTaskContainerInfo containerInfo) { // make all container components unavailable int[] markUnavailable; synchronized (containerInfo.getComponents()) { markUnavailable = containerInfo.getComponents().toArray(); } if (markUnavailable.length > 0) { componentsLock.lock(); try { synchronized (unavailableComponents) { // add in reverse order (as deactivation procedure would do) for (int i = markUnavailable.length - 1; i >= 0; i--) { int handle = markUnavailable[i] & HANDLE_MASK; if (components.isAllocated(handle)) { ComponentInfo componentInfo = (ComponentInfo)components.get(handle); if (componentInfo != null) makeUnavailable(componentInfo); } } } } finally { componentsLock.unlock(); } } } /** * Make Component unavailable. * * @param componentInfo component to be made unavailable. */ private void makeUnavailable(ComponentInfo componentInfo) { final boolean notificationNeeded; String componentName = null; int[] clients = null; synchronized (unavailableComponents) { // !!! ACID 3 // add to unavailable component map (override old info) notificationNeeded = !unavailableComponents.containsKey(componentInfo.getName()); executeCommand(new UnavailableComponentCommandPut(componentInfo.getName(), componentInfo)); //unavailableComponents.put(componentInfo.getName(), componentInfo); if (notificationNeeded) { componentName = componentInfo.getName(); clients = componentInfo.getClients().toArray(); } // clear component reference and container componentInfo.setComponent(null); componentInfo.setContainer(0); // leave container name set } // inform clients about unavailability (if necessary) if (notificationNeeded) notifyComponentUnavailable(0, clients, new String[] { componentName }); } /** * Client specific logout method * @param id handle of the client. * @param pingFailed flag indicating that ping has failed (i.e. is the reason of this logout). */ private void clientLogout(int id, boolean pingFailed) { TimerTaskClientInfo clientInfo = null; int[] componentsArray = null; synchronized (clients) { int handle = id & HANDLE_MASK; // already logged out if (!clients.isAllocated(handle)) return; clientInfo = (TimerTaskClientInfo)clients.get(handle); // !!! ACID - RemoveClientCommand executeCommand(new ClientCommandDeallocate(handle, id, pingFailed ? WhyUnloadedReason.DISAPPEARED : WhyUnloadedReason.REMOVED)); // remove //clients.deallocate(handle); componentsArray = clientInfo.getComponents().toArray(); } // deregister client from the heartbeat manager clientInfo.getTask().cancel(); // spawn another task which will release all clientInfo.getComponents() threadPool.execute(new ReleaseComponentTask(clientInfo.getHandle(), componentsArray)); //internalReleaseComponents(components to be released, owners IntArray) //internalReleaseComponents(clientInfo.getComponents(), clientInfo.getComponents()) /// TODO !!!!!!!!!!!!!! no more handle -> componentInfo data // notify administrators about the logout notifyClientLogout(clientInfo, System.currentTimeMillis()); Client client = clientInfo.getClient(); if (client != null) client.release(); logger.log(Level.INFO,"Client '" + clientInfo.getName() + "' logged out."); } /** * Administrator specific logout method * @param id handle of the administrators. * @param pingFailed flag indicating that ping has failed (i.e. is the reason of this logout). */ private void administratorLogout(int id, boolean pingFailed) { TimerTaskClientInfo clientInfo = null; int[] componentsArray = null; synchronized (administrators) { int handle = id & HANDLE_MASK; // already logged out if (!administrators.isAllocated(handle)) return; clientInfo = (TimerTaskClientInfo)administrators.get(handle); // !!! ACID - RemoveAdministratorCommand executeCommand(new AdministratorCommandDeallocate(handle, id, pingFailed ? WhyUnloadedReason.DISAPPEARED : WhyUnloadedReason.REMOVED)); // remove //administrators.deallocate(handle); componentsArray = clientInfo.getComponents().toArray(); } // deregister client from the heartbeat manager clientInfo.getTask().cancel(); // spawn another task which will release all clientInfo.getComponents() threadPool.execute(new ReleaseComponentTask(clientInfo.getHandle(), componentsArray)); /// TODO !!!!!!!!!!!!!! no more handle -> componentInfo data // notify administrators about the logout notifyClientLogout(clientInfo, System.currentTimeMillis()); Client client = clientInfo.getClient(); if (client != null) client.release(); logger.log(Level.INFO,"Administrator '" + clientInfo.getName() + "' logged out."); } /** * Returns array of currently logged administrators. * @param excludeHandle handle of administrator not to be included in the array, can be 0. * @return ClientInfo[] array of currently logged administrators */ private ClientInfo[] getAdministrators(int excludeHandle) { // array of administrators to be notified ClientInfo[] admins = null; // generate array of Administrators to be notified synchronized (administrators) { int len = administrators.size(); // no administrator to notify if (len > 0) { ArrayList<ClientInfo> list = new ArrayList<ClientInfo>(); int h = administrators.first(); while (h != 0) { ClientInfo adminInfo = (ClientInfo)administrators.get(h); // do not notify administrator itself if (adminInfo.getHandle() != excludeHandle) list.add(adminInfo); h = administrators.next(h); } // copy to array if (list.size() > 0) { admins = new ClientInfo[list.size()]; list.toArray(admins); } } } return admins; } /** * Returns array of currently logged containers. * @return ContainerInfo[] array of currently logged containers */ private ContainerInfo[] getContainersInfo() { // array of containers to be notified ContainerInfo[] acts = null; // generate array of containers to be notified synchronized (containers) { int len = containers.size(); // no containers to notify if (len > 0) { ArrayList<ContainerInfo> list = new ArrayList<ContainerInfo>(); int h = containers.first(); while (h != 0) { ContainerInfo actInfo = (ContainerInfo)containers.get(h); list.add(actInfo); h = containers.next(h); } // copy to array if (list.size() > 0) { acts = new ContainerInfo[list.size()]; list.toArray(acts); } } } return acts; } /** * Returns array of currently logged clients. * @return ClientInfo[] array of currently logged clients */ private ClientInfo[] getClientInfo() { ArrayList<ClientInfo> list = new ArrayList<ClientInfo>(); // get clients synchronized (clients) { int h = clients.first(); while (h != 0) { ClientInfo clientInfo = (ClientInfo)clients.get(h); list.add(clientInfo); h = clients.next(h); } } // get admins synchronized (administrators) { int h = administrators.first(); while (h != 0) { ClientInfo clientInfo = (ClientInfo)administrators.get(h); list.add(clientInfo); h = clients.next(h); } } ClientInfo[] infos = null; if (list.size() > 0) { infos = new ClientInfo[list.size()]; list.toArray(infos); } return infos; } /** * Get client info. for specified handles of <code>Client</code> or <code>Administrator</code>. For <code>Component</code> * handles component's <code>Container</code> is returned. * * @param excludeHandle handle of client not to be included in the array, can be 0. * @param handles handles of the clients whose info. should be returned, non-<code>null</code>. * @param returns requested infos, <code>null</code> if none */ private ClientInfo[] getClients(int excludeHandle, int[] handles) { assert (handles != null); // array of clients to be notified ClientInfo[] clients = null; ArrayList<ClientInfo> list = new ArrayList<ClientInfo>(); for (int i = 0; i < handles.length; i++) if (handles[i] != excludeHandle) { if ((handles[i] & TYPE_MASK) == COMPONENT_MASK) { ComponentInfo componentInfo = getComponentInfo(handles[i]); if (componentInfo != null) { ContainerInfo containerInfo = getContainerInfo(componentInfo.getContainer()); if (containerInfo != null) { ClientInfo info = new ClientInfo(containerInfo.getHandle(), containerInfo.getName(), containerInfo.getContainer()); list.add(info); } } } else { ClientInfo info = getClientInfo(handles[i]); if (info != null) list.add(info); } } // copy to array if (list.size() > 0) { clients = new ClientInfo[list.size()]; list.toArray(clients); } return clients; } /** * Remove component handle from the owners list. * @param componentHandle component handle to be removed from the owners list. * @param owner owner whom to remove the ownership. */ private void removeComponentOwner(int componentHandle, int owner) { if ((owner & TYPE_MASK) == COMPONENT_MASK) { // remove from owner list ComponentInfo componentInfo = getComponentInfo(owner); if (componentInfo != null) { // needed since execute is sync. and action // ComponentInfoCommandComponentRemove then sync. componentInfo componentsLock.lock(); try { // !!! ACID 3 executeCommand(new ComponentInfoCommandComponentRemove(owner & HANDLE_MASK, componentHandle)); //ci.getComponents().remove(componentHandle); } finally { componentsLock.unlock(); } } } else { // remove from owner list ClientInfo clientInfo = getClientInfo(owner); if (clientInfo != null) { // needed since execute is sync. and action // ClientInfoCommandComponentRemove then sync. clientInfo synchronized (clients) { // !!! ACID 3 executeCommand(new ClientInfoCommandComponentRemove(owner, componentHandle)); //clientInfo.getComponents().remove(componentHandle); } } } } /** * Add component handle to the owners list. * @param componentHandle component handle to be addd to the owners list. * @param owner owner whom to add the ownership. */ private void addComponentOwner(int componentHandle, int owner) { if ((owner & TYPE_MASK) == COMPONENT_MASK) { // remove from owner list ComponentInfo componentInfo = getComponentInfo(owner); if (componentInfo != null) { // needed since execute is sync. and action // ComponentInfoCommandComponentAdd then sync. componentInfo componentsLock.lock(); try { // !!! ACID 3 if (!componentInfo.getComponents().contains(componentHandle)) executeCommand(new ComponentInfoCommandComponentAdd(owner & HANDLE_MASK, componentHandle)); //ci.getComponents().add(componentHandle); } finally { componentsLock.unlock(); } } } else { // remove from owner list ClientInfo clientInfo = getClientInfo(owner); if (clientInfo != null) { // needed since execute is sync. and action // ClientInfoCommandComponentAdd then sync. clientInfo synchronized (clients) { // !!! ACID 3 if (!clientInfo.getComponents().contains(componentHandle)) executeCommand(new ClientInfoCommandComponentAdd(owner, componentHandle)); //clientInfo.getComponents().add(componentHandle); } } } } /** * Notifies administrators about newly logged client. * @param clientInfo newly logged client, non-<code>null</code> */ private void notifyClientLogin(final ClientInfo clientInfo, final long timeStamp, final long executionId) { assert (clientInfo != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(clientInfo.getHandle()); if (admins != null) { /** * Task thats invokes <code>Administrator#clientLoggedIn</code> method. */ class ClientLoggedInTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private ClientInfo clientInfo; public ClientLoggedInTask(ClientInfo administratorInfo, ClientInfo clientInfo) { this.administratorInfo = administratorInfo; this.clientInfo = clientInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).clientLoggedIn(clientInfo, timeStamp, executionId); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.clientLoggedIn' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.clientLoggedIn' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ClientLoggedInTask(admins[i], clientInfo); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about newly logged container. * @param containerInfo newly logged container, non-<code>null</code> */ private void notifyContainerLogin(final ContainerInfo containerInfo, final long timeStamp, final long executionId) { assert (containerInfo != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(containerInfo.getHandle()); if (admins != null) { /** * Task thats invokes <code>Administrator#containerLoggedIn</code> method. */ class ContainerLoggedInTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private ContainerInfo containerInfo; public ContainerLoggedInTask(ClientInfo administratorInfo, ContainerInfo containerInfo) { this.administratorInfo = administratorInfo; this.containerInfo = containerInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).containerLoggedIn(containerInfo, timeStamp, executionId); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.containerLoggedIn' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.containerLoggedIn' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ContainerLoggedInTask(admins[i], containerInfo); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about client logging out. * @param client client logging out, non-<code>null</code> */ private void notifyClientLogout(final ClientInfo clientInfo, final long timeStamp) { assert (clientInfo != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(clientInfo.getHandle()); if (admins != null) { /** * Task thats invokes <code>Administrator#clientLoggedOut</code> method. */ class ClientLoggedOutTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private ClientInfo clientInfo; public ClientLoggedOutTask(ClientInfo administratorInfo, ClientInfo clientInfo) { this.administratorInfo = administratorInfo; this.clientInfo = clientInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).clientLoggedOut(clientInfo.getHandle(), timeStamp); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.clientLoggedOut' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.clientLoggedOut' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ClientLoggedOutTask(admins[i], clientInfo); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about container logging out. * @param containerInfo container logging out, non-<code>null</code> */ private void notifyContainerLogout(final ContainerInfo containerInfo, final long timeStamp) { assert (containerInfo != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(containerInfo.getHandle()); if (admins != null) { /** * Task thats invokes <code>Administrator#containerLoggedIn</code> method. */ class ContainerLoggedOutTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private ContainerInfo containerInfo; public ContainerLoggedOutTask(ClientInfo administratorInfo, ContainerInfo containerInfo) { this.administratorInfo = administratorInfo; this.containerInfo = containerInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).containerLoggedOut(containerInfo.getHandle(), timeStamp); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.containerLoggedOut' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.containerLoggedOut' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ContainerLoggedOutTask(admins[i], containerInfo); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies containers to disconnect or shutdown. * @param code code to be sent to container, if <code>0</code> disconnect method will be called. */ private void notifyContainerDisconnectShutdown(int code) { // array of containers to be notified ContainerInfo[] acts = getContainersInfo(); if (acts != null) { /** * Task thats invokes <code>Container#shutdown</code> method. */ class ContainerShutdownTask implements Runnable { private int code;; private ContainerInfo containerInfo; public ContainerShutdownTask(ContainerInfo containerInfo, int code) { this.containerInfo = containerInfo; this.code = code; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { (containerInfo.getContainer()).shutdown(code); //break; } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Container.shutdown' on "+containerInfo+".", re); } } } } /** * Task thats invokes <code>Container#shutdown</code> method. */ class ContainerDisconnectTask implements Runnable { private ContainerInfo containerInfo; public ContainerDisconnectTask(ContainerInfo containerInfo) { this.containerInfo = containerInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { (containerInfo.getContainer()).disconnect(); //break; } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Container.disconnect' on "+containerInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < acts.length; i++) { Runnable task; if (code == 0) task = new ContainerDisconnectTask(acts[i]); else task = new ContainerShutdownTask(acts[i], code); threadPool.execute(task); } } } /** * Informs containers abouts its component shutdown order. * @param containerInfo container to inform * @param handles ordered list of component handles */ private void notifyContainerShutdownOrder(ContainerInfo containerInfo, int[] handles) { /** * Task thats invokes <code>Container#shutdown</code> method. */ class ContainerSetShutdownOrderTask implements Runnable { private int[] handles; private ContainerInfo containerInfo; public ContainerSetShutdownOrderTask(ContainerInfo containerInfo, int[] handles) { this.containerInfo = containerInfo; this.handles = handles; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { (containerInfo.getContainer()).set_component_shutdown_order(handles); //break; } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Container.set_component_shutdown_order' on "+containerInfo+".", re); } } } } // spawn new task which surely does not block threadPool.execute(new ContainerSetShutdownOrderTask(containerInfo, handles)); } /** * Notifies clients to disconnect or shutdown. */ private void notifyClientDisconnectShutdown() { // array of clients to be notified ClientInfo[] clts = getClientInfo(); if (clts != null) { /** * Task thats invokes <code>Client#disconnect</code> method. */ class ClientDisconnectTask extends DefaultGroupedRunnable { private ClientInfo clientInfo; public ClientDisconnectTask(ClientInfo clientInfo) { this.clientInfo = clientInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { (clientInfo.getClient()).disconnect(); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Client.disconnect' on "+clientInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Client.disconnect' on "+clientInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < clts.length; i++) { GroupedRunnable task = new ClientDisconnectTask(clts[i]); registerGroupedNotifyTaks(clts[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about Component request. * @param requestors array of clients requesting the Component, non-<code>null</code> * @param components array of requested the components, non-<code>null</code> */ private void notifyComponentRequested(int[] requestors, int[] components, final long timeStamp) { assert (requestors != null); assert (components != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(0); if (admins != null) { /** * Task thats invokes <code>Administrator#components_requested</code> method. */ class ComponentRequestedTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private int[] requestors; private int[] components; public ComponentRequestedTask(ClientInfo administratorInfo, int[] requestors, int[] components) { this.administratorInfo = administratorInfo; this.requestors = requestors; this.components = components; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).components_requested(requestors, components, timeStamp); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.components_requested' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.components_requested' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ComponentRequestedTask(admins[i], requestors, components); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about Component activation. * @param componentInfo activated component info, non-<code>null</code> */ private void notifyComponentActivated(final ComponentInfo componentInfo, final long timeStamp, final long executionId) { assert (componentInfo != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(0); if (admins != null) { /** * Task thats invokes <code>Administrator#component_activated</code> method. */ class ComponentActivatedTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; public ComponentActivatedTask(ClientInfo administratorInfo) { this.administratorInfo = administratorInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).component_activated(componentInfo, timeStamp, executionId); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.component_activated' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.component_activated' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ComponentActivatedTask(admins[i]); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about Component deactivation. * @param component deactivated component handle, non-<code>0</code> */ private void notifyComponentDeactivated(final int handle, final long timeStamp) { assert (handle != 0); // array of administrators to be notified ClientInfo[] admins = getAdministrators(0); if (admins != null) { /** * Task thats invokes <code>Administrator#component_deactivated</code> method. */ class ComponentDeactivatedTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; public ComponentDeactivatedTask(ClientInfo administratorInfo) { this.administratorInfo = administratorInfo; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).component_deactivated(handle, timeStamp); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.component_deactivated' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.component_deactivated' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ComponentDeactivatedTask(admins[i]); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about Component request. * @param requestors array of clients requesting the Component, non-<code>null</code> * @param components array of requested the components, non-<code>null</code> */ private void notifyComponentReleased(int[] requestors, int[] components, final long timeStamp) { assert (requestors != null); assert (components != null); // array of administrators to be notified ClientInfo[] admins = getAdministrators(0); if (admins != null) { /** * Task thats invokes <code>Administrator#components_requested</code> method. */ class ComponentReleasedTask extends DefaultGroupedRunnable { private ClientInfo administratorInfo; private int[] requestors; private int[] components; public ComponentReleasedTask(ClientInfo administratorInfo, int[] requestors, int[] components) { this.administratorInfo = administratorInfo; this.requestors = requestors; this.components = components; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { ((Administrator)administratorInfo.getClient()).components_released(requestors, components, timeStamp); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Administrator.components_released' on "+administratorInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Administrator.components_released' on "+administratorInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < admins.length; i++) { GroupedRunnable task = new ComponentReleasedTask(admins[i], requestors, components); if (admins[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(admins[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about component request. * @param excludeClient client not to be informed, <code>0</code> if none * @param clientHandles array of client handles to be informed, non-<code>null</code> * @param info component infos to be sent to the clients, non-<code>null</code> */ private void notifyComponentAvailable(int excludeClient, int[] clientHandles, ComponentInfo[] info) { assert (clients != null); assert (info != null); // array of clients to be notified ClientInfo[] clients = getClients(excludeClient, clientHandles); if (clients != null) { /** * Task thats invokes <code>Client#components_available</code> method. */ class ComponentAvailableTask extends DefaultGroupedRunnable { private ClientInfo clientInfo; private ComponentInfo[] info; public ComponentAvailableTask(ClientInfo clientInfo, ComponentInfo[] info) { this.clientInfo = clientInfo; this.info = info; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { clientInfo.getClient().components_available(info); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Client.components_available' on "+clientInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Client.components_available' on "+clientInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < clients.length; i++) { GroupedRunnable task = new ComponentAvailableTask(clients[i], info); if (clients[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(clients[i].getClient(), task); //threadPool.execute(task); } } } /** * Notifies administrators about Component request. * @param excludeClient client not to be informed, <code>0</code> if none * @param clientHandles array of client handles to be informed, non-<code>null</code> * @param names component names to be sent to the clients, non-<code>null</code> */ private void notifyComponentUnavailable(int excludeClient, int[] clientHandles, String[] names) { assert (clients != null); assert (names != null); // array of clients to be notified ClientInfo[] clients = getClients(excludeClient, clientHandles); if (clients != null) { /** * Task thats invokes <code>Client#components_unavailable</code> method. */ class ComponentUnavailableTask extends DefaultGroupedRunnable { private ClientInfo clientInfo; private String[] names; public ComponentUnavailableTask(ClientInfo clientInfo, String[] names) { this.clientInfo = clientInfo; this.names = names; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { clientInfo.getClient().components_unavailable(names); //break; } catch (RemoteTransientException re) { logger.log(Level.WARNING, "RemoteTransientException caught while invoking 'Client.components_unavailable' on "+clientInfo+".", re); cancelAll(); } catch (RemoteException re) { logger.log(Level.WARNING, "RemoteException caught while invoking 'Client.components_unavailable' on "+clientInfo+".", re); } } } } // spawn new task which surely does not block for (int i = 0; i < clients.length; i++) { GroupedRunnable task = new ComponentUnavailableTask(clients[i], names); if (clients[i].getClient() instanceof SynchronousAdministrator) task.run(); else registerGroupedNotifyTaks(clients[i].getClient(), task); //threadPool.execute(task); } } } // /** // * Sends an message to the client. // * @param client client to receive the message, non-<code>null</code> // * @param message message to be sent, non-<code>null</code> // * @param messageType type of the message, non-<code>null</code> // */ // private void sendMessage(Client client, String message, MessageType messageType) // { // sendMessage(client, message, messageType, (short)0); // } /** * Task thats invokes <code>Client#message</code> method. */ private class ClientMessageTask implements Runnable { private Client client; private String message; private MessageType messageType; private short messageID; public ClientMessageTask(Client client, String message, MessageType messageType, short messageID) { this.client = client; this.message = message; this.messageType = messageType; this.messageID = messageID; } public void run() { //final int MAX_RETRIES = 3; //for (int retries = 0; retries < MAX_RETRIES; retries++) { try { if (messageID > 0) { client.taggedmessage(messageType, messageID, message); } else { client.message(messageType, message); } //break; } catch (Throwable re) { logger.log(Level.WARNING, "Exception caught while invoking 'Client.message' on "+client+".", re); } } } } /** * Sends an message to the client. * @param client client to receive the message, non-<code>null</code> * @param message message to be sent, non-<code>null</code> * @param messageType type of the message, non-<code>null</code> * @param messageID identifier for this message */ private void sendMessage(Client client, String message, MessageType messageType, short messageID) { assert (client != null); assert (message != null); assert (messageType != null); boolean newClient = false; synchronized (clientMessageQueue) { LinkedList<ClientMessageTask> list = clientMessageQueue.get(client); if (list == null) { list = new LinkedList<ClientMessageTask>(); clientMessageQueue.put(client, list); newClient = true; } list.addLast(new ClientMessageTask(client, message, messageType, messageID)); } if (newClient) { class ClientQueuMessageTask implements Runnable { private Client client; public ClientQueuMessageTask(Client client) { this.client = client; } public void run() { boolean moreMessages = true; ClientMessageTask clientTask; while (moreMessages) { synchronized (clientMessageQueue) { LinkedList<ClientMessageTask> list = clientMessageQueue.get(client); if (list == null) break; clientTask = list.removeFirst(); moreMessages = !list.isEmpty(); if (!moreMessages) { clientMessageQueue.remove(client); } } // exception safe if (clientTask != null) clientTask.run(); } } } // spawn new task which surely does not block threadPool.execute(new ClientQueuMessageTask(client)); } } /** * Performs security check on given handle and if check if owner has <code>rights</code> permissions granted. * * Validating means checking key part (KEY_MASK) of the handle. * * @param id handle to be checked. * @param rights checks if owner of the handle has this permissions granted, can be 0. * @throws AcsJNoPermissionEx thrown if handle is not valid or handle owner has not enough permissions */ private void securityCheck(int id, int requiredRights) throws AcsJNoPermissionEx { try { // check if already shutdown if (id != this.getHandle() && shutdown.get()) { // already shutdown AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setID(HandleHelper.toString(id)); npe.setReason("Manager in shutdown state."); throw npe; } // parse handle part int handle = id & HANDLE_MASK; int grantedRights = 0; boolean invalidHandle = true; switch (id & TYPE_MASK) { case CONTAINER_MASK: synchronized (containers) { if (containers.isAllocated(handle)) { ContainerInfo info = (ContainerInfo)containers.get(handle); if (info.getHandle() == id) invalidHandle = false; grantedRights = CONTAINER_RIGHTS; } } break; case CLIENT_MASK: synchronized (clients) { if (clients.isAllocated(handle)) { ClientInfo info = (ClientInfo)clients.get(handle); if (info.getHandle() == id) invalidHandle = false; grantedRights = info.getAccessRights(); } } break; case ADMINISTRATOR_MASK: synchronized (administrators) { if (administrators.isAllocated(handle)) { ClientInfo info = (ClientInfo)administrators.get(handle); if (info.getHandle() == id) invalidHandle = false; grantedRights = info.getAccessRights(); } } break; case COMPONENT_MASK: componentsLock.lock(); try { if (components.isAllocated(handle)) { ComponentInfo info = (ComponentInfo)components.get(handle); if (info != null && info.getHandle() == id) invalidHandle = false; grantedRights = AccessRights.REGISTER_COMPONENT; } } finally { componentsLock.unlock(); } break; case MANAGER_MASK: invalidHandle = false; grantedRights = AccessRights.REGISTER_COMPONENT | AccessRights.SHUTDOWN_SYSTEM | AccessRights.INTROSPECT_MANAGER; break; } if (invalidHandle) { // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setID(HandleHelper.toString(id)); HandleMonitorEntry hme = getHandleReleaseLog(id); if (hme != null) { final String timeISO = IsoDateFormat.formatDate(new Date(hme.timestamp)); switch (hme.reason) { case REMOVED: npe.setReason("Invalid handle, handle was properly removed at " + timeISO + "."); break; case TIMEOUT: npe.setReason("Invalid handle, handle was removed due to timeout at " + timeISO + "."); break; case DISAPPEARED: npe.setReason("Invalid handle, handle was removed due to client/container/component disappearing at " + timeISO + "."); break; } } else { if (enableHandleMonitoringDurationMins <= 0) npe.setReason("Invalid handle, handle was never known."); else npe.setReason("Invalid handle, handle is not known for the last " + enableHandleMonitoringDurationMins + " minutes."); } throw npe; } if ((grantedRights & requiredRights) != requiredRights) { // NO_PERMISSION AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setID(HandleHelper.toString(id)); npe.setReason("Insufficient rights."); throw npe; } } catch (AcsJNoPermissionEx ex) { logger.log(AcsLogLevel.DELOUSE, "securityCheck fails with AcsJNoPermissionEx:", ex); throw ex; } } /** * Get client info. for specified id of <code>Client</code> or <code>Administrator</code>. * * @param id handle of the client whose info. should be returned * @param returns requested info, <code>null</code> if client with requested handle does not exits */ public ClientInfo getClientInfo(int id) { // parse handle part int handle = id & HANDLE_MASK; // info to be returned ClientInfo info = null; switch (id & TYPE_MASK) { case CLIENT_MASK: synchronized (clients) { if (clients.isAllocated(handle)) info = (ClientInfo)clients.get(handle); } break; case ADMINISTRATOR_MASK: synchronized (administrators) { if (administrators.isAllocated(handle)) info = (ClientInfo)administrators.get(handle); } break; } return info; } /** * Get client info. (no sync version) for specified id of <code>Client</code> or <code>Administrator</code>. * * @param id handle of the client whose info. should be returned * @param returns requested info, <code>null</code> if client with requested handle does not exits */ public ClientInfo noSyncGetClientInfo(int id) { // parse handle part int handle = id & HANDLE_MASK; // info to be returned ClientInfo info = null; switch (id & TYPE_MASK) { case CLIENT_MASK: { if (clients.isAllocated(handle)) info = (ClientInfo)clients.get(handle); } break; case ADMINISTRATOR_MASK: { if (administrators.isAllocated(handle)) info = (ClientInfo)administrators.get(handle); } break; } return info; } /** * Get container info. for specified id of <code>Container</code>. * * @param id handle of the container whose info. should be returned * @param returns requested info, <code>null</code> if container with requested handle does not exits */ private ContainerInfo getContainerInfo(int id) { // parse handle part int handle = id & HANDLE_MASK; // info to be returned ContainerInfo info = null; synchronized (containers) { if (containers.isAllocated(handle)) info = (ContainerInfo)containers.get(handle); } return info; } /** * Get container info. for specified name of <code>Container</code>. * * @param name name of the container whose info. should be returned, non-<code>null</code> * @param returns requested info, <code>null</code> if container with requested handle does not exits */ private ContainerInfo getContainerInfo(String name) { assert(name != null); // info to be returned ContainerInfo info = null; synchronized (containers) { int h = containers.first(); while (h != 0) { ContainerInfo containerInfo = (ContainerInfo)containers.get(h); if (name.equals(containerInfo.getName())) { info = containerInfo; break; } h = containers.next(h); } } return info; } /** * Get component info. for specified id of <code>Component</code>. * * @param id handle of the component whose info. should be returned * @param returns requested info, <code>null</code> if component with requested handle does not exits */ public ComponentInfo getComponentInfo(int id) { // parse handle part int handle = id & HANDLE_MASK; // info to be returned ComponentInfo info = null; componentsLock.lock(); try { if (components.isAllocated(handle)) info = (ComponentInfo)components.get(handle); } finally { componentsLock.unlock(); } return info; } /*****************************************************************************/ /********************** [ Component (de)activation ] *************************/ /*****************************************************************************/ /** * Internal method for requesting components. * @param requestor requestor of the component. * @param curl curl of the component to be requested. * @param status status of the component. * @return component retuested component. */ private Component internalRequestComponent(int requestor, URI curl, StatusHolder status) throws AcsJCannotGetComponentEx { return internalRequestComponent(requestor, curl, status, true); } /** * Internal method for requesting components. * @param requestor requestor of the component. * @param curl curl of the component to be requested. * @param status status of the component. * @param activate <code>true</code> if component has to be activated * @return component requested component. */ private Component internalRequestComponent(int requestor, URI curl, StatusHolder status, boolean activate) throws AcsJCannotGetComponentEx { // extract unique name String name = extractName(curl); try { checkCyclicDependency(requestor, name); } catch (AcsJCyclicDependencyDetectedEx e) { AcsJCannotGetComponentEx cgce = new AcsJCannotGetComponentEx(e); cgce.setCURL(name); throw cgce; } // try to acquire lock long lockTimeoutMillis = getLockTimeout(); String lockNotAcquiredCause = acquireSynchronizationObject(name, lockTimeoutMillis, "request component " + name); if (lockNotAcquiredCause == null) { boolean releaseRWLock = true; try { // try to acquire activation readers lock first // NOTE: the locks are NOT reentrant activationPendingRWLock.readLock().lock(); // Let AcsJCannotGetComponentEx fly up ComponentInfo componentInfo = null; try { componentInfo = internalNoSyncRequestComponent(requestor, name, null, null, null, RELEASE_TIME_UNDEFINED, status, activate); } catch(AcsJComponentSpecIncompatibleWithActiveComponentEx ciwace) { status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); AcsJCannotGetComponentEx cgce = new AcsJCannotGetComponentEx(ciwace); cgce.setCURL(name); throw cgce; } if (componentInfo != null) { return componentInfo.getComponent(); } else { return null; } } finally { if (releaseRWLock) { activationPendingRWLock.readLock().unlock(); } releaseSynchronizationObject(name); } } else { String msg = "Failed to obtain synchronization lock for component '" + name + "' within '" + lockTimeoutMillis + "' ms, possible deadlock; locked to '" + lockNotAcquiredCause + "'." ; logger.fine(msg); NoResourcesException nre = new NoResourcesException(msg); throw nre; } } /** * Check for cyclic dependency between components, if detected path is returned. * @param requestor handle of requestor component * @param requested handle of requested component * @return if cycle is detected then path is returned, otherwise <code>null</code> */ private ArrayList<ComponentInfo> doCycleCheck(int requestor, int requested) { componentsLock.lock(); try { ComponentInfo info = (ComponentInfo)components.get(requested & HANDLE_MASK); if (info == null) return null; if (requested == requestor) { // detected ArrayList<ComponentInfo> list = new ArrayList<ComponentInfo>(); list.add(info); return list; } int[] subcomponents = info.getComponents().toArray(); for (int i = 0; i < subcomponents.length; i++) { ArrayList<ComponentInfo> list = doCycleCheck(requestor, subcomponents[i]); if (list != null) { list.add(info); return list; } } } finally { componentsLock.unlock(); } return null; } /** * Check for cyclic dependency between components, if detected <code>NoResourcesException</code> exception is thrown. * @param requestor * @param requestedComponentName */ private void checkCyclicDependency(int requestor, String requestedComponentName) throws AcsJCyclicDependencyDetectedEx { // check only if component is requesting component if ((requestor & TYPE_MASK) != COMPONENT_MASK) return; // check if requested component is already activated (and pending activations) ComponentInfo componentInfo = null; componentsLock.lock(); try { int h = components.first(); while (h != 0) { ComponentInfo info = (ComponentInfo)components.get(h); if (info.getName().equals(requestedComponentName)) { // yes, component is already activated componentInfo = info; break; } else h = components.next(h); } // check pending activations... ComponentInfo pendingComponentInfo; synchronized (pendingActivations) { pendingComponentInfo = pendingActivations.get(requestedComponentName); } // if component is already completely activated, we allow cyclic dependencies (but their usage is discouraged) if (componentInfo != null && pendingComponentInfo == null) return; // take pending activation... if (componentInfo == null) componentInfo = pendingComponentInfo; // not activated yet, so no cyclic dependency is possible if (componentInfo == null) return; ArrayList<ComponentInfo> pathList = doCycleCheck(requestor, componentInfo.getHandle()); // no dependency detected if (pathList == null) return; // stringify StringBuffer pathBuffer = new StringBuffer(); for (int i = pathList.size()-1; i >= 0; i--) pathBuffer.append(pathList.get(i).getName()).append(" -> "); pathBuffer.append(componentInfo.getName()); // TODO @todo no pathBuffer is used, printed-out // If we get to this point there is a cyclical dependency and we throw the exception // not an owner AcsJCyclicDependencyDetectedEx cde = new AcsJCyclicDependencyDetectedEx(); cde.setCURL(requestedComponentName); cde.setRequestor(requestor); throw cde; } finally { componentsLock.unlock(); } } /** * Internal method for requesting components. * @param requestor requestor of the component. * @param name name of component to be requested, non-<code>null</code>. * @param type type of component to be requested; if <code>null</code> CDB will be queried. * @param code code of component to be requested; if <code>null</code> CDB will be queried. * @param containerName container name of component to be requested; if <code>null</code> CDB will be queried. * @param status returned completion status of the request. * @param activate <code>true</code> if component has to be activated * @return componentInfo <code>ComponentInfo</code> of requested component. */ private ComponentInfo internalRequestComponent( int requestor, String name, String type, String code, String containerName, int keepAliveTime, StatusHolder status, boolean activate) throws AcsJCannotGetComponentEx, AcsJSyncLockFailedEx, AcsJComponentSpecIncompatibleWithActiveComponentEx { AcsJCannotGetComponentEx bcex = null; if (name == null) { bcex = new AcsJCannotGetComponentEx(); logger.log(Level.SEVERE, "Cannot activate component with NULL name.", bcex); throw bcex; } if (status == null) { bcex = new AcsJCannotGetComponentEx(); logger.log(Level.SEVERE, "Component " + name + " has NULL status.", bcex); throw bcex; } try { checkCyclicDependency(requestor, name); } catch (AcsJCyclicDependencyDetectedEx e) { AcsJCannotGetComponentEx cgce = new AcsJCannotGetComponentEx(e); cgce.setCURL(name); throw cgce; } // try to acquire lock String lockNotAcquiredCause = acquireSynchronizationObject(name, getLockTimeout(), "request component " + name); if (lockNotAcquiredCause == null) { boolean releaseRWLock = true; try { // try to acquire activation readers lock first // NOTE: the locks are NOT reentrant activationPendingRWLock.readLock().lock(); // AcsJComponentSpecIncompatibleWithActiveComponentEx flies up return internalNoSyncRequestComponent(requestor, name, type, code, containerName, keepAliveTime, status, activate); } finally { if (releaseRWLock) activationPendingRWLock.readLock().unlock(); releaseSynchronizationObject(name); } } else { AcsJSyncLockFailedEx slfe = new AcsJSyncLockFailedEx(); slfe.setCURL(name); slfe.setRequestor(requestor); slfe.setProperty("lockCause", lockNotAcquiredCause); throw slfe; } } /** * Internal method for requesting components (non sync). * @param requestor requestor of the component. * @param name name of component to be requested, non-<code>null</code>. * @param type type of component to be requested; if <code>null</code> CDB will be queried. * @param code code of component to be requested; if <code>null</code> CDB will be queried. * @param containerName container name of component to be requested; if <code>null</code> CDB will be queried. * @param status returned completion status of the request. * @param activate <code>true</code> if component has to be activated * @return componentInfo <code>ComponentInfo</code> of requested component. */ private ComponentInfo internalNoSyncRequestComponent( int requestor, String name, String type, String code, String containerName, int keepAliveTime, StatusHolder status, boolean activate) throws AcsJCannotGetComponentEx, AcsJComponentSpecIncompatibleWithActiveComponentEx { AcsJCannotGetComponentEx bcex = null; if (name == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Cannot activate component with NULL name."); throw bcex; } if (status == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Component " + name + " has NULL status."); throw bcex; } boolean isOtherDomainComponent = name.startsWith(CURL_URI_SCHEMA); boolean isDynamicComponent = isOtherDomainComponent ? false : (type != null || code != null || containerName != null); // // check if component is already activated // int h; // if true, component with handle h will be reactivated boolean reactivate = false; ComponentInfo componentInfo = null; componentsLock.lock(); try { h = components.first(); while (h != 0) { componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(name)) { // yes, component is already activated // check if component is unavailable synchronized (unavailableComponents) { if (unavailableComponents.containsKey(name)) { // try to reactivate, possible component reallocation reactivate = true; } } // check for consistency ContainerInfo containerInfo = getContainerInfo(componentInfo.getContainer()); if ((type != null && !componentInfo.getType().equals(type)) || (code != null && componentInfo.getCode() != null && !componentInfo.getCode().equals(code)) || (!reactivate && containerInfo != null && containerName != null && !containerInfo.getName().equals(containerName))) { AcsJComponentSpecIncompatibleWithActiveComponentEx ciwace = new AcsJComponentSpecIncompatibleWithActiveComponentEx(); ciwace.setCURL(componentInfo.getName()); ciwace.setComponentType(componentInfo.getType()); ciwace.setComponentCode(componentInfo.getCode() != null ? componentInfo.getCode() : "<unknown>"); ciwace.setContainerName(containerInfo != null ? containerInfo.getName() : "<none>"); throw ciwace; } if (activate) { // bail out and reactivate if (reactivate) break; // add client/component as an owner (if requestor is not 'reactivation') if (requestor != 0) { // !!! ACID if (!componentInfo.getClients().contains(requestor)) executeCommand(new ComponentCommandClientAdd(componentInfo.getHandle() & HANDLE_MASK, requestor)); //componentInfo.getClients().add(requestor); } // add component to client component list (if requestor is not manager or 'reactivation') if (requestor != this.getHandle() && requestor != 0) addComponentOwner(componentInfo.getHandle(), requestor); // inform administrators about component request notifyComponentRequested(new int[] { requestor }, new int[] { componentInfo.getHandle() }, System.currentTimeMillis()); // notify about the change (only if on the same container) // on complete system shutdown sort will be done anyway if ((requestor & TYPE_MASK) == COMPONENT_MASK) { ComponentInfo requestorComponentInfo = getComponentInfo(requestor); if (requestorComponentInfo != null && requestorComponentInfo.getContainerName() != null && requestorComponentInfo.getContainerName().equals(componentInfo.getContainerName())) topologySortManager.notifyTopologyChange(componentInfo.getContainer()); } // return info status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); return componentInfo; } else { if (reactivate) status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); else status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); return componentInfo; } } h = components.next(h); } } finally { componentsLock.unlock(); } // if we have to reactivate a dynamic component, // then reread info from existing component info // and do not touch CDB if (reactivate && componentInfo.isDynamic()) { if (componentInfo.getType() == null || componentInfo.getCode() == null || componentInfo.getDynamicContainerName() == null) { // failed bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Failed to reactivate dynamic component '"+componentInfo+"'."); status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); throw bcex; } else { // reread info type = componentInfo.getType(); code = componentInfo.getCode(); containerName = componentInfo.getDynamicContainerName(); keepAliveTime = componentInfo.getKeepAliveTime(); } } // is CDB lookup needed else if (!isOtherDomainComponent && (type == null || code == null || containerName == null)) { // // read component info from CDB / remote directory lookup // DAOProxy dao = getComponentsDAOProxy(); if (dao == null || readStringCharacteristics(dao, name, true) == null) { // component with this name does not exists, // make a remote directory lookup Object ref = lookup(name, null); if (ref != null) { // found status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); return new ComponentInfo(0, name, null, null, new ServiceComponent(ref)); } else { // not found bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Component "+ name +" not found in CDB."); status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); throw bcex; } } if (code == null) { code = readStringCharacteristics(dao, name+"/Code"); if (code == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Misconfigured CDB, there is no code of component '"+name+"' defined."); status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); throw bcex; } } if (type == null) { type = readStringCharacteristics(dao, name+"/Type"); if (type == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Misconfigured CDB, there is no type of component '"+name+"' defined."); status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); throw bcex; } } if (containerName == null) { containerName = readStringCharacteristics(dao, name+"/Container"); if (containerName == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Misconfigured CDB, there is no container of component '"+name+"' defined."); status.setStatus(ComponentStatus.COMPONENT_DOES_NO_EXIST); throw bcex; } } if (keepAliveTime == RELEASE_TIME_UNDEFINED) { // defaults to 0 == RELEASE_IMMEDIATELY keepAliveTime = readLongCharacteristics(dao, name+"/KeepAliveTime", RELEASE_IMMEDIATELY, true); } } // we have keepAlive missing, try to get it if (keepAliveTime == RELEASE_TIME_UNDEFINED) { DAOProxy dao = getComponentsDAOProxy(); if (dao != null) { // defaults to 0 == RELEASE_IMMEDIATELY keepAliveTime = readLongCharacteristics(dao, name+"/KeepAliveTime", RELEASE_IMMEDIATELY, true); } else { // this is a case where all data for dynamic component is specified and there is not entry in CDB keepAliveTime = RELEASE_IMMEDIATELY; } } // read impl. language. DAOProxy dao = getComponentsDAOProxy(); String componentImplLang = null; if (dao != null) { // silent read componentImplLang = readStringCharacteristics(dao, name+"/ImplLang", true); } // if requestor did not request activation we are finished if (!activate) { status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); return null; } /****************** component activation ******************/ // // get container/remote manager // Container container = null; ContainerInfo containerInfo = null; Manager remoteManager = null; if (isOtherDomainComponent) { // @todo MF do the login? try { String domainName = CURLHelper.createURI(name).getAuthority(); remoteManager = getManagerForDomain(domainName); if (remoteManager == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Failed to obtain manager for domain '" + domainName + "'."); throw bcex; } } catch (Throwable th) { bcex = new AcsJCannotGetComponentEx(th); bcex.setReason("Failed to obtain non-local manager required by component '"+name+"'.'"); status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); throw bcex; } } else { // search for container by its name containerInfo = getContainerInfo(containerName); // try to start-up container if (containerInfo == null) containerInfo = startUpContainer(containerName); // check state and get container if (containerInfo != null) { checkContainerShutdownState(containerInfo); container = containerInfo.getContainer(); } // required container is not logged in if (container == null) { bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Container '"+containerName+"' required by component '"+name+"' is not logged in."); status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); throw bcex; } } // check container vs component ImplLang ImplLang containerImplLang = containerInfo.getImplLang(); if (containerImplLang != null && containerImplLang != ImplLang.not_specified) { if (componentImplLang != null && ImplLang.fromString(componentImplLang) != containerImplLang) { AcsJCannotGetComponentEx af = new AcsJCannotGetComponentEx(); af.setReason("Component and container implementation language do not match (" + componentImplLang + " != " + containerImplLang.name() + ")"); throw af; } } // // get handle // // obtain handle componentsLock.lock(); try { // only preallocate (if necessary) if (!reactivate) { // !!! ACID 2 Integer objHandle = (Integer)executeCommand(new ComponentCommandPreallocate()); h = (objHandle == null) ? 0 : objHandle.intValue(); //h = components.preallocate(); } // failed to obtain handle if (h == 0) { AcsJCannotGetComponentEx af = new AcsJCannotGetComponentEx(); af.setReason("Preallocation of new handle failed, too many registered components."); throw af; } // create temporary ComponentInfo - to allow hierarchical components if (!reactivate) { ComponentInfo data = new ComponentInfo(h | COMPONENT_MASK, name, type, code, null); data.setKeepAliveTime(keepAliveTime); // !!! ACID executeCommand(new ComponentCommandSet(h, data)); // add to pending activation list synchronized (pendingActivations) { pendingActivations.put(name, data); } // add component to client component list to allow dependency checks if ((requestor & TYPE_MASK) == COMPONENT_MASK) addComponentOwner(data.getHandle(), requestor); } } finally { componentsLock.unlock(); } // // invoke get_component // componentInfo = null; long executionId = 0; long activationTime = 0; boolean timeoutError = false; if (isOtherDomainComponent) { // // invoke get_component on remote manager // try { URI curlName = CURLHelper.createURI(name); StatusHolder statusHolder = new StatusHolder(); // @todo MF tmp (handle) remoteManager.getComponent(INTERDOMAIN_MANAGER_HANDLE, curlName, true, statusHolder); activationTime = System.currentTimeMillis(); if (statusHolder.getStatus() == ComponentStatus.COMPONENT_ACTIVATED) { // local name to be used String localName = curlName.getPath(); if (localName.charAt(0) == '/') localName = localName.substring(1); /// @TODO MF tmp (handle) ComponentInfo[] infos = remoteManager.getComponentInfo(INTERDOMAIN_MANAGER_HANDLE, new int[0], localName, "*", true); if (infos != null && infos.length == 1) { componentInfo = infos[0]; // fix container name componentInfo.setContainerName(CURL_URI_SCHEMA + curlName.getAuthority() + "/" + componentInfo.getContainerName()); } //else // throw new RemoteException("Failed to obtain component info for '"+name+"' from remote manager."); } //else // throw new RemoteException("Failed to obtain component '"+name+"' from remote manager, status: " + statusHolder.getStatus() + "."); } catch (Throwable ex) { bcex = new AcsJCannotGetComponentEx(ex); bcex.setReason("Failed to obtain component '"+name+"' from remote manager."); timeoutError = (ex instanceof TimeoutRemoteException); } } else { // // invoke get_component on container // // log info String handleReadable = HandleHelper.toString(h | COMPONENT_MASK); logger.log(Level.INFO,"Activating component '"+name+"' (" + handleReadable + ") on container '" + containerInfo.getName() + "'."); boolean callSyncActivate = System.getProperties().containsKey(NAME_SYNC_ACTIVATE); if (callSyncActivate) { // sync try { executionId = generateExecutionId(); activationTime = System.currentTimeMillis(); componentInfo = container.activate_component(h | COMPONENT_MASK, executionId, name, code, type); } catch (Throwable ex) { bcex = new AcsJCannotGetComponentEx(ex); bcex.setReason("Failed to activate component '"+name+"' on container '"+containerName+"'."); timeoutError = (ex instanceof TimeoutRemoteException); } } else { // async try { executionId = generateExecutionId(); activationTime = System.currentTimeMillis(); ComponentInfoCompletionCallbackImpl callback = new ComponentInfoCompletionCallbackImpl( requestor, name, type, code, containerName, keepAliveTime, status, isOtherDomainComponent, isDynamicComponent, h, reactivate, container, containerInfo, executionId, activationTime); addPendingContainerAsyncRequest(containerName, callback); try { container.activate_component_async(h | COMPONENT_MASK, executionId, name, code, type, callback); } catch (Throwable t) { // failed call, remove async request from the list removePendingContainerAsyncRequest(containerName, callback); throw t; } logger.log(AcsLogLevel.DELOUSE, "Asynchronous activation of component '"+name+"' (" + handleReadable + ") is running on container '" + containerInfo.getName() + "'."); ComponentInfo ret; try { ret = callback.waitUntilActivated(getLockTimeout()); } catch (Throwable t) { // failed call (most likely timeout), remove async request from the list removePendingContainerAsyncRequest(containerName, callback); throw t; } logger.log(AcsLogLevel.DELOUSE, "Asynchronous activation of component '"+name+"' (" + handleReadable + ") has finished on container '" + containerInfo.getName() + "'."); return ret; } catch (Throwable ex) { bcex = new AcsJCannotGetComponentEx(ex); bcex.setReason("Failed to activate component '"+name+"' on container '"+containerName+"'."); throw bcex; } } } // call this immediately if bcex != null or sync call return internalNoSyncRequestComponentPhase2(requestor, name, type, code, containerName, keepAliveTime, status, bcex, isOtherDomainComponent, isDynamicComponent, h, reactivate, componentInfo, container, containerInfo, executionId, activationTime, timeoutError); } private void addPendingContainerAsyncRequest(String containerName, ComponentInfoCompletionCallback callback) { synchronized (pendingContainerAsyncRequests) { Deque<ComponentInfoCompletionCallback> list = pendingContainerAsyncRequests.get(containerName); if (list == null) { list = new ArrayDeque<Container.ComponentInfoCompletionCallback>(); pendingContainerAsyncRequests.put(containerName, list); } list.add(callback); } } private boolean removePendingContainerAsyncRequest(String containerName, ComponentInfoCompletionCallback callback) { synchronized (pendingContainerAsyncRequests) { Deque<ComponentInfoCompletionCallback> list = pendingContainerAsyncRequests.get(containerName); if (list == null) return false; boolean removed = list.remove(callback); if (list.size() == 0) pendingContainerAsyncRequests.remove(containerName); return removed; } } private void cancelPendingContainerAsyncRequestWithException(String containerName, Throwable cause) { synchronized (pendingContainerAsyncRequests) { Deque<ComponentInfoCompletionCallback> list = pendingContainerAsyncRequests.get(containerName); if (list == null) return; // remove in reverse order while (!list.isEmpty()) { try { list.getLast().failed(null, cause); } catch (Throwable th) { // just log new AcsJUnexpectedExceptionEx(th).log(logger); } } } } private class ComponentInfoCompletionCallbackImpl implements ComponentInfoCompletionCallback { final int requestor; final String name; final String type; final String code; final String containerName; final int keepAliveTime; final StatusHolder status; final boolean isOtherDomainComponent; final boolean isDynamicComponent; final int h; final boolean reactivate; final Container container; final ContainerInfo containerInfo; final long executionId; final long activationTime; boolean timeoutError; // @TODO HSO: Do we need this field? // We use a sync object different from "this" to check if the missing done callback // reported in AIV-11149 and AIV-9450 may come from some other thread getting the monitor // of this object and thus preventing waitUntilActivated from running after the notifyAll. // TODO: Use concurrent lib instead of wait-notify. private final Object sync = new Object(); private volatile ComponentInfo componentInfo; private volatile Throwable exception = null; private volatile boolean done = false; public ComponentInfoCompletionCallbackImpl(int requestor, String name, String type, String code, String containerName, int keepAliveTime, StatusHolder status, boolean isOtherDomainComponent, boolean isDynamicComponent, int h, boolean reactivate, Container container, ContainerInfo containerInfo, long executionId, long activationTime) { this.requestor = requestor; this.name = name; this.type = type; this.code = code; this.containerName = containerName; this.keepAliveTime = keepAliveTime; this.status = status; this.isOtherDomainComponent = isOtherDomainComponent; this.isDynamicComponent = isDynamicComponent; this.h = h; this.reactivate = reactivate; this.container = container; this.containerInfo = containerInfo; this.executionId = executionId; this.activationTime = activationTime; } /** * @throws Throwable Either the exception delivered by the container via {@link #failed(ComponentInfo, Throwable)}, * or InterruptedException if interrupted while waiting for done/failed callback. */ public ComponentInfo waitUntilActivated(long timeToWait) throws Throwable { synchronized (sync) { while (!done) { try { sync.wait(timeToWait); if (!done) { throw new TimeoutRemoteException("Activation did not finish in time."); } } catch (InterruptedException ex) { exception = ex; break; } } if (exception != null) { throw exception; } return componentInfo; } } @Override public void done(ComponentInfo result) { logger.log(AcsLogLevel.DEBUG, "Container responded with 'done' callback to indicate activation of a component '"+name+"'."); // remove and check for duplicate call if (!removePendingContainerAsyncRequest(containerName, this)) { logger.log(AcsLogLevel.DEBUG, "Duplicate async callback response (with 'done') for a component '"+name+"'."); return; } synchronized (sync) { try { componentInfo = internalNoSyncRequestComponentPhase2(requestor, name, type, code, containerName, keepAliveTime, status, null, isOtherDomainComponent, isDynamicComponent, h, reactivate, result, container, containerInfo, executionId, activationTime, timeoutError); // todo: just pass 'false' instead of 'timeoutError' ? } catch (Throwable th) { exception = th; // debug log is enough here, as the exception will be thrown by waitUntilActivated logger.log(AcsLogLevel.DEBUG, "Failed to process the freshly activated '"+name+"' component's data.", th); } finally { done = true; sync.notifyAll(); } } } @Override public void failed(ComponentInfo result, Throwable exception) { // remove and check for duplicate call if (!removePendingContainerAsyncRequest(containerName, this)) { logger.log(AcsLogLevel.DEBUG, "Duplicate async callback response (with 'failed') for a component '"+name+"'."); return; } synchronized (sync) { try { // TODO: Who can this ever be a (locally java defined) TimeoutRemoteException // when it comes from the container? boolean timeoutError = (exception instanceof TimeoutRemoteException); AcsJCannotGetComponentEx bcex; if (exception instanceof AcsJCannotGetComponentEx) bcex = (AcsJCannotGetComponentEx)exception; else bcex = new AcsJCannotGetComponentEx(exception); logger.log(Level.SEVERE, "Failed to activate component '"+name+"' on container '"+containerName+"'.", bcex); componentInfo = internalNoSyncRequestComponentPhase2(requestor, name, type, code, containerName, keepAliveTime, status, bcex, isOtherDomainComponent, isDynamicComponent, h, reactivate, result, container, containerInfo, executionId, activationTime, timeoutError); } catch (Throwable th) { this.exception = th; } finally { done = true; sync.notifyAll(); } } } } private ComponentInfo internalNoSyncRequestComponentPhase2( int requestor, String name, String type, String code, String containerName, int keepAliveTime, StatusHolder status, AcsJCannotGetComponentEx bcex, boolean isOtherDomainComponent, boolean isDynamicComponent, int h, boolean reactivate, ComponentInfo componentInfo, Container container, ContainerInfo containerInfo, long executionId, long activationTime, boolean timeoutError) throws AcsJCannotGetComponentEx { // remove component from client component list, will be added later (first lots of checks has to be done) if ((requestor & TYPE_MASK) == COMPONENT_MASK) { removeComponentOwner(h | COMPONENT_MASK, requestor); } // remove from pending activation list synchronized (pendingActivations) { pendingActivations.remove(name); } // failed to activate if (componentInfo == null || componentInfo.getHandle() == 0 || componentInfo.getComponent() == null) { if (bcex == null) logger.log(Level.SEVERE,"Failed to activate component '"+name+"' (" + HandleHelper.toString(h | COMPONENT_MASK) + ")."); else bcex.setReason("Failed to activate component '"+name+"' (" + HandleHelper.toString(h | COMPONENT_MASK) + ")."); componentsLock.lock(); try { // !!! ACID 3 if (!reactivate) executeCommand(new ComponentCommandDeallocate(h, h | COMPONENT_MASK, timeoutError ? WhyUnloadedReason.TIMEOUT : WhyUnloadedReason.REMOVED, true)); //components.deallocate(h, true); } finally { componentsLock.unlock(); } status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); if (bcex == null) return null; else throw bcex; } // log info logger.log(Level.INFO,"Component '"+name+"' (" + HandleHelper.toString(h | COMPONENT_MASK) + ") activated successfully."); // // check type consistency // if (!isOtherDomainComponent && !componentInfo.getComponent().doesImplement(type)) { // just output SEVERE message logger.log(Level.SEVERE,"Activated component '" + name + "' does not implement specified type '" + type + "'."); } // @todo MF do the component handle mapping here (remember map and fix componentInfo), // component info (to get type and code, container - prefix name) if (isOtherDomainComponent) { // @todo MF tmp (for testing) componentInfo.setHandle(h | COMPONENT_MASK); componentInfo.setClients(new IntArray()); componentInfo.setComponents(new IntArray(0)); componentInfo.setContainer(0); // set variables type = componentInfo.getType(); code = componentInfo.getCode(); } int clients[]; componentsLock.lock(); try { // // check if returned handle matches ours (given) // if (componentInfo.getHandle() != (h | COMPONENT_MASK)) { // container returned different handle // (it seems it has already activated this Component) // check if we can accept it int componentHandle = componentInfo.getHandle() & HANDLE_MASK; if (components.isAllocated(componentHandle)) { // handle is already allocated, we have to reject returned handle // !!! ACID 3 // cancel preallocation if (!reactivate) executeCommand(new ComponentCommandDeallocate(h, componentInfo.getHandle(), WhyUnloadedReason.REMOVED, true)); //components.deallocate(h, true); bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Container '" + containerName + "' returned another '" + name + "' component handle than given, failed to fix handle since returned handle is already allocated."); // log this anyway, since it's an error of a global nature logger.log(Level.SEVERE, "Container '" + containerName + "' returned another '" + name + "' component handle than given, failed to fix handle since returned handle is already allocated.", bcex); status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); // component is activated, but cannot be managed by the Manager throw bcex; } else { // handle is free, relocate handle ComponentInfo existingData = null; // !!! ACID 3 // deallocate old if (!reactivate) executeCommand(new ComponentCommandDeallocate(h, componentInfo.getHandle(), WhyUnloadedReason.REPLACED, true)); //components.deallocate(h, true); else { // !!! ACID 3 existingData = (ComponentInfo)components.get(h); executeCommand(new ComponentCommandDeallocate(h, componentInfo.getHandle(), WhyUnloadedReason.REPLACED)); //components.deallocate(h); } // !!! ACID 3 // preallocate new Integer objHandle = (Integer)executeCommand(new ComponentCommandAllocateHandle(componentHandle, true)); //h = components.allocate(componentHandle, true); if (objHandle == null || (h = objHandle.intValue()) == 0) { // failed to allocate new bcex = new AcsJCannotGetComponentEx(); bcex.setReason("Container '" + containerName + "' returned another '" + name + "' component handle than given, failed to fix handle due to handle relocation failure."); // log this anyway, since it's an error of a global nature logger.log(Level.SEVERE,"Container '" + containerName + "' returned another '" + name + "' component handle than given, failed to fix handle due to handle relocation failure.", bcex); status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); // Component is activated, but cannot be managed by the Manager throw bcex; } // !!! ACID 3 else if (existingData != null) executeCommand(new ComponentCommandSet(h, existingData)); //components.set(h, existingData); logger.log(Level.WARNING,"Container '" + containerName + "' returned another '" + name + "' component handle than given, handle fixed."); } } // // pre-store data // ComponentInfo existingData = (ComponentInfo)components.get(h); // create a new ComponentInfo - do not trust containers ComponentInfo data = new ComponentInfo(componentInfo.getHandle(), name, type, code, componentInfo.getComponent()); if (existingData != null) { data.setClients(existingData.getClients()); data.setComponents(existingData.getComponents()); } if (requestor != 0) if (!data.getClients().contains(requestor)) // hierarchical components need this check data.getClients().add(requestor); if (keepAliveTime <= RELEASE_NEVER) if (!data.getClients().contains(this.getHandle())) // make component immortal data.getClients().add(this.getHandle()); data.setKeepAliveTime(keepAliveTime); // remember keep alive time if (isOtherDomainComponent) { data.setContainer(0); data.setContainerName(componentInfo.getContainerName()); } else { data.setContainer(containerInfo.getHandle()); data.setContainerName(containerInfo.getName()); } data.setAccessRights(0); data.setInterfaces(componentInfo.getInterfaces()); // mark as dynamic component and store its container if (isDynamicComponent) { data.setDynamic(true); data.setDynamicContainerName(containerName); } // !!! ACID executeCommand(new ComponentCommandSet(h, data)); //components.set(h, data); // acknowledge allocation if (!reactivate) // !!! ACID 2 executeCommand(new ComponentCommandAckAlloc(h)); //components.ackAllocation(h); componentInfo = data; clients = componentInfo.getClients().toArray(); } finally { componentsLock.unlock(); } if (!isOtherDomainComponent) { // add component to client component list to allow dependency checks if ((requestor & TYPE_MASK) == COMPONENT_MASK) addComponentOwner(componentInfo.getHandle(), requestor); // // call construct // boolean constructed = false; try { componentInfo.getComponent().construct(); constructed = true; } catch (Throwable ex) { bcex = new AcsJCannotGetComponentEx(ex); bcex.setReason("Failed to construct component '"+name+"', exception caught when invoking 'construct()' method."); } // remove component from client component list, will be added later if ((requestor & TYPE_MASK) == COMPONENT_MASK) removeComponentOwner(componentInfo.getHandle(), requestor); if (!constructed) { // release Component componentsLock.lock(); try { // !!! ACID 3 if (!reactivate) executeCommand(new ComponentCommandDeallocate(h, componentInfo.getHandle(), WhyUnloadedReason.REMOVED)); //components.deallocate(h); // deactivate try { container.deactivate_component(componentInfo.getHandle()); } catch (Exception ex) { bcex = new AcsJCannotGetComponentEx(ex); bcex.setReason("Failed to deactivate component '"+name+"' on container '"+containerName+"'."); } status.setStatus(ComponentStatus.COMPONENT_NOT_ACTIVATED); throw bcex; } finally { componentsLock.unlock(); } } } // for surely activated // add component to client component list if (requestor != this.getHandle() && requestor != 0) addComponentOwner(componentInfo.getHandle(), requestor); // add component to container component list if (!isOtherDomainComponent) synchronized (containerInfo.getComponents()) { /// !!! ACID if (!containerInfo.getComponents().contains(componentInfo.getHandle())) executeCommand(new ContainerInfoCommandComponentAdd(containerInfo.getHandle() & HANDLE_MASK, componentInfo.getHandle())); //containerInfo.getComponents().add(componentInfo.getHandle()); } // remove component from unavailable list if (reactivate) { // !!! ACID synchronized (unavailableComponents) { executeCommand(new UnavailableComponentCommandRemove(name)); //unavailableComponents.remove(name); } } // COMMENTED OUT: this can lead to bad performance on slow FS /* // take snapshot of manager state if( prevayler != null ) { try { synchronized (prevayler) { ((SnapshotPrevayler)prevayler).takeSnapshot(); } } catch (IOException e) { e.printStackTrace(); } } */ // // bind to remote directory // //bind(convertToHiearachical(name), "O", componentInfo.getComponent().getObject()); // // notify administrators about the activation // if (!isOtherDomainComponent) notifyComponentActivated(componentInfo, activationTime, executionId); // // inform clients about availability // if (reactivate) { notifyComponentAvailable(requestor, clients, new ComponentInfo[] { componentInfo }); } // // notify administrators about the activation // notifyComponentRequested(new int[] { requestor }, new int[] { componentInfo.getHandle() }, activationTime); if (reactivate) logger.log(Level.FINE,"Component '"+name+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") reactivated."); else logger.log(Level.FINE,"Component '"+name+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") activated."); // notify about the change (only this-domain container which activated the component)... if (containerInfo != null) topologySortManager.notifyTopologyChange(containerInfo.getHandle()); status.setStatus(ComponentStatus.COMPONENT_ACTIVATED); return componentInfo; } class ReleaseComponentResult { public int owners; public Throwable exception; public ReleaseComponentResult(int owners, Throwable exception) { this.owners = owners; this.exception = exception; } } /** * Internal method for releasing components. * * @param owner owner of the component, if manager's own handle then deactivation will be forced * @param curl CURL of the component to be released. * @param force force deactivate, if still has owners then component will be made unavailable. * @return Number of clients that are still using the component after the operation completed. */ private ReleaseComponentResult internalReleaseComponent(int owner, URI curl, boolean force) throws AcsJNoPermissionEx, AcsJBadParameterEx { // resolve handle from curl int h = 0; String name = extractName(curl); componentsLock.lock(); try { h = components.first(); while (h != 0) { ComponentInfo componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(name)) { h = componentInfo.getHandle(); break; } h = components.next(h); } } finally { componentsLock.unlock(); } // if found, delegate operation, otherwise do nothing if (h != 0) return internalReleaseComponent(owner, h, force); else return new ReleaseComponentResult(0, null); } /** * Internal method for deactivating components. * * @param name name of the component to be released. */ private void internalDeactivateComponent(String name) { // try to acquire lock String lockNotAcquiredCause = acquireSynchronizationObject(name, lockTimeout, "deactivate component " + name); if (lockNotAcquiredCause == null) { boolean releaseRWLock = false; try { // resolve componentInfo from curl ComponentInfo componentInfo = null; componentsLock.lock(); try { int h = components.first(); while (h != 0) { ComponentInfo ci = (ComponentInfo)components.get(h); if (ci.getName().equals(name)) { // a new owner detected, leave component activated if (ci.getClients().size() > 0) return; componentInfo = ci; break; } h = components.next(h); } } finally { componentsLock.unlock(); } // component is already gone, nothing to do if (componentInfo == null) return; // try to acquire activation readers lock first // NOTE: the locks are NOT reentrant releaseRWLock = true; activationPendingRWLock.readLock().lock(); try { internalNoSyncDeactivateComponent(componentInfo); } catch (Throwable th) { // no handling, already logged } } finally { if (releaseRWLock) activationPendingRWLock.readLock().unlock(); releaseSynchronizationObject(name); } } else { NoResourcesException nre = new NoResourcesException("Failed to obtain synchronization lock for component '"+name+"', possible deadlock; locked to '" + lockNotAcquiredCause + "'."); throw nre; } } /** * Internal method for releasing components. * * @param owner owner of the component. * @param h handle of the component to be released. * @param force force deactivate, if still has owners then component will be made unavailable. * @return Number of clients that are still using the component after the operation completed. */ private ReleaseComponentResult internalReleaseComponent(int owner, int h, boolean force) throws AcsJNoPermissionEx, AcsJBadParameterEx { // extract name String name = null; componentsLock.lock(); try { int handle = h & HANDLE_MASK; ComponentInfo componentInfo = null; if (components.isAllocated(handle)) componentInfo = (ComponentInfo)components.get(handle); if (componentInfo == null) { // invalid Component handle AcsJBadParameterEx ex = new AcsJBadParameterEx(); ex.setParameter("componentInfo"); ex.setParameterValue("null"); throw ex; } if (componentInfo.getHandle() != h) { // invalid Component handle AcsJBadParameterEx ex = new AcsJBadParameterEx(); ex.setParameter("h"); throw ex; } name = componentInfo.getName(); } finally { componentsLock.unlock(); } // try to acquire lock String lockNotAcquiredCause = acquireSynchronizationObject(name, lockTimeout, "release component " + name); if (lockNotAcquiredCause == null) { boolean releaseRWLock = true; try { // try to acquire activation readers lock first // NOTE: the locks are NOT reentrant activationPendingRWLock.readLock().lock(); return internalNoSyncReleaseComponent(owner, h, force); } finally { if (releaseRWLock) activationPendingRWLock.readLock().unlock(); releaseSynchronizationObject(name); } } else { NoResourcesException nre = new NoResourcesException("Failed to obtain synchronization lock for component '"+name+"', possible deadlock; locked to '" + lockNotAcquiredCause + "'."); throw nre; } } /** * Internal method for releasing components. * * @param owner owner of the component. * @param h handle of the component to be released. * @param force force deactivate, if still has owners then component will be made unavailable. * @return Number of clients that are still using the component after the operation completed. */ private ReleaseComponentResult internalNoSyncReleaseComponent(int owner, int h, boolean force) throws AcsJNoPermissionEx { int handle = h & HANDLE_MASK; int owners = 0; ComponentInfo componentInfo = null; componentsLock.lock(); try { if (components.isAllocated(handle)) componentInfo = (ComponentInfo)components.get(handle); if (componentInfo == null || componentInfo.getHandle() != h) { // invalid component handle BadParametersException af = new BadParametersException("Invalid component handle."); throw af; } // remove ownership of the component if (!componentInfo.getClients().contains(owner)) { if (!force) { // not an owner AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Unregistering component that client does not own."); npe.setID(HandleHelper.toString(owner)); npe.setProtectedResource(componentInfo.getName()); throw npe; } } else { // ACID - !!! // remove client/component as an owner executeCommand(new ComponentCommandClientRemove(componentInfo.getHandle() & HANDLE_MASK, owner)); //componentInfo.getClients().remove(owner); // remove component from client component list if (owner != this.getHandle()) removeComponentOwner(componentInfo.getHandle(), owner); } owners = componentInfo.getClients().size(); if (owners == 0) { // !!! ACID // this should not be done here (this will throw no permission exception to the component releasing its subcomponents) // deallocate Component //executeCommand(new ComponentCommandDeallocate(handle)); ////components.deallocate(handle); // remove from unavailable list // there is not owner to be unavailable for synchronized (unavailableComponents) { if (unavailableComponents.containsKey(componentInfo.getName())) { // !!! ACID executeCommand(new UnavailableComponentCommandRemove(componentInfo.getName())); //unavailableComponents.remove(componentInfo.getName()); } } } } finally { componentsLock.unlock(); } /****************** component deactivation ******************/ ReleaseComponentResult result = new ReleaseComponentResult(owners, null); // there is no owners of the component, deactivate it if (force) { try { internalNoSyncDeactivateComponent(componentInfo); } catch (Throwable th) { result.exception = th; } } else if (owners == 0) { int keepAliveTime = RELEASE_IMMEDIATELY; String name = componentInfo.getName(); boolean isOtherDomainComponent = name.startsWith(CURL_URI_SCHEMA); if (!isOtherDomainComponent) { keepAliveTime = componentInfo.getKeepAliveTime(); if (keepAliveTime == RELEASE_TIME_UNDEFINED) { // when info is passed from the container DAOProxy dao = getComponentsDAOProxy(); if (dao != null) keepAliveTime = readLongCharacteristics(dao, name+"/KeepAliveTime", RELEASE_IMMEDIATELY, true); else keepAliveTime = RELEASE_IMMEDIATELY; } } if (keepAliveTime == 0) { try { internalNoSyncDeactivateComponent(componentInfo); } catch (Throwable th) { result.exception = th; } } else if (keepAliveTime > 0) delayedDeactivationTask.schedule(new DeactivateComponentTask(name), keepAliveTime * 1000); // negative means immortal, however this could not happen since immortal // components have manager as an owner } /// TODO !!!!!!!!!!!!!! no more handle -> componentInfo data // notify administrators about the release request notifyComponentReleased(new int[] { owner }, new int[] { h }, System.currentTimeMillis()); logger.log(Level.FINE,"Component '"+componentInfo.getName()+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") released."); // component deactivated if (owners == 0 || force) { topologySortManager.notifyTopologyChange(componentInfo.getContainer()); } else if ((owner & TYPE_MASK) == COMPONENT_MASK) { // component dependency changed... // notify about the change (only if on the same container)... // on complete system shutdown sort will be done anyway ComponentInfo ownerComponentInfo = getComponentInfo(owner); if (ownerComponentInfo != null && ownerComponentInfo.getContainerName() != null && ownerComponentInfo.getContainerName().equals(componentInfo.getContainerName())) topologySortManager.notifyTopologyChange(componentInfo.getContainer()); } return result; } /** * Deactivate component, issue deactivate reeust to container (or other manager). * @param componentInfo info about component to be deactivated. */ private void internalNoSyncDeactivateComponent(ComponentInfo componentInfo) throws Throwable { // unbind from remote directory //unbind(convertToHiearachical(componentInfo.getName()), "O"); int handle = componentInfo.getHandle() & HANDLE_MASK; int owners = componentInfo.getClients().size(); try { // // get container/remote manager // String name = componentInfo.getName(); boolean isOtherDomainComponent = name.startsWith(CURL_URI_SCHEMA); if (isOtherDomainComponent) { Manager remoteManager = null; // @todo MF do the login? try { String domainName = CURLHelper.createURI(name).getAuthority(); remoteManager = getManagerForDomain(domainName); if (remoteManager == null) throw new CoreException("Failed to obtain manager for domain '" + domainName + "'."); } catch (Throwable th) { logger.log(Level.WARNING, "Failed to obtain non-local manager required by component '"+name+"'.", th); throw th; } // @todo MF call release_component on other manager (logout?) // release component try { URI curlName = CURLHelper.createURI(name); // @todo MF tmp (handle) remoteManager.releaseComponent(INTERDOMAIN_MANAGER_HANDLE, curlName); } catch (Throwable th) { logger.log(Level.WARNING, "Failed to release component '"+componentInfo.getName()+"' on remote manager.'", th); throw th; } } else { // // search for container by its name // Container container = null; ContainerInfo containerInfo = null; int containerHandle = componentInfo.getContainer(); // if containerHandle equals 0, we have unavailable or registered component if (containerHandle != 0) { containerInfo = getContainerInfo(containerHandle); if (containerInfo != null) { // remove component from container component list synchronized (containerInfo.getComponents()) { // !!! ACID if (containerInfo.getComponents().contains(componentInfo.getHandle())) executeCommand(new ContainerInfoCommandComponentRemove(containerInfo.getHandle() & HANDLE_MASK, componentInfo.getHandle())); //containerInfo.getComponents().remove(componentInfo.getHandle()); } // we allow this (since releasing components is part of container shutdown procedure) //checkContainerState(containerInfo); container = containerInfo.getContainer(); } // required container is not logged in if (container == null) { // then simply do not do the deactivation String containerName; if (containerInfo != null) containerName = containerInfo.getName(); else containerName = HandleHelper.toString(componentInfo.getContainer()); logger.log(Level.WARNING,"Container '"+containerName+"' required by component '"+componentInfo.getName()+"' is not logged in."); } } if (container != null) { // log info logger.log(Level.INFO,"Deactivating component '"+componentInfo.getName()+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") on container '" + containerInfo.getName() + "'."); // destruct try { componentInfo.getComponent().destruct(); } catch (Throwable ex) { RemoteException re = new RemoteException("Failed to destruct component '"+componentInfo.getName()+"', exception caught when invoking 'destruct()' method.", ex); logger.log(Level.SEVERE, re.getMessage(), re); throw ex; } long deactivationTime = 0; // deactivate component in anycase try { container.deactivate_component(componentInfo.getHandle()); deactivationTime = System.currentTimeMillis(); } catch (AcsJException aex) { logger.log(Level.SEVERE, aex.getMessage(), aex); throw aex; } catch (Throwable ex) { RemoteException re = new RemoteException("Failed to deactivate component '"+componentInfo.getName()+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") on container '"+containerInfo.getName()+"'.", ex); logger.log(Level.SEVERE, re.getMessage(), re); throw ex; } // notify administrators about deactivation, but not if failed if (deactivationTime != 0) notifyComponentDeactivated(componentInfo.getHandle(), deactivationTime); // shutdown container if required (and necessary) conditionalShutdownContainer(containerInfo); } } } finally { if (owners == 0) { // deallocate Component componentsLock.lock(); try { executeCommand(new ComponentCommandDeallocate(handle, componentInfo.getHandle(), WhyUnloadedReason.REMOVED)); //components.deallocate(handle); } finally { componentsLock.unlock(); } } } // log info logger.log(Level.INFO,"Component '"+componentInfo.getName()+"' (" + HandleHelper.toString(componentInfo.getHandle()) + ") deactivated."); // release all subcomponents (just like client logoff) // component should have already done this by itself, but take care of clean cleanup // what about that: if subcomponent becomes unavailable, does component also becomes?! // no, it is notified and it handles situation by its own way (e.g. changes component state). // Just like it already handles activation (manager does not care for dependecy trees). int [] subcomponents = null; // no not hold the lock synchronized (componentInfo.getComponents()) { if (componentInfo.getComponents().size() > 0) { IntArray toCleanupList = new IntArray(); IntArray comps = componentInfo.getComponents(); for (int i = 0; i < comps.size(); i++) if (components.isAllocated(comps.get(i) & HANDLE_MASK)) toCleanupList.add(comps.get(i)); if (toCleanupList.size() > 0) subcomponents = toCleanupList.toArray(); } //subcomponents = componentInfo.getComponents().toArray(); } if (subcomponents != null && subcomponents.length > 0) new ReleaseComponentTask(componentInfo.getHandle(), subcomponents).run(); // make unavailable (deactivation was forced) if (owners > 0) makeUnavailable(componentInfo); } /** * Start-up container (if it has a deploy info). * @param containerName name of the container to start up. * @return container info of container, <code>null</code> if failed to start. */ private ContainerInfo startUpContainer(String containerName) { // not to mess with same component name final String LOCK_NAME = "container-" + containerName; // try to acquire lock String lockNotAcquiredCause = acquireSynchronizationObject(LOCK_NAME, lockTimeout, "start-up container " + containerName); if (lockNotAcquiredCause == null) { try { // double check pattern ContainerInfo info = getContainerInfo(containerName); if (info != null) return info; return internalNoSyncStartUpContainer(containerName); } finally { releaseSynchronizationObject(LOCK_NAME); } } else { NoResourcesException nre = new NoResourcesException("Failed to obtain synchronization lock for container '"+containerName+"', possible deadlock; locked to '" + lockNotAcquiredCause + "'."); throw nre; } } /** * Start-up container (if it has a deploy info). * @param containerName name of the container to start up. * @return container info of container, <code>null</code> if failed to start. */ private ContainerInfo internalNoSyncStartUpContainer(String containerName) { DAOProxy dao = getContainersDAOProxy(); if (dao == null) return null; // // read DeployInfo and initiate start-up // String startOnDemand = readStringCharacteristics(dao, containerName + "/DeployInfo/StartOnDemand", true); if (startOnDemand == null || !startOnDemand.equalsIgnoreCase("TRUE")) return null; String host = readStringCharacteristics(dao, containerName + "/DeployInfo/Host", true); if (host == null) return null; String flags = readStringCharacteristics(dao, containerName + "/DeployInfo/Flags", true); if (flags == null) flags = ""; String impLang = readStringCharacteristics(dao, containerName + "/ImplLang", true); if (impLang == null) impLang = ""; // add itself as manager reference flags += " -m " + transport.getManagerReference(); short instance = (short)ACSPorts.getBasePort(); try { Daemon daemon = transport.getDaemon(host); if (daemon != null) daemon.startContainer(impLang, containerName, instance, flags); else throw new RuntimeException("Failed to get daemon."); } catch (Throwable th) { RemoteException re = new RemoteException("Failed to connect to ACS daemon on host '"+host+"' to start container '"+containerName+"'.", th); logger.log(Level.SEVERE, re.getMessage(), re); return null; } // // wait for login // // HSO: raised timeout from 15 sec to 2 min because of increased usage of autostart containers, // where container start times get extremely long when started in parallel on one machine. // TODO: Refactor manager interface to use callbacks for component getter methods, // to not block the manager ORB threads with long-lasting container starts. final int CONTAINER_STARTUP_TIMEOUT = 120000; // notify about new container login synchronized (containerLoggedInMonitor) { int waitTime = CONTAINER_STARTUP_TIMEOUT; while (waitTime > 0) { long start = System.currentTimeMillis(); try { containerLoggedInMonitor.wait(waitTime); } catch (InterruptedException e) { return null; } // check if container has logged in ContainerInfo info = getContainerInfo(containerName); if (info != null) { return info; } waitTime = waitTime - (int)(System.currentTimeMillis() - start); } // container did not logged in within CONTAINER_STARTUP_TIMEOUT ms return null; } } /** * Conditionally (if has no components and is not immortal container) shutdown container. * @param containerInfo container to shutdown. */ private void conditionalShutdownContainer(ContainerInfo containerInfo) { int componentsCount; synchronized (containerInfo.getComponents()) { componentsCount = containerInfo.getComponents().size(); } // noop if there are components activated by this container if (componentsCount > 0) return; // obtain keepAliveTime int keepAliveTime = RELEASE_NEVER; DAOProxy dao = getContainersDAOProxy(); if (dao != null) { // defaults to RELEASE_NEVER keepAliveTime = readLongCharacteristics(dao, containerInfo.getName()+"/DeployInfo/KeepAliveTime", keepAliveTime, true); } // always do shutdown in separate thread if (keepAliveTime >= 0) delayedDeactivationTask.schedule(new ShutdownContainerTask(containerInfo.getName()), keepAliveTime * 1000); // negative means immortal } /** * Internal method for restarting components. * * @param owner owner of the component. * @param curl CURL of the component to be restarted. * @return Newly restarted component, <code>null</code> if failed. */ private Component internalRestartComponent(int owner, URI curl) throws AcsJNoPermissionEx { // resolve handle from curl int h = 0; String name = extractName(curl); componentsLock.lock(); try { h = components.first(); while (h != 0) { ComponentInfo componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(name)) { h = componentInfo.getHandle(); break; } h = components.next(h); } } finally { componentsLock.unlock(); } // if found, delegate operation, otherwise do nothing if (h != 0) return internalRestartComponent(owner, h); else return null; } /** * Internal method for restarting components. * * @param owner owner of the component. * @param h handle of the component to be restarted. * @return Newly restarted component, <code>null</code> if failed. */ private Component internalRestartComponent(int owner, int h) throws AcsJNoPermissionEx { // extract name String name = null; componentsLock.lock(); try { int handle = h & HANDLE_MASK; ComponentInfo componentInfo = null; if (components.isAllocated(handle)) componentInfo = (ComponentInfo)components.get(handle); if (componentInfo == null || componentInfo.getHandle() != h) { // invalid Component handle BadParametersException af = new BadParametersException("Invalid component handle."); throw af; } name = componentInfo.getName(); } finally { componentsLock.unlock(); } // try to acquire lock String lockNotAcquiredCause = acquireSynchronizationObject(name, lockTimeout, "restart component " + name); if (lockNotAcquiredCause == null) { boolean releaseRWLock = true; try { // try to acquire activation readers lock first // NOTE: the locks are NOT reentrant activationPendingRWLock.readLock().lock(); return internalNoSyncRestartComponent(owner, h); } finally { if (releaseRWLock) activationPendingRWLock.readLock().unlock(); releaseSynchronizationObject(name); } } else { NoResourcesException nre = new NoResourcesException("Failed to obtain synchronization lock for component '"+name+"', possible deadlock; locked to '" + lockNotAcquiredCause + "'."); throw nre; } } /** * Internal method for restarting components. * * @param owner owner of the component. * @param h handle of the component to be restarting. * @return Newly restarted component, <code>null</code> if failed. */ // @todo MF not supported private Component internalNoSyncRestartComponent(int owner, int h) throws AcsJNoPermissionEx { int handle = h & HANDLE_MASK; ComponentInfo componentInfo = null; componentsLock.lock(); try { if (components.isAllocated(handle)) componentInfo = (ComponentInfo)components.get(handle); if (componentInfo == null || componentInfo.getHandle() != h) { // invalid component handle BadParametersException af = new BadParametersException("Invalid component handle."); throw af; } // remove ownership of the component if (!componentInfo.getClients().contains(owner)) { // not an owner AcsJNoPermissionEx npe = new AcsJNoPermissionEx(); npe.setReason("Restarting component that client does not own."); npe.setID(HandleHelper.toString(owner)); npe.setProtectedResource(componentInfo.getName()); throw npe; } } finally { componentsLock.unlock(); } /****************** restart component ******************/ // // get container // // search for container by its name Container container = null; ContainerInfo containerInfo = null; int containerHandle = componentInfo.getContainer(); // if containerHandle equals 0, we have unavailable or registered component if (containerHandle != 0) { containerInfo = getContainerInfo(containerHandle); if (containerInfo != null) { checkContainerShutdownState(containerInfo); container = containerInfo.getContainer(); } // required container is not logged in if (container == null) { // then simply do not do the restart String containerName; if (containerInfo != null) containerName = containerInfo.getName(); else containerName = HandleHelper.toString(componentInfo.getContainer()); logger.log(Level.WARNING,"Container '"+containerName+"' required by component '"+componentInfo.getName()+"' is not logged in."); } } // return value Component component = null; if (container != null) { // @todo what about notifying clients, marking component as unavailable... // restart component try { component = container.restart_component(componentInfo.getHandle()); if (component == null) { RemoteException re = new RemoteException("Failed to restart component '"+componentInfo.getName()+"', 'null' returned."); throw re; } // @todo what about notifying clients, marking component as available, updating reference... } catch (Throwable ex) { RemoteException re = new RemoteException("Failed to restart component '"+componentInfo.getName()+"' on container '"+containerInfo.getName()+"'.", ex); logger.log(Level.SEVERE, re.getMessage(), re); } } logger.log(Level.FINE,"Component '"+componentInfo.getName()+"' restarted."); return component; } /*****************************************************************************/ /*********************** [ Dynamic (de)activation ] **************************/ /*****************************************************************************/ /** * @see com.cosylab.acs.maci.Manager#getDefaultComponent(int, java.lang.String) */ // @todo MF not supported public ComponentInfo getDefaultComponent(int id, String type) throws AcsJNoPermissionEx, NoDefaultComponentException { if (type == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'type' expected."); throw af; } /****************************************************************/ // check handle and NONE permissions securityCheck(id, AccessRights.NONE); logger.log(Level.INFO,"Getting default component for type '" + type + "'."); ComponentInfo componentInfo = internalRequestDefaultComponent(id, type); /****************************************************************/ return componentInfo; } /** * Internal method for requesting default components. * @param requestor requestor of the component. * @param typr type of the component * @return componentInfo <code>ComponentInfo</code> of requested default component. */ private ComponentInfo internalRequestDefaultComponent(int requestor, String type) throws NoDefaultComponentException { String defaultComponentName = null; ComponentInfo defaultComponentInfo = null; // first check default components table synchronized (defaultComponents) { defaultComponentInfo = defaultComponents.get(type); } if (defaultComponentInfo != null) defaultComponentName = defaultComponentInfo.getName(); // if not found, search for the default component in the CDB if (defaultComponentName == null) { DAOProxy componentsDAO = getComponentsDAOProxy(); if (componentsDAO != null) { try { // get names of all components /*String[] ids =*/ componentsDAO.get_field_data(""); // @todo here to check if CDB is available String[] ids = getComponentsList(); // test names for (int i = 0; i < ids.length; i++) { // read name String name = ids[i]; //readStringCharacteristics(componentsDAO, ids[i]+"/Name"); if (name == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no type of component '"+ids[i]+"' defined."); continue; } // do not search dynamic components (they cannot be marked as default in CDB anyway) if (!name.equals(ComponentSpec.COMPSPEC_ANY)) { // read type String componentType = readStringCharacteristics(componentsDAO, ids[i]+"/Type"); if (type == null) { logger.log(Level.WARNING,"Misconfigured CDB, there is no type of component '"+name+"' defined."); continue; } // test type final String TRUE_STRING = "true"; if (type.equals(componentType)) { // check if it is default, read silently String isDefault = readStringCharacteristics(componentsDAO, ids[i]+"/Default", true); if (isDefault == null || !isDefault.equalsIgnoreCase(TRUE_STRING)) continue; // got the match defaultComponentName = name; break; } } } } catch (Throwable ex) { CoreException ce = new CoreException("Failed to obtain component data from the CDB.", ex); reportException(ce); } } } // @todo MF if non-local do not make this check!!! type should be given for inter-domain...!!! do not bail in.. // if found get the component if (defaultComponentInfo != null) { try { StatusHolder status = new StatusHolder(); ContainerInfo containerInfo = getContainerInfo(defaultComponentInfo.getContainer()); if (containerInfo == null) { CoreException huse = new CoreException("Failed to return default component: '" + defaultComponentName + "', container with '" + HandleHelper.toString(defaultComponentInfo.getContainer()) + "' not logged in."); reportException(huse); return null; } ComponentInfo componentInfo = internalRequestComponent(requestor, defaultComponentInfo.getName(), defaultComponentInfo.getType(), defaultComponentInfo.getCode(), containerInfo.getName(), RELEASE_IMMEDIATELY, status, true); if (componentInfo == null || status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) { CoreException huse = new CoreException("Failed to obtain default component: '" + defaultComponentName + "'."); reportException(huse); // no error handling... return null; } return componentInfo; } catch (Throwable t) { CoreException huse = new CoreException("Failed to return default component: '" + defaultComponentName + "'.", t); reportException(huse); return null; } } else if (defaultComponentName != null) { // create CURL URI curl = null; try { curl = CURLHelper.createURI(defaultComponentName); } catch (URISyntaxException use) { CoreException huse = new CoreException("Failed to create CURL from default component name: '" + defaultComponentName + "'.", use); reportException(huse); return null; } try { // request for component StatusHolder status = new StatusHolder(); Component component = internalRequestComponent(requestor, curl, status, true); if (component == null || status.getStatus() != ComponentStatus.COMPONENT_ACTIVATED) { CoreException huse = new CoreException("Failed to obtain default component: '" + defaultComponentName + "'."); reportException(huse); return null; } // return component info ComponentInfo[] componentInfo = getComponentInfo(requestor, new int[0], defaultComponentName, type, true); if (componentInfo == null || componentInfo.length != 1) { CoreException huse = new CoreException("Failed to obtain activated default component ComponentInfo: '" + defaultComponentName + "'."); reportException(huse); return null; } else return componentInfo[0]; } catch (Throwable t) { CoreException huse = new CoreException("Failed to return default component: '" + defaultComponentName + "'.", t); reportException(huse); return null; } } // not found NoDefaultComponentException ndce = new NoDefaultComponentException("No default component for type '" + type + "' found."); throw ndce; } /** * @see com.cosylab.acs.maci.Manager#getDynamicComponent(int, com.cosylab.acs.maci.ComponentSpec, boolean) */ // TODO MF not supported public ComponentInfo getDynamicComponent(int id, ComponentSpec componentSpec, boolean markAsDefault) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx, AcsJIncompleteComponentSpecEx, AcsJInvalidComponentSpecEx, AcsJComponentSpecIncompatibleWithActiveComponentEx { try { // check if null if (componentSpec == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec"); throw ex; } // check componentSpec components are null if (componentSpec.getName() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Name"); throw ex; } if (componentSpec.getType() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Type"); throw ex; } if (componentSpec.getCode() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Code"); throw ex; } if (componentSpec.getContainer() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Container"); throw ex; } // check for empty componentSpec.name if (componentSpec.getName().length() == 0) { AcsJBadParameterEx ex = new AcsJBadParameterEx(); ex.setParameter("componentSpec.Name"); ex.setParameterValue("EMPTY"); ex.setReason("Non empty Component Name expected"); throw ex; } } catch (AcsJNullPointerEx e) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(e); throw ex; } catch (AcsJBadParameterEx e) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(e); throw ex; } // check handle and NONE permissions // Throws AcsJNoPermissionEx that is let flying up securityCheck(id, AccessRights.NONE); /****************************************************************/ // Same exceptions fly up ComponentInfo componentInfo = null; try { componentInfo = internalRequestDynamicComponent(id, componentSpec); } catch (AcsJSyncLockFailedEx e) { AcsJCannotGetComponentEx ex = new AcsJCannotGetComponentEx(); ex.setCURL(componentSpec.getName()); ex.setReason("Failed to get Synchronisation lock"); throw ex; } // update default components table if (componentInfo != null && markAsDefault) { synchronized (defaultComponents) { // !!! ACID 3 executeCommand(new DefaultComponentCommandPut(componentInfo.getType(), componentInfo)); //defaultComponents.put(componentInfo.getType(), componentInfo.getName()); } logger.log(Level.INFO,"'" + componentInfo.getName() +"' has been marked as a default component of type '" + componentInfo.getType() +"'."); } /****************************************************************/ if(componentInfo == null) { /** * @todo Is it OK to get here? Always? * This is a place where we have to check carefully. */ AcsJCannotGetComponentEx ex = new AcsJCannotGetComponentEx(); ex.setCURL(componentSpec.getName()); throw ex; } return componentInfo; } /** * @see com.cosylab.acs.maci.Manager#getDynamicComponents(int, com.cosylab.acs.maci.ComponentSpec[]) * @todo this method still returns null in case of errors and only throws AcsJNoPermissions or * a couple of runtime exceptions. * Needs to be refactored or, probably better, deprecated. */ public ComponentInfo[] getDynamicComponents(int id, ComponentSpec[] components) throws AcsJNoPermissionEx { // check if null if (components == null) { // BAD_PARAM BadParametersException af = new BadParametersException("Non-null 'components' expected."); throw af; } // check handle and NONE permissions securityCheck(id, AccessRights.NONE); /****************************************************************/ int obtained = 0; ComponentInfo[] componentInfos = new ComponentInfo[components.length]; for (int i = 0; i < components.length; i++) { try { componentInfos[i] = getDynamicComponent(id, components[i], false); obtained++; } catch (Exception ex) { componentInfos[i] = null; CoreException ce = new CoreException("Failed to get dynamic component '"+components[i]+"'.", ex); reportException(ce); } } logger.log(Level.INFO,obtained + " of " + components.length +" dynamic components obtained."); /****************************************************************/ return componentInfos; } /** * @see com.cosylab.acs.maci.Manager#getCollocatedComponent(int, com.cosylab.acs.maci.ComponentSpec, boolean, URI) */ /// @todo MF not supported public ComponentInfo getCollocatedComponent(int id, ComponentSpec componentSpec, boolean markAsDefault, URI targetComponentURI) throws AcsJCannotGetComponentEx, AcsJNoPermissionEx, AcsJIncompleteComponentSpecEx, AcsJInvalidComponentSpecEx, AcsJComponentSpecIncompatibleWithActiveComponentEx { try { // check if null if (componentSpec == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec"); throw ex; } // check componentSpec components are null if (componentSpec.getName() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Name"); throw ex; } if (componentSpec.getType() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Type"); throw ex; } if (componentSpec.getCode() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Code"); throw ex; } if (componentSpec.getContainer() == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("componentSpec.Container"); throw ex; } // check for empty componentSpec.name if (componentSpec.getName().length() == 0) { AcsJBadParameterEx ex = new AcsJBadParameterEx(); ex.setParameter("componentSpec.Name"); ex.setParameterValue("EMPTY"); ex.setReason("Non empty Component Name expected"); throw ex; } // check if null if (targetComponentURI == null) { AcsJNullPointerEx ex = new AcsJNullPointerEx(); ex.setVariable("targetComponentURI"); throw ex; } if (!componentSpec.getContainer().equals(ComponentSpec.COMPSPEC_ANY)) { AcsJBadParameterEx ex = new AcsJBadParameterEx(); ex.setParameter("componentSpec.Container"); ex.setParameterValue(componentSpec.getContainer()); ex.setReason("COMPSPEC_ANY expected"); throw ex; } } catch (AcsJNullPointerEx e) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(e); throw ex; } catch (AcsJBadParameterEx e) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(e); throw ex; } // check handle and NONE permissions // Throws AcsJNoPermissionEx that is let flying up securityCheck(id, AccessRights.NONE); /****************************************************************/ /// @todo temporary quick implementation (does not look in the CDB if component is not activated) String name = extractName(targetComponentURI); int h = 0; ComponentInfo targetComponentInfo = null; componentsLock.lock(); try { h = components.first(); while (h != 0) { ComponentInfo componentInfo = (ComponentInfo)components.get(h); if (componentInfo.getName().equals(name)) { targetComponentInfo = componentInfo; break; } h = components.next(h); } } finally { componentsLock.unlock(); } // if not found, check the CDB if (targetComponentInfo == null) { DAOProxy componentsDAO = getComponentsDAOProxy(); if (componentsDAO != null) { // read container name String containerName = readStringCharacteristics(componentsDAO, name+"/Container", true); if (containerName != null) componentSpec.setContainer(containerName); } } else componentSpec.setContainer(targetComponentInfo.getContainerName()); // failed to detemine a target container if (componentSpec.getContainer().equals(ComponentSpec.COMPSPEC_ANY)) { AcsJIncompleteComponentSpecEx ex = new AcsJIncompleteComponentSpecEx(); ex.setCURL(name); ex.setContainerName(componentSpec.getContainer()); throw ex; } // request for component // same exceptions are let flying up. ComponentInfo componentInfo = null; try { componentInfo = internalRequestDynamicComponent(id, componentSpec); } catch (AcsJSyncLockFailedEx e) { AcsJCannotGetComponentEx ex = new AcsJCannotGetComponentEx(); ex.setCURL(name); ex.setReason("Failed to get Synchronisation lock"); throw ex; } // update default components table if (componentInfo != null && markAsDefault) { synchronized (defaultComponents) { // !!! ACID 3 executeCommand(new DefaultComponentCommandPut(componentInfo.getType(), componentInfo)); //defaultComponents.put(componentInfo.getType(), componentInfo.getName()); } logger.log(Level.INFO,"'" + componentInfo.getName() +"' has been marked as a default component of type '" + componentInfo.getType() +"'."); } if(componentInfo == null) { AcsJCannotGetComponentEx ex = new AcsJCannotGetComponentEx(); ex.setCURL(name); throw ex; } /****************************************************************/ return componentInfo; } /** * Searches for the best match in Components entry in the CDB. * * @param fieldNames array of fields names to be searched. * @param requiredValues required values of fields, if <code>ComponentSpec.COMPSPEC_ANY</code> any value is accepted. * @param equalityRequired <code>true</code> if <code>requiredValues[i]</code> and <code>fieldNames[i]<code> value values must match, * i.e. String.equals(String) is used. * @param equalityPoints array of points to be given to each field whose value is equal to the CDB value, * used to determine best match. * @return best match found in the CDB, <code>null</code> on failure or if not found. */ private String[] searchDynamicComponent(String[] fieldNames, String[] requiredValues, boolean[] equalityRequired, int[] equalityPoints, IntHolder keepAliveTimeHolder) { assert(fieldNames != null); assert(equalityRequired != null); assert(equalityPoints != null); assert(fieldNames.length == equalityRequired.length); assert(equalityRequired.length == equalityPoints.length); DAOProxy componentsDAO = getComponentsDAOProxy(); if (componentsDAO == null) return null; // get field IDs for all components String[] fieldIDs = null; try { /*fieldIDs =*/ componentsDAO.get_field_data(""); /// @todo here to check if CDB is available fieldIDs = getComponentsList(); } catch (Exception ex) { CoreException af = new CoreException("Failed to retrieve data from CDB.", ex); reportException(af); return null; } int len = fieldNames.length; int maxPoints = Integer.MIN_VALUE; String[] bestMatch = null; String[] currentMatch = new String[len]; int bestMatchKeepAliveTime = RELEASE_TIME_UNDEFINED; // for each entry for (int fi = 0; fi < fieldIDs.length; fi++) { // for each field int i = 0; int points = 0; for (; i < len; i++) { /// @todo not nice way, but necessary to have hierarchical names //String fieldValue = readStringCharacteristics(componentsDAO, fieldIDs[fi]+"/"+fieldNames[i], true); boolean processingNameField = "Name".equals(fieldNames[i]); String fieldValue = null; if (processingNameField) { // problems are multiple "*" (or other duplicated names) -> "*<number>", so reading CDB is necessary if (Character.isDigit(fieldIDs[fi].charAt(fieldIDs[fi].length()-1))) { String readFieldValue = readStringCharacteristics(componentsDAO, fieldIDs[fi]+"/Name", true); fieldValue = fieldIDs[fi].substring(0, fieldIDs[fi].indexOf(readFieldValue)+readFieldValue.length()); } else fieldValue = fieldIDs[fi]; } else fieldValue = readStringCharacteristics(componentsDAO, fieldIDs[fi]+"/"+fieldNames[i], true); if (fieldValue == null) break; boolean equals = requiredValues[i].equals(fieldValue); // required equality if (equalityRequired[i]) { // reject entry if (!equals) break; currentMatch[i] = fieldValue; } // optional equality brings points else { /* override // first check required value condition if (!equals && !(requiredValues[i].equals(ComponentSpec.COMPSPEC_ANY) || fieldValue.equals(ComponentSpec.COMPSPEC_ANY))) break; */ currentMatch[i] = fieldValue; if (equals) points += equalityPoints[i]; // special case for name; disallow entries where names do not match // note that name cannot be overriden else if (processingNameField && (fieldValue.indexOf(ComponentSpec.COMPSPEC_ANY) == -1 || !WildcharMatcher.match(fieldValue, requiredValues[i]))) break; } } // if not rejected and best if (i == len && points > maxPoints) { maxPoints = points; if (bestMatch == null) bestMatch = new String[len]; System.arraycopy(currentMatch, 0, bestMatch, 0, len); bestMatchKeepAliveTime = readLongCharacteristics(componentsDAO, fieldIDs[fi]+"/KeepAliveTime", RELEASE_TIME_UNDEFINED, true); } } keepAliveTimeHolder.value = bestMatchKeepAliveTime; return bestMatch; } /** * Internal method for requesting dynamic components. * * Resolution: * <code>component_name</code> and <code>component_type</code> can be considered as "determinator" fields, * they play important role in search algorithm. * <code>component_code</code> and <code>container_name</code> can be considered as "override" fields, * they only help to find closest match. * Rule: unspecified <code>component_name</code> case implies that a new component will be activated. * Search points (8,4,2,1): <code>component_name</code>, <code>component_type</code>, <code>component_code</code>, <code>container_name</code>. * <pre> * * name | type | search criteria * ----------------------------- * * | * | throw IncompleteComponentSpecException * X | * | (equals, wildcard) * * | X | (equals, equals) w/ name generation * X | X | (wildcard, equals) - overriding type is not allowed * * </pre> * 'name' can be also something like "ANT1/*" (ends with) and is threated just like "*". * * @param requestor requestor of the component. * @param componentSpec requested component <code>ComponentSpec</code> * @return componentInfo <code>ComponentInfo</code> of requested dynamic component. */ private ComponentInfo internalRequestDynamicComponent(int requestor, ComponentSpec componentSpec) throws AcsJCannotGetComponentEx, AcsJSyncLockFailedEx, AcsJNoPermissionEx, AcsJIncompleteComponentSpecEx, AcsJInvalidComponentSpecEx, AcsJComponentSpecIncompatibleWithActiveComponentEx { boolean unspecifiedName = componentSpec.getName().endsWith(ComponentSpec.COMPSPEC_ANY); boolean unspecifiedType = componentSpec.getType().equals(ComponentSpec.COMPSPEC_ANY); // check completeness of componentSpec // * | * | throw IncompleteComponentSpecException if (unspecifiedName && unspecifiedType) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(); ex.setReason("'name' and 'type' cannot be both '" + ComponentSpec.COMPSPEC_ANY +"'."); throw ex; } // all fields are fully specified, no search needed else if (!unspecifiedName && !unspecifiedType && !componentSpec.getCode().equals(ComponentSpec.COMPSPEC_ANY) && !componentSpec.getContainer().equals(ComponentSpec.COMPSPEC_ANY)) { StatusHolder statusHolder = new StatusHolder(); // We let exceptions occurring here fly up return internalRequestComponent(requestor, componentSpec.getName(), componentSpec.getType(), componentSpec.getCode(), componentSpec.getContainer(), RELEASE_TIME_UNDEFINED, statusHolder, true); } // // prepare search conditions // final String[] fieldNames = new String[] { "Name", "Type", "Code", "Container" }; final String[] requiredValues = new String[] { componentSpec.getName(), componentSpec.getType(), componentSpec.getCode(), componentSpec.getContainer() }; final int[] equalityPoints = new int[] { 8, 4 /* never used */, 2, 1 }; boolean[] equalityRequired = null; boolean allowNameGeneration = false; boolean prohibitSearch = false; // X | X | (wildcard, equals) if (!unspecifiedName && !unspecifiedType) { equalityRequired = new boolean[] { false, true, false, false }; allowNameGeneration = true; } // X | * | (equals, wildcard) else if (!unspecifiedName && unspecifiedType) { equalityRequired = new boolean[] { true, false, false, false }; } // * | X | (equals, equals) w/ name generation // prefix* | X | (equals, equals) w/ name generation else if (unspecifiedName && !unspecifiedType) { equalityRequired = new boolean[] { true, true, false, false }; // no search needed case... if (!componentSpec.getCode().equals(ComponentSpec.COMPSPEC_ANY) && !componentSpec.getContainer().equals(ComponentSpec.COMPSPEC_ANY)) prohibitSearch = true; allowNameGeneration = true; } // search IntHolder keepAliveTimeHolder = new IntHolder(RELEASE_TIME_UNDEFINED); String[] result = prohibitSearch ? null : searchDynamicComponent(fieldNames, requiredValues, equalityRequired, equalityPoints, keepAliveTimeHolder); // none found if (result == null) { boolean failed = true; // only name or container not speficied... if ((allowNameGeneration || !unspecifiedName) && !unspecifiedType && !componentSpec.getCode().equals(ComponentSpec.COMPSPEC_ANY)) { // container name already specified, i.e. name is *, which is OK if (!componentSpec.getContainer().equals(ComponentSpec.COMPSPEC_ANY)) { result = new String[] { componentSpec.getName(), componentSpec.getType(), componentSpec.getCode(), componentSpec.getContainer() }; failed = false; } // container name is *, use load balancing if available else if (loadBalancingStrategy != null) { String containerName = loadBalancingStrategy.selectContainer(getClientInfo(requestor), getContainersInfo()); if (containerName != null) { result = new String[] { componentSpec.getName(), componentSpec.getType(), componentSpec.getCode(), containerName }; failed = false; } } } if (failed) { AcsJInvalidComponentSpecEx ex = new AcsJInvalidComponentSpecEx(); ex.setReason("Requested ComponentSpec does not match any entry in the CDB."); throw ex; } } // override... for (int i = 0; i < result.length; i++) if (!requiredValues[i].equals(ComponentSpec.COMPSPEC_ANY)) result[i] = requiredValues[i]; // check completeness int i = 0; if (allowNameGeneration) i++; for (; i < result.length; i++) if (result[i].equals(ComponentSpec.COMPSPEC_ANY)) { // only container not speficied... // if load balancing strategy is registered, use it to determine container name if (fieldNames[i].equals("Container") && loadBalancingStrategy != null) { String containerName = loadBalancingStrategy.selectContainer(getClientInfo(requestor), getContainersInfo()); if (containerName != null) { result[i] = containerName; continue; } } AcsJIncompleteComponentSpecEx ex = new AcsJIncompleteComponentSpecEx(); ex.setReason("'" + fieldNames[i] + "' equals '" + ComponentSpec.COMPSPEC_ANY +"'."); throw ex; } // generate name if necessary if (allowNameGeneration && result[0].endsWith(ComponentSpec.COMPSPEC_ANY)) { synchronized (this) { /// @todo not perfect if (result[0].equals(ComponentSpec.COMPSPEC_ANY)) result[0] = result[1] + "_" + System.currentTimeMillis(); else // ends with case result[0] = result[0].substring(0, result[0].length()-1) + "_" + System.currentTimeMillis(); // flatten hierarchical name (remove IDL separators) if (result[0].indexOf('/') >= 0) result[0] = result[0].replaceAll("/", "_"); } } StatusHolder statusHolder = new StatusHolder(); // Same exceptions are let flying up return internalRequestComponent(requestor, result[0], result[1], result[2], result[3], keepAliveTimeHolder.value, statusHolder, true); } /*****************************************************************************/ /************************** [ Remote Directory ] *****************************/ /*****************************************************************************/ /** * Bind object to root of remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param name name of the object, code non-<code>null</code> * @param object object to be binded */ private void bind(String name, String type, Object object) { bind(remoteDirectory, name, type, object); } /** * Bind object to remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param remoteDirectory remote directory context to be used. * @param name name of the object, code non-<code>null</code> * @param object object to be binded */ private void bind(Context remoteDirectory, String name, String type, Object object) { assert (name != null); // do not bind interdomain names if (name.startsWith(CURL_URI_SCHEMA)) return; if (remoteDirectory != null) { try { // hierarchical name check int pos = name.indexOf('/'); if (pos != -1) { if (pos == 0 || pos == name.length()) throw new IllegalArgumentException("Invalid hierarchical name '" + name + "'."); String parent = name.substring(0, pos); String child = name.substring(pos + 1); // lookup for subcontext, if not found create one Context parentContext = null; try { parentContext = (Context)remoteDirectory.lookup(parent); } catch (NameNotFoundException nnfe) { // noop } if (parentContext == null) { parentContext = remoteDirectory.createSubcontext(parent); /// @todo temp. commented out //generateHiearchyContexts(remoteDirectory, parent, parentContext); } // delegate bind(parentContext, child, type, object); return; } NameParser parser = remoteDirectory.getNameParser(""); Name n; if (type != null) n = parser.parse(name+"."+type); else n = parser.parse(name); // special case if (name.endsWith(".D")) { remoteDirectory.rebind(n, object); /// @todo temp. commented out //generateHiearchyContexts(remoteDirectory, name, (Context)remoteDirectory.lookup(name)); } else remoteDirectory.bind(n, object); } catch (NameAlreadyBoundException nabe) { rebind(remoteDirectory, name, type, object); } catch (NamingException ne) { CoreException ce = new CoreException("Failed to bind name '"+name+"' to the remote directory.", ne); logger.log(Level.FINE, ce.getMessage(), ce); } } } /** * @param remoteDirectory parent context of parentContext. * @param parent name of context to bind. * @param parentContext context to bind. * @throws NamingException * private void generateHiearchyContexts(Context remoteDirectory, String parent, Context parentContext) throws NamingException { /// @todo CORBA specific if (remoteDirectory instanceof com.sun.jndi.cosnaming.CNCtx) { boolean isParentDomain = remoteDirectory.getNameInNamespace().length() == 0 || remoteDirectory.getNameInNamespace().endsWith(".D"); org.omg.CORBA.Object parentRepresentation = ((com.sun.jndi.cosnaming.CNCtx)remoteDirectory)._nc; if (parent.endsWith(".D")) { // domain binding parentContext.rebind("Parent.D", parentRepresentation); } else if (parent.endsWith(".F")) { // hierarchical component binding if (isParentDomain) parentContext.rebind("Domain.D", parentRepresentation); else parentContext.rebind("Domain.D", remoteDirectory.lookup("Domain.D")); parentContext.rebind("Parent.F", parentRepresentation); } } else if (remoteDirectory instanceof InitialContext) generateHiearchyContexts((Context)((InitialContext)remoteDirectory).lookup(""), parent, parentContext); else new MessageLogEntry(this, "generateHiearchyContexts", "Unsupported remote directory, class '" + remoteDirectory.getClass().getName() + "'.", Level.WARNING).dispatch(); }*/ /** * Rebind object to the root of remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * NOTE: Does not support hierarchical names. * * @param name name of the object, code non-<code>null</code> * @param type type of the object * @param object object to be binded * private void rebind(String name, String type, Object object) { rebind(remoteDirectory, name, type, object); }*/ /** * Rebind object to remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * NOTE: Does not support hierarchical names. * * @param remoteDirectory remote directory context to be used. * @param name name of the object, code non-<code>null</code> * @param type type of the object * @param object object to be binded */ private void rebind(Context remoteDirectory, String name, String type, Object object) { assert (name != null); // do not bind interdomain names if (name.startsWith(CURL_URI_SCHEMA)) return; if (remoteDirectory != null) { try { NameParser parser = remoteDirectory.getNameParser(""); Name n; if (type != null) n = parser.parse(name+"."+type); else n = parser.parse(name); remoteDirectory.rebind(n, object); } catch (NamingException ne) { CoreException ce = new CoreException("Failed to rebind name '"+name+"' to the remote directory.", ne); logger.log(Level.FINE, ce.getMessage(), ce); } } } /** * Lookups for an object in root of the remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param name name of the object, code non-<code>null</code> * @param type type of the object * @return object object found in the remote directory, <code>null<code> if nout found */ private Object lookup(String name, String type) { return lookup(remoteDirectory, name, type); } /** * Lookups for an object in remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param remoteDirectory remote directory context to be used. * @param name name of the object, code non-<code>null</code> * @param type type of the object * @return object object found in the remote directory, <code>null<code> if nout found */ private Object lookup(Context remoteDirectory, String name, String type) { assert (name != null); // do not look for interdomain names if (name.startsWith(CURL_URI_SCHEMA)) return null; Object resolved = null; if (remoteDirectory != null) { try { NameParser parser = remoteDirectory.getNameParser(""); Name n; if (type != null) n = parser.parse(name+"."+type); else n = parser.parse(name); resolved = remoteDirectory.lookup(n); } catch (NamingException ne) { CoreException ce = new CoreException("Failed to lookup name '"+name+"' in the remote directory.", ne); logger.log(Level.FINE, ce.getMessage(), ce); } } return resolved; } /** * Unbind object to remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param name name of the object, code non-<code>null</code> * @param type type of the object */ private void unbind(String name, String type) { unbind(remoteDirectory, name, type); } /** * Unbind object to remote directory. * * Use INS syntax specified in the INS specification. * In short, the syntax is that components are left-to-right slash ('/') separated and case-sensitive. * The id and kind of each component are separated by the period character ('.'). * * @param remoteDirectory remote directory context to be used. * @param name name of the object, code non-<code>null</code> * @param type type of the object */ private void unbind(Context remoteDirectory, String name, String type) { assert (name != null); // do not unbind interdomain names if (name.startsWith(CURL_URI_SCHEMA)) return; if (remoteDirectory != null) { try { NameParser parser = remoteDirectory.getNameParser(""); Name n; if (type != null) n = parser.parse(name+"."+type); else n = parser.parse(name); remoteDirectory.unbind(n); // NOTE: cleaning up the empty context nodes is not implemented // since access NS cannot provide quantum actions (transactions) // cleanup only empty ".F" contexts - only local manager // should modify local NS cleanupEmptyFContext(remoteDirectory, name); } catch (NamingException ne) { CoreException ce = new CoreException("Failed to unbind name '"+name+"' from the remote directory.", ne); logger.log(Level.FINE, ce.getMessage(), ce); } } } /** * Removes empty ".F" context(s) (recirsive). * @param remoteDirectory directory root. * @param name name of the child object being just removed from the potential empty parent context. */ private void cleanupEmptyFContext(Context remoteDirectory, String name) { try { // hierarchical name check int pos = name.lastIndexOf('/'); if (pos != -1) { if (pos == 0 || pos == name.length()) throw new IllegalArgumentException("Invalid hierarchical name '" + name + "'."); String parent = name.substring(0, pos); if (parent.endsWith(".F") && !remoteDirectory.list(parent).hasMore()) { remoteDirectory.unbind(parent); cleanupEmptyFContext(remoteDirectory, parent); } } } catch (Throwable th) { CoreException ce = new CoreException("Failed to unbind (potential) empty context for '"+name+"'.", th); logger.log(Level.FINE, ce.getMessage(), ce); } } /*****************************************************************************/ /*************************** [ Utility methods ] *****************************/ /*****************************************************************************/ /** * Returns human-readable and meaningful name of handle. * * @param id handle to stringifys * @return human-readable and meaningful name of handle. */ private String getRequestorName(int id) { // parse handle part int reqHandle = id & HANDLE_MASK; boolean invalidHandle = true; StringBuffer name = new StringBuffer(30); switch (id & TYPE_MASK) { case CONTAINER_MASK: //name.append("Container "); synchronized (containers) { if (containers.isAllocated(reqHandle)) { ContainerInfo info = (ContainerInfo)containers.get(reqHandle); if (info.getHandle() == id) { invalidHandle = false; name.append(info.getName()); } } } break; case CLIENT_MASK: //name.append("Client "); synchronized (clients) { if (clients.isAllocated(reqHandle)) { ClientInfo info = (ClientInfo)clients.get(reqHandle); if (info.getHandle() == id) { invalidHandle = false; name.append(info.getName()); } } } break; case ADMINISTRATOR_MASK: //name.append("Administrator "); synchronized (administrators) { if (administrators.isAllocated(reqHandle)) { ClientInfo info = (ClientInfo)administrators.get(reqHandle); if (info.getHandle() == id) { invalidHandle = false; name.append(info.getName()); } } } break; case COMPONENT_MASK: //name.append("Component "); componentsLock.lock(); try { if (components.isAllocated(reqHandle)) { ComponentInfo info = (ComponentInfo)components.get(reqHandle); // do additional preallocation check if (info != null && info.getHandle() == id) { invalidHandle = false; name.append(info.getName()); } } } finally { componentsLock.unlock(); } break; case MANAGER_MASK: //name.append("Manager "); name.append("Manager"); invalidHandle = false; break; } if (invalidHandle) name.append("<unknown>"); //name.append(" [0x").append(Integer.toHexString(id)).append(']'); return name.toString(); } /** * Verifies URI if it is valid, in CURL format; also checks if it belongs to this domain. * If URI is not valid, <code>BadParametersException</code> exception is thrown. * Allows non-local domains. * * @param uri uri to be check to be a valid curl */ private void checkCURL(URI curl) throws AcsJBadParameterEx { checkCURL(curl, true); } /** * Verifies URI if it is valid, in CURL format; also checks if it belongs to this domain. * If URI is not valid, <code>BadParametersException</code> exception is thrown. * * @param uri uri to be check to be a valid curl * @param allowNonLocalDomains allow non-local domains */ private void checkCURL(URI curl, boolean allowNonLocalDomains) throws AcsJBadParameterEx { // check if null if (curl == null) { // BAD_PARAM AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("curl"); af.setParameterValue("null"); throw af; } if (curl.getPath() == null || curl.getPath().length() == 0 || ((curl.getScheme() != null && curl.getScheme().startsWith(CURL_URI_SCHEMA)))) { // BAD_PARAM AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("curl"); af.setParameterValue(curl.toString()); throw af; } // check if CURL belongs to this domain if (!allowNonLocalDomains && !isLocalDomainCURL(curl)) { // BAD_PARAM String domain = curl.getAuthority(); AcsJBadParameterEx af = new AcsJBadParameterEx(); af.setParameter("curl"); af.setParameterValue("CURL does not belong to this domain ('"+domain+"' not one of '"+domains+"')."); throw af; } } /** * Check if CURL belongs to local domain. * @param curl CURL to be checked. * @return <code>true</code> if CURL belongs to local domain, <code>false</code> otherwise. */ private boolean isLocalDomainCURL(URI curl) { String domain = curl.getAuthority(); return (domain == null || domain.length() == 0 || domains.contains(domain)); } /** * Extract component name from the URI. * Only name is returned for local domain, full CURL string for non-local domains. * @param curl curl from which to extract name. */ private String extractName(URI curl) { if(curl == null) return ""; if (isLocalDomainCURL(curl)) { String name = curl.getPath(); // skip trailing slash if (name.charAt(0) == '/') name = name.substring(1); return name; } else return curl.toString(); } /** * Checks if component name is a service component name, list of names is defined in the CDB. * * @param name name to be checked, non-<code>null</code> * @returns <code>true</code> if component name is service component name, <code>false</code> otherwise */ public boolean isServiceComponent(String name) { assert (name != null); boolean retVal = false; // get CDB access dao DAOProxy dao = getManagerDAOProxy(); if (dao != null) { try { // query String[] names = dao.get_string_seq("ServiceComponents"); // find it for (int i = 0; i < names.length; i++) if (name.equals(names[i])) { retVal = true; break; } } catch (Throwable ex) { CoreException ce = new CoreException("Failed to retrieve list of service components.", ex); logger.log(Level.FINE, ce.getMessage(), ce); } } return retVal; } /*****************************************************************************/ /********************* [ Activation synchronization ] ************************/ /*****************************************************************************/ /** * Acquire synchronization lock for named object. * @param name name of the object whose lock to acquire. * @param msecs the number of milleseconds to wait. * An argument less than or equal to zero means not to wait at all. * @param acquireTaskDesc Description of the task that needs a synchronization lock. * @return <code>null</code> if acquired, <code>non-null</code> othwerwise (contains lock cause description). */ private String acquireSynchronizationObject(String name, long msec, String acquireTaskDesc) { if (acquireTaskDesc == null) acquireTaskDesc = "(unknown lock cause)"; ReferenceCountingLock lock; synchronized (activationSynchronization) { // get synchronization object lock = activationSynchronization.get(name); // none is found, create and return new one // increment references if (lock == null) { lock = new ReferenceCountingLock(); activationSynchronization.put(name, lock); } else lock.increment(); } boolean success = lock.acquire(msec); if (!success) { releaseSynchronizationObject(name, false); return (String)lock.getUserData(); } else { lock.setUserData(acquireTaskDesc); return null; } } /** * Release synchronization lock for named object. * @param name name of the object whose lock to release. */ private void releaseSynchronizationObject(String name) { releaseSynchronizationObject(name, true); } /** * Release synchronization lock for named object. * @param name name of the object whose lock to release. * @param release set to <code>false</code> if there is no need to call release * on synchronization lock. */ private void releaseSynchronizationObject(String name, boolean release) { synchronized (activationSynchronization) { // get synchronization object ReferenceCountingLock lock = activationSynchronization.get(name); // release lock if (lock != null) { // if there only one current lock exists // remove it from the map if (lock.decrement() <= 0) activationSynchronization.remove(name); // release the lock if (release) lock.release(); } } } /*****************************************************************************/ /************************* [ CDB access methods ] ****************************/ /*****************************************************************************/ /** * Returns, if necessary also creates, Manager DAO (CDB access). * * @return DAOProxy Manager DAO (CDB access), otherwise <code>null</code> */ private synchronized DAOProxy getManagerDAOProxy() { if (System.getProperties().containsKey(NAME_CDB_DISABLE)) return null; if (managerDAO == null) managerDAO = createDAO("MACI/Managers/Manager"); return managerDAO; } /** * Read manager configuration. */ private void readManagerConfiguration() { enableHandleMonitoring = System.getProperties().containsKey(NAME_HANDLE_MONITORING); enableHandleMonitoringDurationMins = Integer.getInteger(NAME_HANDLE_MONITORING_TIME, HANDLE_MONITORING_TIME_MIN); DAOProxy managerDAO = getManagerDAOProxy(); if (managerDAO == null) return; clientPingInterval = (int)(readDoubleCharacteristics(managerDAO, "ClientPingInterval", clientPingInterval/(double)1000, true)*1000); clientPingInterval = Math.max(1000, clientPingInterval); administratorPingInterval = (int)(readDoubleCharacteristics(managerDAO, "AdministratorPingInterval", administratorPingInterval/(double)1000, true)*1000); administratorPingInterval = Math.max(1000, administratorPingInterval); containerPingInterval = (int)(readDoubleCharacteristics(managerDAO, "ContainerPingInterval", containerPingInterval/(double)1000, true)*1000); containerPingInterval = Math.max(1000, containerPingInterval); try { String strTimeOut = System.getProperty("jacorb.connection.client.pending_reply_timeout"); if (strTimeOut != null) { long t = Long.valueOf(strTimeOut); if (t > 0) { // add 1 minute lockTimeout = t + 60000; // check for overflow if (lockTimeout < 0) lockTimeout = Long.MAX_VALUE; } } } catch (Throwable th) { // noop (left default) } poolThreads = readLongCharacteristics(managerDAO, "ServerThreads", poolThreads, true); poolThreads = Math.max(3, poolThreads); } /** * Destroys Manager DAO (CDB access). */ private synchronized void destroyManagerDAOProxy() { if (managerDAO != null) destroyDAO(managerDAO); managerDAO = null; } /** * Returns, if necessary also creates, components DAO (CDB access). * * @return DAOProxy components DAO (CDB access), otherwise <code>null</code> */ private synchronized DAOProxy getComponentsDAOProxy() { if (System.getProperties().containsKey(NAME_CDB_DISABLE)) return null; if (componentsDAO == null) { componentsDAO = createDAO("MACI/Components"); if (componentsDAO != null) { // initial refresh componentListCache = refreshComponentsList(componentsDAO); // ... and install link listener (to refresh after reconnect) componentsDAO.addConnectionListener( new DAOProxyConnectionListener() { public void connected(DAOProxy proxy) { componentListCache = refreshComponentsList(proxy); } public void disconnected(DAOProxy proxy) { /* noop */ } } ); } } return componentsDAO; } /** * Get list of all components. * @return list of all components. */ private String[] getComponentsList() { return componentListCache; } /** * Searches dao for all potential (nodes containing Name attribute) ComponentInfo nodes. * @param dc dao to be searched. * @returns list of all potential ComponentInfo nodes. */ private String[] refreshComponentsList(DAOProxy dc) { ArrayList componentList = new ArrayList(); try { LinkedHashSet nodes = new LinkedHashSet(); // current nodes.add(""); String[] subnodes = cdbAccess.getSubNodes(dc); if (subnodes != null) for (int i = 0; i < subnodes.length; i++) nodes.add(subnodes[i]); Iterator iter = nodes.iterator(); while (iter.hasNext()) { String prefix = iter.next().toString(); if (prefix.length() > 0) prefix += "/"; String attributes = dc.get_field_data(prefix + "_characteristics"); // convert into array StringTokenizer tokenizer = new StringTokenizer(attributes, ","); while (tokenizer.hasMoreTokens()) { String subname = tokenizer.nextToken().toString(); String componentName = prefix + subname; // check if potentially valid ComponentInfo entry - read name /// @todo this could be done better (to check if all attributes exist) if (readStringCharacteristics(dc, componentName + "/Name", true) != null) componentList.add(componentName); } } } catch (Throwable th) { CoreException ce = new CoreException("Failed to obtain list of all components.", th); logger.log(Level.WARNING, ce.getMessage(), ce); } String[] retVal = new String[componentList.size()]; componentList.toArray(retVal); logger.log(Level.INFO,"Found " + retVal.length + " component entries in the configuration database."); return retVal; } /** * Destroys components DAO (CDB access). */ private synchronized void destroyComponetsDAOProxy() { if (componentsDAO != null) destroyDAO(componentsDAO); componentsDAO = null; } /** * Returns, if necessary also creates, containers DAO (CDB access). * * @return DAOProxy containers DAO (CDB access), otherwise <code>null</code> */ private synchronized DAOProxy getContainersDAOProxy() { if (System.getProperties().containsKey(NAME_CDB_DISABLE)) return null; if (containersDAO == null) containersDAO = createDAO("MACI/Containers"); return containersDAO; } /** * Destroys containers DAO (CDB access). */ private synchronized void destroyContainersDAOProxy() { if (containersDAO != null) destroyDAO(containersDAO); containersDAO = null; } /** * Creates DAO (CDB access) for requested entity. * * @param name name of the entity, non-<code>null</code>. * @return DAOProxy DAO (CDB access) for requested entity, <code>null</code> on failure. */ private DAOProxy createDAO(String entity) { assert (entity != null); DAOProxy dao = null; // cdbAccess must be given if (cdbAccess != null) { try { dao = cdbAccess.createDAO(entity); } catch (Throwable th) { logger.log(Level.FINE,"Failed to create DAO for '"+entity+"'."); // TODO @todo logging?! //CoreException ce = new CoreException("Failed to create DAO for '"+entity+"'.", th); //logger.log(Level.FINE, ce.getMessage(), ce); } } return dao; } /** * Reads DAO (CDB access) of string type (uses <code>getStringCharctareistics</code> method). * * @param path path to be read non-<code>null</code>. * @param dao DAO on which to perform read request. * @return String value read, <code>null</code> on failure */ private String readStringCharacteristics(DAOProxy dao, String path) { return readStringCharacteristics(dao, path, false); } /** * Reads DAO (CDB access) of string type. * * @param path path to be read non-<code>null</code>. * @param dao DAO on which to perform read request. * @param silent do not complain, if characteristics not found * @return String value read, <code>null</code> on failure */ private String readStringCharacteristics(DAOProxy dao, String path, boolean silent) { assert (path != null); String retVal = null; try { retVal = dao.get_string(path); } catch (Throwable th) { CoreException ce = new CoreException("Failed to read '"+path+"' field on DAO dao '"+dao+"'.", th); if (!silent) reportException(ce); } return retVal; } /** * Reads DAO (CDB access) of long type. * * @param path path to be read non-<code>null</code>. * @param dao DAO on which to perform read request. * @param silent do not complain, if characteristics not found. * @return int value read, <code>0</code> on failure. */ private int readLongCharacteristics(DAOProxy dao, String path, int defaultValue, boolean silent) { assert (path != null); int retVal = defaultValue; try { retVal = dao.get_long(path); } catch (Throwable th) { CoreException ce = new CoreException("Failed to read '"+path+"' field on DAO dao '"+dao+"'.", th); if (!silent) reportException(ce); } return retVal; } /** * Reads DAO (CDB access) of double type. * * @param path path to be read non-<code>null</code>. * @param dao DAO on which to perform read request. * @param silent do not complain, if characteristics not found. * @return double value read, <code>0.0</code> on failure. */ private double readDoubleCharacteristics(DAOProxy dao, String path, double defaultValue, boolean silent) { assert (path != null); double retVal = defaultValue; try { retVal = dao.get_double(path); } catch (Throwable th) { CoreException ce = new CoreException("Failed to read '"+path+"' field on DAO dao '"+dao+"'.", th); if (!silent) reportException(ce); } return retVal; } /** * Destroys DAO (CDB access). * * @param dao DAO to be destroyed. */ private void destroyDAO(DAOProxy dao) { if (dao != null) { try { dao.destroy(); } catch (Throwable th) { CoreException ce = new CoreException("Failed to destroy DAO dao '"+dao+"'.", th); logger.log(Level.FINE, ce.getMessage(), th); } } } /*****************************************************************************/ /************************* [ Federation methods ] ****************************/ /*****************************************************************************/ /** * Initialize manager federation. */ public void initializeFederation(Hashtable federationDirectoryProperties) throws CoreException { assert(federationDirectoryProperties != null); // // read domain list // HashSet givenDomainList = new HashSet(); try { String domainList = System.getProperty(NAME_DOMAIN_LIST); if (domainList != null) { // parse space/comma separated list StringTokenizer tokenizer = new StringTokenizer(domainList, ", "); while (tokenizer.hasMoreTokens()) { String domainName = tokenizer.nextToken(); // do not allow '/' in domain names if (domainName.indexOf('/') == -1) givenDomainList.add(domainName); } } if (givenDomainList.size() > 0) logger.log(Level.INFO,"Using domain list: " + givenDomainList + "."); } catch (Throwable t) { logger.log(Level.WARNING, "Failed to parse domain list '" + NAME_DOMAIN_LIST + "' variable, " + t.getMessage(), t); } // recovery data consistency check if (domains.size() != 0 && !domains.equals(givenDomainList)) { CoreException ie = new CoreException("Given domain list is not consistent with recovery data: " + givenDomainList + " != " + domains + "."); throw ie; } else if (domains.size() == 0 && givenDomainList.size() != 0) domains = givenDomainList; // no domain to control, no federation if (domains.size() == 0) { logger.log(Level.CONFIG,"No domain list given, manager federation disabled."); return; } // // local NS reference check // if (remoteDirectoryComponentReference == null) { logger.log(Level.WARNING,"No valid local domain naming service reference found, manager federation disabled."); return; } // // read federation naming service // String domainNS = System.getProperty(NAME_DOMAIN_DIRECTORY); if (domainNS == null) { logger.log(Level.WARNING,"No federation directory reference given, manager federation disabled."); return; } // set NS address federationDirectoryProperties.put(Context.PROVIDER_URL, domainNS); logger.log(Level.INFO,"Connecting to the federation directory with reference '"+domainNS+"'..."); try { federationDirectory = new InitialContext(federationDirectoryProperties); } catch (Throwable th) { logger.log(Level.INFO, "Failed to connect to the federation directory with reference '"+domainNS+"'...", th); return; } logger.log(Level.INFO,"Connected to the federation directory with reference '"+domainNS+"'."); // register domains Iterator iter = domains.iterator(); while (iter.hasNext()) { bind(federationDirectory, dottedToHierarchical(iter.next().toString()), null, remoteDirectoryComponentReference); } federationEnabled = true; logger.log(Level.INFO,"Manager federation enabled."); // set domain name string (one name or set of names) if (domains.size() == 1) domain = domains.iterator().next().toString(); else domain = domains.toString(); } /** * Converts dotted name (e.g. "te1.hq.eso.org") to hierachical name (e.g. "org.D/eso.D/hq.D/te1.D"). * @param dottedName dotted name to be converted. * @return hierarchical name. */ private static String dottedToHierarchical(String dottedName) { final String kindSuffix = ".D"; StringTokenizer tokenizer = new StringTokenizer(dottedName, "."); if (!tokenizer.hasMoreTokens()) return dottedName; String name = tokenizer.nextToken().toString() + kindSuffix; while (tokenizer.hasMoreTokens()) name = tokenizer.nextToken().toString() + kindSuffix + "/" + name; return name; } /** * Converts component name (e.g. "TOWER1/DOOR1") to hierachical name (e.g. "TOWER1.F/DOOR"). * @param component name component name to be converted. * @return hierarchical name. */ private static String convertToHiearachical(String componentName) { if (componentName.indexOf('/') == -1) return componentName; final String kindSuffix = ".F"; StringTokenizer tokenizer = new StringTokenizer(componentName, "/"); if (!tokenizer.hasMoreTokens()) return componentName; StringBuffer name = new StringBuffer(componentName.length()+10); name.append(tokenizer.nextToken().toString()); while (tokenizer.hasMoreTokens()) name.append(kindSuffix).append('/').append(tokenizer.nextToken().toString()); return name.toString(); } /** * Get manager for given domain. * @param domainName domain name. * @return manager for given domain, <code>null</code> if not found. */ private synchronized Manager getManagerForDomain(String domainName) { // cache lookup if (managerCache.containsKey(domainName)) return managerCache.get(domainName); final Object obj = lookup(federationDirectory, dottedToHierarchical(domainName) + "/Manager", null); if (obj == null) return null; /// @todo CORBA specific Manager remoteManager = new ManagerProxy(obj); // store into cache managerCache.put(domainName, remoteManager); return remoteManager; } /** * Finalize manager federation. */ private void finalizeFederation() { if (!federationEnabled) return; // register domains Iterator iter = domains.iterator(); while (iter.hasNext()) { unbind(federationDirectory, dottedToHierarchical(iter.next().toString()), null); } } /** * Returns a single-line rendition of this instance into text. * * @return internal state of this instance */ public String toString() { StringBuffer sbuff = new StringBuffer(); sbuff.append("ManagerImpl = { "); sbuff.append("domain = '"); sbuff.append(domain); sbuff.append("' }"); return new String(sbuff); } /*****************************************************************************/ /************************* [ Prevayler methods ] *****************************/ /*****************************************************************************/ // ALARM SYSTEM codes protected final static String FAULT_FAMILY = "Manager"; protected final static String FAULT_MEMBER = "Prevayler"; protected final static int FAULT_CODE = 2; /** * Convenience method for send_alarm with given state. * * @param faultMember * @param state */ public void reportPrevaylerState(final boolean raise, Throwable alarmEx) { // log message if (raise) { logger.log(Level.WARNING, "Manager persistence subsystem failed to store pesistent data.", alarmEx); } else { logger.log(Level.FINER, "Manager persistence subsystem is functional."); } // if no alarm system initialized ignore if (alarmSource == null) { return; } try { // alarm source internally does async processing alarmSource.setAlarm(FAULT_FAMILY, FAULT_MEMBER, FAULT_CODE, raise); } catch (Throwable th) { logger.log(Level.WARNING, "Failed to send alarm.", th); } } @Override public void setStatePersistence(int id, boolean enable) throws AcsJNoPermissionEx { // TODO introduce new rights securityCheck(id, AccessRights.INTROSPECT_MANAGER); if (prevayler != null) { synchronized (prevayler) { // on enable, first take current snapshot to start with clean state if (enable) { // already enabled check if (statePersitenceFlag.get()) return; try { synchronized (prevayler) { statePersitenceFlag.set(true); ((SnapshotPrevayler)prevayler).takeSnapshot(); } } catch (IOException e) { // @todo better exception statePersitenceFlag.set(false); throw new NoResourcesException("Failed to create current state snapshot: " + e.toString()); } } else { statePersitenceFlag.set(false); } } } } public AtomicBoolean getStatePersitenceFlag() { return statePersitenceFlag; } /** * @param command * @return */ private Serializable executeCommand(Command command) throws NoResourcesException { if (prevayler != null && statePersitenceFlag.get()) { try { final Serializable retVal; synchronized (prevayler) { retVal = prevayler.executeCommand(command); } reportPrevaylerState(false, null); return retVal; } catch (IOException ioex) { // filesystem error, prevailey failed // log, raise alarm and bypass prevayler (do not return here) reportPrevaylerState(true, ioex); } catch (Throwable th) { // most likely command execution error throw new NoResourcesException("Failed to execute command.", th); } } // bypass prevayler try { return command.execute(this); } catch (Throwable th) { throw new NoResourcesException("Failed to execute command.", th); } } /** * Returns the containers. * @return HandleDataStore */ public HandleDataStore getContainers() { return containers; } /** * Returns the administrators. * @return HandleDataStore */ public HandleDataStore getAdministrators() { return administrators; } /** * Returns the clients. * @return HandleDataStore */ public HandleDataStore getClients() { return clients; } /** * Returns the components. * @return HandleDataStore */ public HandleDataStore getComponents() { return components; } /** * Log handle release. * @param handle * @param reason */ public void logHandleRelease(int handle, WhyUnloadedReason reason) { if (!enableHandleMonitoring) return; final long now = System.currentTimeMillis(); synchronized (releasedHandles) { // this simply overrides the old entry releasedHandles.put(handle, new HandleMonitorEntry(now, reason)); } } /** * Log handle cleanup (removes logs older than maxAgeMs). * @param maxAgeMs */ public void logHandleCleanup(long maxAgeMs) { if (!enableHandleMonitoring) return; final long now = System.currentTimeMillis(); synchronized (releasedHandles) { ArrayList<Integer> toRemoveList = new ArrayList<Integer>(); for (Entry<Integer,HandleMonitorEntry> entry : releasedHandles.entrySet()) { if ((now - entry.getValue().timestamp) > maxAgeMs) toRemoveList.add(entry.getKey()); } for (Integer key : toRemoveList) releasedHandles.remove(key); } } /** * Get handle release log. * @param handle * @return log or <code>null</code> if it does not exist. */ public HandleMonitorEntry getHandleReleaseLog(int handle) { synchronized (releasedHandles) { return releasedHandles.get(handle); } } /** * Get handle released map. * @return */ public Map<Integer, HandleMonitorEntry> getReleasedHandles() { return releasedHandles; } /** * Returns the unavailableComponents. * @return Map */ public Map getUnavailableComponents() { return unavailableComponents; } /** * Returns the defaultComponents. * @return Map */ public Map<String, ComponentInfo> getDefaultComponents() { return defaultComponents; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /** * Returns the remoteDirectory. * @return Context */ public Context getRemoteDirectory() { return remoteDirectory; } /** * Set the transport. * @param transport */ public void setTransport(Transport transport) { this.transport = transport; } /** * Sets the remoteDirectory. * @param remoteDirectory The remoteDirectory to set */ public void setRemoteDirectory(Context remoteDirectory) { this.remoteDirectory = remoteDirectory; } /** * Returns the managerComponentReference. * @return Object */ public Object getManagerComponentReference() { return managerComponentReference; } /** * Returns the remoteDirectoryComponentReference. * @return Object */ public Object getRemoteDirectoryComponentReference() { return remoteDirectoryComponentReference; } /** * Sets the managerComponentReference. * @param managerComponentReference The managerComponentReference to set */ public void setManagerComponentReference(Object managerComponentReference) { // unbind Manager if (this.managerComponentReference != null) unbind("Manager", null); this.managerComponentReference = managerComponentReference; // bind Manager if (this.managerComponentReference != null) bind("Manager", null, managerComponentReference); } /** * Sets the remoteDirectoryComponentReference. * @param remoteDirectoryComponentReference The remoteDirectoryComponentReference to set */ public void setRemoteDirectoryComponentReference(Object remoteDirectoryComponentReference) { this.remoteDirectoryComponentReference = remoteDirectoryComponentReference; } /** * Return shutdown status of the Manager. * @return shutdown status of the Manager */ public boolean isShuttingDown() { return shutdown.get(); } /** * Returns the shutdownImplementation. * @return ManagerShutdown */ public ManagerShutdown getShutdownImplementation() { return shutdownImplementation; } /** * Sets the shutdownImplementation. * @param shutdownImplementation The shutdownImplementation to set */ public void setShutdownImplementation(ManagerShutdown shutdownImplementation) { this.shutdownImplementation = shutdownImplementation; } /** * Returns lock timeout (deadlock detection time) in ms. * @return lock timeout (deadlock detection time) in ms */ public long getLockTimeout() { return lockTimeout; } /** * Sets lock timeout (deadlock detection time) in ms. * @param l lock timeout (deadlock detection time) in ms */ public void setLockTimeout(long l) { lockTimeout = l; } /** * Sets CDB access. * @param cdbAccess cdbAccess to set. */ public void setCDBAccess(CDBAccess cdbAccess) { destroyComponetsDAOProxy(); destroyManagerDAOProxy(); if (this.cdbAccess != null) this.cdbAccess.destroy(); this.cdbAccess = cdbAccess; if (cdbAccess != null) { getManagerDAOProxy(); getComponentsDAOProxy(); } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /** * @return the activeAlarms */ public HashSet getActiveAlarms() { return activeAlarms; } /** * Flag if alarm is active. */ public boolean hasActiveAlarm(String faultMember) { synchronized (activeAlarms) { return activeAlarms.contains(faultMember); } } /** * Remember that alarms has been raised. */ public void alarmRaised(String faultMember) { synchronized (activeAlarms) { executeCommand(new AlarmRaised(faultMember)); } } /** * Remember that alarms has been cleared. */ public void alarmCleared(String faultMember) { synchronized (activeAlarms) { executeCommand(new AlarmCleared(faultMember)); } } }