/**
* 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 org.jboss.loom.utils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.apache.commons.io.DirectoryWalker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.lang.StringUtils;
import org.jboss.loom.ex.CliScriptException;
import org.jboss.loom.ex.CopyException;
import org.jboss.loom.ex.MigrationException;
import org.jboss.loom.migrators.Origin;
import org.jboss.loom.spi.IConfigFragment;
import org.jboss.loom.tools.report.Reporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Global utils class.
*
* @author Roman Jakubco
*/
public class Utils {
private static final Logger log = LoggerFactory.getLogger(Utils.class);
/**
* Prints app help.
*/
public static void writeHelp() {
System.out.println();
System.out.println(" JBoss configuration migration tool for AS 5 / EAP 5 -> AS 7 / EAP 6 / WildFly 8");
System.out.println();
System.out.println(" Usage:");
System.out.println();
System.out.println(" java -jar AsMigrator.jar [<option>, ...] [as5.dir=]<as5.dir> [as7.dir=]<as7.dir>");
System.out.println();
System.out.println(" <as5.dir> is expected to contain path to AS 5 or EAP 5 home directory, i.e. the one with server/ subdirectory.");
System.out.println();
System.out.println(" <as7.dir> is expected to contain path to AS 7 or EAP 6 home directory, i.e. the one with jboss-modules.jar.");
System.out.println();
System.out.println(" Options:");
System.out.println();
System.out.println(" as5.profile=<name>");
System.out.println(" Path to AS 5 profile.");
System.out.println(" Default: \"default\"");
System.out.println();
System.out.println(" as7.confPath=<path> ");
System.out.println(" Path to AS 7 config file.");
System.out.println(" Default: \"standalone/configuration/standalone.xml\"");
System.out.println();
System.out.println(" conf.<module>.<property>=<value> := Module-specific options.");
System.out.println(" <module> := Name of one of modules. E.g. datasource, jaas, security, ...");
System.out.println(" <property> := Name of the property to set. Specific per module. " +
"May occur multiple times.");
System.out.println();
}
/**
* Searches a file of given name under given directory tree.
* @throws CopyException if nothing found.
*/
public static Collection<File> searchForFile(String fileName, File dir) throws FileNotFoundException {
IOFileFilter nff = new NameFileFilter(fileName);
Collection<File> found = FileUtils.listFiles(dir, nff, FileFilterUtils.trueFileFilter());
if( found.isEmpty() ) {
throw new FileNotFoundException("File '" + fileName + "' was not found in " + dir.getAbsolutePath());
}
return found;
}
/**
* Searches a file of given name under given directory tree.
* @throws CopyException if nothing found.
*/
public static List<File> searchForFileOrDir( final String name, final File dir ) throws IOException {
List<File> found = new DirectoryWalker(){
@Override protected boolean handleDirectory( File directory, int depth, Collection results ) throws IOException {
if( directory.getName().equals( name ))
results.add( directory );
return true;
}
@Override protected void handleFile( File file, int depth, Collection results ) throws IOException {
if( file.getName().equals( name ))
results.add( file );
}
public List<File> search() throws IOException {
List<File> found = new LinkedList();
try {
this.walk( dir, found );
} catch( IOException ex ) {
throw new IOException("Failed traversing directory '" + dir.getAbsolutePath() + "' when looking for '" + name + "'");
}
return found;
}
}.search();
if( found.isEmpty() ) {
throw new FileNotFoundException("File '" + name + "' was not found in " + dir.getAbsolutePath());
}
return found;
}
/**
* Builds up a File object with path consisting of given components.
*/
public static File createPath(String parent, String child, String... more) {
return createPath( new File(parent), child, more);
}
public static File createPath(File parent, String child, String... more) {
File file = new File(parent, child);
for (String component : more) {
file = new File(file, component);
}
return file;
}
/**
* Missing from Commons IO's FileUtils...
*/
public static void copyFileOrDirectory( File src, File dest ) throws IOException {
if( src.isFile() )
FileUtils.copyFile( src, dest );
else if( src.isDirectory() )
FileUtils.copyDirectory( src, dest );
else
throw new UnsupportedOperationException("Can only copy file or directory. Not this: " + src.getPath());
}
// ======= Lang utils ====== //
/**
* Finds a subclass of given class in current stacktrace.
* Returns null if not found.
*/
public static <T> Class<? extends T> findSubclassInStackTrace(Class<T> parentClass) {
// 0 - Thread.getStackTrace().
// 1 - This method.
// 2 - Whatever called this method.
return findSubclassInStackTrace( parentClass, Thread.currentThread().getStackTrace(), 2 );
}
/**
* Finds a subclass of given $parentClass in given $stackTrace, skipping $skip levels.
* Returns null if not found.
*/
public static <T> Class<? extends T> findSubclassInStackTrace(Class<T> parentClass, StackTraceElement[] stackTrace, int skip) {
//for( StackTraceElement call : stackTrace) {
for( int i = skip; i < stackTrace.length; i++ ) {
StackTraceElement call = stackTrace[i];
try {
Class<?> callClass = Class.forName( call.getClassName() );
if( parentClass.isAssignableFrom( callClass ) )
return (Class<? extends T>) callClass;
} catch( ClassNotFoundException ex ) {
Reporter.log.error("Can't load class " + call.getClassName() + ":\n " + ex.getMessage());
}
}
return null;
}
/**
* Extracts all String getters properties to a map.
* @see also @Property.Utils.convert*()
*/
public static Map<String, String> describeBean(IConfigFragment bean){
Map<String, String> ret = new LinkedHashMap();
Method[] methods = bean.getClass().getMethods();
for( Method method : methods ) {
boolean get = false;
String name = method.getName();
// Only use getters which return String.
if( method.getParameterTypes().length != 0 ) continue;
if( ! method.getReturnType().equals( String.class ) ) continue;
if( name.startsWith("get") ) get = true;
if( ! (get || name.startsWith("is")) ) continue;
// Remove "get" or "is".
name = name.substring( get ? 3 : 2 );
// Uncapitalize, unless it's getDLQJNDIName.
if( name.length() > 1 && ! Character.isUpperCase( name.charAt(2) ) )
name = StringUtils.uncapitalize( name );
try {
ret.put( name, (String) method.invoke(bean));
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
log.warn("Failed extracting property from " + bean.getClass().getSimpleName() + ":\n " + ex.getMessage(), ex );
}
}
return ret;
}
/**
* Throws a formatted message (name + errMsg) if string is null or empty.
*/
public static void throwIfBlank(String string, String errMsg, String name) throws CliScriptException {
if ((string == null) || (string.isEmpty())) {
throw new CliScriptException(name + errMsg);
}
}
/**
* Returns null for empty strings.
*/
public static String nullIfEmpty(String str){
return str == null ? null : (str.isEmpty() ? null : str);
}
public static Properties mapToProperties( Map<String, String> map ) {
Properties props = new Properties();
Set<Map.Entry<String, String>> entries = map.entrySet();
for( Map.Entry<String, String> entry : entries ) {
props.put( entry.getKey(), entry.getValue() );
}
return props;
}
public static <T extends Object> void validate( T object ) throws MigrationException {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<T>> valRes = validator.validate( object );
if( ! valRes.isEmpty() ){
StringBuilder sb = new StringBuilder("Validation failed for: ");
if( object instanceof Origin.Wise )
sb.append( ((Origin.Wise)object).getOrigin() );
else
sb.append(object);
for( ConstraintViolation<T> fail : valRes) {
sb.append("\n ").append( fail.getPropertyPath() ).append(" ").append( fail.getMessage() );
}
throw new MigrationException( sb.toString() );
}
}// validate()
public static Throwable getRootCause( Throwable ex ){
Throwable cause;
do{
cause = ex.getCause();
if( cause == null )
return ex;
ex = cause;
} while( true ); // Can exceptions ever create a loop?
}
}// class