/* * Copyright (c) 2011, Cloudera, Inc. All Rights Reserved. * * Cloudera, Inc. licenses this file to you 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 * * This software 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.cloudera.lib.lang; import com.cloudera.lib.io.IOUtils; import com.cloudera.lib.server.ServiceException; import com.cloudera.lib.util.Check; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLDecoder; import java.text.MessageFormat; import java.util.Enumeration; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; /** * Class related utilities. */ public class ClassUtils { /** * Finds the JAR file containing a class. * * @param klass class to find its JAR. * @return the path to the JAR. */ public static String getJar(Class klass) { Check.notNull(klass, "klass"); ClassLoader loader = klass.getClassLoader(); if (loader != null) { String class_file = klass.getName().replaceAll("\\.", "/") + ".class"; try { for (Enumeration itr = loader.getResources(class_file); itr.hasMoreElements(); ) { URL url = (URL) itr.nextElement(); if ("jar".equals(url.getProtocol())) { String toReturn = url.getPath(); if (toReturn.startsWith("file:")) { toReturn = toReturn.substring("file:".length()); } toReturn = URLDecoder.decode(toReturn, "UTF-8"); return toReturn.replaceAll("!.*$", ""); } } } catch (IOException e) { throw new RuntimeException(e); } } return null; } /** * Convenience method that returns a resource as inputstream from the * classpath. * <p/> * It first attempts to use the Thread's context classloader and if not * set it uses the <code>ClassUtils</code> classloader. * * @param name resource to retrieve. * @return inputstream with the resource, NULL if the resource does not * exist. */ public static InputStream getResource(String name) { Check.notEmpty(name, "name"); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassUtils.class.getClassLoader(); } return cl.getResourceAsStream(name); } /** * Creates a JAR file with the specified classes. * * @param jarFile jar file path. * @param classes classes to add to the JAR. * @throws IOException thrown if an IO error occurred. */ public static void createJar(File jarFile, Class... classes) throws IOException { Check.notNull(jarFile, "jarFile"); File jarDir = jarFile.getParentFile(); if (!jarDir.exists()) { if (!jarDir.mkdirs()) { throw new IOException(MessageFormat.format("could not create dir [{0}]", jarDir)); } } createJar(new FileOutputStream(jarFile), classes); } /** * Writes the specified classes to an outputstream. * * @param os outputstream to write the classes to. * @param classes classes to write to the outputstream. * @throws IOException thrown if an IO error occurred. */ public static void createJar(OutputStream os, Class... classes) throws IOException { Check.notNull(os, "os"); File classesDir = File.createTempFile("createJar", "classes"); if (!classesDir.delete()) { throw new IOException(MessageFormat.format("could not delete temp file [{0}]", classesDir)); } for (Class clazz : classes) { String classPath = clazz.getName().replace(".", "/") + ".class"; String classFileName = classPath; if (classPath.lastIndexOf("/") > -1) { classFileName = classPath.substring(classPath.lastIndexOf("/") + 1); } String packagePath = new File(classPath).getParent(); File dir = new File(classesDir, packagePath); if (!dir.exists()) { if (!dir.mkdirs()) { throw new IOException(MessageFormat.format("could not create dir [{0}]", dir)); } } InputStream is = getResource(classPath); OutputStream classOS = new FileOutputStream(new File(dir, classFileName)); IOUtils.copy(is, classOS); } JarOutputStream zos = new JarOutputStream(os, new Manifest()); IOUtils.zipDir(classesDir, "", zos); IOUtils.delete(classesDir); } /** * Finds a public-static method by name in a class. * <p/> * In case of method overloading it will return the first method found. * * @param className name to look for the method. * @param methodName method name to look in the class. * @return the <code>Method</code> instance. * @throws IllegalArgumentException thrown if the method does not exist, * it is not public or it is not static. */ public static Method findMethod(String className, String methodName) { Check.notEmpty(className, "className"); Check.notEmpty(methodName, "methodName"); Method method = null; try { Class klass = Thread.currentThread().getContextClassLoader().loadClass(className); for (Method m : klass.getMethods()) { if (m.getName().equals(methodName)) { method = m; break; } } if (method == null) { throw new IllegalArgumentException(MessageFormat.format("class#method not found [{0}#{1}]", className, methodName)); } if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) { throw new IllegalArgumentException(MessageFormat.format( "class#method does not have PUBLIC or STATIC modifier [{0}#{1}]", className, methodName)); } } catch (ClassNotFoundException ex) { throw new IllegalArgumentException(MessageFormat.format("class not found [{0}]", className)); } return method; } /** * Finds a constant by name in a class. * <p/> * * @param className name to look for the method. * @param constantName constant name to look in the class. * @return the constant instance. * @throws IllegalArgumentException thrown if the constant does not exist, * it is not public or it is not static. */ public static Object findConstant(String className, String constantName) throws ServiceException { Check.notEmpty(className, "className"); Check.notEmpty(constantName, "constantName"); try { Class klass = Thread.currentThread().getContextClassLoader().loadClass(className); Field field = klass.getField(constantName); if ((field.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) { throw new IllegalArgumentException(MessageFormat.format( "class#constant does not have PUBLIC or STATIC modifier [{0}#{1}]", className, constantName)); } return field.get(null); } catch (IllegalAccessException ex) { throw new IllegalArgumentException(ex); } catch (NoSuchFieldException ex) { throw new IllegalArgumentException(MessageFormat.format("class#constant not found [{0}#{1}]", className, constantName)); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException(MessageFormat.format("class not found [{0}]", className)); } } }