SHARE
TWEET

Dynamic method invocation in Java 6

a guest Sep 9th, 2010 191 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * DynTool.java
  3.  */
  4. package at.mwildam.common;
  5.  
  6. import java.beans.Expression;
  7. import java.io.File;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.lang.reflect.Method;
  10. import java.net.MalformedURLException;
  11. import java.net.URL;
  12. import java.net.URLClassLoader;
  13. import java.util.Date;
  14. import java.util.logging.Level;
  15. import java.util.logging.Logger;
  16. import javax.tools.JavaCompiler;
  17.  
  18.  
  19. /**
  20.  * Dynamic class loading and method invocation
  21.  * <p>
  22.  * By <a href="http://www.google.com/profiles/mwildam" target="_blank">Martin Wildam</a>
  23.  * <p>
  24.  * @author Martin Wildam
  25.  */
  26. public class DynTool
  27. {
  28.     /**
  29.      * Tries to instanciate an object of the given class and returns a pointer to it.
  30.      * <p>
  31.      * Expects the class files in the directory given by the
  32.      * <i>dynClassPath</i> parameter. For each dot (.) in the class path there
  33.      * must be given a subfolder. If there exists a .java file instead of
  34.      * the expected .class file then the method tries to compile the .java file.
  35.      * It tries also a compile if both the .java and the .class file exists but
  36.      * the .class file is out of date (older than the .java file.
  37.      * The <i>addClassPath</i> parameter is only used if the attempt to
  38.      * compile is done.
  39.      * <p>
  40.      * You can also specify a jar file in the <i>dynClassPath</i> parameter if
  41.      * you have a fully compiled and prepared package.
  42.      * <p>
  43.      * Returns null if the object could not be created.
  44.      * <p>
  45.      * Sample: getPluginInstance("/Work/Java/TestGUI/dist/TestGUI.jar", "testgui.TestPluginClass", "/Work/Java/TestGUI/dist/lib")
  46.      * <p>
  47.      * <b>Note:</b> Dynamic class loading and calls evaluated during runtime
  48.      * using reflection is time consuming in general and therefore should be
  49.      * avoided for performance citrical operations (although already better
  50.      * since Java 5).
  51.      * <p>
  52.      * We are returning null here in error case although not recommended
  53.      * because I do agree with <a href="http://www.joelonsoftware.com/">Joel Spolsky</a> on
  54.      * <a href="http://www.joelonsoftware.com/items/2003/10/13.html">Exceptions</a>.
  55.      *
  56.      * @param   dynClassPath path to classes root dir where to search for the class to instantiate.
  57.      *          Can also be a .jar file.
  58.      * @param   className Full class name for the plugin class to use.
  59.      * @param   addClassPath is an additional option for classpath to search (;-separated)
  60.      *          which is only used when a compile attempt is done.
  61.      * @return  Object or null in error case.
  62.      */
  63.     public static Object getPluginInstance(String dynClassPath, String className, String addClassPath)
  64.     {
  65.         if (!dynClassPath.toLowerCase().endsWith(".jar"))
  66.         {
  67.             String classFile = className.replace(".", "/");
  68.             classFile = dynClassPath + "/" + classFile;
  69.             String javaFile = classFile;
  70.             javaFile += ".java";
  71.             classFile += ".class";
  72.             if (addClassPath.length() != 0) addClassPath = ";" + addClassPath;
  73.  
  74.             if ((!existsFile(classFile) && existsFile(javaFile))
  75.                     || getFileDate(classFile).before(getFileDate(javaFile)))
  76.             {
  77.                 JavaCompiler jc = javax.tools.ToolProvider.getSystemJavaCompiler();
  78.                 int r = jc.run(null, null, null, "-classpath", dynClassPath + addClassPath, "-d", dynClassPath, javaFile);
  79.                 if (r != 0) return null;
  80.             }
  81.         }
  82.         else
  83.             addFileToClassPath(dynClassPath);
  84.  
  85.         try
  86.         {
  87.             if (addClassPath != null && addClassPath.length() > 0)
  88.                 addFilesToClassPath(addClassPath);
  89.             URL url = new URL(DynTool.getUrlFromPath(dynClassPath));
  90.             URL[] clsList = new URL[1];
  91.             clsList[0] = url;
  92.             URLClassLoader ucl = new URLClassLoader(clsList);
  93.             Class cls = ucl.loadClass(className);
  94.             return cls.newInstance();
  95.         }
  96.         catch (NoClassDefFoundError ex)
  97.         {
  98.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  99.             return null;
  100.         }
  101.         catch (ClassNotFoundException ex)
  102.         {
  103.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  104.             return null;
  105.         }
  106.         catch (InstantiationException ex)
  107.         {
  108.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  109.             return null;
  110.         }
  111.         catch (IllegalAccessException ex)
  112.         {
  113.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  114.             return null;
  115.         }
  116.         catch (MalformedURLException ex)
  117.         {
  118.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  119.             return null;
  120.         }
  121.     }
  122.  
  123.  
  124.     /**
  125.      * Late binding method call on an already instantiated object.
  126.      * <p>
  127.      * Invokes the requested method of a given object instance where the
  128.      * object class is not specified at compile time.
  129.      * <p>
  130.      * To call a method that does not have a parameter then pass null for the
  131.      * <i>params</i> parameters.
  132.      * <p>
  133.      * What the method returns is routed to the caller of this method as Object
  134.      * so you have to cast the return type to something more specific if needed
  135.      * or just use .tostring. If the called method is declared void then this
  136.      * method returns null.
  137.      * <p>
  138.      * <b>Note:</b> Dynamic class loading and calls evaluated during runtime
  139.      * using reflection is time consuming in general and therefore should be
  140.      * avoided for performance critical operations.
  141.      * <p>
  142.      * We are returning null here in error case although not recommended
  143.      * because I do agree with <a href="http://www.joelonsoftware.com/">Joel Spolsky</a> on
  144.      * <a href="http://www.joelonsoftware.com/items/2003/10/13.html">Exceptions</a>.
  145.      *
  146.      * @param   instance A not well known object for that we hope to be able to
  147.      *          call the requested method.
  148.      * @param   methodName Name of the method to be called
  149.      * @param   params parameter objects to pass to the method (best matching declaration variant is searched)
  150.      * @return  Returned object or null if method is declared void or an error occurred.
  151.      */
  152.     public static Object call(Object instance, String methodName, Object... params)
  153.     {
  154.         //Statement stmt = new Statement(obj, methodName, null);
  155.         //stmt.execute();
  156.  
  157.         Expression expr = new Expression(instance, methodName, params);
  158.         //expr.execute(); //Not necessary, called automatically on getValue();
  159.         Object result = null;
  160.         try
  161.         {
  162.             result = expr.getValue();
  163.         }
  164.         catch (Exception ex)
  165.         {
  166.             Logger.getLogger(DynTool.class.getName()).log(Level.SEVERE, null, ex);
  167.         }
  168.         return result;
  169.     }
  170.  
  171.  
  172.     /**
  173.      * Adds a resource given as URL to the classpath.
  174.      * <p>
  175.      * Adds the given url to the classpath dynamically.
  176.      * <p>
  177.      * From antony_miguel at
  178.      * <a href="http://forums.sun.com/thread.jspa?threadID=300557&start=0&tstart=0">http://forums.sun.com/thread.jspa?threadID=300557</a>:
  179.      * <blockquote><i>I've seen a lot of forum posts about how to modify the
  180.      * classpath at runtime and a lot of answers saying it can't be done.
  181.      * I needed to add JDBC driver JARs at runtime so I figured out the
  182.      * following method.
  183.      * <p>
  184.      * The system classloader (ClassLoader.getSystemClassLoader()) is a subclass
  185.      * of URLClassLoader. It can therefore be casted into a URLClassLoader and
  186.      * used as one.
  187.      * <p>
  188.      * URLClassLoader has a protected method addURL(URL url), which you can use
  189.      * to add files, jars, web addresses - any valid URL in fact.
  190.      * <p>
  191.      * Since the method is protected you need to use reflection to invoke it.
  192.      * </i></blockquote>
  193.      * <p>
  194.      * The class path change does not reflect in the system property
  195.      * "" because that property does not get modified any more after application
  196.      * start. So don't check success by checking
  197.      * <i>System.getProperty("java.class.path");</i>.
  198.      *
  199.      * @param   url Url to add to the class path.
  200.      * @return  True if operation was successful
  201.      */
  202.     public static boolean addUrlToClassPath(URL url)
  203.     {
  204.         if (url == null)
  205.         {
  206.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, "Missing url to add to classpath.");
  207.             return false;
  208.         }
  209.  
  210.         boolean b = false;
  211.  
  212.         URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
  213.         Class sysclass = URLClassLoader.class;
  214.         try
  215.         {
  216.  
  217.             Class[] methodParams = new Class[1];
  218.             methodParams[0] = URL.class;
  219.             //There could be really different classes in the array.
  220.             @SuppressWarnings("unchecked")
  221.             Method method = sysclass.getDeclaredMethod("addURL", methodParams);
  222.             method.setAccessible(true);
  223.             method.invoke(sysloader, new Object[]
  224.                     {
  225.                         url
  226.                     });
  227.             b = true;
  228.         }
  229.         catch (IllegalAccessException ex)
  230.         {
  231.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  232.         }
  233.         catch (IllegalArgumentException ex)
  234.         {
  235.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  236.         }
  237.         catch (InvocationTargetException ex)
  238.         {
  239.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  240.         }
  241.         catch (NoSuchMethodException ex)
  242.         {
  243.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  244.         }
  245.         catch (SecurityException ex)
  246.         {
  247.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  248.         }
  249.  
  250.         return b;
  251.     }
  252.  
  253.  
  254.     /**
  255.      * Converts a file path to an url link.
  256.      * <p>
  257.      * Returns the url link equivalent to the given path as string.
  258.      *
  259.      * @param   path
  260.      * @return  Url string
  261.      */
  262.     public static String getUrlFromPath(String path)
  263.     {
  264.         try
  265.         {
  266.             return new File(path).toURI().toURL().toString();
  267.         }
  268.         catch (MalformedURLException ex)
  269.         {
  270.             return "";
  271.         }
  272.     }
  273.  
  274.  
  275.     /**
  276.      * Returns a boolean true if a "normal" file with the specified name exists.
  277.      * <p>
  278.      * Note: Returns false for files that are part of the kernel system.
  279.      * So it returns true only for "normal" files.
  280.      *
  281.      * @param   fileFullName FQPN of the file to be searched for
  282.      * @return  Boolean
  283.      */
  284.     public static Boolean existsFile(String fileFullName)
  285.     {
  286.         if (fileFullName == null)
  287.             return false;
  288.         else
  289.         {
  290.             File f = new File(fileFullName);
  291.             return f.exists() && f.isFile();
  292.         }
  293.     }
  294.  
  295.  
  296.     /**
  297.      * Returns the timestamp of the file with the given name if exists otherwise 0.
  298.      * <p>
  299.      * Returns a Date of 0 if the file could not be found otherwise the last
  300.      * file modification date.
  301.      *
  302.      * @param   fileName Name of the file from which to read the date and timestamp
  303.      * @return  Date
  304.      */
  305.     public static Date getFileDate(String fileName)
  306.     {
  307.         if (!existsFile(fileName))
  308.             return new Date(0);
  309.         else
  310.         {
  311.             File f = new File(fileName);
  312.             return new Date(f.lastModified());
  313.         }
  314.     }
  315.  
  316.  
  317.     /**
  318.      * Adds the given file dynamically to the class path.
  319.      * <p>
  320.      * Further details see {@link #addUrlToClassPath(java.net.URL) }.
  321.      *
  322.      * @param   file File object to be added dynamically to the class path.
  323.      * @return  True if operation was successful.
  324.      */
  325.     public static boolean addFileToClassPath(File file)
  326.     {
  327.         if (file == null)
  328.         {
  329.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, "Missing file to add to classpath.");
  330.             return false;
  331.         }
  332.  
  333.         URL url;
  334.         try
  335.         {
  336.             url = file.toURI().toURL();
  337.         }
  338.         catch (MalformedURLException ex)
  339.         {
  340.             Logger.getLogger(DynTool.class.getName()).log(Level.WARNING, null, ex);
  341.             return false;
  342.         }
  343.  
  344.         return addUrlToClassPath(url);
  345.     }
  346.  
  347.  
  348.     /**
  349.      * Adds the given file dynamically to the class path.
  350.      * <p>
  351.      * Further details see {@link #addUrlToClassPath(java.net.URL) }.
  352.      *
  353.      * @param   fileName FQPN of the file object to be added dynamically to the class path.
  354.      * @return  True if operation was successful.
  355.      */
  356.     public static boolean addFileToClassPath(String fileName)
  357.     {
  358.         return addFileToClassPath(new File(fileName));
  359.     }
  360.  
  361.  
  362.     /**
  363.      * Adds files in the given folder dynamically to the class path.
  364.      * <p>
  365.      * You can specify multiple paths separating them by ";".
  366.      * Further details see {@link #addUrlToClassPath(java.net.URL) }.
  367.      * Path must contain only jars and class files (subfolders not included).
  368.      *
  369.      * @param   path FQPN of the path to be added dynamically to the class path
  370.      *          or multiple paths separated by ";".
  371.      * @return  Number of jars and class files added.
  372.      */
  373.     public static int addFilesToClassPath(String path)
  374.     {
  375.         if (path == null || path.length() == 0) return 0;
  376.         int n = 0;
  377.  
  378.         if (path.contains(";"))
  379.         {
  380.             String[] subPaths = path.split(";");
  381.             for (String subPath : subPaths)
  382.             {
  383.                 n = n + addFilesToClassPath(subPath);
  384.             }
  385.         }
  386.         else
  387.         {
  388.             File dir = new File(path);
  389.             File[] contents = dir.listFiles(); //Path Must contain jars and class files only
  390.             for (int i = 0; i < contents.length; i++)
  391.             {
  392.                 File file = contents[i];
  393.                 if (addFileToClassPath(file))
  394.                     n++;
  395.             }
  396.         }
  397.         return n;
  398.     }
  399.  
  400.  
  401. }
RAW Paste Data
Pastebin PRO Summer Special!
Get 60% OFF on Pastebin PRO accounts!
Top