/*
* Copyright 2013 Matt Sicker and Contributors
*
* 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 atg.tools.dynunit.util;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
/**
* Utility class to create component properties files. This is useful for making component files on the fly during
* unit tests, Nucleus instantiation, etc.
*
* @author msicker
* @version 1.0.0
*/
public final class ComponentUtil {
private static final Logger logger = LogManager.getLogger();
private ComponentUtil() {
}
/**
* Creates a new component properties file in {@code parent} named {@code name} using the class
* {@code canonicalClassName} and configured with the given {@code properties}.
*
* @param parent
* Directory to place the new component properties file.
* @param name
* Name of component to create. Determines the file name.
* @param canonicalClassName
* Full package-qualified class name to use for component.
* @param properties
* Any properties to set in the component properties file.
*
* @return Newly created (or recreated) component properties file.
*
* @throws IOException
* if the properties file couldn't be written to.
*/
public static File newComponent(final File parent,
final String name,
final String canonicalClassName,
final Properties properties)
throws IOException {
logger.entry(parent, name, canonicalClassName, properties);
makeDirectory(parent);
final File output = getComponentPropertiesFile(parent, name);
newComponentForFile(output, canonicalClassName, properties);
return logger.exit(output);
}
/**
* Creates a new component properties file in {@code parent} named {@code name} using the class {@code klass} and
* configured with the given {@code properties}.
*
* @param parent
* Directory to place the new component properties file.
* @param name
* Name of component to create. Determines the file name.
* @param klass
* Class of component to create.
* @param properties
* Any properties to set in the component properties file.
*
* @return Newly created (or recreated) component properties file.
*
* @throws IOException
* if the properties file couldn't be written to.
*/
public static File newComponent(final File parent,
final String name,
final Class<?> klass,
final Properties properties)
throws IOException {
logger.entry(parent, name, klass, properties);
final File output = newComponent(parent, name, klass.getCanonicalName(), properties);
return logger.exit(output);
}
public static File newComponent(final File parent,
final String name,
final Class<?> klass)
throws IOException {
logger.entry(parent, name, klass);
final File output = newComponent(parent, name, klass, new Properties());
return logger.exit(output);
}
/**
* Creates a new component properties file in {@code parent} named {@code name} and configured with the given
* {@code properties}. This particular builder does not specify a class, so unless the {@code $class} property is
* given in the {@code properties}, then the created file will be treated as a general configuration as ATG does
* when no class is specified. This is useful for creating {@code GLOBAL.properties} files as well as overriding
* default components.
*
* @param parent
* Directory to place the new component properties file.
* @param name
* Name of component to create. Determines the file name.
* @param properties
* Any properties to set in the component properties file.
*
* @return Newly created (or recreated) component properties file.
*
* @throws IOException
* if the properties file couldn't be written to.
*/
public static File newComponent(final File parent,
final String name,
final Properties properties)
throws IOException {
logger.entry(parent, name, properties);
final File output = newComponent(parent, name, "", properties);
return logger.exit(output);
}
/**
* Creates a new component properties file in {@code parent} named the same as the class {@code klass} and
* configured with the given {@code properties}.
*
* @param parent
* Directory to place the new component properties file.
* @param klass
* Class of component to create. Determines the file name. If the class name ends with {@code Impl}, that
* is also stripped from the name.
* @param properties
* Any properties to set in the component properties file.
*
* @return Newly created (or recreated) component properties file.
*
* @throws IOException
* if the properties file couldn't be written to.
*/
public static File newComponent(final File parent,
final Class<?> klass,
final Properties properties)
throws IOException {
logger.entry(parent, klass, properties);
final File output = newComponent(parent, getSimplifiedClassName(klass), klass.getName(), properties);
return logger.exit(output);
}
/**
* Creates a new component properties file in {@code parent} using the class {@code klass}, named the same as
* {@code klass}, and using no additional properties.
*
* @param parent
* Directory to place the new component properties file.
* @param klass
* Class of component to create. Determines the file name.
*
* @return Newly created (or recreated) component properties file.
*
* @throws IOException
* if the properties file couldn't be written to.
*/
public static File newComponent(final File parent,
final Class<?> klass)
throws IOException {
logger.entry(parent, klass);
final File output = newComponent(parent, klass, new Properties());
return logger.exit(output);
}
/**
* Creates a new component properties file at {@code output} using the class {@code canonicalClassName} and
* configured with the given {@code properties}. This method is generally useful when you have a properties file
* already and wish to overwrite it.
*
* @param output
* The component properties file to write to.
* @param canonicalClassName
* Full package-qualified class name to use for component.
* @param properties
* Any properties to set in the component properties file.
*
* @return The provided {@code output} component properties file.
*
* @throws IOException
* if {@code output} couldn't be written to.
*/
public static File newComponentForFile(final File output,
final String canonicalClassName,
final Properties properties)
throws IOException {
logger.entry(output, canonicalClassName, properties);
recreateFile(output);
writeComponentPropertiesToFile(output, canonicalClassName, properties);
return logger.exit(output);
}
/**
* Creates a new component properties file at {@code output} using the class {@code klass} and configured with the
* given {@code properties}. This method is generally useful when you have a properties file already and wish to
* overwrite it.
*
* @param output
* The component properties file to write to.
* @param klass
* Class of component to create.
* @param properties
* Any properties to set in the component properties file.
*
* @return The provided {@code output} component properties file.
*
* @throws IOException
* if {@code output} couldn't be written to.
*/
public static File newComponentForFile(final File output,
final Class<?> klass,
final Properties properties)
throws IOException {
logger.entry(output, klass, properties);
newComponentForFile(output, klass.getCanonicalName(), properties);
return logger.exit(output);
}
/**
* Creates a new component properties file at {@code output} for the class {@code klass}.
*
* @param output
* The component properties file to write to.
* @param klass
* Class of component to create.
*
* @return The provided {@code output} file.
*
* @throws IOException
* if {@code output} couldn't be written to.
*/
public static File newComponentForFile(final File output, final Class<?> klass)
throws IOException {
logger.entry(output, klass);
newComponentForFile(output, klass, new Properties());
return logger.exit(output);
}
private static void makeDirectory(@Nullable final File directory)
throws IOException {
logger.entry(directory);
if (directory != null) {
FileUtils.forceMkdir(directory);
}
logger.exit();
}
private static File getComponentPropertiesFile(final File parent, final String name)
throws FileNotFoundException {
logger.entry(parent, name);
if (name == null) {
throw logger.throwing(new FileNotFoundException("No name was given."));
}
return logger.exit(new File(parent, name + ".properties"));
}
private static void recreateFile(@Nullable final File file)
throws IOException {
logger.entry(file);
if (file != null) {
if (file.exists()) {
FileUtils.forceDelete(file);
}
if (!file.createNewFile()) {
throw logger.throwing(new IOException("File already exists but can't be deleted."));
}
}
logger.exit();
}
private static void writeComponentPropertiesToFile(final File output,
final String canonicalClassName,
final Properties properties)
throws IOException {
logger.entry(output, canonicalClassName, properties);
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(output));
if (StringUtils.isNotEmpty(canonicalClassName)) {
out.write("$class=");
out.write(canonicalClassName);
out.newLine();
}
properties.store(out, canonicalClassName);
} finally {
if (out != null) {
out.close();
}
logger.exit();
}
}
private static String getSimplifiedClassName(Class<?> klass) {
String className = klass.getSimpleName();
if (className.endsWith("Impl")) {
className = className.substring(0, className.lastIndexOf("Impl"));
}
return className;
}
}