1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist;
17
18import java.io.*;
19import java.net.*;
20
21/**
22 * A class search-path specified with URL (http).
23 *
24 * @see javassist.ClassPath
25 * @see ClassPool#insertClassPath(ClassPath)
26 * @see ClassPool#appendClassPath(ClassPath)
27 */
28public class URLClassPath implements ClassPath {
29    protected String hostname;
30    protected int port;
31    protected String directory;
32    protected String packageName;
33
34    /**
35     * Creates a search path specified with URL (http).
36     *
37     * <p>This search path is used only if a requested
38     * class name starts with the name specified by <code>packageName</code>.
39     * If <code>packageName</code> is "org.javassist." and a requested class is
40     * "org.javassist.test.Main", then the given URL is used for loading that class.
41     * The <code>URLClassPath</code> obtains a class file from:
42     *
43     * <ul><pre>http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
44     * </pre></ul>
45     *
46     * <p>Here, we assume that <code>host</code> is "www.javassist.org",
47     * <code>port</code> is 80, and <code>directory</code> is "/java/classes/".
48     *
49     * <p>If <code>packageName</code> is <code>null</code>, the URL is used
50     * for loading any class.
51     *
52     * @param host              host name
53     * @param port              port number
54     * @param directory         directory name ending with "/".
55     *                          It can be "/" (root directory).
56     *                          It must start with "/".
57     * @param packageName       package name.  It must end with "." (dot).
58     */
59    public URLClassPath(String host, int port,
60                        String directory, String packageName) {
61        hostname = host;
62        this.port = port;
63        this.directory = directory;
64        this.packageName = packageName;
65    }
66
67    public String toString() {
68        return hostname + ":" + port + directory;
69    }
70
71    /**
72     * Opens a class file with http.
73     *
74     * @return null if the class file could not be found.
75     */
76    public InputStream openClassfile(String classname) {
77        try {
78            URLConnection con = openClassfile0(classname);
79            if (con != null)
80                return con.getInputStream();
81        }
82        catch (IOException e) {}
83        return null;        // not found
84    }
85
86    private URLConnection openClassfile0(String classname) throws IOException {
87        if (packageName == null || classname.startsWith(packageName)) {
88            String jarname
89                    = directory + classname.replace('.', '/') + ".class";
90            return fetchClass0(hostname, port, jarname);
91        }
92        else
93            return null;    // not found
94    }
95
96    /**
97     * Returns the URL.
98     *
99     * @return null if the class file could not be obtained.
100     */
101    public URL find(String classname) {
102        try {
103            URLConnection con = openClassfile0(classname);
104            InputStream is = con.getInputStream();
105            if (is != null) {
106                is.close();
107                return con.getURL();
108            }
109        }
110        catch (IOException e) {}
111        return null;
112    }
113
114    /**
115     * Closes this class path.
116     */
117    public void close() {}
118
119    /**
120     * Reads a class file on an http server.
121     *
122     * @param host              host name
123     * @param port              port number
124     * @param directory         directory name ending with "/".
125     *                          It can be "/" (root directory).
126     *                          It must start with "/".
127     * @param classname         fully-qualified class name
128     */
129    public static byte[] fetchClass(String host, int port,
130                                    String directory, String classname)
131        throws IOException
132    {
133        byte[] b;
134        URLConnection con = fetchClass0(host, port,
135                directory + classname.replace('.', '/') + ".class");
136        int size = con.getContentLength();
137        InputStream s = con.getInputStream();
138        try {
139            if (size <= 0)
140                b = ClassPoolTail.readStream(s);
141            else {
142                b = new byte[size];
143                int len = 0;
144                do {
145                    int n = s.read(b, len, size - len);
146                    if (n < 0)
147                        throw new IOException("the stream was closed: "
148                                              + classname);
149
150                    len += n;
151                } while (len < size);
152            }
153        }
154        finally {
155            s.close();
156        }
157
158        return b;
159    }
160
161    private static URLConnection fetchClass0(String host, int port,
162                                             String filename)
163        throws IOException
164    {
165        URL url;
166        try {
167            url = new URL("http", host, port, filename);
168        }
169        catch (MalformedURLException e) {
170            // should never reache here.
171            throw new IOException("invalid URL?");
172        }
173
174        URLConnection con = url.openConnection();
175        con.connect();
176        return con;
177    }
178}
179