169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/*
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Javassist, a Java-bytecode translator toolkit.
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * The contents of this file are subject to the Mozilla Public License Version
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1.1 (the "License"); you may not use this file except in compliance with
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the License.  Alternatively, the contents of this file may be used under
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the terms of the GNU Lesser General Public License Version 2.1 or later.
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Software distributed under the License is distributed on an "AS IS" basis,
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * for the specific language governing rights and limitations under the
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * License.
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage javassist;
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.*;
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.net.*;
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * A class search-path specified with URL (http).
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see javassist.ClassPath
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see ClassPool#insertClassPath(ClassPath)
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see ClassPool#appendClassPath(ClassPath)
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class URLClassPath implements ClassPath {
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected String hostname;
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected int port;
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected String directory;
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected String packageName;
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Creates a search path specified with URL (http).
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>This search path is used only if a requested
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * class name starts with the name specified by <code>packageName</code>.
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * If <code>packageName</code> is "org.javassist." and a requested class is
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * "org.javassist.test.Main", then the given URL is used for loading that class.
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The <code>URLClassPath</code> obtains a class file from:
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <ul><pre>http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * </pre></ul>
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>Here, we assume that <code>host</code> is "www.javassist.org",
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <code>port</code> is 80, and <code>directory</code> is "/java/classes/".
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>If <code>packageName</code> is <code>null</code>, the URL is used
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * for loading any class.
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param host              host name
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param port              port number
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param directory         directory name ending with "/".
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *                          It can be "/" (root directory).
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *                          It must start with "/".
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param packageName       package name.  It must end with "." (dot).
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public URLClassPath(String host, int port,
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        String directory, String packageName) {
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        hostname = host;
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this.port = port;
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this.directory = directory;
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this.packageName = packageName;
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public String toString() {
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return hostname + ":" + port + directory;
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Opens a class file with http.
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return null if the class file could not be found.
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public InputStream openClassfile(String classname) {
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            URLConnection con = openClassfile0(classname);
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (con != null)
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return con.getInputStream();
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IOException e) {}
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return null;        // not found
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private URLConnection openClassfile0(String classname) throws IOException {
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (packageName == null || classname.startsWith(packageName)) {
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            String jarname
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    = directory + classname.replace('.', '/') + ".class";
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return fetchClass0(hostname, port, jarname);
9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        else
9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return null;    // not found
9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns the URL.
9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return null if the class file could not be obtained.
10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public URL find(String classname) {
10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            URLConnection con = openClassfile0(classname);
10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            InputStream is = con.getInputStream();
10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (is != null) {
10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                is.close();
10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return con.getURL();
10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IOException e) {}
11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return null;
11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Closes this class path.
11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public void close() {}
11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Reads a class file on an http server.
12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param host              host name
12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param port              port number
12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param directory         directory name ending with "/".
12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *                          It can be "/" (root directory).
12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *                          It must start with "/".
12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param classname         fully-qualified class name
12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static byte[] fetchClass(String host, int port,
13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                    String directory, String classname)
13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws IOException
13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        byte[] b;
13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        URLConnection con = fetchClass0(host, port,
13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                directory + classname.replace('.', '/') + ".class");
13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int size = con.getContentLength();
13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        InputStream s = con.getInputStream();
13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (size <= 0)
14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                b = ClassPoolTail.readStream(s);
14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            else {
14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                b = new byte[size];
14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                int len = 0;
14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                do {
14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    int n = s.read(b, len, size - len);
14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    if (n < 0)
14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        throw new IOException("the stream was closed: "
14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                              + classname);
14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    len += n;
15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                } while (len < size);
15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        finally {
15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            s.close();
15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return b;
15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static URLConnection fetchClass0(String host, int port,
16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                             String filename)
16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws IOException
16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        URL url;
16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            url = new URL("http", host, port, filename);
16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (MalformedURLException e) {
17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            // should never reache here.
17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new IOException("invalid URL?");
17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        URLConnection con = url.openConnection();
17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        con.connect();
17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return con;
17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
179