All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.distributeme.servicebuilder.mojo.ServiceMojo Maven / Gradle / Ivy

package org.distributeme.servicebuilder.mojo;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import net.anotheria.util.IOUtils;
import net.anotheria.util.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * TODO comment this class
 *
 * @author lrosenberg
 * @since 14/02/2017 13:40
 */
@Mojo(name="service")
public class ServiceMojo extends AbstractMojo {

	@Parameter
	private File definitionsFile;

	@Parameter
	private String dockerImageName;

	@Parameter
	private String outputDirectory = "distribution";

	@Parameter
	private String dockerOutputDirectory = "docker";

	@Parameter
	private String binDirectoryName = "bin";
	private String libDirectoryName = "lib";
	private String confDirectoryName = "conf";
	private String logDirectoryName = "logs";
	private String locallibDirectoryName = "locallib";
	private String localconfDirectoryName = "localconf";

	@Parameter
	private String pathToEnvironmentSh;

	@Parameter
	private String dockerRepository;

	private HashMap> knownProfiles = new HashMap<>();


	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		getLog().info( "Building distributeme-based services." );

		if (definitionsFile == null){
			getLog().info("Definition file is null");
			throw new MojoExecutionException("Can't process definition file, it is not set");
		}

		if (!definitionsFile.exists()){
			getLog().info("Definition file doesn't exists - " + definitionsFile.getAbsolutePath());
			throw new MojoExecutionException("Can't process definition file, it doesn't exists "+definitionsFile.getAbsolutePath());
		}

		byte[] fileData = null;
		try {
			fileData = IOUtils.readFileAtOnce(definitionsFile);
		}catch(IOException e){
			throw new MojoExecutionException("Can't read file "+definitionsFile.getAbsolutePath(), e);
		}

		JsonParser jsonParser = new JsonParser();
		JsonObject jo = (JsonObject)jsonParser.parse(new String(fileData));

		//read profiles
		JsonArray profiles = jo.getAsJsonArray("profiles");
		for (int i=0; i());
		}


		JsonArray jsonArr = jo.getAsJsonArray("services");

		Gson gson = new GsonBuilder().create();
		Type listType = new TypeToken>() {}.getType();
		ArrayList list = gson.fromJson(jsonArr, listType);

		for (ServiceEntry entry : list){
			try{
				generateService(entry);
			}catch(IOException ex){
				throw new MojoExecutionException("Can't create service "+entry.getName(), ex);
			}
		}

		//generate generic start script for all services.
		try{
			generateCommonPart();
		}catch(IOException ex){
			throw new MojoExecutionException("Can't create common directories", ex);
		}

		//generate generic scripts for docker service instances.
		if (!StringUtils.isEmpty(dockerImageName)) {
			try {
				generateDocker(list);
			}catch(IOException e){
				throw new MojoExecutionException("Can't create docker directories and content", e);
			}
		}

	}

	private void generateDocker(ArrayList services) throws IOException{
		getLog().info("Generating docker files");
		String target = "target/"+dockerOutputDirectory+"/";
		File targetDirFile = new File(target);
		if (!targetDirFile.exists()){
			targetDirFile.mkdirs();
		}

		String containerDir = target+"service/";
		String scriptsDir = target+"scripts/";
		File containerDirFile = new File(containerDir); containerDirFile.mkdirs();
		File scriptsDirFile = new File(scriptsDir); scriptsDirFile.mkdirs();

		writeOutScript(containerDirFile, "docker/start.sh", "start.sh");
		writeOutScript(containerDirFile, "docker/Dockerfile", "Dockerfile");
		writeOutScript(containerDirFile, "docker/.profile", ".profile");
		writeOutScript(containerDirFile, "docker/java.policy", "java.policy");

		File lib = new File(containerDir + libDirectoryName); lib.mkdirs();
		File conf = new File(containerDir + confDirectoryName); conf.mkdirs();

		for (ServiceEntry service : services){

			String scriptsContainerDir = scriptsDir + service.getName() + "/";
			new File(scriptsContainerDir).mkdirs();
			new File(scriptsContainerDir + "logs").mkdirs();

			File envFile = new File(scriptsContainerDir + service.getName() + ".env");
			FileOutputStream fOutEnvFile = new FileOutputStream(envFile);
			fOutEnvFile.write(("SERVICE_CLASS="+service.getStartClass()+"\n").getBytes());
			fOutEnvFile.write(("SERVICE_PORT="+service.getRmiPort()+"\n").getBytes());
			fOutEnvFile.write(("JVM_OPTIONS="+service.getJvmOptions()+"\n").getBytes());
			fOutEnvFile.write(("GOOGLE_APPLICATION_CREDENTIALS_FILE=" + service.getGoogleApplicationCredentialsFile() + "\n").getBytes());
			fOutEnvFile.close();

			File startFile = new File(scriptsContainerDir + "start_container.sh");
			FileOutputStream fOutStartFile = new FileOutputStream(startFile);
			fOutStartFile.write("#!/bin/bash\n".getBytes());
			fOutStartFile.write("if [ -z \"$1\" ] || [ \"$1\" == \"null\" ]; then\n".getBytes());
			fOutStartFile.write("	tagName=\"latest\"\n".getBytes());
			fOutStartFile.write("else\n".getBytes());
			fOutStartFile.write("	tagName=\"$1\"\n".getBytes());
			fOutStartFile.write("fi\n\n".getBytes());
			fOutStartFile.write("source ../environment.sh\n".getBytes());
			fOutStartFile.write(("docker run -d --env CONFIGUREME_ENVIRONMENT=$CONFIGUREME_ENVIRONMENT " +
					" -v `pwd`/logs:/app/logs " +
					" $DOCKER_CONTAINER_OPTS " +
					" --cidfile "+service.getName()+".cid "+
					" --env SERVICE_REGISTRATION_IP=$SERVICE_REGISTRATION_IP --env-file "+service.getName()+".env -p "+service.getRmiPort()+":"+service.getRmiPort()+
					" --name "+service.getName()+" " + (StringUtils.isEmpty(dockerRepository) ? "" : (dockerRepository + "/")) + dockerImageName + ":$tagName"

				+"\n").getBytes());
			fOutStartFile.close();
			startFile.setExecutable(true);

			File stopFile = new File(scriptsContainerDir + "stop_container.sh");
			FileOutputStream fOutStopFile = new FileOutputStream(stopFile);
			fOutStopFile.write("#!/bin/bash\n".getBytes());
			fOutStopFile.write(("CIDFILE=" + service.getName() + ".cid\n").getBytes());
			fOutStopFile.write("echo stopping $CIDFILE - `cat $CIDFILE`\n".getBytes());
			fOutStopFile.write("docker container stop `cat $CIDFILE`\n".getBytes());
			fOutStopFile.write("docker container rm `cat $CIDFILE`\n".getBytes());
			fOutStopFile.write(("rm " + service.getName() + ".cid\n").getBytes());
			fOutStopFile.write(("echo stopped " + service.getName() + "\n").getBytes());


			fOutStopFile.close();
			stopFile.setExecutable(true);
		}

		createSymbolicLinkForEnvironment(scriptsDir);
		writeOutGenericScripts(scriptsDirFile, "./start_container.sh", "./stop_container.sh");
	}

	public File getDefinitionsFile() {
		return definitionsFile;
	}

	public void setDefinitionsFile(File definitionsFile) {
		this.definitionsFile = definitionsFile;
	}

	protected void generateCommonPart() throws IOException{
		//common part for all services scripts
		File bin = new File("target/"+outputDirectory+"/" + binDirectoryName); bin.mkdirs();
		File lib = new File("target/"+outputDirectory+"/" + libDirectoryName); lib.mkdirs();
		File conf = new File("target/"+outputDirectory+"/" + confDirectoryName); conf.mkdirs();
		createSymbolicLinkForEnvironment("target/"+outputDirectory+"/");
		writeOutScript(bin, "start.sh");
		writeOutScript(bin, "stop.sh");
		writeOutGenericScripts(bin, "bin/start_service.sh", "bin/stop_service.sh");
	}

	private void createSymbolicLinkForEnvironment(String targetDir) throws IOException {
		Path envLinkTarget = Paths.get(pathToEnvironmentSh);
		Path envLinksource = Paths.get(targetDir + "environment.sh");
		try {
			Files.createSymbolicLink(envLinksource, envLinkTarget);
		}catch(FileAlreadyExistsException toignore){
			getLog().info("symlink "+envLinksource+" already exists, skipping");
		}
	}

	protected void generateService(ServiceEntry entry) throws IOException{

		//add service to the corresponding profile service list.
		List serviceProfiles = entry.getProfiles();
		for (String p : serviceProfiles){
			List servicesForProfile = knownProfiles.get(p);
			if (serviceProfiles == null)
				throw new RuntimeException("Can't find profile '"+p+"' for service '"+entry.getName()+"'");
			servicesForProfile.add(entry.getName());
		}

		String target = "target/"+outputDirectory+"/"+entry.getName()+"/";
		String common = "../";

		File log = new File(target + logDirectoryName); log.mkdirs();
		File localLib = new File(target + locallibDirectoryName); localLib.mkdirs();
		File localConf = new File(target + localconfDirectoryName); localConf.mkdirs();
		File bin = new File(target + binDirectoryName); bin.mkdirs();

		Path libLinkTarget = Paths.get(common+libDirectoryName);
		Path libLinksource = Paths.get(target+libDirectoryName);
		try{
			Files.createSymbolicLink( libLinksource, libLinkTarget);
		}catch(FileAlreadyExistsException toignore){
			getLog().info("symlink "+libLinksource+" already exists, skipping");
		}

		Path confLinkTarget = Paths.get(common+confDirectoryName);
		Path confLinksource = Paths.get(target+confDirectoryName);
		try{
			Files.createSymbolicLink( confLinksource, confLinkTarget);
		}catch(FileAlreadyExistsException toignore){
			getLog().info("symlink "+confLinksource+" already exists, skipping");
		}

		//generate service specific scripts
		generateServiceStartScripts(bin, entry);
	}

	protected void generateServiceStartScripts(File targetDirectory, ServiceEntry serviceEntry) throws IOException{
		//first write out the service definition file.
		FileOutputStream serviceDefinitionFile = new FileOutputStream(targetDirectory+"/service.sh");
		writeLine(serviceDefinitionFile, "#This is service definition file, it is sourced from the start script.");
		writeLine(serviceDefinitionFile,"#This service definition is created from services.json with entry: "+serviceEntry+".");
		writeLine(serviceDefinitionFile,"export SERVICE_NAME="+serviceEntry.getName());
		writeLine(serviceDefinitionFile,"export TARGET_PID="+serviceEntry.getName()+".pid");
		writeLine(serviceDefinitionFile,"export TARGET_CLASS="+serviceEntry.getStartClass());
		writeLine(serviceDefinitionFile,"export RMI_PORT="+serviceEntry.getRmiPort());
		writeLine(serviceDefinitionFile,"export JVM_OPTIONS=\""+(serviceEntry.getJvmOptions()!=null ? serviceEntry.getJvmOptions() : "none")+"\"");
		writeLine(serviceDefinitionFile,"## profiles for this service are: "+serviceEntry.getProfiles());
		//export LOCAL_RMI_PORT=9405
		serviceDefinitionFile.close();

		writeOutScript(targetDirectory, "start_service.sh");
		writeOutScript(targetDirectory, "stop_service.sh");


	}

	protected void writeOutGenericScripts(File targetDirectory, String startAction, String stopAction) throws IOException{

		File startAllFile = new File(targetDirectory.getAbsolutePath()+"/"+"start_all.sh");
		FileOutputStream startAll = new FileOutputStream(startAllFile);
		writeLine(startAll, "#!/usr/bin/env bash");
		writeLine(startAll ,"source environment.sh");
		writeLine(startAll ,"echo current profile: $DISTRIBUTEME_PROFILE");
		writeLine(startAll ,"profile_found=\"false\"");

		//generate service definition for every profile.
		for (Map.Entry> entries : knownProfiles.entrySet()){
			String serviceListForProfile = "";
			for (String s : entries.getValue()){
				serviceListForProfile += s + " ";
			}
			String profileNameForScript = "SERVICES_"+entries.getKey();
			writeLine(startAll, profileNameForScript+"=\""+serviceListForProfile+"\"");
		}
		writeLine(startAll, "");

		//generate profile name check for every profile.
		for (Map.Entry> entries : knownProfiles.entrySet()){
			String profileNameForScript = "SERVICES_"+entries.getKey();
			writeLine(startAll, "if [ \""+entries.getKey()+"\" = \"$DISTRIBUTEME_PROFILE\" ]; then");
			writeLine(startAll, "  profile_found=\"true\"");
			writeLine(startAll, "  SERVICES=$"+profileNameForScript);
			writeLine(startAll, "fi");
		}
		writeLine(startAll, "");

		writeLine(startAll, "if [ $profile_found = \"false\" ]; then");
		writeLine(startAll, "  echo profile $DISTRIBUTEME_PROFILE not found!");
		writeLine(startAll, "else");
		writeLine(startAll, "  echo starting services $SERVICES");
		writeLine(startAll, "fi");

		writeLine(startAll, "");
		writeLine(startAll, "if [ -z \"$1\" ] || [ \"$1\" == \"null\" ]; then");
		writeLine(startAll, "	tagName=\"latest\"");
		writeLine(startAll,"else");
		writeLine(startAll,"	tagName=\"$1\"");
		writeLine(startAll,"fi");

		writeLine(startAll, "");
		writeLine(startAll, "for i in $SERVICES; do");
		writeLine(startAll, "\techo starting service $i");
		writeLine(startAll, "\tcd $i");
		writeLine(startAll, "\t" + startAction + " $tagName");
		writeLine(startAll, "\tcd ..");
		writeLine(startAll, "done");
		startAll.close();
		startAllFile.setExecutable(true);

		File stopAllFile = new File(targetDirectory.getAbsolutePath()+"/"+"stop_all.sh");
		FileOutputStream stopAll = new FileOutputStream(stopAllFile);
		writeLine(stopAll,"#!/usr/bin/env bash");
		writeLine(stopAll ,"source environment.sh");
		writeLine(stopAll ,"echo current profile: $DISTRIBUTEME_PROFILE");
		writeLine(stopAll ,"profile_found=\"false\"");

		//generate service definition for every profile.
		for (Map.Entry> entries : knownProfiles.entrySet()){
			String serviceListForProfile = "";
			for (String s : entries.getValue()){
				serviceListForProfile += s + " ";
			}
			String profileNameForScript = "SERVICES_"+entries.getKey();
			writeLine(stopAll, profileNameForScript+"=\""+serviceListForProfile+"\"");
		}
		writeLine(stopAll, "");

		//generate profile name check for every profile.
		for (Map.Entry> entries : knownProfiles.entrySet()){
			String profileNameForScript = "SERVICES_"+entries.getKey();
			writeLine(stopAll, "if [ \""+entries.getKey()+"\" = \"$DISTRIBUTEME_PROFILE\" ]; then");
			writeLine(stopAll, "  profile_found=\"true\"");
			writeLine(stopAll, "  SERVICES=$"+profileNameForScript);
			writeLine(stopAll, "fi");
		}
		writeLine(stopAll, "");

		writeLine(stopAll, "if [ $profile_found = \"false\" ]; then");
		writeLine(stopAll, "  echo profile $DISTRIBUTEME_PROFILE not found!");
		writeLine(stopAll, "else");
		writeLine(stopAll, "  echo stoping services $SERVICES");
		writeLine(stopAll, "fi");

		writeLine(stopAll,"for i in $SERVICES; do");
		writeLine(stopAll,"\techo stoping service $i");
		writeLine(stopAll,"\tcd $i");
		writeLine(stopAll,"\t" + stopAction);
		writeLine(stopAll,"\tcd ..");
		writeLine(stopAll,"done");
		stopAll.close();
		stopAllFile.setExecutable(true);
	}

	private void writeLine(FileOutputStream fOut, String line) throws IOException{
		fOut.write( (line+"\n").getBytes());
	}

	protected void writeOutScript(File targetDirectory, String filename) throws IOException{
		writeOutScript(targetDirectory, filename, filename);
	}

	protected void writeOutScript(File targetDirectory, String filename, String outputFileName) throws IOException{
		InputStream fIn = ServiceMojo.class.getResourceAsStream("/"+filename);
		File file = new File(targetDirectory.getAbsolutePath()+"/"+outputFileName);
		FileOutputStream fOut = new FileOutputStream(file);
		int c = -1;
		while ( (c = fIn.read()) != -1 )
			fOut.write(c);
		fOut.close();
		file.setExecutable(true);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy