/* * 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. */ /* * WorkspaceManager.java * Creation date: Oct 2, 2002. * By: Edward Lam */ package org.openquark.cal.services; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.ModuleSourceDefinitionGroup; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.ProgramModifier; import org.openquark.cal.machine.ProgramManager; import org.openquark.cal.machine.ProgramResourceRepository; import org.openquark.cal.machine.StatusListener; import org.openquark.cal.runtime.MachineType; import org.openquark.cal.services.CALWorkspace.SyncInfo; /** * A manager for CAL source and compiled forms of modules. * Note: compile() compiles source into the current program. * We may want the flexibility to compile to a new program without modifying the existing one. * @author Edward Lam */ public class WorkspaceManager extends ProgramModelManager { /** The default workspace root overrider. */ static final SourceGenerationRootOverrideProvider DEFAULT_ROOT_OVERRIDER = new SourceGenerationRootOverrideProvider() { public File getSourceGenerationRootOverride(String clientID) { // Use the system property. return WorkspaceConfiguration.getOutputDirectoryFromProperty(); } }; /** The workspace to manage. */ private final CALWorkspace workspace; /** * The stream provider for the workspace declaration used for initializing * the workspace. Can be null if the * {@link #initWorkspace(WorkspaceDeclaration.StreamProvider, Status)} * method has not been called. */ private WorkspaceDeclaration.StreamProvider initialWorkspaceDeclarationProvider; /** * This provider interface allows the client to override where the root of the workspace for source generation * will be located when constructing the workspace maanger. * * @author Edward Lam */ public interface SourceGenerationRootOverrideProvider { /** * @param clientID the discrete workspace id, or null for a nullary workspace * @return the file folder into which program resources will be generated, or null to generate to the default location. */ public File getSourceGenerationRootOverride(String clientID); } /** * Private constructor for a workspace manager. * @param programManager the program manager used by this manager. * @param workspace the workspace managed by this manager. */ private WorkspaceManager(ProgramManager programManager, CALWorkspace workspace) { super(programManager); this.workspace = workspace; if (workspace == null) { throw new NullPointerException("Argument 'workspace' must not be null. "); } } /** * Factory method for a workspace manager. * @param clientID a string identifying the client for this workspace. * Client id's are used to refer to specific workspaces. */ public static WorkspaceManager getWorkspaceManager(String clientID) { return getWorkspaceManager(clientID, null, DEFAULT_ROOT_OVERRIDER); } /** * Package-scoped factory method for a workspace manager -- DO NOT EXPOSE. * This is intended to be called by the unit testing infrastructure to enable designation of a specific machine type * and source generation root. * * @param clientID * a string identifying the client for this workspace. Client * id's are used to refer to specific workspaces. * @param machineTypeOverride the machine type to use, or null to use the default machine type. * @param rootOverrideProvider * the WorkspaceRootOverrideProvider instance to use for overriding (if necessary) the root of the * Workspace for source generation for this WorkspaceManager. */ static WorkspaceManager getWorkspaceManager(String clientID, MachineType machineTypeOverride, SourceGenerationRootOverrideProvider rootOverrideProvider) { // Get the workspace provider factory. CALWorkspaceEnvironmentProvider.Factory providerFactory = WorkspaceLoader.getWorkspaceProviderFactory(); // Create the workspace provider, and use this to get the workspace. CALWorkspaceEnvironmentProvider workspaceProvider = providerFactory.createCALWorkspaceProvider(clientID); CALWorkspace workspace = workspaceProvider.getCALWorkspace(); // Instantiate the repository provider. Override default if the source generation root is overridden. File sourceGenerationRootOverride = rootOverrideProvider.getSourceGenerationRootOverride(clientID); ProgramResourceRepository.Provider programResourceRepositoryProvider; if (sourceGenerationRootOverride != null) { programResourceRepositoryProvider = BasicProgramResourcePathRepository.getResourceRepositoryProvider(sourceGenerationRootOverride); } else { programResourceRepositoryProvider = workspaceProvider.getDefaultProgramResourceRepositoryProvider(); } if (programResourceRepositoryProvider == null) { return null; } // Create the program manager using the repository provider. // Override the machine type if given. ProgramResourceRepository.Provider carAwareProgramResourceRepositoryProvider = workspace.getProviderForCarAwareProgramResourceRepository(programResourceRepositoryProvider); ProgramManager programManager = machineTypeOverride == null ? ProgramManager.getProgramManager(carAwareProgramResourceRepositoryProvider, workspace) : ProgramManager.getProgramManager(machineTypeOverride, carAwareProgramResourceRepositoryProvider, workspace); // Note: if the g-machine also uses a resource manager, we can remove the if test. final MachineType realMachineType = programManager.getMachineType(); if (realMachineType == MachineType.LECC) { // Register the program resource manager so that the resources can be managed by the workspace. // Override default if the source generation root is overridden. final ResourceManager fileSystemBasedProgramResourceManager; if (sourceGenerationRootOverride != null) { fileSystemBasedProgramResourceManager = ProgramResourceManager.FileSystemBased.make(realMachineType, sourceGenerationRootOverride); } else { fileSystemBasedProgramResourceManager = workspaceProvider.getDefaultProgramResourceManager(realMachineType); } workspace.registerCarAwareResourceManager(new VirtualResourceManager.CarAwareResourceManagerProvider() { public String getResourceType() { return fileSystemBasedProgramResourceManager.getResourceType(); } public ResourceManager getDefaultResourceManager() { return fileSystemBasedProgramResourceManager; } public ResourceManager getResourceManagerForCar(Car.Accessor carAccessor) { return ProgramResourceManager.CarBased.make(realMachineType, carAccessor); } }); } return new WorkspaceManager(programManager, workspace); } /** * Initialize the workspace manager with the given workspace contents. * If a previously-persisted workspace exists, that workspace will be loaded, unless the system property to force regeneration is set. * Otherwise, a new workspace will be created from the given stream provider on the workspace declaration. * * No compilation will take place. * * @param workspaceDeclarationProvider the provider for the input stream containing the workspace declaration. * The input stream will only be read if a previously-persisted workspace does not exist. * @param initStatus the tracking status object. */ public void initWorkspace(WorkspaceDeclaration.StreamProvider workspaceDeclarationProvider, Status initStatus) { // Get the system property which indicates whether the workspace should be regenerated. boolean forceRegen = System.getProperty(WorkspaceConfiguration.WORKSPACE_PROP_REGENERATE) != null; initWorkspace(workspaceDeclarationProvider, forceRegen, initStatus); } /** * Initialize the workspace manager with the given workspace contents. * If a previously-persisted workspace exists, that workspace can be optionally loaded. * Otherwise, a new workspace will be created from the given stream provider on the workspace declaration. * * No compilation will take place. * * @param workspaceDeclarationProvider the provider for the input stream containing the workspace declaration. * The input stream will only be read if a previously-persisted workspace does not exist. * @param forceRegen whether to force regeneration of the workspace, even in the presence of the previously-existing workspace. * @param initStatus the tracking status object. */ public void initWorkspace(WorkspaceDeclaration.StreamProvider workspaceDeclarationProvider, boolean forceRegen, Status initStatus) { initialWorkspaceDeclarationProvider = workspaceDeclarationProvider; // Use any previously-persisted workspace to initialize. if (!forceRegen && loadPersistedWorkspace(initStatus)) { return; } if (initStatus.getSeverity().compareTo(Status.Severity.ERROR) >= 0) { String message = "Problems were encountered loading the previous workspace: \n" + initStatus.getDebugMessage(); initStatus.add(new Status(Status.Severity.ERROR, message, null)); return; } // No previously-persisted workspace. // Use the provided provider if (workspaceDeclarationProvider == null) { String message = "A previous workspace does not exist, and a new Workspace definition cannot be found."; initStatus.add(new Status(Status.Severity.ERROR, message, null)); return; } // Create a new workspace from the workspace definition, replacing any existing definition. StoredVaultElement.Module[] storedModules = WorkspaceLoader.getStoredModules(workspaceDeclarationProvider, workspace.getVaultRegistry(), initStatus); if (storedModules != null) { workspace.initializeWorkspace(storedModules, initStatus); } if (initStatus.getSeverity().compareTo(Status.Severity.ERROR) >= 0) { String message = "Problems encountered while constructing the workspace:\n" + initStatus.getDebugMessage(); initStatus.add(new Status(Status.Severity.ERROR, message, null)); } } /** * Package-scoped helper for getting the workspace declaration used for initializing the workspace. * Intended only for use by {@link CarBuilder#makeConfigurationsForOneCarPerWorkspaceDeclaration}. * * @return the stream provider for the workspace declaration used for initializing * the workspace. Can be null if the * {@link #initWorkspace(WorkspaceDeclaration.StreamProvider, Status)} * method has not been called. */ WorkspaceDeclaration.StreamProvider getInitialWorkspaceDeclarationProvider() { return initialWorkspaceDeclarationProvider; } /** * @return the name of the workspace declaration used for initializing the workspace. */ public String getInitialWorkspaceDeclarationName() { return getInitialWorkspaceDeclarationProvider().getName(); } /** * @return the debug info for the workspace declaration used for initializing the workspace. */ public String getInitialWorkspaceDeclarationDebugInfo() { return getInitialWorkspaceDeclarationProvider().getDebugInfo(workspace.getVaultRegistry()); } /** * Compile the modules in this manager. * @param logger the logger used to log error messages. * @param dirtyModulesOnly if true, only dirty (uncompiled or changed) modules will be compiled. * @param statusListener a listener, possibly null, for the status of the compilation process */ public void compile(CompilerMessageLogger logger, boolean dirtyModulesOnly, StatusListener statusListener) { compile(logger, dirtyModulesOnly, statusListener, new CompilationOptions()); } /** * Compile the modules in this manager. * @param logger the logger used to log error messages. * @param dirtyModulesOnly if true, only dirty (uncompiled or changed) modules will be compiled. * @param statusListener a listener, possibly null, for the status of the compilation process * @param options - values for compilation options */ public void compile(CompilerMessageLogger logger, boolean dirtyModulesOnly, StatusListener statusListener, CompilationOptions options) { compile(getSourceDefinitionGroup(), logger, dirtyModulesOnly, statusListener, options); } /** * The CAL based optimizer will be used. */ public void useOptimizer(boolean useOptimizer){ getProgramManager().useOptimizer(useOptimizer); } /** * Sync the workspace with a given workspace declaration. * * No compilation will take place. * * @param workspaceDeclarationProvider the provider for the workspace spec. * @param syncStatus the tracking status object. */ public SyncInfo syncWorkspaceToDeclaration(WorkspaceDeclaration.StreamProvider workspaceDeclarationProvider, Status syncStatus) { // Get the stored modules from the workspace declaration. StoredVaultElement.Module[] storedModules = WorkspaceLoader.getStoredModules(workspaceDeclarationProvider, workspace.getVaultRegistry(), syncStatus); CALWorkspace.SyncInfo syncInfo = new CALWorkspace.SyncInfo(); if (storedModules != null) { // Iterate over the stored modules. for (final StoredVaultElement.Module storedModule : storedModules) { // Sync to the stored module. CALWorkspace.SyncInfo newSyncInfo = workspace.syncModuleToStoredModule(storedModule, false, syncStatus); syncInfo.addInfo(newSyncInfo); } } return syncInfo; } /** * Initialize the workspace manager from any previously-persisted workspace. * @param loadStatus the tracking status object. * @return true if there was a previously-persisted workspace, and it was successfully loaded. */ private boolean loadPersistedWorkspace(Status loadStatus) { return workspace.loadPersistedWorkspace(loadStatus); } /** * Get the program held by this manager. * @return Program the program held by this manager. */ public CALWorkspace getWorkspace() { return workspace; } /** * {@inheritDoc} */ @Override protected ProgramModifier makeProgramModifier(CompilationOptions options) { ProgramModifier newProgramModifier = super.makeProgramModifier(options); // the workspace Program registers as a // listener so that it can update itself as the program changes. newProgramModifier.addChangeListener(workspace); return newProgramModifier; } /** * @return the source definition group for the current program. */ protected ModuleSourceDefinitionGroup getSourceDefinitionGroup() { return workspace.getSourceDefinitionGroup(); } /** * @return debugging information about the workspace, e.g. the actual location of each module's resources. */ public String getDebugInfo() { String result; result = "Workspace declaration:\n" + initialWorkspaceDeclarationProvider.getName() + "\n" + " - " + initialWorkspaceDeclarationProvider.getLocation() + "\n" + " - " + initialWorkspaceDeclarationProvider.getDebugInfo(workspace.getVaultRegistry()) + "\n\n" + "Workspace location:\n" + workspace.getWorkspaceLocationString() + "\n\n" + "Workspace contents:\n" + workspace.getDebugInfo() + "\n"; return result; } /** * Dumps info to the console about all metadata files which do not * correspond to features in the featureNameList. * * These "orphaned" files come about when a feature's name or type is * changed without updating the corresponding metadata file. */ public void dumpOrphanedMetadata() { dumpOrphanedResources(WorkspaceResource.METADATA_RESOURCE_TYPE); } /** * Dumps info to the console about all resources files which do not * correspond to features in a cal program * * These "orphaned" files come about when a feature's name or type is * changed without updating the corresponding resource file. */ public void dumpOrphanedResources(String resourceType) { // names of all modules in the current program. Set<ModuleName> moduleNameSet = new HashSet<ModuleName>(); // CALFeatureNames for all features in the current program. Set<CALFeatureName> programFeatureNameSet = new HashSet<CALFeatureName>(); // Grab the module names and feature names. ModuleName[] compiledModuleNames = getProgramManager().getModuleNames(); for (final ModuleName moduleName : compiledModuleNames) { MetaModule metaModule = workspace.getMetaModule(moduleName); if (metaModule != null) { moduleNameSet.add(moduleName); programFeatureNameSet.addAll(metaModule.getFeatureNames()); } } System.out.println("\n-- Searching for Orphaned Resources -- \n"); List<FeatureName> orphanedResourceNameList = new ArrayList<FeatureName>(); Set<ModuleName> orphanedModuleNameSet = new HashSet<ModuleName>(); // At this point, we have the names of all the modules and features. Now check resource iterators.. for (final ModuleName moduleName : moduleNameSet) { ResourceManager resourceManager = workspace.getResourceManager(moduleName, resourceType); if (resourceManager == null) { System.out.println("No registered resource manager for type: " + resourceType + " for module: " + moduleName); continue; } if (!(resourceManager instanceof ModuleResourceManager)) { String message = "There can be no orphaned resources for type \'" + resourceType + "\", since this is not a module resource type."; System.out.println(message); break; } ResourceStore.Module resourceStore = (ResourceStore.Module)resourceManager.getResourceStore(); // add to the set of all module names managed by all resource stores orphanedModuleNameSet.addAll(resourceStore.getModuleNames()); for (Iterator<WorkspaceResource> it = resourceStore.getResourceIterator(moduleName); it.hasNext(); ) { WorkspaceResource workspaceResource = it.next(); FeatureName resourceFeatureName = workspaceResource.getIdentifier().getFeatureName(); if (!programFeatureNameSet.contains(resourceFeatureName)) { orphanedResourceNameList.add(resourceFeatureName); } } } if (!orphanedResourceNameList.isEmpty()) { System.out.println("The following features have resources of type '" + resourceType + "' but no longer exist:\n"); for (final FeatureName featureName : orphanedResourceNameList) { System.out.println(featureName); } System.out.println(); } else { System.out.println("No orphaned resource files were found for type '" + resourceType + "'.\n"); } // (orphaned modules) = (all modules managed by all resource) - (modules in workspace) orphanedModuleNameSet.removeAll(moduleNameSet); if (!orphanedModuleNameSet.isEmpty()) { System.out.println("The following modules have resources of type '" + resourceType + "' but are not currently loaded:\n"); for (final ModuleName moduleName : orphanedModuleNameSet) { System.out.println(moduleName); } System.out.println(); } System.out.println("Finished.\n"); } public boolean removeModule (ModuleName moduleName, Status status) { return removeModule (moduleName, true, status); } public boolean removeModule (ModuleName moduleName, boolean includeDependentsInWorkspace, Status status) { Set<ModuleName> moduleNames; if (includeDependentsInWorkspace) { moduleNames = getProgramManager().getSetOfDependentModuleNames(moduleName, true); } else { moduleNames = new HashSet<ModuleName>(); } moduleNames.add (moduleName); if (workspace.containsModule(moduleName)) { if (!workspace.removeModules(moduleNames, status)) { return false; } } return super.removeModule(moduleName); } /** * * @param moduleName * @return type info for the named module. Null if module doesn't exist. */ @Override public ModuleTypeInfo getModuleTypeInfo (ModuleName moduleName) { if (!workspace.containsModule(moduleName)) { return null; } return super.getModuleTypeInfo(moduleName); } /** * @return the repository for program resources, or null if none. */ public ProgramResourceRepository getRepository() { // TODOEL: Should try and hide this method. Exposure allows clients to screw up the runtime. return getProgramManager().getProgramResourceRepository(); } }