Commits

Anonymous committed 5fb78bb

Moving ReloadingClassLoader impl from the Convention plugin into XWork

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@2018e221344d-f017-0410-9bd5-d282ab1896d7

  • Participants
  • Parent commits c1bc28c

Comments (0)

Files changed (5)

core/src/main/java/com/opensymphony/xwork2/util/classloader/FileResourceStore.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Reads a class from disk
+ *  class taken from Apache JCI
+ */
+public final class FileResourceStore implements ResourceStore {
+    private static final Logger LOG = LoggerFactory.getLogger(FileResourceStore.class);
+    private final File root;
+
+    public FileResourceStore(final File pFile) {
+        root = pFile;
+    }
+
+    public byte[] read(final String pResourceName) {
+        FileInputStream fis = null;
+        try {
+            File file = getFile(pResourceName);
+            byte[] data = new byte[(int) file.length()];
+            fis = new FileInputStream(file);
+            fis.read(data);
+
+            return data;
+        } catch (Exception e) {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Unable to read file [#0]", e, pResourceName);
+            return null;
+        } finally {
+            closeQuietly(fis);
+        }
+    }
+
+    public void write(final String pResourceName, final byte[] pData) {
+
+    }
+
+    private void closeQuietly(InputStream is) {
+        try {
+            if (is != null)
+                is.close();
+        } catch (IOException e) {
+            if (LOG.isErrorEnabled())
+                LOG.error("Unable to close file input stream", e);
+        }
+    }
+
+    private File getFile(final String pResourceName) {
+        final String fileName = pResourceName.replace('/', File.separatorChar);
+        return new File(root, fileName);
+    }
+
+    public String toString() {
+        return this.getClass().getName() + root.toString();
+    }
+}

core/src/main/java/com/opensymphony/xwork2/util/classloader/JarResourceStore.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+package com.opensymphony.xwork2.util.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+import java.io.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Read resources from a jar file
+ */
+public class JarResourceStore implements ResourceStore {
+    private static final Logger LOG = LoggerFactory.getLogger(JarResourceStore.class);
+
+    private final File file;
+
+    public JarResourceStore(File file) {
+        this.file = file;
+    }
+
+    public void write(String pResourceName, byte[] pResourceData) {
+    }
+
+    public byte[] read(String pResourceName) {
+        InputStream in = null;
+        try {
+            ZipFile jarFile = new ZipFile(file);
+            ZipEntry entry = jarFile.getEntry(pResourceName);
+
+            //read into byte array
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            in = jarFile.getInputStream(entry);
+            copy(in, out);
+
+            return out.toByteArray();
+        } catch (Exception e) {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Unable to read file [#0] from [#1]", e, pResourceName, file.getName());
+            return null;
+        } finally {
+            closeQuietly(in);
+        }
+    }
+
+    public static long copy(InputStream input, OutputStream output)
+            throws IOException {
+        byte[] buffer = new byte[1024 * 4];
+        long count = 0;
+        int n = 0;
+        while (-1 != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    private void closeQuietly(InputStream is) {
+        try {
+            if (is != null)
+                is.close();
+        } catch (IOException e) {
+            if (LOG.isErrorEnabled())
+                LOG.error("Unable to close input stream", e);
+        }
+    }
+}

core/src/main/java/com/opensymphony/xwork2/util/classloader/ReloadingClassLoader.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.classloader.FileResourceStore;
+import com.opensymphony.xwork2.XWorkException;
+
+import java.io.InputStream;
+import java.io.File;
+import java.net.URL;
+import java.net.URISyntaxException;
+
+/**
+ * The ReloadingClassLoader uses a delegation mechanism to allow
+ * classes to be reloaded. That means that loadClass calls may
+ * return different results if the class was changed in the underlying
+ * ResourceStore.
+ * <p/>
+ * class taken from Apache JCI
+ */
+public class ReloadingClassLoader extends ClassLoader {
+    private static final Logger LOG = LoggerFactory.getLogger(ReloadingClassLoader.class);
+    private final ClassLoader parent;
+    private ResourceStore[] stores;
+    private ClassLoader delegate;
+
+    public ReloadingClassLoader(final ClassLoader pParent) {
+        super(pParent);
+        parent = pParent;
+        URL root = pParent.getResource("");
+        try {
+            if (root != null) {
+                stores = new ResourceStore[]{new FileResourceStore(new File(root.toURI()))};
+            }
+            else {
+                throw new XWorkException("Unable to start the reloadable class loader, consider setting 'struts.convention.classes.reload' to false");
+            }
+        } catch (URISyntaxException e) {
+            throw new XWorkException("Unable to start the reloadable class loader, consider setting 'struts.convention.classes.reload' to false", e);
+        } catch (RuntimeException e) {
+            // see WW-3121
+            // TODO: Fix this for a reloading mechanism to be marked as stable
+            if (root != null)
+                LOG.error("Exception while trying to build the ResourceStore for URL [#0]", e, root.toString());
+            else
+                 LOG.error("Exception while trying to get root resource from class loader", e);
+            LOG.error("Consider setting struts.convention.classes.reload=false");
+            throw e;
+        }
+
+        delegate = new ResourceStoreClassLoader(parent, stores);
+    }
+
+    public boolean addResourceStore(final ResourceStore pStore) {
+        try {
+            final int n = stores.length;
+            final ResourceStore[] newStores = new ResourceStore[n + 1];
+            System.arraycopy(stores, 0, newStores, 1, n);
+            newStores[0] = pStore;
+            stores = newStores;
+            delegate = new ResourceStoreClassLoader(parent, stores);
+            return true;
+        } catch (final RuntimeException e) {
+            LOG.error("Could not add resource store", e);
+        }
+        return false;
+    }
+
+    public boolean removeResourceStore(final ResourceStore pStore) {
+
+        final int n = stores.length;
+        int i = 0;
+
+        // FIXME: this should be improved with a Map
+        // find the pStore and index position with var i
+        while ((i < n) && (stores[i] != pStore)) {
+            i++;
+        }
+
+        // pStore was not found
+        if (i == n) {
+            return false;
+        }
+
+        // if stores length > 1 then array copy old values, else create new empty store
+        final ResourceStore[] newStores = new ResourceStore[n - 1];
+        if (i > 0) {
+            System.arraycopy(stores, 0, newStores, 0, i);
+        }
+        if (i < n - 1) {
+            System.arraycopy(stores, i + 1, newStores, i, (n - i - 1));
+        }
+
+        stores = newStores;
+        delegate = new ResourceStoreClassLoader(parent, stores);
+        return true;
+    }
+
+    public void reload() {
+        if (LOG.isTraceEnabled())
+            LOG.trace("Reloading class loader");
+        delegate = new ResourceStoreClassLoader(parent, stores);
+    }
+
+    public void clearAssertionStatus() {
+        delegate.clearAssertionStatus();
+    }
+
+    public URL getResource(String name) {
+        return delegate.getResource(name);
+    }
+
+    public InputStream getResourceAsStream(String name) {
+        return delegate.getResourceAsStream(name);
+    }
+
+    public Class loadClass(String name) throws ClassNotFoundException {
+        return delegate.loadClass(name);
+    }
+
+    public void setClassAssertionStatus(String className, boolean enabled) {
+        delegate.setClassAssertionStatus(className, enabled);
+    }
+
+    public void setDefaultAssertionStatus(boolean enabled) {
+        delegate.setDefaultAssertionStatus(enabled);
+    }
+
+    public void setPackageAssertionStatus(String packageName, boolean enabled) {
+        delegate.setPackageAssertionStatus(packageName, enabled);
+    }
+}
+

core/src/main/java/com/opensymphony/xwork2/util/classloader/ResourceStore.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util.classloader;
+
+/**
+ * *interface taken from Apache JCI
+ */
+public interface ResourceStore {
+
+    void write(final String pResourceName, final byte[] pResourceData);
+
+    byte[] read(final String pResourceName);
+}
+

core/src/main/java/com/opensymphony/xwork2/util/classloader/ResourceStoreClassLoader.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * class taken from Apache JCI
+ */
+public final class ResourceStoreClassLoader extends ClassLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ResourceStoreClassLoader.class);
+
+    private final ResourceStore[] stores;
+
+    public ResourceStoreClassLoader(final ClassLoader pParent, final ResourceStore[] pStores) {
+        super(pParent);
+
+        stores = new ResourceStore[pStores.length];
+        System.arraycopy(pStores, 0, stores, 0, stores.length);
+    }
+
+    private Class fastFindClass(final String name) {
+
+        if (stores != null) {
+            for (final ResourceStore store : stores) {
+                final byte[] clazzBytes = store.read(name.replace('.', '/') + ".class");
+                if (clazzBytes != null) {
+                    return defineClass(name, clazzBytes, 0, clazzBytes.length);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Class clazz = findLoadedClass(name);
+
+        if (clazz == null) {
+            clazz = fastFindClass(name);
+
+            if (clazz == null) {
+                final ClassLoader parent = getParent();
+                if (parent != null) {
+                    clazz = parent.loadClass(name);
+                } else {
+                    throw new ClassNotFoundException(name);
+                }
+
+            }
+        }
+
+        if (resolve) {
+            resolveClass(clazz);
+        }
+
+        return clazz;
+    }
+
+    protected Class findClass(final String name) throws ClassNotFoundException {
+        final Class clazz = fastFindClass(name);
+        if (clazz == null) {
+            throw new ClassNotFoundException(name);
+        }
+        return clazz;
+    }
+}