package org.gcube.informationsystem.registry.impl.porttypes; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Method; import java.rmi.RemoteException; import java.util.Calendar; import org.apache.axis.components.uuid.UUIDGen; import org.apache.axis.components.uuid.UUIDGenFactory; import org.gcube.common.core.contexts.GCUBEServiceContext; import org.gcube.common.core.faults.GCUBEFault; import org.gcube.common.core.porttypes.GCUBEPortType; import org.gcube.common.core.resources.GCUBEHostingNode; import org.gcube.common.core.resources.GCUBEResource; import org.gcube.common.core.scope.GCUBEScope; import org.gcube.common.core.utils.events.GCUBEEvent; import org.gcube.common.core.utils.logging.GCUBELog; import org.gcube.informationsystem.registry.impl.contexts.FactoryContext; import org.gcube.informationsystem.registry.impl.contexts.ProfileContext; import org.gcube.informationsystem.registry.impl.contexts.ServiceContext; import org.gcube.informationsystem.registry.impl.filters.FilterManager; import org.gcube.informationsystem.registry.impl.filters.FilterExecutor.InvalidFilterException; import org.gcube.informationsystem.registry.impl.profilemanagement.ProfileManager; import org.gcube.informationsystem.registry.impl.profilemanagement.GHN; import org.gcube.informationsystem.registry.impl.resourcemanagement.Pair; import org.gcube.informationsystem.registry.impl.state.ProfileResource; import org.gcube.informationsystem.registry.impl.state.RegistryFactoryResource; import org.gcube.informationsystem.registry.stubs.CreateResourceMessage; import org.gcube.informationsystem.registry.stubs.ProfileAlreadyRegisteredFault; import org.gcube.informationsystem.registry.stubs.RegistryProperty; import org.gcube.informationsystem.registry.stubs.RemoveResourceMessage; import org.gcube.informationsystem.registry.stubs.RemoveResourceResponse; import org.gcube.informationsystem.registry.stubs.ResourceNotAcceptedFault; import org.gcube.informationsystem.registry.stubs.SchemaValidationFault; import org.gcube.informationsystem.registry.stubs.UpdateResourceMessage; import org.gcube.informationsystem.registry.stubs.UpdateResourceResponse; import static org.gcube.informationsystem.registry.impl.core.RegistryConfiguration.ResourceType; /** * Implementation of the Registry Factory portType * * @author Lucio Lelii, Manuele Simi (ISTI-CNR) * */ public class RegistryFactory extends GCUBEPortType { /** Name of temporaryResourceLifetimeInMs JNDI environment. */ private static final String LIFETIME_JNDI_NAME = "temporaryResourceLifetimeInMs"; /** * Lifetime of temporary resources */ private static long temporaryResourceLifetimeInMs = 120000; /** The UUIDGen */ private static final UUIDGen uuidgen = UUIDGenFactory.getUUIDGen(); /** Object logger */ protected final GCUBELog logger = new GCUBELog(RegistryFactory.class); enum OperationType {create, update, destroy}; /** the key used to label the Factory Resource */ public static final String NOTIFICATOR_RESOURCE_KEY = "RegistryResource"; /** * {@inheritDoc} */ @Override protected void onInitialisation() throws Exception { temporaryResourceLifetimeInMs = (Long) ServiceContext.getContext().getProperty(LIFETIME_JNDI_NAME); logger.info("Temporary resources lifetime = " + temporaryResourceLifetimeInMs); } /** * * Creates a new GCUBEResource's profile manager * * @param inputMessage * @return the profile as string * @throws SchemaValidationFault * @throws RemoteException * @throws ProfileAlreadyRegisteredFault */ @SuppressWarnings("unchecked") public String createResource(CreateResourceMessage mess) throws SchemaValidationFault, RemoteException, ProfileAlreadyRegisteredFault, ResourceNotAcceptedFault { GCUBEResource resource = null; logger.info("CreateResource operation invoked"); // logSecurityInfo("createResource"); String profile = mess.getProfile(); if (profile == null || profile.compareTo("") == 0) { String msg = "Profile file is empty"; logger.debug(msg); throw new RemoteException(msg); } try { resource = ResourceType.valueOf(mess.getType()).getResourceClass(); resource.load(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(profile.getBytes("UTF-8")), "UTF-8"))); // the parse Profile class allows to extract from profiles // information about type/SCOPE/UniqueID // in order to distinguish among different Resource Type // Adding scopes to Profile String[] scopes = mess.getScopes(); if (scopes != null) { for (String scope : scopes) { logger.debug(resource.getID() + " Adding Scopes to Profile " + scope); resource.addScope(GCUBEScope.getScope(scope)); } } // check ID if (resource.getID() == null || resource.getID().compareTo("") == 0) { resource.setID(uuidgen.nextUUID()); } } catch (Exception ex) { logger.error("Error trying to load profile", ex); throw new SchemaValidationFault(); } // apply resource filter try { if (!FilterManager.getExecutor(resource.getType()).accept(resource)) { logger.warn("Resource " + resource.getID() + " NOT accepted "); throw new ResourceNotAcceptedFault(); } logger.trace("Resource " + resource.getID() + " accepted "); } catch (InvalidFilterException e) { logger.warn("Invalid filter selected, the resource " + resource.getID() + " CANNOT be filtered "); } // check if the Resource already exists if (isResourceCreated(resource.getID())) { // update the existing resource logger.warn("A Resource with ID " + resource.getID() + " is already registered"); UpdateResourceMessage upmess = new UpdateResourceMessage(); upmess.setUniqueID(resource.getID()); upmess.setXmlProfile(mess.getProfile()); upmess.setType(mess.getType()); this.updateResource(upmess); // throw new ProfileAlreadyRegisteredFault(); } else { // try to create resource try { logger.debug(resource.getID() + " Creating the stateful resource for " + resource.getID()); ProfileResource presource = (ProfileResource) ProfileContext.getContext().getWSHome().create(ProfileContext.getContext() .makeKey(resource.getID()), resource); presource.store(); // Deleting the WSResource created if != from GHN if (resource.getType().compareTo(GCUBEHostingNode.TYPE) != 0) this.scheduleForLazyDeletion(presource); } catch (Exception ex) { String msg = "Error creating Resource " + resource.getID(); logger.error(msg, ex); throw new RemoteException(msg); } try { updateCounterInfo(resource.getID(), ResourceType.valueOf(mess.getType()), OperationType.create, Calendar.getInstance()); GCUBEEvent event = new GCUBEEvent(); event.setPayload(resource); ServiceContext.getContext().getTopicProducer().notify( ServiceContext.RegistryTopic.CREATE, event); } catch (Exception e) { logger.warn("Error updating Counting info for resource with ID " + resource.getID(), e); } } StringWriter writer = new StringWriter(); try { logger.debug("Persisting the stateful resource for " + resource.getID()); resource.store(writer); } catch (Exception e) { logger.error("Persistence failed for " + resource.getID(), e); } logger.info("Profile " + resource.getID() + " created "); return writer.toString(); } /** * Schedules a resource for a delayed deletion * * @param presource the resource to delete */ private void scheduleForLazyDeletion(ProfileResource presource) { long timestamp = System.currentTimeMillis() + temporaryResourceLifetimeInMs; GCUBEScope scope = ServiceContext.getContext().getScope().getType() == GCUBEScope.Type.VRE ? ServiceContext .getContext().getScope().getEnclosingScope(): ServiceContext.getContext().getScope(); Pair c = new Pair(timestamp, presource); if (!ServiceContext.threadTable.get(scope.getName()).getStack().contains(c)) { ServiceContext.threadTable.get(scope.getName()).getStack().add(c); logger.trace("Adding resource to EliminatePoolingThread " + presource.getGCubeResource().getID()); } else { //add more life to the resource by further delaying its deletion int index = ServiceContext.threadTable.get(scope.getName()).getStack().indexOf(c); ServiceContext.threadTable.get(scope.getName()).getStack().get(index).lifetime = timestamp+ temporaryResourceLifetimeInMs; } } /** * Updates Resource Profiles (in case of an update of an ID not present it * invokes calls the Create resource operation) * * @param mess * Complex Object that contains a String representing the XML * profile to update and the diligentID * @return UpdateResourceResponse * @throws RemoteException * Exception * @throws SchemaValidationException * Exception */ @SuppressWarnings("unchecked") public UpdateResourceResponse updateResource(UpdateResourceMessage mess) throws RemoteException, SchemaValidationFault, ResourceNotAcceptedFault, GCUBEFault { logger.info("UpdateResource operation invoked"); String ID = mess.getUniqueID(); String xmlProfile = mess.getXmlProfile(); GCUBEResource resource = null; CreateResourceMessage input = null; // validating input parameters if (ID == null || ID.compareTo("") == 0) { logger.debug(" ID missing "); throw new RemoteException("Error, ID missing"); } if (xmlProfile == null || xmlProfile.compareTo("") == 0) { logger.debug("Profile missing"); throw new RemoteException("Error, profile missing"); } // check if the profile exist if (!isResourceCreated(ID)) { input = new CreateResourceMessage(); input.setProfile(xmlProfile); input.setType(mess.getType()); this.createResource(input); return new UpdateResourceResponse(); } try { resource = ResourceType.valueOf(mess.getType()).getResourceClass(); resource.load(new StringReader(xmlProfile)); // apply resource filter try { if (!FilterManager.getExecutor(resource.getType()).accept(resource)) { logger.warn("Resource " + resource.getID() + " NOT accepted "); throw new ResourceNotAcceptedFault(); } logger.trace("Resource " + resource.getID() + " accepted "); } catch (InvalidFilterException e) { logger.warn("Invalid filter selected, the resource " + resource.getID() + " CANNOT be filtered "); } ProfileResource presource = getProfileResource(ID); presource.updateResource(resource); // Deleting the WSResource created if (resource.getType().compareTo(GCUBEHostingNode.TYPE) != 0) this.scheduleForLazyDeletion(presource); } catch (Exception e) { logger.error("Error updating profile for ID " + e); throw new RemoteException(e.toString()); } try { this.updateCounterInfo(ID, ResourceType.valueOf(mess.getType()), OperationType.update, Calendar.getInstance()); GCUBEEvent event = new GCUBEEvent(); event.setPayload(resource); ServiceContext.getContext().getTopicProducer().notify(ServiceContext.RegistryTopic.UPDATE, event); } catch (Exception e) { logger.warn("Error while updating the counting info for resource with ID " + resource.getID(), e); } logger.info("Profile " + mess.getUniqueID() + " updated"); return new UpdateResourceResponse(); } /** * Removes a Resource profile identified by the given the resource ID * * @param inputMessage defined into WSDL file * @return RemoveResourceResponse * @throws RemoteException */ @SuppressWarnings("unchecked") public RemoveResourceResponse removeResource(RemoveResourceMessage inputMessage) throws RemoteException, GCUBEFault { //logSecurityInfo("removeResource"); String ID = inputMessage.getUniqueID(); String type = inputMessage.getType(); logger.info("RemoveResource operation invoked on resource ID=" + ID + ", type=" + type); if (ID == null || ID.compareTo("") == 0) { logger.warn("Resource ID is missing, cannot manage the resource"); throw new RemoteException("Resource ID is missing, cannot manage the resource"); } try { logger.debug("Trying to remove the resource from the IS-IC"); new ProfileManager().removeFromISIC(ID, type, ServiceContext.getContext().getScope(), ServiceContext.getContext()); logger.info("Resource removed from the IS-IC"); } catch (Exception e) { logger.error("Unable to remove the resource", e); throw ServiceContext.getContext().getDefaultException("", e).toFault( "Unable to remove the resource: the remote IS-IC returned an error"); } // if the resource is a GHN, remove also the related RIs try { if (type.compareTo(GCUBEHostingNode.TYPE) == 0) { logger.debug("Removing the related RIs"); GHN ghn = new GHN(ID); ghn.unregisterHostedRIs(this); logger.debug("Related RIs removed"); } } catch (Exception e) { logger.error("Error while removing RI profiles related to the GHN", e); } if (this.isResourceCreated(ID)) { // destroy the resource try { logger.debug("Destroying the local stateful Resoruce"); ProfileContext.getContext().getWSHome().remove(ProfileContext.getContext().makeKey(ID)); logger.info(" Resource Destroyed"); } catch (Exception e) { logger.error("Error removing resource for ID ", e); throw new RemoteException(); } try { GCUBEEvent event = new GCUBEEvent(); GCUBEResource resource = ResourceType.valueOf(type).getResourceClass(); resource.setID(ID); event.setPayload(resource); ServiceContext.getContext().getTopicProducer().notify(ServiceContext.RegistryTopic.REMOVE, event); updateCounterInfo(ID, ResourceType.valueOf(type),OperationType.destroy, Calendar.getInstance()); } catch (Exception e) { logger.warn("Error updating counting info for resource with ID " + ID, e); } } return new RemoveResourceResponse(); } /** * Checks if the Resource has been created * * @param id * the Resource ID * @return the resource ( null if the resource has not been created yet */ protected synchronized ProfileResource getProfileResource(String id) { try { return (ProfileResource) ProfileContext.getContext().getWSHome() .find(ProfileContext.getContext().makeKey(id)); } catch (Exception e) { logger.debug("A profile with the given id " + id + " has not been created yet"); } return null; } /** * * @param id * @return */ protected synchronized boolean isResourceCreated(String id) { if (getProfileResource(id) != null) return true; else return false; } /** * Updates the RegistryFactoryResource RPs for notification * * * @param ID * resource ID * @param type * the resource type * @param operationType * the type of Operation performed on the Profile * @param updateTime * The last operation Time * @throws Exception * Exception */ private synchronized void updateCounterInfo(String ID, ResourceType resType, OperationType opType, Calendar updateTime) throws Exception { RegistryProperty property = new RegistryProperty(); property.setUniqueID(ID); property.setOperationType(opType.name()); property.setChangeTime(updateTime); // select the type of the resource to update for (Method method : this.getResource().getClass().getDeclaredMethods()) { if (method.getName().contains(resType.name()) && method.getName().contains("set")) { method.invoke(this.getResource(), property); break; } } this.getResource().store(); } /** * return the resource * * @return NotifierResource resource * @throws RemoteException * */ private RegistryFactoryResource getResource() throws RemoteException { Object resource = null; try { resource = FactoryContext.getContext().getWSHome().find( FactoryContext.getContext().makeKey(NOTIFICATOR_RESOURCE_KEY)); } catch (Exception e) { logger.error(" Unable to access resource", e); } RegistryFactoryResource factoryResource = (RegistryFactoryResource) resource; return factoryResource; } /** * {@inheritDoc} */ @Override protected GCUBEServiceContext getServiceContext() { return ServiceContext.getContext(); } }