/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.integration.internal; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import javax.imageio.ImageIO; import javax.imageio.stream.FileImageInputStream; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.osgi.framework.BundleContext; import de.rcenvironment.core.communication.api.PlatformService; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.integration.RemoteToolIntegrationService; import de.rcenvironment.core.component.integration.ToolIntegrationConstants; import de.rcenvironment.core.component.integration.ToolIntegrationContext; import de.rcenvironment.core.component.integration.ToolIntegrationContextRegistry; import de.rcenvironment.core.component.integration.ToolIntegrationService; import de.rcenvironment.core.component.model.api.ComponentInstallation; import de.rcenvironment.core.component.model.api.ComponentInstallationBuilder; import de.rcenvironment.core.component.model.api.ComponentInterface; import de.rcenvironment.core.component.model.api.ComponentInterfaceBuilder; import de.rcenvironment.core.component.model.api.ComponentRevisionBuilder; import de.rcenvironment.core.component.model.configuration.api.ComponentConfigurationModelFactory; import de.rcenvironment.core.component.model.configuration.api.ConfigurationDefinition; import de.rcenvironment.core.component.model.configuration.api.ConfigurationDefinitionConstants; import de.rcenvironment.core.component.model.configuration.api.ConfigurationExtensionDefinition; import de.rcenvironment.core.component.model.endpoint.api.ComponentEndpointModelFactory; import de.rcenvironment.core.component.model.endpoint.api.EndpointDefinition; import de.rcenvironment.core.component.model.endpoint.api.EndpointDefinitionConstants; import de.rcenvironment.core.component.model.endpoint.api.EndpointDefinitionsProvider; import de.rcenvironment.core.component.model.endpoint.api.EndpointMetaDataConstants.Visibility; import de.rcenvironment.core.component.registration.api.ComponentRegistry; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.datamodel.api.EndpointType; import de.rcenvironment.core.utils.common.CompressingHelper; import de.rcenvironment.core.utils.common.CrossPlatformFilenameUtils; import de.rcenvironment.core.utils.common.JsonUtils; import de.rcenvironment.core.utils.common.ServiceUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.TempFileServiceAccess; import de.rcenvironment.core.utils.common.rpc.RemoteOperationException; import de.rcenvironment.core.utils.common.security.AllowRemoteAccess; import de.rcenvironment.core.utils.incubator.ImageResize; import de.rcenvironment.core.utils.incubator.ServiceRegistry; import de.rcenvironment.core.utils.incubator.ServiceRegistryAccess; /** * Implementation of {@link ToolIntegrationService}. * * @author Sascha Zur */ public class ToolIntegrationServiceImpl implements ToolIntegrationService, RemoteToolIntegrationService { private static final String IDENTIFIER = "identifier"; private static final String META_DATA = "metaData"; private static final String NAME = "name"; private static final String DATA_TYPES = "dataTypes"; private static final String DEFAULT_DATA_TYPE = "defaultDataType"; private static final String INPUT_HANDLINGS = "inputHandlingOptions"; private static final String DEFAULT_INPUT_HANDLING = "defaultInputHandling"; private static final String INPUT_EXECUTION_CONSTRAINTS = "inputExecutionConstraintOptions"; private static final String DEFAULT_INPUT_EXECUTION_CONSTRAINT = "defaultInputExecutionConstraint"; private static final String STRING_0 = "0"; private static final String DEFAULT_VALUE = "defaultValue"; private static final String POSSIBLE_VALUES = "possibleValues"; private static final int ICONSIZE32 = 32; private static final int ICONSIZE16 = 16; private static final String COULD_NOT_READ_TOOL_CONFIGURATION = "Could not read tool configuration: "; private static final String ERROR_WRITING_TOOL_INTEGRATION_CONFIG_FILE = "Error writing tool integration config file: "; private static final BigInteger DOCU_DIRECTORY_MAXIMUM_SIZE = new BigInteger("52428800"); // max. // 50 // mb. private static final Log LOGGER = LogFactory.getLog(ToolIntegrationServiceImpl.class); private static Map<String, String> toolNameToPath = Collections.synchronizedMap(new HashMap<String, String>()); private static ComponentRegistry registry; private final Map<String, Map<String, Object>> integratedConfiguration = Collections .synchronizedMap(new HashMap<String, Map<String, Object>>()); private Set<String> publishedComponents = Collections.synchronizedSet(new HashSet<String>()); private PlatformService platformService; private final ObjectMapper mapper = JsonUtils.getDefaultObjectMapper(); private ToolIntegrationFileWatcherManager watchManager; public ToolIntegrationServiceImpl() { this.watchManager = new ToolIntegrationFileWatcherManager(this); } @Override public void integrateTool(Map<String, Object> configurationMap, ToolIntegrationContext context) { integrateTool(configurationMap, context, true); } @SuppressWarnings("unchecked") @Override public void integrateTool(Map<String, Object> configurationMap, ToolIntegrationContext context, boolean savePublished) { byte[] icon16 = readIcons(ICONSIZE16, configurationMap, context); byte[] icon32 = readIcons(ICONSIZE32, configurationMap, context); String docuHash = createDocumentationHash(configurationMap, context); String toolComponentID = context.getPrefixForComponentId() + (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME); String toolClassName = context.getImplementingComponentClassName(); EndpointDefinitionsProvider inputProvider; EndpointDefinitionsProvider outputProvider; boolean isPublished = false; readPublishedComponents(context); if ((publishedComponents.contains(configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)) || publishedComponents .contains(context.getRootPathToToolIntegrationDirectory() + File.separator + context.getNameOfToolIntegrationDirectory() + File.separator + (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)))) { isPublished = true; } ConfigurationDefinition configuration; try { Set<EndpointDefinition> inputs = createInputs(configurationMap); inputProvider = ComponentEndpointModelFactory.createEndpointDefinitionsProvider(inputs); Set<EndpointDefinition> outputs = createOutputs(configurationMap); outputProvider = ComponentEndpointModelFactory.createEndpointDefinitionsProvider(outputs); configuration = generateConfiguration(configurationMap); } catch (IllegalArgumentException e) { LOGGER.warn("Could not read endpoints from " + toolComponentID + ": ", e); inputProvider = ComponentEndpointModelFactory.createEndpointDefinitionsProvider(new HashSet<EndpointDefinition>()); outputProvider = ComponentEndpointModelFactory.createEndpointDefinitionsProvider(new HashSet<EndpointDefinition>()); configuration = ComponentConfigurationModelFactory.createConfigurationDefinition(new LinkedList<>(), new LinkedList<>(), new LinkedList<>(), new HashMap<String, String>()); } String groupName = (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_GROUPNAME); if (groupName == null || groupName.isEmpty()) { groupName = context.getComponentGroupId(); } List<String> supportedIds = new LinkedList<>(); supportedIds.add(toolComponentID); supportedIds.add(ToolIntegrationConstants.COMPONENT_IDS[1] + "_" + (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)); ComponentInterface componentInterface = new ComponentInterfaceBuilder() .setIdentifier(toolComponentID) .setIdentifiers(supportedIds) .setDisplayName((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)) .setGroupName(groupName) .setIcon16(icon16) .setIcon32(icon32) .setDocumentationHash(docuHash) .setVersion( ((Map<String, String>) ((List<Object>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0)) .get(ToolIntegrationConstants.KEY_VERSION)) .setInputDefinitionsProvider(inputProvider).setOutputDefinitionsProvider(outputProvider) .setConfigurationDefinition(configuration) .setConfigurationExtensionDefinitions(new HashSet<ConfigurationExtensionDefinition>()) .setColor(ComponentConstants.COMPONENT_COLOR_STANDARD) .setShape(ComponentConstants.COMPONENT_SHAPE_STANDARD) .setSize(ComponentConstants.COMPONENT_SIZE_STANDARD) .build(); String limitExecutionCount = ""; if ((((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0)) .get(ToolIntegrationConstants.KEY_LIMIT_INSTANCES) != null) { limitExecutionCount = (((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0)) .get(ToolIntegrationConstants.KEY_LIMIT_INSTANCES); } else { limitExecutionCount = (((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0)) .get(ToolIntegrationConstants.KEY_LIMIT_INSTANCES_OLD); } String maxParallelCountString = (((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0)) .get(ToolIntegrationConstants.KEY_LIMIT_INSTANCES_COUNT); Integer maxParallelCount = null; if (limitExecutionCount != null && Boolean.parseBoolean(limitExecutionCount) && maxParallelCountString != null && !maxParallelCountString.equals("")) { maxParallelCount = Integer.parseInt(maxParallelCountString); if (maxParallelCount < 1) { LOGGER.error(StringUtils.format( "A maximum count of parallel executions of %d is invalid, it must be >= 1; a maximum count of 1 is used instead", maxParallelCount)); maxParallelCount = 1; } } ComponentInstallation ci = new ComponentInstallationBuilder() .setComponentRevision( new ComponentRevisionBuilder() .setComponentInterface(componentInterface) .setClassName(toolClassName).build()) .setNodeId(platformService.getLocalDefaultLogicalNodeId()) .setInstallationId(componentInterface.getIdentifier()) .setIsPublished(isPublished) .setMaximumCountOfParallelInstances(maxParallelCount) .build(); if (configurationMap.get(ToolIntegrationConstants.IS_ACTIVE) == null || (Boolean) configurationMap.get(ToolIntegrationConstants.IS_ACTIVE)) { registry.addComponent(ci); } synchronized (integratedConfiguration) { integratedConfiguration.put(toolComponentID, configurationMap); } LOGGER.debug("ToolIntegration: Registered new Component " + toolComponentID); } private String createDocumentationHash(Map<String, Object> configurationMap, ToolIntegrationContext context) { File toolDir = createToolDirFile(configurationMap, context); File docDir = new File(toolDir, ToolIntegrationConstants.DOCS_DIR_NAME); if (!docDir.exists()) { docDir.mkdirs(); } if (docDir.listFiles() != null && docDir.listFiles().length > 0) { if (docDir.exists() && validateDocumentationDirectory(docDir)) { try { byte[] zippedByteArray = CompressingHelper.createZippedByteArrayFromFolder(docDir); return DigestUtils.md5Hex(zippedByteArray); } catch (IOException e) { LOGGER.error("Could not create hash for documentation: ", e); } } } return ""; } private boolean validateDocumentationDirectory(File docDir) { boolean valid = true; BigInteger directorySize = FileUtils.sizeOfDirectoryAsBigInteger(docDir); if (DOCU_DIRECTORY_MAXIMUM_SIZE.compareTo(directorySize) < 0) { LOGGER.error(StringUtils.format("Size of documentation directory %s too big (max. 50 Mb).", docDir.getAbsolutePath())); valid = false; } for (File f : docDir.listFiles()) { if (f.isDirectory()) { LOGGER.error(StringUtils.format("Directories not allowed in documentation directory %s.", docDir.getAbsolutePath())); valid = false; } else if (!ArrayUtils.contains( ToolIntegrationConstants.VALID_DOCUMENTATION_EXTENSIONS, FilenameUtils.getExtension(f.getName()))) { // ignore .nfs files since they are an delete artifact if (CrossPlatformFilenameUtils.isNFSFile(f.getName())) { continue; } LOGGER.error( StringUtils.format("Invalid filetype of %s in documentation directory %s. (Valid filetypes: %s)", f.getName(), docDir.getAbsolutePath(), Arrays.toString(ToolIntegrationConstants.VALID_DOCUMENTATION_EXTENSIONS).replaceAll("\\[", "").replaceAll("\\]", ""))); valid = false; } } return valid; } private File createToolDirFile(Map<String, Object> configurationMap, ToolIntegrationContext context) { return new File(new File(context.getRootPathToToolIntegrationDirectory(), context.getNameOfToolIntegrationDirectory()), (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)); } private byte[] readIcons(int size, Map<String, Object> configurationMap, ToolIntegrationContext context) { byte[] iconArray = null; iconArray = readDefaultToolIcon(size); String iconPath = (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH); if (iconPath != null && !iconPath.isEmpty()) { if (!new File(iconPath).isAbsolute()) { iconPath = context.getRootPathToToolIntegrationDirectory() + File.separator + context.getNameOfToolIntegrationDirectory() + File.separator + context.getToolDirectoryPrefix() + configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME) + File.separator + iconPath; } File icon = new File(iconPath); if (icon.exists() && icon.isFile()) { try { final File iconsource = icon; final File icontarget = TempFileServiceAccess.getInstance().createTempFileFromPattern("icon_" + size + "*.png"); Image image = ImageIO.read(icon); if (image == null) { iconArray = readDefaultToolIcon(size); } else { if (iconsource.exists()) { try (FileImageInputStream imageInputStream = new FileImageInputStream(iconsource)) { BufferedImage bi = ImageResize.resize(ImageIO.read(imageInputStream), size); if (bi != null && icontarget != null) { ImageIO.write(bi, "PNG", icontarget); iconArray = FileUtils.readFileToByteArray(icontarget); TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile(icontarget); } } } } } catch (IOException e) { LOGGER.debug("Could not load icon, use default icon"); } } } return iconArray; } private byte[] readDefaultToolIcon(int iconSize) { try (InputStream inputStream = ToolIntegrationServiceImpl.class.getResourceAsStream("/resources/icons/tool" + iconSize + ".png")) { return IOUtils.toByteArray(inputStream); } catch (FileNotFoundException e) { return null; } catch (IOException e) { return null; } } @SuppressWarnings("unchecked") @Override public void removeTool(String componentID, ToolIntegrationContext context) { String toolComponentID = componentID; if (!componentID.startsWith(context.getPrefixForComponentId())) { toolComponentID = context.getPrefixForComponentId() + componentID; } if (integratedConfiguration.containsKey(toolComponentID)) { String toolIDAndVersion = toolComponentID + ComponentConstants.ID_SEPARATOR + ((List<Map<String, String>>) integratedConfiguration .get(toolComponentID).get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0) .get(ToolIntegrationConstants.KEY_VERSION); synchronized (integratedConfiguration) { integratedConfiguration.remove(toolComponentID); } registry.removeComponent(toolIDAndVersion); } LOGGER.debug("ToolIntegration: Removed Component " + toolComponentID); } private ConfigurationDefinition generateConfiguration(Map<String, Object> configurationMap) { List<Object> configuration = new LinkedList<>(); List<Object> configurationMetadata = new LinkedList<>(); readConfigurationWithMetaDataToLists(configurationMap, configuration, configurationMetadata); Map<String, String> readOnlyConfiguration = createReadOnlyConfiguration(configurationMap); return ComponentConfigurationModelFactory.createConfigurationDefinition(configuration, new LinkedList<>(), configurationMetadata, readOnlyConfiguration); } @SuppressWarnings("unchecked") private void readConfigurationWithMetaDataToLists(Map<String, Object> configurationMap, List<Object> configuration, List<Object> configurationMetadata) { Map<String, Object> properties = (Map<String, Object>) configurationMap.get(ToolIntegrationConstants.KEY_PROPERTIES); if (properties != null) { for (String groupKey : properties.keySet()) { Map<String, Object> group = (Map<String, Object>) properties.get(groupKey); String configFileName = null; if (group.get(ToolIntegrationConstants.KEY_PROPERTY_CREATE_CONFIG_FILE) != null && (Boolean) group.get(ToolIntegrationConstants.KEY_PROPERTY_CREATE_CONFIG_FILE)) { configFileName = (String) group.get(ToolIntegrationConstants.KEY_PROPERTY_CONFIG_FILENAME); } for (String propertyOrConfigfile : group.keySet()) { int i = 0; if (!(group.get(propertyOrConfigfile) instanceof String || group.get(propertyOrConfigfile) instanceof Boolean)) { Map<String, String> property = (Map<String, String>) group.get(propertyOrConfigfile); Map<String, String> config = new HashMap<>(); config.put(ConfigurationDefinitionConstants.KEY_CONFIGURATION_KEY, property.get(ToolIntegrationConstants.KEY_PROPERTY_KEY)); config.put(ComponentConstants.KEY_DEFAULT_VALUE, property.get(ToolIntegrationConstants.KEY_PROPERTY_DEFAULT_VALUE)); configuration.add(config); Map<String, String> configMetadata = new HashMap<>(); configMetadata.put(ConfigurationDefinitionConstants.KEY_METADATA_GUI_NAME, property.get(ToolIntegrationConstants.KEY_PROPERTY_DISPLAYNAME)); if (configFileName != null) { configMetadata.put(ToolIntegrationConstants.KEY_PROPERTY_CONFIG_FILENAME, configFileName); } configMetadata.put(ConfigurationDefinitionConstants.KEY_METADATA_GUI_GROUP_NAME, groupKey); configMetadata.put(ConfigurationDefinitionConstants.KEY_METADATA_GUI_POSITION, "" + i++); configMetadata.put(ConfigurationDefinitionConstants.KEY_METADATA_CONFIG_KEY, property.get(ToolIntegrationConstants.KEY_PROPERTY_KEY)); configurationMetadata.add(configMetadata); } } } } Map<String, String> historyConfig = new HashMap<>(); historyConfig.put(ConfigurationDefinitionConstants.KEY_CONFIGURATION_KEY, ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM); historyConfig.put(ComponentConstants.KEY_DEFAULT_VALUE, "" + false); configuration.add(historyConfig); } @SuppressWarnings("unchecked") private Map<String, String> createReadOnlyConfiguration(Map<String, Object> configurationMap) { Map<String, String> configuration = new HashMap<>(); for (String key : configurationMap.keySet()) { if (configurationMap.get(key) instanceof String) { configuration.put(key, (String) configurationMap.get(key)); } if (configurationMap.get(key) instanceof Boolean) { configuration.put(key, ((Boolean) configurationMap.get(key)).toString()); } } configuration.put(ToolIntegrationConstants.KEY_ROOT_WORKING_DIRECTORY, ((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0).get( ToolIntegrationConstants.KEY_ROOT_WORKING_DIRECTORY)); configuration.put(ToolIntegrationConstants.KEY_TOOL_DIRECTORY, ((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0).get( ToolIntegrationConstants.KEY_TOOL_DIRECTORY)); return configuration; } @SuppressWarnings("unchecked") private Set<EndpointDefinition> createOutputs(Map<String, Object> configurationMap) { List<Map<String, String>> definedOutputs = (List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_ENDPOINT_OUTPUTS); Set<EndpointDefinition> outputs = new HashSet<>(); if (definedOutputs != null) { for (Map<String, String> output : definedOutputs) { Map<String, Object> description = new HashMap<>(); description.put(NAME, output.get(ToolIntegrationConstants.KEY_ENDPOINT_NAME)); description.put(DEFAULT_DATA_TYPE, output.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE)); List<String> dataTypes = new LinkedList<>(); dataTypes.add(output.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE)); description.put(DATA_TYPES, dataTypes); outputs.add(ComponentEndpointModelFactory.createEndpointDefinition(description, EndpointType.OUTPUT)); } } List<Map<String, Object>> dynamicOutputs = (List<Map<String, Object>>) configurationMap.get(ToolIntegrationConstants.KEY_ENDPOINT_DYNAMIC_OUTPUTS); if (dynamicOutputs != null) { for (Map<String, Object> output : dynamicOutputs) { Map<String, Object> description = new HashMap<>(); description.put(IDENTIFIER, output.get(ToolIntegrationConstants.KEY_ENDPOINT_IDENTIFIER)); description.put(DEFAULT_DATA_TYPE, output.get(ToolIntegrationConstants.KEY_ENDPOINT_DEFAULT_TYPE)); List<String> dataTypes = new LinkedList<>(); dataTypes.addAll((List<String>) output.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPES)); description.put(DATA_TYPES, dataTypes); Map<String, Object> metadata = (Map<String, Object>) output.get(ToolIntegrationConstants.KEY_ENDPOINT_METADATA); description.put(META_DATA, metadata); outputs.add(ComponentEndpointModelFactory.createEndpointDefinition(description, EndpointType.OUTPUT)); } } return outputs; } @SuppressWarnings("unchecked") private Set<EndpointDefinition> createInputs(Map<String, Object> configurationMap) { List<Map<String, String>> definedInputs = (List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_ENDPOINT_INPUTS); Set<EndpointDefinition> inputs = new HashSet<>(); if (definedInputs != null) { for (Map<String, String> input : definedInputs) { Map<String, Object> description = new HashMap<>(); description.put(EndpointDefinitionConstants.KEY_NAME, input.get(ToolIntegrationConstants.KEY_ENDPOINT_NAME)); description.put(DEFAULT_DATA_TYPE, input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE)); List<String> dataTypes = new LinkedList<>(); dataTypes.add(input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE)); description.put(DATA_TYPES, dataTypes); description.put(DEFAULT_DATA_TYPE, input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE)); // migration code: usage (required, initial, optional) -> constant, single String[] inputHandlings; if (input.containsKey(ToolIntegrationConstants.KEY_INPUT_HANDLING)) { inputHandlings = StringUtils.splitAndUnescape(input.get(ToolIntegrationConstants.KEY_INPUT_HANDLING)); if (input.containsKey(ToolIntegrationConstants.KEY_DEFAULT_INPUT_HANDLING)) { description.put(DEFAULT_INPUT_HANDLING, input.get(ToolIntegrationConstants.KEY_DEFAULT_INPUT_HANDLING)); } else { description.put(DEFAULT_INPUT_HANDLING, inputHandlings[0]); } } else { inputHandlings = new String[] { EndpointDefinition.InputDatumHandling.Single.name() }; if (input.get(ToolIntegrationConstants.KEY_ENDPOINT_USAGE).equals("initial")) { inputHandlings = new String[] { EndpointDefinition.InputDatumHandling.Constant.name() }; } description.put(DEFAULT_INPUT_HANDLING, inputHandlings[0]); } description.put(INPUT_HANDLINGS, Arrays.asList(inputHandlings)); // migration code: usage (required, initial, optional) -> required, not required String[] inputExecutionConstraints; if (input.containsKey(ToolIntegrationConstants.KEY_INPUT_EXECUTION_CONSTRAINT)) { inputExecutionConstraints = StringUtils.splitAndUnescape(input .get(ToolIntegrationConstants.KEY_INPUT_EXECUTION_CONSTRAINT)); if (input.containsKey(ToolIntegrationConstants.KEY_DEFAULT_INPUT_EXECUTION_CONSTRAINT)) { description.put(DEFAULT_INPUT_EXECUTION_CONSTRAINT, input.get(ToolIntegrationConstants.KEY_DEFAULT_INPUT_EXECUTION_CONSTRAINT)); } else { description.put(DEFAULT_INPUT_EXECUTION_CONSTRAINT, inputExecutionConstraints[0]); } } else { inputExecutionConstraints = new String[] { EndpointDefinition.InputExecutionContraint.Required.name() }; if (input.get(ToolIntegrationConstants.KEY_ENDPOINT_USAGE).equals("optional")) { inputExecutionConstraints = new String[] { EndpointDefinition.InputExecutionContraint.NotRequired.name() }; } description.put(DEFAULT_INPUT_EXECUTION_CONSTRAINT, inputExecutionConstraints[0]); } description.put(INPUT_EXECUTION_CONSTRAINTS, Arrays.asList(inputExecutionConstraints)); Map<String, Map<String, Object>> metadata = new HashMap<>(); if ((input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE).equals(DataType.FileReference.name()) || input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPE).equals(DataType.DirectoryReference.name())) && input.get(ToolIntegrationConstants.KEY_ENDPOINT_FILENAME) != null) { Map<String, Object> metadataFilename = new HashMap<>(); metadataFilename.put(EndpointDefinitionConstants.KEY_GUI_NAME, "Filename"); metadataFilename.put(EndpointDefinitionConstants.KEY_GUI_POSITION, STRING_0); metadataFilename.put(EndpointDefinitionConstants.KEY_GUIGROUP, "File"); List<String> possibleValuesListFilename = new LinkedList<>(); possibleValuesListFilename.add(input.get(ToolIntegrationConstants.KEY_ENDPOINT_FILENAME)); metadataFilename.put(POSSIBLE_VALUES, possibleValuesListFilename); metadataFilename.put(DEFAULT_VALUE, input.get(ToolIntegrationConstants.KEY_ENDPOINT_FILENAME)); metadataFilename.put(EndpointDefinitionConstants.KEY_VISIBILITY, Visibility.developerConfigurable.toString()); metadata.put(ToolIntegrationConstants.KEY_ENDPOINT_FILENAME, metadataFilename); } description.put(META_DATA, metadata); inputs.add(ComponentEndpointModelFactory.createEndpointDefinition(description, EndpointType.INPUT)); } } if (configurationMap.containsKey(ToolIntegrationConstants.KEY_ENDPOINT_DYNAMIC_INPUTS)) { List<Map<String, Object>> dynamicInputs = (List<Map<String, Object>>) configurationMap.get(ToolIntegrationConstants.KEY_ENDPOINT_DYNAMIC_INPUTS); for (Map<String, Object> input : dynamicInputs) { Map<String, Object> description = new HashMap<>(); description.put(IDENTIFIER, input.get(ToolIntegrationConstants.KEY_ENDPOINT_IDENTIFIER)); description.put(DEFAULT_DATA_TYPE, input.get(ToolIntegrationConstants.KEY_ENDPOINT_DEFAULT_TYPE)); List<String> dataTypes = new LinkedList<>(); dataTypes.addAll((List<String>) input.get(ToolIntegrationConstants.KEY_ENDPOINT_DATA_TYPES)); description.put(DATA_TYPES, dataTypes); Map<String, Object> metadata = (Map<String, Object>) input.get(ToolIntegrationConstants.KEY_ENDPOINT_METADATA); description.put(META_DATA, metadata); // migration code: usage (required, initial, optional) -> consuming vs. immutable // and required vs. required if connected if (metadata.containsKey("usage")) { description.put(DEFAULT_INPUT_HANDLING, EndpointDefinition.InputDatumHandling.Single.name()); List<String> inputHandlingOptions = new LinkedList<>(); inputHandlingOptions.add(EndpointDefinition.InputDatumHandling.Single.name()); inputHandlingOptions.add(EndpointDefinition.InputDatumHandling.Constant.name()); description.put(INPUT_HANDLINGS, inputHandlingOptions); description.put(DEFAULT_INPUT_EXECUTION_CONSTRAINT, EndpointDefinition.InputExecutionContraint.Required.name()); List<String> inputinputExecutionConstraintOptions = new LinkedList<>(); inputinputExecutionConstraintOptions.add(EndpointDefinition.InputExecutionContraint.Required.name()); inputinputExecutionConstraintOptions.add(EndpointDefinition.InputExecutionContraint.RequiredIfConnected.name()); description.put(INPUT_EXECUTION_CONSTRAINTS, inputinputExecutionConstraintOptions); metadata.remove("usage"); } else { if (input.containsKey(ToolIntegrationConstants.KEY_INPUT_HANDLING_OPTIONS)) { description.put(DEFAULT_INPUT_HANDLING, input.get(ToolIntegrationConstants.KEY_DEFAULT_INPUT_HANDLING)); List<String> inputHandlingOptions = new LinkedList<>(); inputHandlingOptions.addAll((List<String>) input.get(ToolIntegrationConstants.KEY_INPUT_HANDLING_OPTIONS)); description.put(INPUT_HANDLINGS, inputHandlingOptions); } if (input.containsKey(ToolIntegrationConstants.KEY_INPUT_EXECUTION_CONSTRAINT_OPTIONS)) { description.put(DEFAULT_INPUT_EXECUTION_CONSTRAINT, input.get(ToolIntegrationConstants.KEY_DEFAULT_INPUT_EXECUTION_CONSTRAINT)); List<String> inputinputExecutionConstraintOptions = new LinkedList<>(); inputinputExecutionConstraintOptions.addAll((List<String>) input .get(ToolIntegrationConstants.KEY_INPUT_EXECUTION_CONSTRAINT_OPTIONS)); description.put(INPUT_EXECUTION_CONSTRAINTS, inputinputExecutionConstraintOptions); } } inputs.add(ComponentEndpointModelFactory.createEndpointDefinition(description, EndpointType.INPUT)); } } return inputs; } @Override public void readAndIntegratePersistentTools(ToolIntegrationContext context) { String configFolder = context.getRootPathToToolIntegrationDirectory(); File toolIntegrationFile = new File(configFolder, context.getNameOfToolIntegrationDirectory()); readPublishedComponents(context); if (toolIntegrationFile.exists() && toolIntegrationFile.isDirectory() && toolIntegrationFile.listFiles().length > 0) { LOGGER.debug("Reading integration tool directory :" + toolIntegrationFile.getAbsolutePath()); for (File toolFolder : toolIntegrationFile.listFiles()) { if (toolFolder.isDirectory() && !toolFolder.getName().equals("null")) { readToolDirectory(toolFolder, context); } } } watchManager.createWatcherForToolRootDirectory(context); updatePublishedComponents(context); } @Override @SuppressWarnings("unchecked") public void readToolDirectory(File toolFolder, ToolIntegrationContext information) { File configFile = new File(toolFolder, information.getConfigurationFilename()); if (configFile.exists() && configFile.isFile()) { try { Map<String, Object> configurationMap = mapper.readValue(configFile, new HashMap<String, Object>().getClass()); if (!integratedConfiguration.containsKey(information.getPrefixForComponentId() + configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME))) { toolNameToPath.put((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME), toolFolder.getAbsolutePath()); checkIcon(toolFolder, configurationMap); if (!isToolIntegrated(configurationMap, information)) { integrateTool(configurationMap, information); } } else { LOGGER.warn("Tool with foldername already exists: " + toolFolder.getName()); } } catch (JsonParseException e) { LOGGER.error(COULD_NOT_READ_TOOL_CONFIGURATION, e); } catch (JsonMappingException e) { LOGGER.error(COULD_NOT_READ_TOOL_CONFIGURATION, e); } catch (IOException e) { LOGGER.error(COULD_NOT_READ_TOOL_CONFIGURATION, e); } } } private void checkIcon(File toolFolder, Map<String, Object> configurationMap) { if (configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH) != null) { File icon = new File((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH)); if (!icon.isAbsolute()) { icon = new File(toolFolder, icon.getName()); } } } @Override public void writeToolIntegrationFileToSpecifiedFolder(String folder, Map<String, Object> configurationMap, ToolIntegrationContext information) throws IOException { if (!configurationMap.containsKey(ToolIntegrationConstants.KEY_TOOL_INTEGRATION_VERSION)) { configurationMap.put(ToolIntegrationConstants.KEY_TOOL_INTEGRATION_VERSION, ToolIntegrationConstants.CURRENT_TOOLINTEGRATION_VERSION); } // TODO : Code for removing deprecated key; should be removed in the future 8/3/16 zur_sa @SuppressWarnings("unchecked") Map<String, String> launchSettings = ((List<Map<String, String>>) configurationMap.get(ToolIntegrationConstants.KEY_LAUNCH_SETTINGS)).get(0); String value = launchSettings.remove(ToolIntegrationConstants.KEY_LIMIT_INSTANCES_OLD); if (!launchSettings.containsKey(ToolIntegrationConstants.KEY_LIMIT_INSTANCES) && value != null) { launchSettings.put(ToolIntegrationConstants.KEY_LIMIT_INSTANCES, value); } File toolConfigFile = new File(folder, information.getNameOfToolIntegrationDirectory() + File.separator + information.getToolDirectoryPrefix() + configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME)); toolConfigFile.mkdirs(); handleToolIcon(configurationMap, toolConfigFile); handleDoc(configurationMap, toolConfigFile); configurationMap.remove(ToolIntegrationConstants.TEMP_KEY_PUBLISH_COMPONENT); Map<String, Object> sortedMap = new TreeMap<>(); sortedMap.putAll(configurationMap); mapper.writerWithDefaultPrettyPrinter().writeValue(new File(toolConfigFile, information.getConfigurationFilename()), sortedMap); toolNameToPath.put((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME), toolConfigFile.getAbsolutePath()); } private void handleDoc(Map<String, Object> configurationMap, File toolConfigFile) { if ((String) configurationMap.get(ToolIntegrationConstants.KEY_DOC_FILE_PATH) != null && !((String) configurationMap.get(ToolIntegrationConstants.KEY_DOC_FILE_PATH)).isEmpty()) { File docfile = new File((String) configurationMap.get(ToolIntegrationConstants.KEY_DOC_FILE_PATH)); File docDir = new File(toolConfigFile, ToolIntegrationConstants.DOCS_DIR_NAME); if (docfile.isAbsolute() && docfile.exists() && docfile.isFile()) { if (docDir.exists() && docDir.listFiles().length > 0) { for (File f : docDir.listFiles()) { try { FileUtils.forceDelete(f); } catch (IOException e) { LOGGER.error("Could not delete old documentation file: " + f.getAbsolutePath() + ": " + e.getMessage()); } } } File destination = new File(docDir, docfile.getName()); if (!destination.getAbsolutePath().equals(docfile.getAbsolutePath())) { try { FileUtils.copyFile(docfile, destination); configurationMap.put(ToolIntegrationConstants.KEY_DOC_FILE_PATH, docfile.getName()); } catch (IOException e) { LOGGER.error("Could not copy documentation to tool directory: ", e); } } } } } private void handleToolIcon(Map<String, Object> configurationMap, File toolConfigFile) { if ((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH) != null && !((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH)).isEmpty()) { File icon = new File((String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_ICON_PATH)); if (configurationMap.get(ToolIntegrationConstants.KEY_UPLOAD_ICON) != null && (Boolean) configurationMap.get(ToolIntegrationConstants.KEY_UPLOAD_ICON)) { if (icon.exists() && icon.isFile() && icon.isAbsolute()) { File destination = new File(toolConfigFile, icon.getName()); if (!destination.getAbsolutePath().equals(icon.getAbsolutePath())) { try { FileUtils.copyFile(icon, destination); configurationMap.remove(ToolIntegrationConstants.KEY_UPLOAD_ICON); configurationMap.put(ToolIntegrationConstants.KEY_TOOL_ICON_PATH, icon.getName()); } catch (IOException e) { LOGGER.warn("Could not copy icon to tool directory: ", e); } } } } } } @Override public void writeToolIntegrationFile(Map<String, Object> configurationMap, ToolIntegrationContext information) throws IOException { String configFolder = information.getRootPathToToolIntegrationDirectory(); writeToolIntegrationFileToSpecifiedFolder(configFolder, configurationMap, information); } @Override public void savePublishedComponents(ToolIntegrationContext context) { File toolsfolder = new File(context.getRootPathToToolIntegrationDirectory(), context.getNameOfToolIntegrationDirectory()); if (!toolsfolder.exists()) { toolsfolder.mkdirs(); } try { File publishedComponentsFile = new File(toolsfolder, ToolIntegrationConstants.PUBLISHED_COMPONENTS_FILENAME); if (!publishedComponentsFile.exists()) { publishedComponentsFile.createNewFile(); } synchronized (publishedComponents) { if (publishedComponents != null) { Set<String> componentsToWrite = new HashSet<>(); for (String component : publishedComponents) { if (component.startsWith(toolsfolder.getAbsolutePath())) { componentsToWrite.add(component); } } FileUtils.writeLines(publishedComponentsFile, componentsToWrite); } } } catch (IOException e) { LOGGER.error(ERROR_WRITING_TOOL_INTEGRATION_CONFIG_FILE, e); } } private void readPublishedComponents(ToolIntegrationContext context) { File toolsfolder = new File(context.getRootPathToToolIntegrationDirectory(), context.getNameOfToolIntegrationDirectory()); if (publishedComponents == null) { publishedComponents = Collections.synchronizedSet(new HashSet<String>()); } if (toolsfolder.exists()) { try { File publishedComponentsFile = new File(toolsfolder, ToolIntegrationConstants.PUBLISHED_COMPONENTS_FILENAME); if (!publishedComponentsFile.exists()) { publishedComponentsFile.createNewFile(); publishedComponentsFile.setReadable(true); publishedComponentsFile.setWritable(true); } if (publishedComponentsFile.canWrite()) { Set<String> newPublishedComponents = new HashSet<>(FileUtils.readLines(publishedComponentsFile)); for (String newComp : newPublishedComponents) { String comp = newComp.trim(); if (!new File(newComp).isAbsolute()) { comp = new File(toolsfolder, comp).getAbsolutePath(); } if (!newComp.isEmpty() && new File(comp).exists()) { publishedComponents.add(comp); } } } } catch (IOException e) { LOGGER.error(ERROR_WRITING_TOOL_INTEGRATION_CONFIG_FILE, e); } } } @Override public synchronized Set<String> getPublishedComponents() { return publishedComponents; } @Override public synchronized Map<String, Object> getToolConfiguration(String toolId) { return integratedConfiguration.get(toolId); } @Override public synchronized Set<String> getIntegratedComponentIds() { return integratedConfiguration.keySet(); } @Override public synchronized Set<String> getActiveComponentIds() { Set<String> activeIds = new HashSet<>(); for (String key : integratedConfiguration.keySet()) { if (integratedConfiguration.get(key).get(ToolIntegrationConstants.IS_ACTIVE) == null || (Boolean) integratedConfiguration.get(key).get(ToolIntegrationConstants.IS_ACTIVE)) { activeIds.add(key); } } return activeIds; } /** * This method will be called when the bundle is started. * * @param context of the bundle */ public void activate(final BundleContext context) {} protected void bindComponentRegistry(ComponentRegistry newRegistry) { registry = newRegistry; } protected void unbindComponentRegistry(ComponentRegistry newRegistry) { registry = ServiceUtils.createFailingServiceProxy(ComponentRegistry.class); } protected void bindPlatformService(PlatformService newService) { platformService = newService; } protected void unbindPlatformService(PlatformService newService) { platformService = ServiceUtils.createFailingServiceProxy(PlatformService.class); } @Override public String getPathOfComponentID(String id, ToolIntegrationContext context) { if (id.startsWith(context.getPrefixForComponentId())) { return toolNameToPath.get(id.substring(context.getPrefixForComponentId().length())); } return toolNameToPath.get(id); } @Override public boolean isToolIntegrated(Map<String, Object> configurationMap, ToolIntegrationContext integrationContext) { String toolComponentID = integrationContext.getPrefixForComponentId() + (String) configurationMap.get(ToolIntegrationConstants.KEY_TOOL_NAME); return integratedConfiguration.keySet().contains(toolComponentID); } @Override public String getToolNameToPath(String path) { for (Entry<String, String> e : toolNameToPath.entrySet()) { if (e.getValue().trim().equals(path.trim())) { return e.getKey(); } } return null; } @Override public void putToolNameToPath(String toolName, File parentFile) { toolNameToPath.put(toolName, parentFile.getAbsolutePath()); } @Override public void updatePublishedComponents(ToolIntegrationContext context) { Set<String> oldPublishedComponents = new HashSet<>(); synchronized (publishedComponents) { oldPublishedComponents.addAll(publishedComponents); Set<String> toRemove = new HashSet<>(); for (String path : publishedComponents) { if (path.startsWith(context.getRootPathToToolIntegrationDirectory() + File.separator + context.getNameOfToolIntegrationDirectory())) { toRemove.add(path); } } publishedComponents.removeAll(toRemove); readPublishedComponents(context); Set<String> addPublished = new HashSet<>(); for (String newComp : publishedComponents) { if (!oldPublishedComponents.contains(newComp) && newComp.startsWith(context.getRootPathToToolIntegrationDirectory() + File.separator + context.getNameOfToolIntegrationDirectory())) { addPublished.add(newComp); } } Set<String> removePublished = new HashSet<>(); for (String oldComp : oldPublishedComponents) { if (!publishedComponents.contains(oldComp) && oldComp.startsWith(context.getRootPathToToolIntegrationDirectory() + File.separator + context.getNameOfToolIntegrationDirectory())) { removePublished.add(oldComp); } } for (String path : addPublished) { String toolComponentID = context.getPrefixForComponentId() + getToolNameToPath(path); Map<String, Object> configuration = integratedConfiguration.get(toolComponentID); removeTool(toolComponentID, context); if (configuration != null) { integrateTool(configuration, context, false); } } for (String path : removePublished) { String toolComponentID = context.getPrefixForComponentId() + getToolNameToPath(path); Map<String, Object> configuration = integratedConfiguration.get(toolComponentID); removeTool(toolComponentID, context); if (configuration != null) { integrateTool(configuration, context, false); } } } } @Override public synchronized void addPublishedTool(String toolPath) { publishedComponents.add(toolPath); } @Override public synchronized void unpublishTool(String toolPath) { publishedComponents.remove(toolPath); } @Override @AllowRemoteAccess public byte[] getToolDocumentation(String identifier) throws RemoteOperationException { ToolIntegrationContext context = getContextForIdentifier(identifier); if (context == null) { return null; } String name = identifier.substring(context.getPrefixForComponentId().length()); name = name.substring(0, name.indexOf(ComponentConstants.ID_SEPARATOR)); try { return CompressingHelper .createZippedByteArrayFromFolder(new File(toolNameToPath.get(name), ToolIntegrationConstants.DOCS_DIR_NAME)); } catch (IOException e) { LOGGER.error("Could not create zip file for documentation: ", e); } return null; } private ToolIntegrationContext getContextForIdentifier(String identifier) { ToolIntegrationContext result = null; ServiceRegistryAccess serviceRegistryAccess = ServiceRegistry.createAccessFor(this); for (ToolIntegrationContext context : serviceRegistryAccess.getService(ToolIntegrationContextRegistry.class) .getAllIntegrationContexts()) { if (identifier.startsWith(context.getPrefixForComponentId())) { result = context; } } return result; } @Override public void setFileWatcherActive(boolean value) { watchManager.setAllWatcherActivity(value); } @Override public void unregisterIntegration(String previousToolName, ToolIntegrationContext integrationContext) { watchManager.unregister(previousToolName, integrationContext); } @Override public void registerRecursive(String toolName, ToolIntegrationContext integrationContext) { watchManager.registerRecursive(toolName, integrationContext); } @Override public void deactivateIntegrationService() { watchManager.shutdown(); } }