001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.q2;
020
021import javax.management.*;
022import java.io.File;
023import java.io.FileFilter;
024import java.net.MalformedURLException;
025import java.net.URL;
026import java.net.URLClassLoader;
027import java.security.PrivilegedAction;
028
029/**
030 * Q2 Class Loader (scans deploy/lib directory for new jars)
031 *
032 * @author <a href="mailto:apr@cs.com.uy">Alejandro P. Revilla</a>
033 * @author <a href="mailto:taherkordy@dpi2.dpi.net.ir">Alireza Taherkordi</a>
034 * @version $Revision$ $Date$
035 */
036@SuppressWarnings("deprecation")
037public class QClassLoader
038    extends URLClassLoader 
039    implements QClassLoaderMBean, FileFilter {
040    File libDir;
041    ObjectName loaderName;
042    MBeanServer server;
043    long lastModified;
044
045    /**
046     * Constructs a Q2 classloader and registers it with the given MBean server.
047     *
048     * @param server MBean server hosting this classloader
049     * @param libDir directory scanned for {@code .jar} files
050     * @param loaderName JMX name under which this loader is registered
051     * @param mainClassLoader parent class loader
052     */
053    public QClassLoader
054        (MBeanServer server, File libDir, ObjectName loaderName,
055         ClassLoader mainClassLoader)
056    {
057        super(new URL[] { }, mainClassLoader);
058        this.loaderName = loaderName;
059        this.libDir     = libDir;
060        this.server     = server;
061    }
062    
063    public void addURL (String url) throws MalformedURLException {
064        addURL (new URL (url));
065    }
066    
067    public boolean accept (File f) {
068        return f.getName().endsWith (".jar");
069    }
070
071    /**
072     * Indicates whether the watched lib directory has changed since the last scan.
073     *
074     * @return {@code true} if {@link #libDir} is readable and its mtime has advanced
075     */
076    public boolean isModified () {
077        return libDir.canRead () && lastModified != libDir.lastModified();
078    }
079    /**
080     * Re-scans the lib directory, replacing the live class loader if needed,
081     * and returns the up-to-date loader.
082     *
083     * @param forceNewClassLoader force a fresh loader even when the directory is unchanged
084     * @return the (possibly new) classloader
085     * @throws InstanceAlreadyExistsException if the loader can't be re-registered
086     * @throws InstanceNotFoundException if the previous loader can't be unregistered
087     * @throws NotCompliantMBeanException if the loader fails MBean compliance checks
088     * @throws MBeanRegistrationException if MBean (un)registration fails
089     */
090    public QClassLoader scan (boolean forceNewClassLoader)
091        throws InstanceAlreadyExistsException,
092               InstanceNotFoundException,
093               NotCompliantMBeanException,
094               MBeanRegistrationException
095    
096    {
097        if (!isModified () && !forceNewClassLoader || !libDir.canRead())
098            return this;
099        QClassLoader loader;
100        if (server.isRegistered (loaderName)) {
101            server.unregisterMBean (loaderName);
102            loader = new QClassLoader(server, libDir, loaderName, getParent());
103        } else
104            loader = this;
105
106        File file[] = libDir.listFiles (this);
107        for (File aFile : file) {
108            try {
109                loader.addURL(aFile.toURL());
110            } catch (MalformedURLException e) {
111                e.printStackTrace();
112            }
113        }
114        loader.lastModified = libDir.lastModified ();
115        server.registerMBean (loader, loaderName);
116        return loader;
117    }
118    /** Forces {@link #scan(boolean)} to instantiate a fresh classloader on its next call. */
119    public void forceNewClassLoaderOnNextScan() {
120        this.lastModified = 0L;
121    }
122}
123