Java tutorial
/* * Copyright (C) 2014 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package interactivespaces.workbench.tasks; import interactivespaces.InteractiveSpacesException; import interactivespaces.SimpleInteractiveSpacesException; import interactivespaces.configuration.Configuration; import interactivespaces.resource.NamedVersionedResourceCollection; import interactivespaces.resource.NamedVersionedResourceWithData; import interactivespaces.resource.Version; import interactivespaces.resource.VersionRange; import interactivespaces.system.core.configuration.CoreConfiguration; import interactivespaces.system.core.container.ContainerFilesystemLayout; import interactivespaces.util.graph.DependencyResolver; import interactivespaces.util.io.FileSupport; import interactivespaces.util.io.FileSupportImpl; import interactivespaces.workbench.InteractiveSpacesContainer; import interactivespaces.workbench.InteractiveSpacesWorkbench; import interactivespaces.workbench.project.Project; import interactivespaces.workbench.project.ProjectDependency; import interactivespaces.workbench.project.ProjectManager; import com.google.common.collect.Lists; import com.google.common.io.Closeables; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.FilenameFilter; import java.util.Collections; import java.util.List; /** * The main context for a workbench task run. * * @author Keith M. Hughes */ public class WorkbenchTaskContext { /** * The base folder for extras in the workbench. */ public static final String EXTRAS_BASE_FOLDER = "extras"; /** * File extension for a Java jar file. */ public static final String FILENAME_JAR_EXTENSION = ".jar"; /** * The file extension used for files which give container extensions. */ public static final String EXTENSION_FILE_EXTENSION = ".ext"; /** * The keyword header for a package line on an extensions file. */ public static final String EXTENSION_FILE_PATH_KEYWORD = "path:"; /** * The length of the keyword header for a package line on an extensions file. */ public static final int EXTENSION_FILE_PATH_KEYWORD_LENGTH = EXTENSION_FILE_PATH_KEYWORD.length(); /** * Configuration property giving the project path for the workbench. */ public static final String CONFIGURATION_INTERACTIVESPACES_WORKBENCH_PROJECT_PATH = "interactivespaces.workbench.project.path"; /** * Configuration property giving the location of the controller the workbench is using. */ public static final String CONFIGURATION_CONTROLLER_BASEDIR = "interactivespaces.controller.basedir"; /** * Configuration property giving the location of the master the workbench is using. */ public static final String CONFIGURATION_MASTER_BASEDIR = "interactivespaces.master.basedir"; /** * A file filter for detecting directories. */ private static final FileFilter DIRECTORY_FILE_FILTER = new FileFilter() { @Override public boolean accept(File pathname) { return pathname.isDirectory(); } }; /** * The workbench for the context. */ private final InteractiveSpacesWorkbench workbench; /** * The workbench configuration to be used by this task. */ private final Configuration workbenchConfig; /** * The list of tasks. */ private final List<WorkbenchTask> tasks = Lists.newArrayList(); /** * The collection of projects scanned from the project path. */ private final NamedVersionedResourceCollection<NamedVersionedResourceWithData<Project>> projectsPath; /** * {@code true} if the project path has been scanned. */ private boolean projectPathScanned = false; /** * File support for file operations. */ private final FileSupport fileSupport = FileSupportImpl.INSTANCE; /** * Some task had an error during the task processing process. */ private boolean errors = false; /** * Construct a new context. * * @param workbench * the workbench context * @param workbenchConfig * the workbench configuration to be used for this context */ public WorkbenchTaskContext(InteractiveSpacesWorkbench workbench, Configuration workbenchConfig) { this.workbench = workbench; this.workbenchConfig = workbenchConfig; projectsPath = NamedVersionedResourceCollection.newNamedVersionedResourceCollection(); } /** * Get the workbench for the context. * * @return the workbench */ public InteractiveSpacesWorkbench getWorkbench() { return workbench; } /** * An error has happened, handle it properly. * * @param message * message for the error * @param e * any exception that may have happened, can be {@code null} */ public void handleError(String message, Throwable e) { errors = true; logException(e); throw new InteractiveSpacesWorkbenchTaskInterruptionException(); } /** * Log an exception. * * @param e * the exception to log */ private void logException(Throwable e) { if (e instanceof SimpleInteractiveSpacesException) { workbench.getLog().error(((SimpleInteractiveSpacesException) e).getCompoundMessage()); } else { workbench.getLog().error("Error executing workbench commands", e); } } /** * An error has happened, handle it properly. * * @param message * message for the error */ public void handleError(String message) { handleError(message, null); } /** * Add a new collection of tasks to the context. * * @param tasks * the tasks to add * * @return this task context */ public WorkbenchTaskContext addTasks(WorkbenchTask... tasks) { if (tasks != null) { Collections.addAll(this.tasks, tasks); } return this; } /** * Do all tasks in the task list. */ public void doTasks() { for (WorkbenchTask task : orderTasksInDependencyOrder()) { performTask(task); if (errors) { break; } } } /** * Perform an individual task. * * @param task * the task to perform */ private void performTask(WorkbenchTask task) { try { task.perform(this); } catch (Throwable e) { // last change catching of any exceptions not explicitly covered. if (!(e instanceof InteractiveSpacesWorkbenchTaskInterruptionException)) { errors = true; logException(e); } } } /** * Order the tasks in dependency order. * * @return the tasks in dependency order */ private List<WorkbenchTask> orderTasksInDependencyOrder() { DependencyResolver<WorkbenchTask, WorkbenchTask> resolver = new DependencyResolver<WorkbenchTask, WorkbenchTask>(); for (WorkbenchTask task : tasks) { resolver.addNode(task, task); resolver.addNodeDependencies(task, task.getTaskDependencies()); } resolver.resolve(); List<WorkbenchTask> orderedTasks = resolver.getOrdering(); return orderedTasks; } /** * Has the project tasks had an error? * * @return {@code true} if errors */ public boolean hasErrors() { return errors; } /** * Get all files in the workbench bootstrap folders, both system and user. * * @return all files in bootstrap folder. */ public List<File> getAllWorkbenchBootstrapFiles() { List<File> files = Lists.newArrayList(); addJarFiles(workbench.getWorkbenchFileSystem().getSystemBootstrapDirectory(), files); File userBootstrap = workbench.getWorkbenchFileSystem().getUserBootstrapDirectory(); if (userBootstrap.exists() && userBootstrap.isDirectory()) { addJarFiles(userBootstrap, files); } return files; } /** * Get a list of all files on the controller's system bootstrap classpath. * * @return all files on the classpath */ public List<File> getControllerSystemBootstrapClasspath() { List<File> classpath = Lists.newArrayList(); addJarFiles( fileSupport.newFile(getControllerDirectory(), ContainerFilesystemLayout.FOLDER_SYSTEM_BOOTSTRAP), classpath); File controllerDirectory = getControllerDirectory(); File javaSystemDirectory = fileSupport.newFile(controllerDirectory, InteractiveSpacesContainer.INTERACTIVESPACES_CONTAINER_FOLDER_LIB_SYSTEM_JAVA); if (!javaSystemDirectory.isDirectory()) { throw new SimpleInteractiveSpacesException( String.format("Controller directory %s configured by %s does not appear to be valid.", controllerDirectory, CONFIGURATION_CONTROLLER_BASEDIR)); } // TODO(keith): Get these in a file somewhere. classpath.add(new File(javaSystemDirectory, "com.springsource.org.apache.commons.logging-1.1.1.jar")); classpath.add(new File(javaSystemDirectory, "org.apache.felix.framework-4.2.1.jar")); addControllerExtensionsClasspath(classpath); return classpath; } /** * Add all JAR files from the given directory to the list of files. * * @param directory * the directory to get the jar files from * @param fileList * the list to add the files to */ public void addJarFiles(File directory, List<File> fileList) { File[] files = directory.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().endsWith(FILENAME_JAR_EXTENSION); } }); if (files != null) { Collections.addAll(fileList, files); } } /** * Get the controller directory which is supporting this workbench. * * @return the controller directory */ public File getControllerDirectory() { String controllerPath = workbenchConfig.getPropertyString(CONFIGURATION_CONTROLLER_BASEDIR); File controllerDirectory = new File(controllerPath); if (controllerDirectory.isAbsolute()) { return controllerDirectory; } File homeDir = fileSupport .newFile(workbenchConfig.getPropertyString(CoreConfiguration.CONFIGURATION_INTERACTIVESPACES_HOME)); return fileSupport.newFile(homeDir, controllerPath); } /** * Add all extension classpath entries that the controller specifies. * * @param files * the list of files to add to. */ private void addControllerExtensionsClasspath(List<File> files) { File[] extensionFiles = fileSupport.newFile( fileSupport.newFile(getControllerDirectory(), ContainerFilesystemLayout.FOLDER_DEFAULT_CONFIG), ContainerFilesystemLayout.FOLDER_CONFIG_ENVIRONMENT).listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(EXTENSION_FILE_EXTENSION); } }); if (extensionFiles != null) { for (File extensionFile : extensionFiles) { processExtensionFile(files, extensionFile, getControllerDirectory()); } } } /** * Add all extension classpath entries that the controller specifies. * * @param classpath * the list of files to add to * @param extraComponent * the extra component to add */ public void addExtrasControllerExtensionsClasspath(List<File> classpath, String extraComponent) { File[] extraComponentFiles = new File( new File(workbench.getWorkbenchFileSystem().getInstallDirectory(), EXTRAS_BASE_FOLDER), extraComponent).listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(FILENAME_JAR_EXTENSION); } }); if (extraComponentFiles != null) { Collections.addAll(classpath, extraComponentFiles); } } /** * process an extension file. * * @param files * the collection of jars described in the extension files * @param extensionFile * the extension file to process * @param controllerBaseDir * base directory of the controller */ private void processExtensionFile(List<File> files, File extensionFile, File controllerBaseDir) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(extensionFile)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (!line.isEmpty()) { int pos = line.indexOf(EXTENSION_FILE_PATH_KEYWORD); if (pos == 0 && line.length() > EXTENSION_FILE_PATH_KEYWORD_LENGTH) { String classpathAddition = line.substring(EXTENSION_FILE_PATH_KEYWORD_LENGTH).trim(); // Want to be able to have files relative to the controller File classpathFile = fileSupport.newFile(classpathAddition); if (!classpathFile.isAbsolute()) { classpathFile = fileSupport.newFile(controllerBaseDir, classpathAddition); } files.add(classpathFile); } } } } catch (Exception e) { handleError("Error while creating project", e); } finally { Closeables.closeQuietly(reader); } } /** * Scan the project path. */ public void scanProjectPath() { if (!projectPathScanned) { ProjectManager projectManager = workbench.getProjectManager(); List<String> projectPaths = workbenchConfig.getPropertyStringList( CONFIGURATION_INTERACTIVESPACES_WORKBENCH_PROJECT_PATH, File.pathSeparator); if (projectPaths != null) { for (String projectPath : projectPaths) { File projectPathBaseDir = fileSupport.newFile(projectPath); processProjectPathDirectory(projectPathBaseDir, projectManager); } } projectPathScanned = true; } } /** * Process a directory as part of the project path. * * @param projectPathBaseDir * the base directory to be scanned * @param projectManager * the project manager */ private void processProjectPathDirectory(File projectPathBaseDir, ProjectManager projectManager) { if (projectManager.isProjectFolder(projectPathBaseDir)) { processProjectPathProjectDirectory(projectPathBaseDir, projectManager); } else { processProjectPathSubDirectories(projectPathBaseDir, projectManager); } } /** * Process a project directory from the project path. * * @param projectDir * the project directory * @param projectManager * the project manager */ private void processProjectPathProjectDirectory(File projectDir, ProjectManager projectManager) { Project project = projectManager.readProject(projectDir, workbench.getLog()); addProjectToProjectPath(project); } /** * Add a project to the project path. * * @param project * the project to add to the project path */ public void addProjectToProjectPath(Project project) { String identifyingName = project.getIdentifyingName(); Version version = project.getVersion(); projectsPath.addResource(identifyingName, version, new NamedVersionedResourceWithData<Project>(identifyingName, version, project)); } /** * Recursively process a directory for any project directories contained within. * * @param projectPathBaseDir * the base directory to be scanned for subdirectories * @param projectManager * the project manager to use */ private void processProjectPathSubDirectories(File projectPathBaseDir, ProjectManager projectManager) { File[] subdirectories = projectPathBaseDir.listFiles(DIRECTORY_FILE_FILTER); if (subdirectories != null) { for (File subdirectory : subdirectories) { processProjectPathDirectory(subdirectory, projectManager); } } } /** * Get a project dependency from the project path. * * @param projectDependency * the project dependency * * @return the project, or {@code null} if no projects satisfies the request */ public Project getDynamicProjectFromProjectPath(ProjectDependency projectDependency) { return getDynamicProjectFromProjectPath(projectDependency.getIdentifyingName(), projectDependency.getVersion()); } /** * Get a project from the project path. * * @param identifyingName * identifying name of the project * @param versionRange * version range of the project * * @return the project, or {@code null} if no projects satisfies the request */ public Project getDynamicProjectFromProjectPath(String identifyingName, VersionRange versionRange) { // TODO(keith): Consider moving all project repository scanning functionality into its own object that can be shared // between tasks. scanProjectPath(); NamedVersionedResourceWithData<Project> project = projectsPath.getResource(identifyingName, versionRange); if (project != null) { return project.getData(); } else { return null; } } /** * An interruption of workbench tasks. * * @author Keith M. Hughes */ public static class InteractiveSpacesWorkbenchTaskInterruptionException extends InteractiveSpacesException { /** * Construct a new exception. */ public InteractiveSpacesWorkbenchTaskInterruptionException() { super("Task execution interruption"); } } }