When looking to improve your work environment or pushing to use a new method, it’s not uncommon to go looking for what might be considered Best Practice in that area. It’s as natural and common as it is problematic and dangerous.
First of all, the very notion of a Best Practice is that there is some way of performing a task or method that is superior to all other possible ways of performing that task or method. It is also suggested that the practice is superior in all aspects, meaning it delivers the most value and costs the least resources (if applicable).
So, what’s the problem with wanting to find, and ultimately putting to use, best practices? Wouldn’t it be a great thing if we could find more best practices and let all interested parties in the world share as many best practices as possible? In theory; yes. In practice; no thanks.
The problem with many things marketed as best practices is that it’s not. It is extraordinarily uncommon for a technique och method to be so absolutely, overwhelmingly better than all other ways in which you could perform a task that it could be declared a true and undisputed Best Practice. That’s not to say there aren’t any Best Practices, I’m believe there are at least a few lurking about in this great universe of ours, it’s just that they’re not so common as we would think.
Take the field I’m working in (and probably you as well, since you’re stuck reading this): IT. Software Development. What’s the best way to develop software? What’s the best way to handle source code? What’s the best way to make sure we stay on track, do the right thing and always deliver maximum business value for the least amount of resources while boosting the user and developer experience and maximizing shareholder profits? Please let me know the best practice for that! No really, please do!
The obvious answer is that IT is a way to complex field to have any large, powerful Best Practices that all can agree upon. If you don’t agree, tell me the best computer language the world agreed upon. Or the best web framework. The catch being here that different people and organizations have different best practices, which serves to make them anything but unequivocally best. Of course, there is no single best computer language or web framework that is best for all cases.
So, if best practices are super-rare, bordering on non-existant, what should we do when we want to know how people smarter than us have solved a recurring problem we have just faced? We should do like other industries, look for Good Practices. In many industries there are several agreed upon Good Practices that form the basis for making sure we do not all spend obscene amount of times reinventing an inferior wheel.
Focusing on Good Practices instead of the mythical Best Practices has many benefits: A practice that has proven to work in a good fashion for a team doesn not have the burden of proof as to wether it is also the absolute best practice concievable meaning there should be more good practices to choose from. As there are many, competing good practices you are encouraged to constantly inspect, adapt and revise any decisions to go with any particular practice, which is great since that causes you to pay ongoing attention to how you work and how your practices are working for your particular environment and people. Focusing on good practices also removes the stigma of trying on a Best Practice and finding it doesn’t deliver for your needs. If it truly was best, you must be doing something wrong. If the practice you tried was merely good, you can just keep looking or adapting the practice.
To round up: stop looking for Best Practices and embrace imperfection and ongoing improvement by going for Good Practices. I’ll follow this up by posting some good practices I’ve used in projects over the following months. Stay tuned!
Find Java classes in the classpath
My brother Oskar needed to read some Java classes from disk, so I whipped up some code to show a possible way. Then I figured you might like it as well.
Here it is (and find the latest version at GitHub where you can submit your improved fork)
Ain’t java short and sweet?
package com.palbrattberg.classpathtools; import java.io.File; import java.io.FilenameFilter; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Find classes in the classpath (reads JARs and classpath folders). * * @author Pål Brattberg, brattberg@gmail.com * @see http://gist.github.com/pal */ @SuppressWarnings("unchecked") public class ClasspathInspector { static boolean DEBUG = false; public static List<Class> getAllKnownClasses() { List<Class> classFiles = new ArrayList<Class>(); List<File> classLocations = getClassLocationsForCurrentClasspath(); for (File file : classLocations) { classFiles.addAll(getClassesFromPath(file)); } return classFiles; } public static List<Class> getMatchingClasses(Class interfaceOrSuperclass) { List<Class> matchingClasses = new ArrayList<Class>(); List<Class> classes = getAllKnownClasses(); log("checking %s classes", classes.size()); for (Class clazz : classes) { if (interfaceOrSuperclass.isAssignableFrom(clazz)) { matchingClasses.add(clazz); log("class %s is assignable from %s", interfaceOrSuperclass, clazz); } } return matchingClasses; } public static List<Class> getMatchingClasses(String validPackagePrefix, Class interfaceOrSuperclass) { throw new IllegalStateException("Not yet implemented!"); } public static List<Class> getMatchingClasses(String validPackagePrefix) { throw new IllegalStateException("Not yet implemented!"); } private static Collection<? extends Class> getClassesFromPath(File path) { if (path.isDirectory()) { return getClassesFromDirectory(path); } else { return getClassesFromJarFile(path); } } private static String fromFileToClassName(final String fileName) { return fileName.substring(0, fileName.length() - 6).replaceAll("/|\\\\", "\\."); } private static List<Class> getClassesFromJarFile(File path) { List<Class> classes = new ArrayList<Class>(); log("getClassesFromJarFile: Getting classes for %s", path); try { if (path.canRead()) { JarFile jar = new JarFile(path); Enumeration<JarEntry> en = jar.entries(); while (en.hasMoreElements()) { JarEntry entry = en.nextElement(); if (entry.getName().endsWith("class")) { String className = fromFileToClassName(entry.getName()); log("\tgetClassesFromJarFile: found %s", className); loadClass(classes, className); } } } } catch (Exception e) { throw new RuntimeException("Failed to read classes from jar file: " + path, e); } return classes; } private static List<Class> getClassesFromDirectory(File path) { List<Class> classes = new ArrayList<Class>(); log("getClassesFromDirectory: Getting classes for " + path); // get jar files from top-level directory List<File> jarFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }, false); for (File file : jarFiles) { classes.addAll(getClassesFromJarFile(file)); } // get all class-files List<File> classFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".class"); } }, true); // List<URL> urlList = new ArrayList<URL>(); // List<String> classNameList = new ArrayList<String>(); int substringBeginIndex = path.getAbsolutePath().length() + 1; for (File classfile : classFiles) { String className = classfile.getAbsolutePath().substring(substringBeginIndex); className = fromFileToClassName(className); log("Found class %s in path %s: ", className, path); loadClass(classes, className); } return classes; } private static List<File> listFiles(File directory, FilenameFilter filter, boolean recurse) { List<File> files = new ArrayList<File>(); File[] entries = directory.listFiles(); // Go over entries for (File entry : entries) { // If there is no filter or the filter accepts the // file / directory, add it to the list if (filter == null || filter.accept(directory, entry.getName())) { files.add(entry); } // If the file is a directory and the recurse flag // is set, recurse into the directory if (recurse && entry.isDirectory()) { files.addAll(listFiles(entry, filter, recurse)); } } // Return collection of files return files; } public static List<File> getClassLocationsForCurrentClasspath() { List<File> urls = new ArrayList<File>(); String javaClassPath = System.getProperty("java.class.path"); if (javaClassPath != null) { for (String path : javaClassPath.split(File.pathSeparator)) { urls.add(new File(path)); } } return urls; } // todo: this is only partial, probably public static URL normalize(URL url) throws MalformedURLException { String spec = url.getFile(); // get url base - remove everything after ".jar!/??" , if exists final int i = spec.indexOf("!/"); if (i != -1) { spec = spec.substring(0, spec.indexOf("!/")); } // uppercase windows drive url = new URL(url, spec); final String file = url.getFile(); final int i1 = file.indexOf(':'); if (i1 != -1) { String drive = file.substring(i1 - 1, 2).toUpperCase(); url = new URL(url, file.substring(0, i1 - 1) + drive + file.substring(i1)); } return url; } private static void log(String pattern, final Object... args) { if (DEBUG) System.out.printf(pattern + "\n", args); } private static void loadClass(List<Class> classes, String className) { try { Class claz = Class.forName(className, false, ClassLoader.getSystemClassLoader()); classes.add(claz); } catch (ClassNotFoundException cnfe) { log("ClassNotFoundException: Could not load class %s: %s", className, cnfe); } catch (NoClassDefFoundError e) { log("NoClassDefFoundError: Could not load class %s: %s", className, e); } } public static void main(String[] args) { // find all classes in classpath List<Class> allClasses = ClasspathInspector.getAllKnownClasses(); System.out.printf("There are %s classes available in the classpath\n", allClasses.size()); // find all classes that implement/subclass an interface/superclass List<Class> serializableClasses = ClasspathInspector.getMatchingClasses(Serializable.class); for (Class clazz : serializableClasses) { System.out.printf("%s is Serializable\n", clazz); } System.out.printf("There are %s Serializable classes available in the classpath\n", serializableClasses.size()); } }Gimme closures baby!