/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* VirtualResourceManager.java
* Creation date: Jan 12, 2006.
* By: Joseph Wong
*/
package org.openquark.cal.services;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleSourceDefinition;
import org.openquark.cal.machine.AsynchronousFileWriter;
import org.openquark.cal.machine.ProgramResourceLocator;
import org.openquark.cal.machine.ProgramResourceRepository;
import org.openquark.cal.metadata.MetadataManager;
import org.openquark.cal.runtime.MachineType;
/**
* This class encapsulates the notion that there can be multiple resource
* managers for each kind of module resource, and that there is a mapping from
* module name to the set of resource managers for it. This is an extension of
* the concept represented by the {@link ResourceManagerRegistry}, which maps a
* resource type to a resource manager.
* <p>
* This logic is meant to handle working with CAL Archives (Cars), where a
* module can either be found in the main location (e.g. on the file system) or
* in some Car, which has its own managers for the resources found inside the
* Car.
*
* @author Joseph Wong
*/
public final class VirtualResourceManager {
/**
* The resource manager registry for resource types that are not module oriented (and hence not Car-aware).
*/
private final ResourceManagerRegistry mainRegistry;
/**
* A Map mapping a Car name to the corresponding Car accessor. This map contains as values all the Cars that
* are managed by this manager.
*/
private final Map<ModuleName, Car.Accessor> carAccessors;
/**
* A Set of CarAwareResourceManagerProvider - the providers for Car-aware resource managers, which will
* be called upon to create new resource managers when a Car is added to this manager.
*/
private final Set<CarAwareResourceManagerProvider> carAwareResourceManagerProviders;
/**
* This class encapsulates a module name to resource manager mapping. It is intended to be used as
* return values for API methods that need to return a copy of the underlying module name to Car mapping.
*
* While this class is public, it is not intended to be instantiated by clients - only by methods of
* {@link VirtualResourceManager}.
*
* @author Joseph Wong
*/
// TODOEL: This is here for the RenameRefactorer. We may want to remove this when we refactor the renamer.
public static class ModuleNameToResourceManagerMapping {
/**
* The resource type of the resource managers in this mapping.
*/
private final String resourceType;
/**
* The Map mapping a module name to the corresponding resource manager.
*/
private final Map<ModuleName, ResourceManager> mapping;
/**
* The default manager to return when a module name is not contained in the mapping. Can be null.
*/
private final ResourceManager defaultManager;
/**
* Private constructor for ModuleNameToResourceManagerMapping.
* @param resourceType the resource type of the resource managers in this mapping.
* @param mapping the mapping from module names to resource managers.
* @param defaultManager the default manager to return when a module name is not contained in the mapping. Can be null.
*/
private ModuleNameToResourceManagerMapping(final String resourceType, final Map<ModuleName, ResourceManager> mapping, final ResourceManager defaultManager) {
if (resourceType == null || mapping == null) {
throw new NullPointerException();
}
this.resourceType = resourceType;
this.mapping = mapping;
this.defaultManager = defaultManager;
}
/**
* @return the resource type of the resource managers in this mapping.
*/
public String getResourceType() {
return resourceType;
}
/**
* Returns the resource manager for the specified module.
* @param moduleName the name of the module.
* @return the corresponding resource manager. Can be null if there is no appropriate resource manager for the module.
*/
public ResourceManager getManager(final ModuleName moduleName) {
final ResourceManager resourceManager = mapping.get(moduleName);
if (resourceManager != null) {
return resourceManager;
} else {
return defaultManager;
}
}
}
/**
* This inner class implements a Car-aware ProgramResourceRepository that relies on the module name to Car mapping
* of the enclosing instance to properly dispatch the various operations to the correct ProgramResourceRepository
* based on the module name in the {@link ProgramResourceLocator}.
*
* @author Joseph Wong
*/
private class CarAwareProgramResourceRepository implements ProgramResourceRepository {
/**
* The base repository to use when a module name is not found in the Car mapping.
*/
private final ProgramResourceRepository baseRepository;
/**
* The path mapper to use for the program resources.
*/
private final ProgramResourcePathMapper pathMapper;
/**
* Private constructor for CarAwareProgramResourceRepository. Instances should be created via
* {@link VirtualResourceManager#getProviderForCarAwareProgramResourceRepository}.
*
* @param baseRepository the base repository to use when a module name is not found in the Car mapping.
* @param pathMapper the path mapper to use for the program resources.
*/
private CarAwareProgramResourceRepository(final ProgramResourceRepository baseRepository, final ProgramResourcePathMapper pathMapper) {
if (baseRepository == null || pathMapper == null) {
throw new NullPointerException();
}
this.baseRepository = baseRepository;
this.pathMapper = pathMapper;
}
/**
* {@inheritDoc}
*/
public ModuleName[] getModules() {
final ModuleName[] modulesFromBaseRepository = baseRepository.getModules();
final Set<ModuleName> modulesFromCars = carAccessors.keySet();
final Set<ModuleName> combinedModules = new HashSet<ModuleName>(Arrays.asList(modulesFromBaseRepository));
combinedModules.addAll(modulesFromCars);
return combinedModules.toArray(new ModuleName[combinedModules.size()]);
}
/**
* {@inheritDoc}
*/
public ProgramResourceLocator[] getMembers(final ProgramResourceLocator.Folder folder) {
final Car.Accessor carAccessor = carAccessors.get(folder.getModuleName());
if (carAccessor == null) {
return baseRepository.getMembers(folder);
} else {
return carAccessor.getFolderMembers(folder, ProgramResourcePathMapper.getResourcePath(folder, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public InputStream getContents(final ProgramResourceLocator.File fileLocator) throws IOException {
final Car.Accessor carAccessor = carAccessors.get(fileLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.getContents(fileLocator);
} else {
return carAccessor.getResource(ProgramResourcePathMapper.getResourcePath(fileLocator, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public void setContents(final ProgramResourceLocator.File fileLocator, final InputStream source) throws IOException {
final Car.Accessor carAccessor = carAccessors.get(fileLocator.getModuleName());
if (carAccessor == null) {
baseRepository.setContents(fileLocator, source);
} else {
throw new IOException("A Car file is read-only and cannot be modified.");
}
}
/**
* {@inheritDoc}
*/
public void ensureFolderExists(final ProgramResourceLocator.Folder resourceFolder) throws IOException {
final Car.Accessor carAccessor = carAccessors.get(resourceFolder.getModuleName());
if (carAccessor == null) {
baseRepository.ensureFolderExists(resourceFolder);
} else {
throw new IOException("A Car file is read-only and cannot be modified.");
}
}
/**
* {@inheritDoc}
*/
public boolean exists(final ProgramResourceLocator resourceLocator) {
final Car.Accessor carAccessor = carAccessors.get(resourceLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.exists(resourceLocator);
} else {
return carAccessor.doesResourceExist(ProgramResourcePathMapper.getResourcePath(resourceLocator, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public void delete(final ProgramResourceLocator resourceLocator) throws IOException {
final Car.Accessor carAccessor = carAccessors.get(resourceLocator.getModuleName());
if (carAccessor == null) {
baseRepository.delete(resourceLocator);
} else {
throw new IOException("A Car file is read-only and cannot be modified.");
}
}
/**
* {@inheritDoc}
*/
public void delete(final ProgramResourceLocator[] resourceLocators) throws IOException {
for (final ProgramResourceLocator element : resourceLocators) {
delete(element);
}
}
/**
* {@inheritDoc}
*/
public long lastModified(final ProgramResourceLocator resourceLocator) {
final Car.Accessor carAccessor = carAccessors.get(resourceLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.lastModified(resourceLocator);
} else {
return carAccessor.getResourceLastModifiedTime(ProgramResourcePathMapper.getResourcePath(resourceLocator, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public long getSize(final ProgramResourceLocator.File fileLocator) {
final Car.Accessor carAccessor = carAccessors.get(fileLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.getSize(fileLocator);
} else {
return carAccessor.getResourceSize(ProgramResourcePathMapper.getResourcePath(fileLocator, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public File getFile(final ProgramResourceLocator resourceLocator) {
final Car.Accessor carAccessor = carAccessors.get(resourceLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.getFile(resourceLocator);
} else {
// the contract says that null should be returned if the resource is not backed by the file system and does not have a java.io.File representation
return null;
}
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return baseRepository.isEmpty() && carAccessors.isEmpty();
}
/**
* {@inheritDoc}
*/
public String getLocationString() {
final StringBuilder location = new StringBuilder(baseRepository.getLocationString());
for (final Map.Entry<ModuleName, Car.Accessor> entry : carAccessors.entrySet()) {
final ModuleName moduleName = entry.getKey();
final Car.Accessor carAccessor = entry.getValue();
location.append("\n[Car: ").append(carAccessor.getCarFileName()).append(", module: ").append(moduleName).append("]");
}
return location.toString();
}
/**
* {@inheritDoc}
*/
public String getDebugInfo(final ProgramResourceLocator.File fileLocator) {
final Car.Accessor carAccessor = carAccessors.get(fileLocator.getModuleName());
if (carAccessor == null) {
return baseRepository.getDebugInfo(fileLocator);
} else {
return carAccessor.getDebugInfo(ProgramResourcePathMapper.getResourcePath(fileLocator, pathMapper));
}
}
/**
* {@inheritDoc}
*/
public AsynchronousFileWriter getAsynchronousFileWriter() {
return new BasicAsynchronousFileWriter(this);
}
}
/**
* This interface specifies a provider that is capable of creating a new resource manager
* of a particular resource type for each Car in the system.
*
* @author Joseph Wong
*/
public static interface CarAwareResourceManagerProvider {
/**
* @return the resource type handled by this provider.
*/
public String getResourceType();
/**
* @return the default manager to use when a module name is not contained in the module name to Car mapping. Can be null.
*/
public ResourceManager getDefaultResourceManager();
/**
* Creates a resource manager for the given Car.
* @param carAccessor the accessor for the Car.
* @return the resource manager created for used with that Car.
*/
public ResourceManager getResourceManagerForCar(Car.Accessor carAccessor);
}
/**
* Constructs an instance of VirtualResourceManager.
*/
public VirtualResourceManager() {
this.mainRegistry = new ResourceManagerRegistry();
this.carAccessors = new HashMap<ModuleName, Car.Accessor>();
this.carAwareResourceManagerProviders = new HashSet<CarAwareResourceManagerProvider>();
}
/**
* Returns a ProgramResourceRepository.Provider that provides Car-aware ProgramResourceRepository instances.
*
* @param baseProvider the base repository for use by the Car-aware ProgramResourceRepository when
* a module name is not found in the Car mapping.
*
* @return the provider for Car-aware ProgramResourceRepository instances.
*/
ProgramResourceRepository.Provider getProviderForCarAwareProgramResourceRepository(final ProgramResourceRepository.Provider baseProvider) {
return new ProgramResourceRepository.Provider() {
public ProgramResourceRepository getRepository(final MachineType machineType) {
final ProgramResourceRepository baseRepository = baseProvider.getRepository(machineType);
return new CarAwareProgramResourceRepository(baseRepository, new ProgramResourcePathMapper(machineType));
}
};
}
/**
* Registers a non-Car-aware resource manager.
* @param resourceManager the resource manager to be registered.
*/
public void registerNonCarAwareResourceManager(final ResourceManager resourceManager) {
mainRegistry.registerResourceManager(resourceManager);
}
/**
* Registers a Car-aware resource manager (via registering the affiliated CarAwareResourceManagerProvider).
* @param provider the provider for the Car-aware resource manager.
*/
public void registerCarAwareResourceManager(final CarAwareResourceManagerProvider provider) {
mainRegistry.registerResourceManager(provider.getDefaultResourceManager());
// it is often the case that many modules map to the same Car, so we want to
// use a set so that we do not have to iterate over the same Car over and over again
// (if we iterate through carAccessors.values() directly).
final Set<Car.Accessor> carAccessorSet = new HashSet<Car.Accessor>(carAccessors.values());
for (final Car.Accessor carAccessor : carAccessorSet) {
carAccessor.registerResourceManager(provider.getResourceManagerForCar(carAccessor));
}
}
/**
* Associates a module name with the given Car.
* @param moduleName the name of the module.
* @param carAccessor the accessor for the Car.
*/
void linkModuleWithCar(final ModuleName moduleName, final Car.Accessor carAccessor) {
carAccessors.put(moduleName, carAccessor);
for (final CarAwareResourceManagerProvider provider : carAwareResourceManagerProviders) {
// if the Car accessor has been linked with another module before, it would have
// the additional resource managers already, so we do not register the resource manager again in that case
if (carAccessor.getResourceManager(provider.getResourceType()) == null) {
carAccessor.registerResourceManager(provider.getResourceManagerForCar(carAccessor));
}
}
}
/**
* Disassociates the module name from any Cars.
* @param moduleName the name of the module.
*/
void unlinkModuleFromCars(final ModuleName moduleName) {
carAccessors.remove(moduleName);
}
/**
* @return the registered source manager, if any.
*/
public CALSourceManager getSourceManager(final ModuleName moduleName) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getSourceManager();
} else {
return carAccessor.getSourceManager();
}
}
/**
* @return a mapping from module name to the source manager responsible for that module.
*/
public ModuleNameToResourceManagerMapping getModuleNameToSourceManagerMapping() {
return getModuleNameToResourceManagerMapping(ModuleSourceDefinition.RESOURCE_TYPE);
}
/**
* @return the registered metadataManager, if any.
*/
public MetadataManager getMetadataManager(final ModuleName moduleName) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getMetadataManager();
} else {
return carAccessor.getMetadataManager();
}
}
/**
* @return a mapping from module name to the metadata manager responsible for that module.
*/
public ModuleNameToResourceManagerMapping getModuleNameToMetadataManagerMapping() {
return getModuleNameToResourceManagerMapping(WorkspaceResource.METADATA_RESOURCE_TYPE);
}
/**
* @return the registered gem design manager, if any.
*/
public GemDesignManager getDesignManager(final ModuleName moduleName) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getDesignManager();
} else {
return carAccessor.getDesignManager();
}
}
/**
* @return a mapping from module name to the gem design manager responsible for that module.
*/
public ModuleNameToResourceManagerMapping getModuleNameToDesignManagerMapping() {
return getModuleNameToResourceManagerMapping(WorkspaceResource.GEM_DESIGN_RESOURCE_TYPE);
}
/**
* @return the registered user resource manager, if any.
*/
public UserResourceManager getUserResourceManager(final ModuleName moduleName) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getUserResourceManager();
} else {
return carAccessor.getUserResourceManager();
}
}
/**
* @return a mapping from module name to the user resource manager responsible for that module.
*/
public ModuleNameToResourceManagerMapping getModuleNameToUserResourceManagerMapping() {
return getModuleNameToResourceManagerMapping(WorkspaceResource.USER_RESOURCE_TYPE);
}
/**
* Get the resource manager which handles ModuleResources of a given type for the given module.
* @param moduleName the name of the module whose resource manager is to be returned.
* @param resourceType the String which identifies the type of resource.
* @return the resource manager which handles resource of that type for that module, or null if there isn't any.
*/
public ResourceManager getModuleSpecificResourceManager(final ModuleName moduleName, final String resourceType) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getResourceManager(resourceType);
} else {
return carAccessor.getResourceManager(resourceType);
}
}
/**
* Returns a mapping from module name to the resource manager responsible for that module for the given resource type.
* @param resourceType the String which identifies the type of resource.
* @return a mapping from module name to the resource manager responsible for that module for the given resource type.
* This will not be null.
*/
public ModuleNameToResourceManagerMapping getModuleNameToResourceManagerMapping(final String resourceType) {
final Map<ModuleName, ResourceManager> mapping = new HashMap<ModuleName, ResourceManager>();
for (final Map.Entry<ModuleName, Car.Accessor> entry : carAccessors.entrySet()) {
final ModuleName moduleName = entry.getKey();
final Car.Accessor carAccessor = entry.getValue();
mapping.put(moduleName, carAccessor.getResourceManager(resourceType));
}
return new ModuleNameToResourceManagerMapping(resourceType, mapping, mainRegistry.getResourceManager(resourceType));
}
/**
* @return the registered workspace manager, if any.
*/
public WorkspaceDeclarationManager getWorkspaceDeclarationManager() {
return (WorkspaceDeclarationManager)mainRegistry.getResourceManager(WorkspaceResource.WORKSPACE_DECLARATION_RESOURCE_TYPE);
}
/**
* @return the registered Car manager, if any.
*/
public CarManager getCarManager() {
return (CarManager)mainRegistry.getResourceManager(WorkspaceResource.CAR_RESOURCE_TYPE);
}
/**
* @param moduleName the name of the module whose resource managers are to be fetched
* @return the resource managers for the resources of the given module.
*/
public Set<ResourceManager> getResourceManagersForModule(final ModuleName moduleName) {
final Car.Accessor carAccessor = carAccessors.get(moduleName);
if (carAccessor == null) {
return mainRegistry.getResourceManagers();
} else {
return carAccessor.getResourceManagers();
}
}
/**
* Retrieves the resource manager that manages the specified resource.
* @param identifier the identifier of the resource whose manager is to be returned.
* @return the corresponding resource manager, or null if there is none.
*/
public ResourceManager getResourceManagerForResource(final ResourceIdentifier identifier) {
final FeatureName featureName = identifier.getFeatureName();
final String resourceType = identifier.getResourceType();
if (featureName instanceof CALFeatureName) {
final CALFeatureName calFeatureName = (CALFeatureName)featureName;
return getModuleSpecificResourceManager(calFeatureName.toModuleName(), resourceType);
} else {
return mainRegistry.getResourceManager(resourceType);
}
}
/**
* Removes all modifiable resources from this manager.
* @param removeStatus the tracking status object.
*/
void removeAllResources(final Status removeStatus) {
// the resources in Cars do not need to be removed, since their enclosing Cars would be
// removed by the CarManager in the main registry.
for (final ResourceManager resourceManager : mainRegistry.getResourceManagers()) {
resourceManager.removeAllResources(removeStatus);
}
}
}