package ftp.provisioner; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import ftp.service.FtpUser; import ftp.service.FtpUserManagerConfiguration; import ftp.service.FtpUserRepository; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ftpserver.ftplet.FtpException; import org.apache.ftpserver.ftplet.User; import org.apache.ftpserver.ftplet.UserManager; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlows; import org.springframework.integration.dsl.amqp.Amqp; import org.springframework.integration.transformer.GenericTransformer; import java.util.HashMap; import java.util.Map; import java.util.UUID; @SpringBootApplication @Import(FtpUserManagerConfiguration.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } private final ObjectMapper mapper = new ObjectMapper(); private Log log = LogFactory.getLog(getClass()); @Value("${ftp.host}") private String host; @Value("${ftp.port}") private int port; @Value("${ftp.amqp.requests}") private String ftpRequests; @Bean RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); return rabbitTemplate; } @Bean CommandLineRunner provisionerCLR(FtpUserRepository repository) { return args -> { log.info("FTP provisioner started at " + System.currentTimeMillis()); repository.findAll().forEach(u -> this.log.info( buildFtpConnectionString(host, port, u))); }; } // TODO: Rewrite this in Spring Cloud Stream as a module // https://github.com/spring-cloud/spring-cloud-dataflow/blob/master/spring-cloud-dataflow-module-deployers/spring-cloud-dataflow-module-deployer-lattice/src/main/java/org/springframework/cloud/dataflow/module/deployer/lattice/TaskModuleDeployer.java // Write a planner that preemptive-ly schedules new instances at 80%? @Bean IntegrationFlow amqpReplyFlow(ConnectionFactory rabbitConnectionFactory, UserManager ftpUserManager) { return IntegrationFlows.from(Amqp.inboundGateway(rabbitConnectionFactory, this.ftpRequests) .messageConverter(new Jackson2JsonMessageConverter())) .transform(String.class, new GenericTransformer<String, String>() { @Override public String transform(String source) { try { Map<String, String> map = toMap(source); String ws = map.get("workspace"); String usr = map.get("user"); String password = UUID.randomUUID().toString(); FtpUser user = new FtpUser(ws, usr, password, true); ftpUserManager.save(user); String ftpUri = buildFtpConnectionString(host, port, user); log.info("registering: workspace: " + ws + ", " + "user: " + usr + ", ftp URI: " + ftpUri); return ftpUri; } catch (FtpException e) { throw new RuntimeException(e); } } }).get(); } private String buildFtpConnectionString(String host, int port, User user) { return String.format("ftp://%s:%s@%s:%s", user.getName(), user.getPassword(), host, port); } private Map<String, String> toMap(String src) { try { TypeReference<HashMap<String, String>> valueTypeRef = new TypeReference<HashMap<String, String>>() { }; return mapper.readValue(src, valueTypeRef); } catch (Exception e) { throw new RuntimeException(e); } } }