/* * Copyright 2013-2015 the original author or authors. * * 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 org.springframework.xd.dirt.module; import java.io.IOException; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Properties; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.core.io.WritableResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.data.hadoop.configuration.ConfigurationFactoryBean; import org.springframework.data.hadoop.fs.HdfsResourceLoader; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import org.springframework.xd.dirt.core.RuntimeIOException; import org.springframework.xd.module.ModuleDefinition; import org.springframework.xd.module.ModuleType; /** * Writable extension of {@link ResourceModuleRegistry}. * * <p>Will generate MD5 hash files for written modules.</p> * * @author Eric Bottard * @author Janne Valkealahti */ public class WritableResourceModuleRegistry extends ResourceModuleRegistry implements WritableModuleRegistry, InitializingBean { final protected static byte[] HEX_DIGITS = "0123456789ABCDEF".getBytes(); final protected static String XD_CONFIG_HOME = "xd.config.home"; /** * Whether to attempt to create the directory structure at startup (disable for read-only implementations). */ private boolean createDirectoryStructure = true; private ConfigurableEnvironment environment; public WritableResourceModuleRegistry(String root) { super(root); setRequireHashFiles(true); } @Override public boolean delete(ModuleDefinition definition) { try { Resource archive = getResources(definition.getType().name(), definition.getName(), ARCHIVE_AS_FILE_EXTENSION).iterator().next(); if (archive instanceof WritableResource) { WritableResource writableResource = (WritableResource) archive; WritableResource hashResource = (WritableResource) hashResource(writableResource); // Delete hash first ExtendedResource.wrap(hashResource).delete(); return ExtendedResource.wrap(writableResource).delete(); } else { return false; } } catch (IOException e) { throw new RuntimeIOException("Exception while trying to delete module " + definition, e); } } @Override public boolean registerNew(ModuleDefinition definition) { if (!(definition instanceof UploadedModuleDefinition)) { return false; } UploadedModuleDefinition uploadedModuleDefinition = (UploadedModuleDefinition) definition; try { Resource archive = getResources(definition.getType().name(), definition.getName(), ARCHIVE_AS_FILE_EXTENSION).iterator().next(); if (archive instanceof WritableResource) { WritableResource writableResource = (WritableResource) archive; Assert.isTrue(!writableResource.exists(), "Could not install " + uploadedModuleDefinition + " at location " + writableResource + " as that file already exists"); MessageDigest md = MessageDigest.getInstance("MD5"); DigestInputStream dis = new DigestInputStream(uploadedModuleDefinition.getInputStream(), md); FileCopyUtils.copy(dis, writableResource.getOutputStream()); WritableResource hashResource = (WritableResource) hashResource(writableResource); // Write hash last FileCopyUtils.copy(bytesToHex(md.digest()), hashResource.getOutputStream()); return true; } else { return false; } } catch (IOException | NoSuchAlgorithmException e) { throw new RuntimeException("Error trying to save " + uploadedModuleDefinition, e); } } public void setEnvironment(Environment environment) { this.environment = (ConfigurableEnvironment) environment; } @Override public void afterPropertiesSet() throws Exception { if (root.startsWith("hdfs:")) { boolean securityConfigProvided = false; String authMethod = ""; String namenodePrincipal = ""; String rmManagerPrincipal = ""; String userPrincipal = ""; String userKeytab = ""; // try servers.yml first if (environment != null && environment.containsProperty("spring.hadoop.security.authMethod")) { securityConfigProvided = true; authMethod = environment.getProperty("spring.hadoop.security.authMethod"); namenodePrincipal = environment.getProperty("spring.hadoop.security.namenodePrincipal"); rmManagerPrincipal = environment.getProperty("spring.hadoop.security.rmManagerPrincipal"); userPrincipal = environment.getProperty("spring.hadoop.security.userPrincipal"); userKeytab = environment.getProperty("spring.hadoop.security.userKeytab"); } // check hadoop.properties and load it as // Properties to get additional configs Properties hadoopProps = null; if (!securityConfigProvided && environment != null) { String xdConfigHadoopProps = environment.getProperty(XD_CONFIG_HOME) + "hadoop.properties"; try { hadoopProps = PropertiesLoaderUtils.loadProperties(new UrlResource(xdConfigHadoopProps)); } catch (IOException ignore) { } if (hadoopProps != null && (hadoopProps.containsKey("spring.hadoop.security.authMethod") || hadoopProps.containsKey("hadoop.security.authentication"))) { securityConfigProvided = true; authMethod = hadoopProps.getProperty("spring.hadoop.security.authMethod"); if (!StringUtils.hasText(authMethod)) { authMethod = hadoopProps.getProperty("hadoop.security.authentication"); } namenodePrincipal = hadoopProps.getProperty("spring.hadoop.security.namenodePrincipal"); if (!StringUtils.hasText(namenodePrincipal)) { namenodePrincipal = hadoopProps.getProperty("dfs.namenode.kerberos.principal"); } rmManagerPrincipal = hadoopProps.getProperty("spring.hadoop.security.rmManagerPrincipal"); if (!StringUtils.hasText(rmManagerPrincipal)) { rmManagerPrincipal = hadoopProps.getProperty("yarn.resourcemanager.principal"); } userPrincipal = hadoopProps.getProperty("spring.hadoop.security.userPrincipal"); userKeytab = hadoopProps.getProperty("spring.hadoop.security.userKeytab"); } } // get 'spring.hadoop.config.' props from other // sources to support generic hadoop settings // via yml Properties factoryProps = new Properties(); if (environment != null) { Iterator<PropertySource<?>> i = environment.getPropertySources().iterator(); while (i.hasNext()) { PropertySource<?> p = i.next(); if (p instanceof EnumerablePropertySource) { for (String name : ((EnumerablePropertySource) p).getPropertyNames()) { if (name.startsWith("spring.hadoop.config.")) { // remove 'spring.hadoop.config.' prefix factoryProps.put(name.substring(21), environment.getProperty(name)); } } } } } ConfigurationFactoryBean configurationFactoryBean = new ConfigurationFactoryBean(); configurationFactoryBean.setRegisterUrlHandler(true); configurationFactoryBean.setFileSystemUri(root); if (hadoopProps != null) { // hadoop.properties will override factoryProps.putAll(hadoopProps); } configurationFactoryBean.setProperties(factoryProps); if (securityConfigProvided && "kerberos".equals(authMethod)) { configurationFactoryBean.setSecurityMethod(authMethod); configurationFactoryBean.setNamenodePrincipal(namenodePrincipal); configurationFactoryBean.setRmManagerPrincipal(rmManagerPrincipal); configurationFactoryBean.setUserPrincipal(userPrincipal); configurationFactoryBean.setUserKeytab(userKeytab); } configurationFactoryBean.afterPropertiesSet(); this.resolver = new HdfsResourceLoader(configurationFactoryBean.getObject()); } if (createDirectoryStructure) { // Create intermediary folders for (ModuleType type : ModuleType.values()) { Resource folder = getResources(type.name(), "", "").iterator().next(); if (!folder.exists()) { ExtendedResource.wrap(folder).mkdirs(); } } } } public void setCreateDirectoryStructure(boolean createDirectoryStructure) { this.createDirectoryStructure = createDirectoryStructure; } private byte[] bytesToHex(byte[] bytes) { byte[] hexChars = new byte[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = HEX_DIGITS[v >>> 4]; hexChars[j * 2 + 1] = HEX_DIGITS[v & 0x0F]; } return hexChars; } }