package org.opennaas.core.resources;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opennaas.core.persistence.GenericRepository;
import org.opennaas.core.resources.capability.ICapability;
import org.opennaas.core.resources.capability.ICapabilityFactory;
import org.opennaas.core.resources.descriptor.CapabilityDescriptor;
import org.opennaas.core.resources.descriptor.ResourceDescriptor;
import org.opennaas.core.resources.descriptor.ResourceDescriptorRepository;
import org.opennaas.core.resources.profile.IProfile;
import org.opennaas.core.resources.profile.IProfileManager;
import org.opennaas.core.resources.protocol.IProtocolManager;
import org.opennaas.core.resources.protocol.IProtocolSessionManager;
import org.opennaas.core.resources.protocol.ProtocolException;
import org.opennaas.core.resources.protocol.ProtocolSessionContext;
/**
* Base class for all the resource repository implementations.
*
* @author Mathieu Leman (ITI)
*
*/
public class ResourceRepository implements IResourceRepository {
/** logger */
private Log logger = LogFactory.getLog(ResourceRepository.class);
/** The map of running resource instances **/
protected Map<String, IResource> resourceRepository = null;
/** The resource descriptor repository **/
protected GenericRepository<ResourceDescriptor, String> descriptorRepository = null;
/** The capability factories by capability id **/
protected Map<String, ICapabilityFactory> capabilityFactories = null;
/** This Repository's resource Type **/
protected String resourceType = null;
private IResourceBootstrapperFactory bootstrapperFactory = null;
// @Inject
// private IProtocolManager;
/**
* Construct a new resource repository for resources of the given type
*
* @throws ResourceException
*/
public ResourceRepository(String resourceType) {
if (resourceType == null)
throw new IllegalArgumentException("ResourceType can not be null");
logger.debug("Creating a new resource repository of type " + resourceType);
this.resourceType = resourceType;
this.capabilityFactories = new Hashtable<String, ICapabilityFactory>();
this.resourceRepository = new Hashtable<String, IResource>();
}
/**
* Construct a new resource repository for resources of the given type with the given list of CapabiltyFactories FIXME JUST FOR TESTING!!!
*/
public ResourceRepository(String resourceType,
Map<String, ICapabilityFactory> capabilityFactories) {
this(resourceType);
this.capabilityFactories = capabilityFactories;
}
/* SETTERS AND GETTERS */
public void setResourceDescriptorRepository(GenericRepository<ResourceDescriptor, String> descriptorRepository) {
// TODO REMOVE
if (descriptorRepository == null)
throw new IllegalArgumentException();
this.descriptorRepository = descriptorRepository;
}
/**
* Specialized setter for resourceDescriptorRepository.
*
* The only purpose of this setter is to work around issues with Blueprint not being able to cast to the generic type of the real setter.
*/
public void setResourceDescriptorRepository(ResourceDescriptorRepository descriptorRepository) {
setResourceDescriptorRepository((GenericRepository<ResourceDescriptor, String>) descriptorRepository);
}
public void setResourceBootstrapperFactory(IResourceBootstrapperFactory bootstrapperFactory) {
this.bootstrapperFactory = bootstrapperFactory;
}
@Override
public Map<String, ICapabilityFactory> getCapabilityFactories() {
return capabilityFactories;
}
/** Set the capability factories **/
public void setCapabilityFactories(
Map<String, ICapabilityFactory> capabilityFactories) {
this.capabilityFactories = capabilityFactories;
}
/**
* @return the resourceType
*/
@Override
public String getResourceType() {
return resourceType;
}
/**
* Set the type of resource for the repository
*
* @param resourceType
* the resourceType to set
*/
// public void setResourceType(String resourceType) {
// this.resourceType = resourceType;
// }
@Override
public List<IResource> listResources() {
Iterator<String> ids = resourceRepository.keySet().iterator();
List<IResource> result = new ArrayList<IResource>();
while (ids.hasNext()) {
result.add(resourceRepository.get(ids.next()));
}
return result;
}
@Override
public IResource getResource(String identifier) throws ResourceException {
IResource result = resourceRepository.get(identifier);
if (result == null) {
throw new ResourceException("No resource with ID " + identifier
+ " was found.");
}
return result;
}
private void addResourceToRepository(IResource resource) {
resourceRepository.put(resource.getResourceIdentifier().getId(), resource);
logger.debug("New resource with id "
+ resource.getResourceIdentifier().getId()
+ " created an added to the repository");
}
private IResource removeResourceFromRepository(String resourceId) {
IResource removed = resourceRepository.remove(resourceId);
logger.debug("Removing resource with id "
+ resourceId
+ " from the repository");
return removed;
}
public void init() throws ResourceException {
logger.debug("Initializing Resource Repository " + resourceType);
// try {
loadExistingResources();
// } catch (ResourceException e) {
// logger.warn("Failed to load some resources from database");
// }
}
/* RESOURCE METHODS */
/**
* Uses a new resourceIdentifier to identify the resource
*/
@Override
public IResource createResource(ResourceDescriptor resourceDescriptor) throws ResourceException {
logger.debug("Creating resource from configuration");
// Each repository can override this method to add new conditions to create a resource
checkResourceCanBeCreated(resourceDescriptor);
logger.debug("Resource Checked");
IResource resource;
try {
resource = initResource(new Resource(), resourceDescriptor, new ResourceIdentifier(resourceType));
} catch (CorruptStateException e) {
throw new ResourceException(e);
}
if (protocolManagerIsAvailable()) {
try {
createProtocolSessionManagerForResource(resource.getResourceIdentifier().getId());
} catch (ActivatorException e) {
// ignored, protocolManager availability is already checked in protocolManagerIsAvailable()
logger.warn("Ignoring fail to retrieve protocolManager during createProtocolSessionManagerForResource");
}
}
logger.debug("Resource Initialized");
resourceDescriptor = persistResourceDescriptor(resourceDescriptor);
logger.debug("Resource Persisted");
return resource;
}
@Override
public void removeResource(String identifier) throws ResourceException {
logger.info("Removing resource with ID #" + identifier);
IResource resource = getResource(identifier);
shutdownResource(identifier);
if (protocolManagerIsAvailable()) {
try {
removeProtocolSessionManagerForResource(identifier);
} catch (ActivatorException e) {
// ignored, protocolManager availability is already checked in protocolManagerIsAvailable()
logger.warn("Ignoring fail to retrieve protocolManager during removeProtocolSessionManagerForResource");
}
}
unpersistResourceDescriptor(resource.getResourceDescriptor());
}
@Override
public IResource modifyResource(String identifier, ResourceDescriptor descriptor) throws ResourceException {
try {
// Get the old resource
Resource resource = (Resource) getResource(identifier);
ResourceDescriptor oldConfig = resource.getResourceDescriptor();
IResourceIdentifier resourceIdentifier = resource.getResourceIdentifier();
validateResourceDescriptor(descriptor);
try {
shutdownResource(identifier);
unpersistResourceDescriptor(oldConfig);
// Keep old resourceIdentifier, but use new descriptor
initResource(resource, descriptor, resourceIdentifier);
persistResourceDescriptor(descriptor);
} catch (ResourceException e) {
logger.info("Could not modify configuration for resource ID #"
+ resource.getResourceDescriptor().getId());
try {
// restore old config
initResource(resource, oldConfig, resourceIdentifier);
persistResourceDescriptor(oldConfig);
} catch (ResourceException e1) {
logger.error("Impossible to restore old configuration for resource ID #"
+ resource.getResourceDescriptor().getId());
throw e1;
}
throw e;
}
return resource;
} catch (CorruptStateException e) {
throw new ResourceException(e);
}
}
@Override
public void startResource(String identifier) throws ResourceException {
logger.debug("Starting resource runtime object for ID #"
+ identifier);
try {
activateResource(identifier);
} catch (CorruptStateException e) {
throw new ResourceException(e);
}
logger.info("Started resource with ID #" + identifier);
}
@Override
public void stopResource(String identifier) throws ResourceException {
logger.debug("Stopping resource runtime object for ID #"
+ identifier);
try {
deactivateResource(identifier);
} catch (CorruptStateException e) {
throw new ResourceException(e);
}
logger.info("Stopped resource with ID #" + identifier);
}
@Override
public void forceStopResource(String identifier) throws ResourceException {
logger.debug("Stopping resource runtime object for ID #"
+ identifier);
forceDeactivateResource(identifier);
logger.info("Stopped resource with ID #" + identifier);
}
public void loadExistingResources() throws ResourceException {
try {
logger.debug("Repository " + resourceType + " loading existing resources");
List<ResourceDescriptor> descriptors = ((ResourceDescriptorRepository) descriptorRepository).getResourceDescriptors(resourceType);
logger.debug("Repository " + resourceType + " found " + descriptors.size() + " resources");
for (int i = 0; i < descriptors.size(); i++) {
this.loadResource(descriptors.get(i));
}
} catch (Exception ex) {
// it should fail if a single resource fail to load.
throw new ResourceException(ex);
}
}
/**
* Loads and starts a resource into memory. Uses resourceDescriptor id to identify the resource
*
* @param resourceDescriptor
* @throws ResourceException
*/
private void loadResource(ResourceDescriptor resourceDescriptor) throws ResourceException {
logger.debug("Repository " + resourceType + " loading resource " + resourceDescriptor.getId());
ResourceIdentifier resourceIdentifier = new ResourceIdentifier(resourceType, resourceDescriptor.getId());
try {
initResource(new Resource(), resourceDescriptor, resourceIdentifier);
} catch (CorruptStateException e) {
throw new ResourceException(e);
}
}
private IResource initResource(Resource resource, ResourceDescriptor resourceDescriptor, IResourceIdentifier resourceIdentifier)
throws ResourceException, CorruptStateException {
logger.debug("Initializing resource " + resourceIdentifier.getId() + " ...");
// keeping variables to make rollback
IResourceIdentifier oldIdentifier = resource.getResourceIdentifier();
ResourceDescriptor oldDescriptor = resource.getResourceDescriptor();
String oldId = resourceDescriptor.getId();
// Setting the variables for the resource
resource.setResourceIdentifier(resourceIdentifier);
resourceDescriptor.setId(resource.getResourceIdentifier().getId());
resource.setResourceDescriptor(resourceDescriptor);
addResourceToRepository(resource);
try {
try {
resource.initialize();
} catch (IncorrectLifecycleStateException e) {
throw new ResourceException(e);
}
} catch (ResourceException re) {
// roll back
resource.setResourceIdentifier(oldIdentifier);
resource.setResourceDescriptor(oldDescriptor);
resourceDescriptor.setId(oldId);
throw re;
}
logger.debug("Resource initialized");
return resource;
}
private void activateResource(String resourceId) throws ResourceException, CorruptStateException {
logger.debug("Activating resource " + resourceId + " ...");
Resource resource = (Resource) getResource(resourceId);
// Each repository can override this method to add new conditions to start a resource
checkResourceCanBeStarted(resource);
if (protocolManagerIsAvailable()) {
try {
createProtocolSessions(resourceId);
} catch (Exception e) {
try {
destroyProtocolSessions(resourceId);
} catch (Exception e1) {
logger.warn("Error destroying protocol sessions", e1);
}
throw new ResourceException(e);
}
}
/* prepare capabilities */
logger.debug(" Obtaining capabilities...");
List<? extends ICapability> oldCapabilities = resource.getCapabilities();
List<ICapability> capabilities = createCapabilities(resource);
resource.setCapabilities(capabilities);
logger.debug(" Capabilities obtained. Loading bootstrapper...");
/* prepare bootstrapper */
IResourceBootstrapper oldBootstrapper = resource.getBootstrapper();
if (bootstrapperFactory != null) {
resource.setBootstrapper(bootstrapperFactory.createResourceBootstrapper());
}
logger.debug(" Bootstrapper loaded");
/* get profile id and load profile from its bundle */
IProfile oldProfile = resource.getProfile();
if (resource.getResourceDescriptor().getProfileId() != null &&
!resource.getResourceDescriptor().getProfileId().isEmpty()) {
logger.debug(" Loading profile...");
loadProfileInResource(resource, resource.getResourceDescriptor().getProfileId());
logger.debug(" Profile loaded");
}
try {
try {
resource.activate();
logger.debug("Resource activated");
} catch (IncorrectLifecycleStateException e) {
throw new ResourceException(e);
}
} catch (ResourceException re) {
// roll back
logger.debug("Rolling back activation...");
resource.setCapabilities(oldCapabilities);
resource.setBootstrapper(oldBootstrapper);
resource.setProfile(oldProfile);
if (protocolManagerIsAvailable()) {
try {
destroyProtocolSessions(resourceId);
} catch (Exception e) {
logger.warn("Error destroying protocol sessions", e);
}
}
logger.debug("Rolling back done");
throw re;
}
}
private void deactivateResource(String resourceId) throws ResourceException, CorruptStateException {
logger.debug("Deactivating resource " + resourceId + " ...");
Resource resource = (Resource) getResource(resourceId);
try {
resource.deactivate();
} catch (IncorrectLifecycleStateException e) {
throw new ResourceException(e);
}
try {
if (resource.getResourceDescriptor().getProfileId() != null && !resource.getResourceDescriptor().getProfileId().isEmpty()) {
unregisterProfileInResource(resource);
logger.debug(" Profile removed from resource");
}
resource.setBootstrapper(null);
logger.debug(" Bootstrapper removed from resource");
resource.setCapabilities(new ArrayList<ICapability>());
logger.debug(" Capabilities removed from resource");
if (protocolManagerIsAvailable()) {
try {
destroyProtocolSessions(resourceId);
} catch (Exception e) {
logger.warn("Error destorying protocolSessions", e);
}
}
logger.debug("Resource deactivated");
} catch (ResourceException e) {
// roll back
try {
resource.activate();
} catch (IncorrectLifecycleStateException e1) {
throw new CorruptStateException("Failed to roll back deactivateResource.", e1);
} catch (ResourceException e1) {
throw new CorruptStateException("Failed to roll back deactivateResource.", e1);
}
}
}
private void shutdownResource(String resourceId) throws ResourceException {
logger.debug("Shutting down resource " + resourceId + " ...");
Resource resource = (Resource) getResource(resourceId);
try {
resource.shutdown();
} catch (IncorrectLifecycleStateException e) {
throw new ResourceException(e);
}
removeResourceFromRepository(resourceId);
logger.debug("Resource shut down");
}
/**
* Checks if the resource is valid to be started
*
* @param resourceDescriptor
* @throws ResourceException
*/
protected void checkResourceCanBeStarted(IResource resource)
throws ResourceException {
// by default, any resource can be started
}
/**
* Checks if the resource type is valid so it can be created.
*
* @param resourceDescriptor
* @throws ResourceException
*/
protected void checkResourceCanBeCreated(ResourceDescriptor resourceDescriptor)
throws ResourceException {
validateResourceDescriptor(resourceDescriptor);
for (String resourceID : resourceRepository.keySet()) {
String name = resourceRepository.get(resourceID).getResourceDescriptor().getInformation().getName();
if (name.equals(resourceDescriptor.getInformation().getName())) {
throw new ResourceException(
"There is already a resource in this respository with this name: " + resourceDescriptor.getInformation().getName());
}
}
}
protected void validateResourceDescriptor(ResourceDescriptor resourceDescriptor) throws ResourceException {
assert (resourceType != null);
// if (resourceType == null) {
// throw new ResourceException("The resourceType field cannot be null");
// }
if (resourceDescriptor.getInformation().getName() == null || resourceDescriptor.getInformation().getName().equals("")) {
throw new ResourceException(
"The resourceName field cannot be null");
}
if (!resourceDescriptor.getInformation().getType().equals(resourceType)) {
throw new ResourceException(
"This repository cannot create resources of type "
+ resourceDescriptor.getInformation().getType() + ", only of type "
+ resourceType);
}
}
private List<ICapability> createCapabilities(IResource resource) throws ResourceException {
List<CapabilityDescriptor> capabilityDescriptors;
try {
capabilityDescriptors = resource.getResourceDescriptor().getCapabilityDescriptors();
} catch (Exception e) {
throw new ResourceException("No capability descriptor found, this will create an unusable resource. Please, check the descriptor format",
e);
}
List<ICapability> capabilities = new ArrayList<ICapability>();
if (capabilityDescriptors == null) {
logger.warn("No capability descriptor found, this will create an unusable resource. Please, check the descriptor format");
} else {
Iterator<CapabilityDescriptor> capabilityIterator = capabilityDescriptors.iterator();
while (capabilityIterator.hasNext()) {
CapabilityDescriptor capabilityDescriptor = capabilityIterator.next();
ICapabilityFactory factory = capabilityFactories.get(capabilityDescriptor.getCapabilityInformation().getType());
if (factory == null) {
throw new ResourceException("Factory not found for capabilities of type: " + capabilityDescriptor.getCapabilityInformation()
.getType());
}
ICapability capability = factory.create(resource);
capabilities.add(capability);
}
}
return capabilities;
}
private void loadProfileInResource(Resource resource, String profileId) throws ResourceException {
try {
IProfileManager profileManager = Activator.getProfileManagerService();
IProfile profile = profileManager.getProfile(profileId);
profileManager.registerResource(profileId, resource);
resource.setProfile(profile);
} catch (Exception e) {
throw new ResourceException("Failed to load resource profile", e);
}
}
private void unregisterProfileInResource(Resource resource) throws ResourceException {
try {
String profileId = resource.getResourceDescriptor().getProfileId();
if (!profileId.isEmpty()) {
IProfileManager profileManager = Activator.getProfileManagerService();
profileManager.unregisterResource(profileId, resource);
resource.setProfile(null);
}
} catch (ResourceException e) {
// IGNORED: if there's no such profileId there's nothing to unregister
} catch (Exception e) {
// could not access ProfileManager Service
throw new ResourceException("Failed to unregister resource profile", e);
}
}
private void forceDeactivateResource(String resourceId) throws ResourceException {
logger.debug("Deactivating resource " + resourceId + " ...");
// TODO Remove cast (put forceDeactivate into IResource iface)
Resource resource = (Resource) getResource(resourceId);
String messageErrors = "";
try {
resource.forceDeactivate();
} catch (ResourceException e) {
messageErrors = e.getLocalizedMessage() + '\n' + messageErrors;
}
try {
if (!resource.getResourceDescriptor().getProfileId().isEmpty()) {
forceUnregisterProfileInResource(resource);
logger.debug(" Profile removed from resource");
}
} catch (ResourceException e) {
messageErrors = e.getLocalizedMessage() + '\n' + messageErrors;
}
resource.setBootstrapper(null);
logger.debug(" Bootstrapper removed from resource");
resource.setCapabilities(new ArrayList<ICapability>());
logger.debug(" Capabilities removed from resource");
logger.debug("Resource deactivated");
}
private void forceUnregisterProfileInResource(Resource resource) throws ResourceException {
try {
String profileId = resource.getResourceDescriptor().getProfileId();
if (!profileId.isEmpty()) {
IProfileManager profileManager = Activator.getProfileManagerService();
profileManager.unregisterResource(profileId, resource);
resource.setProfile(null);
}
} catch (Exception e) {
// TODO: log.error()
}
}
private ResourceDescriptor persistResourceDescriptor(ResourceDescriptor descriptor) throws ResourceException {
if (descriptorRepository == null) {
throw new ResourceException("Failed to persist resource. No descriptorRepository found.");
}
return descriptorRepository.save(descriptor);
}
private void unpersistResourceDescriptor(ResourceDescriptor descriptor) throws ResourceException {
if (descriptorRepository == null) {
throw new ResourceException("Failed to unpersist resource. No descriptorRepository found.");
}
descriptorRepository.delete(descriptor);
}
private void createProtocolSessionManagerForResource(String resourceId) throws ActivatorException {
try {
getProtocolManager().getProtocolSessionManager(resourceId);
} catch (ProtocolException e) {
// ignored:
// it can only fail for resourceId already associated to a protocolSession (nothing wrong then)
logger.debug("Could not destroy protocolSessionManager. Given resourceId has none associated.");
}
}
private void removeProtocolSessionManagerForResource(String resourceId) throws ActivatorException {
try {
getProtocolManager().destroyProtocolSessionManager(resourceId);
} catch (ProtocolException e) {
// ignored:
// cannot happen
logger.debug("Could not destroy protocolSessionManager for an unknown reason.", e);
}
}
/**
* Destroys all sessions for given resourceId
*
* @param resourceId
* @throws ProtocolException
* @throws Exception
*/
private void destroyProtocolSessions(String resourceId) throws ProtocolException, ActivatorException {
IProtocolSessionManager sessionManager = getProtocolManager().getProtocolSessionManager(resourceId);
for (String sessionId : sessionManager.getAllProtocolSessionIds()) {
sessionManager.destroyProtocolSession(sessionId);
}
}
/**
* Create one session per registered context
*
* @param resourceId
* @throws Exception
*/
private void createProtocolSessions(String resourceId) throws ProtocolException, ActivatorException {
IProtocolSessionManager sessionManager = getProtocolManager().getProtocolSessionManager(resourceId);
for (ProtocolSessionContext context : sessionManager.getRegisteredContexts()) {
sessionManager.obtainSession(context, false); // create session but do not lock it
}
}
private IProtocolManager getProtocolManager() throws ActivatorException {
return Activator.getProtocolManagerService();
}
private boolean protocolManagerIsAvailable() {
IProtocolManager protocolManager = null;
try {
protocolManager = Activator.getProtocolManagerService();
} catch (ActivatorException e) {
}
return protocolManager != null;
}
}