/* * 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 com.github.sakserv.minicluster.impl; import com.github.sakserv.minicluster.MiniCluster; import com.github.sakserv.minicluster.util.FileUtils; import com.google.common.base.Throwables; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.gateway.GatewayServer; import org.apache.hadoop.gateway.services.DefaultGatewayServices; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.topology.impl.DefaultTopologyService; import org.apache.hadoop.gateway.topology.Service; import org.apache.hadoop.gateway.topology.Topology; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * @author Vincent Devillers */ public class KnoxLocalCluster implements MiniCluster { // Logger private static final Logger LOG = LoggerFactory.getLogger(KnoxLocalCluster.class); private GatewayServer gatewayServer; private File gatewayDir; private String host; private Integer port; private String homeDir; private String path; private String topology; private String cluster; public Integer getPort() { return port; } public String getHomeDir() { return homeDir; } public String getTopology() { return topology; } public String getPath() { return path; } public String getHost() { return host; } public String getCluster() { return cluster; } private KnoxLocalCluster(Builder builder) { this.port = builder.port; this.homeDir = builder.homeDir; this.path = builder.path; this.topology = builder.topology; this.host = builder.host; this.cluster = builder.cluster; } public static class Builder { private String host = "localhost"; private Integer port; private String homeDir; private String path; private String topology; private String cluster; public Builder setHost(String Host) { this.host = Host; return this; } public Builder setPort(Integer Port) { this.port = Port; return this; } public Builder setHomeDir(String homeDir) { this.homeDir = homeDir; return this; } public Builder setCluster(String cluster) { this.cluster = cluster; return this; } public Builder setPath(String Path) { this.path = Path; return this; } public Builder setTopology(String topology) { this.topology = topology; return this; } public KnoxLocalCluster build() { KnoxLocalCluster knoxLocalCluster = new KnoxLocalCluster(this); validateObject(knoxLocalCluster); return knoxLocalCluster; } public void validateObject(KnoxLocalCluster knoxLocalCluster) { if (knoxLocalCluster.port == null) { throw new IllegalArgumentException("ERROR: Missing required config: port"); } if (knoxLocalCluster.homeDir == null) { throw new IllegalArgumentException("ERROR: Missing required config: homeDir"); } if (knoxLocalCluster.path == null) { throw new IllegalArgumentException("ERROR: Missing required config: path"); } if (knoxLocalCluster.topology == null) { throw new IllegalArgumentException("ERROR: Missing required config: topology"); } if (knoxLocalCluster.cluster == null) { throw new IllegalArgumentException("ERROR: Missing required config: cluster"); } } } @Override public void start() throws Exception { LOG.info("KNOX: Starting GatewayServer"); configure(); gatewayDir = new File(homeDir, "gateway-home-" + UUID.randomUUID()); gatewayDir.mkdirs(); LocalGatewayConfig config = new LocalGatewayConfig(); config.set("GATEWAY_HOME", gatewayDir.getAbsolutePath()); config.set(LocalGatewayConfig.HTTP_HOST, host); config.setInt(LocalGatewayConfig.HTTP_PORT, port); config.set(LocalGatewayConfig.HTTP_PATH, path); config.set("default.app.topology.name", cluster); config.set("ssl.exclude.protocols", "none"); // {GATEWAY_HOME}/data File dataDir = new File(config.getGatewayDataDir()); dataDir.mkdirs(); // {GATEWAY_HOME}/data/deployments File deploymentDir = new File(config.getGatewayDeploymentDir()); deploymentDir.mkdirs(); // {GATEWAY_HOME}/data/security File securityDir = new File(config.getGatewaySecurityDir()); securityDir.mkdirs(); // {GATEWAY_HOME}/data/services File stacksDir = new File(config.getGatewayServicesDir()); stacksDir.mkdirs(); // {GATEWAY_HOME}/conf //config.set(LocalGatewayConfig.HADOOP_CONF_DIR, new File(gatewayDir, "conf").getAbsolutePath()); File confDir = new File(config.getGatewayConfDir()); confDir.mkdirs(); // {GATEWAY_HOME}/conf/topologies File topologiesDirectory = new File(config.getGatewayTopologyDir()); topologiesDirectory.mkdirs(); LOG.info("Using topology: {}", topology); File descriptor = new File(topologiesDirectory, cluster + ".xml"); try (FileOutputStream stream = new FileOutputStream(descriptor)) { stream.write(topology.getBytes()); } DefaultGatewayServices services = new DefaultGatewayServices(); Map<String, String> options = new HashMap<>(); options.put("persist-master", "false"); options.put("master", "password"); try { services.init(config, options); } catch (ServiceLifecycleException e) { LOG.error("Unable to init the services", e); throw Throwables.propagate(e); } URL resource = getClass().getClassLoader().getResource("services"); // Copy the services definitions from the JAR copyResourcesRecursively(resource, stacksDir); StringWriter writer = new StringWriter(); config.writeXml(writer); LOG.info("Using gateway-site.xml:{}", writer.toString()); gatewayServer = GatewayServer.startGateway(config, services); LOG.info("Gateway address = " + gatewayServer.getURI()); DefaultTopologyService topologyService = services.getService(DefaultGatewayServices.TOPOLOGY_SERVICE); Topology topology = topologyService.getTopologies().iterator().next(); for (Service service : topology.getServices()) { LOG.info("Deployed: {} -> {}", service.getRole(), StringUtils.join(service.getUrls(), ",")); } } @Override public void stop() throws Exception { stop(true); } @Override public void stop(boolean cleanUp) throws Exception { LOG.info("KNOX: Stopping GatewayServer"); gatewayServer.stop(); if (cleanUp) { cleanUp(); } } @Override public void configure() throws Exception { } @Override public void cleanUp() throws Exception { FileUtils.deleteFolder(gatewayDir.getAbsolutePath()); FileUtils.deleteFolder(new File(homeDir).getAbsolutePath()); } public static boolean copyFile(final File toCopy, final File destFile) { try { return copyStream(new FileInputStream(toCopy), new FileOutputStream(destFile)); } catch (final FileNotFoundException e) { e.printStackTrace(); } return false; } private static boolean copyFilesRecusively(final File toCopy, final File destDir) { assert destDir.isDirectory(); if (!toCopy.isDirectory()) { return copyFile(toCopy, new File(destDir, toCopy.getName())); } else { final File newDestDir = new File(destDir, toCopy.getName()); if (!newDestDir.exists() && !newDestDir.mkdir()) { return false; } for (final File child : toCopy.listFiles()) { if (!copyFilesRecusively(child, newDestDir)) { return false; } } } return true; } public static boolean copyJarResourcesRecursively(final File destDir, final JarURLConnection jarConnection) throws IOException { final JarFile jarFile = jarConnection.getJarFile(); for (final Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) { final JarEntry entry = e.nextElement(); if (entry.getName().startsWith(jarConnection.getEntryName())) { final String filename = StringUtils.removeStart(entry.getName(), // jarConnection.getEntryName()); final File f = new File(destDir, filename); if (!entry.isDirectory()) { final InputStream entryInputStream = jarFile.getInputStream(entry); if (!copyStream(entryInputStream, f)) { return false; } entryInputStream.close(); } else { if (!ensureDirectoryExists(f)) { throw new IOException("Could not create directory: " + f.getAbsolutePath()); } } } } return true; } public static boolean copyResourcesRecursively( // final URL originUrl, final File destination) { try { final URLConnection urlConnection = originUrl.openConnection(); if (urlConnection instanceof JarURLConnection) { return copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection); } else { return copyFilesRecusively(new File(originUrl.getPath()), destination); } } catch (final IOException e) { e.printStackTrace(); } return false; } private static boolean copyStream(final InputStream is, final File f) { try { return copyStream(is, new FileOutputStream(f)); } catch (final FileNotFoundException e) { e.printStackTrace(); } return false; } private static boolean copyStream(final InputStream is, final OutputStream os) { try { final byte[] buf = new byte[1024]; int len = 0; while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } is.close(); os.close(); return true; } catch (final IOException e) { e.printStackTrace(); } return false; } private static boolean ensureDirectoryExists(final File f) { return f.exists() || f.mkdir(); } }