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

org.etlunit.BasicRuntimeSupport Maven / Gradle / Ivy

There is a newer version: 1.6.9
Show newest version
package org.etlunit;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.codehaus.jackson.JsonNode;
import org.etlunit.feature.Feature;
import org.etlunit.feature.RuntimeOption;
import org.etlunit.io.ApacheProcessExecutor;
import org.etlunit.io.FileBuilder;
import org.etlunit.json.validator.ClasspathSchemaResolver;
import org.etlunit.json.validator.JsonSchemaValidationException;
import org.etlunit.json.validator.JsonValidator;
import org.etlunit.parser.ETLTestValueObject;
import org.etlunit.util.Hex;
import org.etlunit.util.Incomplete;
import org.etlunit.util.NeedsTest;
import org.etlunit.util.StringUtils;

import javax.inject.Inject;
import javax.inject.Named;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.MessageDigest;
import java.util.*;

public class BasicRuntimeSupport implements RuntimeSupport
{
	private final File root;
	private final File tempDir;
	private final File resourceDir;
	private final File referenceDir;
	private final File configDir;
	private final File generatedSourcesDir;
	private final File reportRootDir;

	private Log applicationLog;
	private Log userLog;

	private ProcessExecutor processExecutor;
	private final File srcRoot;
	private final String projectName;
	private final String projectVersion;
	private final String projectUser;
	private final File vendorBinaryDir;
	private final String projectUid;

	private final List featureList;

	private final Map runtimeOptions = new HashMap();

	public BasicRuntimeSupport()
	{
		this(getSystemTempDir());
	}

	public BasicRuntimeSupport(File proot)
	{
		featureList = Collections.EMPTY_LIST;

		try
		{
			this.root = proot.getCanonicalFile();
		}
		catch (IOException e)
		{
			throw new RuntimeException(e);
		}

		this.tempDir = new File(root, "temp");
		tempDir.mkdirs();

		this.reportRootDir = new File(root, "reports");
		reportRootDir.mkdirs();

		vendorBinaryDir = tempDir;

		this.resourceDir = new File(root, "resource");
		resourceDir.mkdirs();

		this.referenceDir = new File(root, "reference");
		referenceDir.mkdirs();

		configDir = new File(resourceDir, "config");
		configDir.mkdirs();

		generatedSourcesDir = new File(root, "build");
		generatedSourcesDir.mkdirs();

		projectName = "proj";
		projectVersion = "1_0";
		projectUser = "builduser";

		// use the apache commons exec implementation by default
		installProcessExecutor(new ApacheCommonsExecutor());
		srcRoot = new File(this.root, "src");
		projectUid = readProjectUid(System.getProperty("project.uid")).toLowerCase();
	}

/*
	public BasicRuntimeSupport(File root, File src, File temp, File rsrc, File cfg) {
		this.root = root;
		this.temp = temp;
		this.srcRoot = src;
		resourceDir = rsrc;
		configDir = cfg;

		// use the apache commons exec implementation by default
		installProcessExecutor(new ApacheCommonsExecutor());
	}
	 */

	public BasicRuntimeSupport(Configuration configuration, ETLTestVM vm)
	{
		featureList = vm.getFeatures();

		// validate against the schema
		JsonNode configJson = configuration.getRoot().getJsonNode();
		try
		{
			JsonValidator vlad = new JsonValidator("org/etlunit/Configuration.jsonSchema", new ClasspathSchemaResolver(this));

			vlad.validate(configJson);
		}
		catch (JsonSchemaValidationException e)
		{
			throw new RuntimeException("Configuration schema does not validate", e);
		}

		// configuration is valid - proceed
		projectName = StringUtils.sanitize(configuration.query("project-name").getValueAsString(), '_').toLowerCase();
		projectVersion = StringUtils.sanitize(configuration.query("project-version").getValueAsString(), '_').toLowerCase();
		projectUser = StringUtils.sanitize(configuration.query("project-user").getValueAsString(), '_').toLowerCase();

		try
		{
			root = new File(configuration.query("project-root-directory").getValueAsString()).getCanonicalFile();

			srcRoot = new File(configuration.query("test-sources-directory").getValueAsString()).getCanonicalFile();
			configDir = new File(configuration.query("configuration-directory").getValueAsString()).getCanonicalFile();
			generatedSourcesDir =
					new File(configuration.query("generated-source-directory").getValueAsString()).getCanonicalFile();
			resourceDir = new File(configuration.query("resource-directory").getValueAsString()).getCanonicalFile();
			referenceDir = new File(configuration.query("reference-directory").getValueAsString()).getCanonicalFile();
			tempDir = new File(configuration.query("temp-directory").getValueAsString()).getCanonicalFile();
			reportRootDir = new File(configuration.query("reports-directory").getValueAsString()).getCanonicalFile();

			vendorBinaryDir = new File(configuration.query("vendor-binary-directory").getValueAsString()).getCanonicalFile();
		}
		catch (IOException e)
		{
			throw new IllegalArgumentException(e);
		}

		if (!tempDir.exists())
		{
			tempDir.mkdirs();
		}

		if (!vendorBinaryDir.exists())
		{
			vendorBinaryDir.mkdirs();
		}

		installProcessExecutor(new ApacheCommonsExecutor());

		ETLTestValueObject query = configuration.query("project-uid");
		projectUid = readProjectUid(query != null ? query.getValueAsString() : null);

		ETLTestValueObject optionsNode = configuration.query("options");

		Gson gson = new GsonBuilder()
				.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
				.create();

		if (optionsNode != null)
		{
			Map map = optionsNode.getValueAsMap();

			for (Map.Entry featureEntry : map.entrySet())
			{
				// at this level read the actual properties
				String featureNasme = featureEntry.getKey();
				ETLTestValueObject featureObject = featureEntry.getValue();

				Map featureMap = featureObject.getValueAsMap();

				for (Map.Entry optionEntry : featureMap.entrySet())
				{
					// at this level read the actual properties
					String optionName = optionEntry.getKey();
					ETLTestValueObject optionObject = optionEntry.getValue();
					String json = optionObject.getJsonNode().toString();
					RuntimeOption runtimeOption = gson.fromJson(json, RuntimeOption.class);
					runtimeOption.setName(featureNasme + "." + optionName);

					runtimeOptions.put(runtimeOption.getName(), runtimeOption);
				}
			}
		}
	}

	private String readProjectUid(String uid)
	{
		if (uid != null)
		{
			return uid.toLowerCase();
		}

		try
		{
			StringBuffer
					buffer =
					new StringBuffer(root.getAbsolutePath()).append("|")
							.append(projectName)
							.append("|")
							.append(projectVersion)
							.append("|");

			InetAddress inet = InetAddress.getLocalHost();

			if (!inet.isLoopbackAddress())
			{
				NetworkInterface ni = NetworkInterface.getByInetAddress(inet);

				String mac = Hex.hexEncode(ni.getHardwareAddress());

				buffer.append(mac);
			}
			else
			{
				buffer.append(inet.getCanonicalHostName());
			}

			byte[] bytesOfMessage = buffer.toString().getBytes("UTF-8");

			MessageDigest md = MessageDigest.getInstance("MD5");
			String thedigest = Hex.hexEncode(md.digest(bytesOfMessage));

			return thedigest.toLowerCase();
		}
		catch (Exception e)
		{
			throw new RuntimeException("Error reading local host information", e);
		}
	}

	@Inject
	public void setApplicationLogger(@Named("applicationLog") Log log)
	{
		this.applicationLog = log;
	}

	@Inject
	public void setUserLogger(@Named("userLog") Log log)
	{
		this.userLog = log;
	}

	public static File getSystemTempDir()
	{
		return new File(System.getProperty("java.io.tmpdir"));
	}

	@Override
	public File getTempDirectory()
	{
		return tempDir;
	}

	@Override
	public File createTempFile(String name)
	{
		return new File(getTempDirectory(), name);
	}

	@Override
	public File getGeneratedSourceDirectory(String feature)
	{
		return new FileBuilder(generatedSourcesDir).subdir(feature).mkdirs().file();
	}

	@Override
	public File createGeneratedSourceFile(String feature, String name)
	{
		return new File(getGeneratedSourceDirectory(feature), name);
	}

	@Override
	public File getFeatureSourceDirectory(String feature)
	{
		return new FileBuilder(resourceDir).subdir(feature).mkdirs().file();
	}

	@Override
	public File getSourceDirectory()
	{
		return srcRoot;
	}

	@Override
	public File getTestSourceDirectory()
	{
		return getTestSourceDirectory(null);
	}

	@Override
	public File getTestSourceDirectory(String _package)
	{
		FileBuilder fb = new FileBuilder(getSourceDirectory());

		if (_package != null)
		{
			fb = fb.subdir(_package);
		}

		return fb.file();
	}

	@Override
	public File getReportDirectory(String type)
	{
		return new FileBuilder(reportRootDir).subdir(type + "-reports").mkdirs().file();
	}

	@Override
	public String getProjectName()
	{
		return projectName;
	}

	@Override
	public String getProjectVersion()
	{
		return projectVersion;
	}

	@Override
	public String getProjectUser()
	{
		return projectUser;
	}

	@Override
	public String getProjectUID()
	{
		return projectUid;
	}

	@Override
	public File getFeatureConfigurationDirectory(String feature)
	{
		return new FileBuilder(configDir).subdir(feature).mkdirs().file();
	}

	@Override
	public File getSourceDirectory(String package_)
	{
		if (package_ == null)
		{
			return getSourceDirectory();
		}

		return new FileBuilder(getSourceDirectory()).subdir(package_.replace('.', File.separatorChar)).mkdirs().file();
	}

	@Override
	public ProcessExecutor getProcessExecutor()
	{
		return processExecutor;
	}

	@Override
	public File createSourceFile(String feature, String name)
	{
		return new File(getSourceDirectory(feature), name);
	}

	@Override
	public File getReferenceDirectory(String path_)
	{
		return new FileBuilder(referenceDir).subdir(path_).mkdirs().file();
	}

	@Override
	public File getReferenceFile(String path_, String name)
	{
		return new File(getReferenceDirectory(path_), name);
	}

	@Incomplete
	@NeedsTest
	@Override
	public void installProcessExecutor(ProcessExecutor od)
	{
		processExecutor = od;
	}

	@Incomplete
	@NeedsTest
	public ProcessFacade execute(final ProcessDescription pd) throws IOException
	{
		applicationLog.info("Executing process: " + pd.debugString());

		File fi = pd.getOutputFile();

		if (fi != null)
		{
			applicationLog.info("Logging process output to file: " + fi.getAbsolutePath());
		}

		if (processExecutor != null)
		{
			return processExecutor.execute(pd);
		}

		ProcessBuilder pb = new ProcessBuilder(pd.getCommand());
		if (pd.hasArguments())
		{
			pb = pb.command(pd.getArguments());
		}

		pb.environment().putAll(pd.getEnvironment());


		final Process proc = pb.start();

		return new ProcessFacade()
		{
			@Override
			public ProcessDescription getDescriptor()
			{
				return pd;
			}

			public Process getProcess()
			{
				return proc;
			}

			@Override
			public void waitForCompletion()
			{
				try
				{
					proc.waitFor();
				}
				catch (InterruptedException e)
				{
					throw new IllegalStateException("Process interrupted");
				}
			}

			@Override
			public int getCompletionCode()
			{
				return proc.exitValue();
			}

			@Override
			public void kill()
			{
				proc.destroy();
			}

			@Override
			public void waitForStreams()
			{
				//these are always available
			}

			@Override
			public Writer getWriter()
			{
				return new BufferedWriter(new OutputStreamWriter(getOutputStream()));
			}

			@Override
			public BufferedReader getReader()
			{
				return new BufferedReader(new InputStreamReader(getInputStream()));
			}

			@Override
			public BufferedReader getErrorReader()
			{
				return new BufferedReader(new InputStreamReader(getErrorStream()));
			}

			@Override
			public StringBuffer getInputBuffered() throws IOException
			{
				return buffer(getReader());
			}

			@Override
			public StringBuffer getErrorBuffered() throws IOException
			{
				return buffer(getErrorReader());
			}

			private StringBuffer buffer(BufferedReader errorReader) throws IOException
			{
				StringBuffer stb = new StringBuffer();

				String line = null;

				int lineC = 0;

				while ((line = errorReader.readLine()) != null)
				{
					if (lineC != 0)
					{
						stb.append('\n');
					}

					lineC++;

					stb.append(line);
				}

				return stb;
			}

			@Override
			public OutputStream getOutputStream()
			{
				return proc.getOutputStream();
			}

			@Override
			public InputStream getInputStream()
			{
				return proc.getInputStream();
			}

			@Override
			public InputStream getErrorStream()
			{
				return proc.getErrorStream();
			}
		};
	}

	@Override
	public RuntimeOption getRuntimeOption(String qualifiedName)
	{
		return runtimeOptions.get(qualifiedName);
	}

	@Override
	public List getRuntimeOptions(String featureName)
	{
		List options = new ArrayList();

		for (Map.Entry entry : runtimeOptions.entrySet())
		{
			String key = entry.getKey();
			RuntimeOption value = entry.getValue();

			if (featureName == null || key.startsWith(featureName + "."))
			{
				options.add(value);
			}
		}

		return options;
	}

	@Override
	public List getRuntimeOptions()
	{
		return getRuntimeOptions(null);
	}

	private void getPackages(File root, final String prefix, final List packages, final PackageValidator validator)
	{
		root.listFiles(new FileFilter()
		{
			public boolean accept(File pathname)
			{
				if (pathname.isDirectory() && !pathname.getName().equals(".svn") && (validator == null
						|| validator.packageNameValid(pathname.getName())))
				{
					String pack = (prefix != null ? (prefix + ".") : "") + pathname.getName();
					packages.add(pack);

					getPackages(pathname, pack, packages, validator);
				}

				return false;
			}
		});
	}

	@Override
	public List getPackages(File root)
	{
		final List packages = new ArrayList();

		// add a reference to the default package
		packages.add(null);

		getPackages(root, null, packages, null);

		return packages;
	}

	@Override
	public List getReferencePackages(String path)
	{
		File referenceDirectory = getReferenceDirectory(path);
		return getPackages(referenceDirectory);
	}

	interface PackageValidator
	{
		boolean packageNameValid(String name);
	}

	@Override
	public List getTestPackages()
	{
		final List packages = new ArrayList();

		// add a reference to the default package
		packages.add(null);

		getPackages(getTestSourceDirectory(), null, packages, new TestPackageValidator());

		return packages;
	}

	private class TestPackageValidator implements PackageValidator
	{
		private final Map nameMap = new HashMap();

		TestPackageValidator()
		{
			for (Feature feature : featureList)
			{
				for (String dirName : feature.getTestSupportFolderNames())
				{
					nameMap.put(dirName, dirName);
				}
			}
		}

		@Override
		public boolean packageNameValid(String name)
		{
			return !nameMap.containsKey(name);
		}
	}
}

/*This work is done here to more readily test apache commons exe
 */
@Incomplete
class ApacheCommonsExecutor implements ProcessExecutor
{
	private final ApacheProcessExecutor apacheProcessExecutor = new ApacheProcessExecutor();

	@Incomplete
	@Override
	public ProcessFacade execute(final ProcessDescription pd)
	{
		return apacheProcessExecutor.execute(pd);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy