diff options
Diffstat (limited to 'libjava/classpath/java/net')
51 files changed, 16218 insertions, 0 deletions
diff --git a/libjava/classpath/java/net/Authenticator.java b/libjava/classpath/java/net/Authenticator.java new file mode 100644 index 000000000..229e140cc --- /dev/null +++ b/libjava/classpath/java/net/Authenticator.java @@ -0,0 +1,313 @@ +/* Authenticator.java -- Abstract class for obtaining authentication info + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This abstract class provides a model for obtaining authentication + * information (in the form of a username and password) required by + * some network operations (such as hitting a password protected + * web site). + * <p> + * To make use of this feature, a programmer must create a subclass + * that knows how to obtain the necessary info. An example + * would be a class that popped up a dialog box to prompt the user. + * After creating an instance of that subclass, the static + * <code>setDefault</code> method of this class is called to set up + * that instance as the object to use on subsequent calls to obtain + * authorization. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status Believed to be JDK 1.4 complete + */ +public abstract class Authenticator +{ + /* + * Class Variables + */ + + /** + * This is the default Authenticator object to use for password requests + */ + private static Authenticator defaultAuthenticator; + + /* + * Instance Variables + */ + + /** + * The hostname of the site requesting authentication + */ + private String host; + + /** + * InternetAddress of the site requesting authentication + */ + private InetAddress addr; + + /** + * The port number of the site requesting authentication + */ + private int port; + + /** + * The protocol name of the site requesting authentication + */ + private String protocol; + + /** + * The prompt to display to the user when requesting authentication info + */ + private String prompt; + + /** + * The authentication scheme in use + */ + private String scheme; + + /* + * Class Methods + */ + + /** + * This method sets the default <code>Authenticator</code> object (an + * instance of a subclass of <code>Authenticator</code>) to use when + * prompting the user for + * information. Note that this method checks to see if the caller is + * allowed to set this value (the "setDefaultAuthenticator" permission) + * and throws a <code>SecurityException</code> if it is not. + * + * @param defAuth The new default <code>Authenticator</code> object to use + * + * @exception SecurityException If the caller does not have permission + * to perform this operation + */ + public static void setDefault(Authenticator defAuth) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("setDefaultAuthenticator")); + + defaultAuthenticator = defAuth; + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default <code>Authenticator</code> object, the method then returns + * <code>null</code>. Otherwise, the default authenticators's instance + * variables are initialized and it's <code>getPasswordAuthentication</code> + * method is called to get the actual authentication information to return. + * + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A <code>PasswordAuthentication</code> object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + */ + public static PasswordAuthentication requestPasswordAuthentication(InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + return requestPasswordAuthentication(null, addr, port, protocol, prompt, + scheme); + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default <code>Authenticator</code> object, the method then returns + * <code>null</code>. Otherwise, the default authenticators's instance + * variables are initialized and it's <code>getPasswordAuthentication</code> + * method is called to get the actual authentication information to return. + * This method is the preferred one as it can be used with hostname + * when addr is unknown. + * + * @param host The hostname requesting authentication + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A <code>PasswordAuthentication</code> object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + * + * @since 1.4 + */ + public static PasswordAuthentication requestPasswordAuthentication(String host, + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("requestPasswordAuthentication")); + + if (defaultAuthenticator == null) + return null; + + defaultAuthenticator.host = host; + defaultAuthenticator.addr = addr; + defaultAuthenticator.port = port; + defaultAuthenticator.protocol = protocol; + defaultAuthenticator.prompt = prompt; + defaultAuthenticator.scheme = scheme; + + return defaultAuthenticator.getPasswordAuthentication(); + } + + /* + * Constructors + */ + + /** + * Default, no-argument constructor for subclasses to call. + */ + public Authenticator() + { + } + + /* + * Instance Methods + */ + + /** + * This method returns the address of the site that is requesting + * authentication. + * + * @return The requesting site's address + */ + protected final InetAddress getRequestingSite() + { + return addr; + } + + /** + * Returns the hostname of the host or proxy requesting authorization, + * or <code>null</code> if not available. + * + * @return The name of the host requesting authentication, or + * <code>null</code> if it is not available. + * + * @since 1.4 + */ + protected final String getRequestingHost() + { + return host; + } + + /** + * This method returns the port of the site that is requesting + * authentication. + * + * @return The requesting port + */ + protected final int getRequestingPort() + { + return port; + } + + /** + * This method returns the requesting protocol of the operation that is + * requesting authentication + * + * @return The requesting protocol + */ + protected final String getRequestingProtocol() + { + return protocol; + } + + /** + * Returns the prompt that should be used when requesting authentication + * information from the user + * + * @return The user prompt + */ + protected final String getRequestingPrompt() + { + return prompt; + } + + /** + * This method returns the authentication scheme in use + * + * @return The authentication scheme + */ + protected final String getRequestingScheme() + { + return scheme; + } + + /** + * This method is called whenever a request for authentication is made. It + * can call the other getXXX methods to determine the information relevant + * to this request. Subclasses should override this method, which returns + * <code>null</code> by default. + * + * @return The <code>PasswordAuthentication</code> information + */ + protected PasswordAuthentication getPasswordAuthentication() + { + return null; + } +} // class Authenticator diff --git a/libjava/classpath/java/net/BindException.java b/libjava/classpath/java/net/BindException.java new file mode 100644 index 000000000..cfb509a70 --- /dev/null +++ b/libjava/classpath/java/net/BindException.java @@ -0,0 +1,74 @@ +/* BindException.java -- An exception occurred while binding to a socket + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to bind + * socket to a particular port. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class BindException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5945005768251722951L; + + /** + * Create a new instance without a descriptive error message. + */ + public BindException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public BindException(String message) + { + super(message); + } +} // class BindException diff --git a/libjava/classpath/java/net/ConnectException.java b/libjava/classpath/java/net/ConnectException.java new file mode 100644 index 000000000..c115d2fe0 --- /dev/null +++ b/libjava/classpath/java/net/ConnectException.java @@ -0,0 +1,75 @@ +/* ConnectException.java -- An exception occurred while connecting to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to + * connect to a remote host. Often this indicates that the remote host + * refused the connection (ie, is not listening on the target socket). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3831404271622369215L; + + /** + * Create a new instance without a descriptive error message. + */ + public ConnectException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public ConnectException(String message) + { + super(message); + } +} // class ConnectException diff --git a/libjava/classpath/java/net/ContentHandler.java b/libjava/classpath/java/net/ContentHandler.java new file mode 100644 index 000000000..fed8f3de4 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandler.java @@ -0,0 +1,126 @@ +/* ContentHandler.java -- Abstract class for handling content from URL's + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This is an abstract class that is the superclass for classes that read + * objects from URL's. Calling the <code>getContent()</code> method in the + * <code>URL</code> class or the <code>URLConnection</code> class will cause + * an instance of a subclass of <code>ContentHandler</code> to be created for + * the MIME type of the object being downloaded from the URL. Thus, this + * class is seldom needed by applications/applets directly, but only + * indirectly through methods in other classes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class ContentHandler +{ + /* + * Constructors + */ + + /** + * Default, no-argument constructor. + */ + public ContentHandler() + { + } + + /* + * Instance Methods + */ + + /** + * This method reads from the <code>InputStream</code> of the passed in URL + * connection and uses the data downloaded to create an <code>Object</code> + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an <code>Image</code> object. This method + * must be implemented by subclasses. + * + * @param urlc A <code>URLConnection</code> object to read data from. + * + * @return An object representing the data read + * + * @exception IOException If an error occurs + */ + public abstract Object getContent(URLConnection urlc) + throws IOException; + + /** + * This method reads from the <code>InputStream</code> of the passed in URL + * connection and uses the data downloaded to create an <code>Object</code> + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an <code>Image</code> object. This method + * must be implemented by subclasses. This method uses the list of + * supplied classes as candidate types. If the data read doesn't match + * any of the supplied type, <code>null</code> is returned. + * + * @param urlc A <code>URLConnection</code> object to read data from. + * @param classes An array of types of objects that are candidate types + * for the data to be read. + * + * @return An object representing the data read, or <code>null</code> + * if the data does not match any of the candidate types. + * + * @exception IOException If an error occurs + * + * @since 1.3 + */ + public Object getContent(URLConnection urlc, Class[] classes) + throws IOException + { + Object obj = getContent(urlc); + + for (int i = 0; i < classes.length; i++) + { + if (classes[i].isInstance(obj)) + return obj; + } + + return null; + } +} // class ContentHandler diff --git a/libjava/classpath/java/net/ContentHandlerFactory.java b/libjava/classpath/java/net/ContentHandlerFactory.java new file mode 100644 index 000000000..51a92cf15 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandlerFactory.java @@ -0,0 +1,65 @@ +/* ContentHandlerFactory.java -- Interface for creating content handlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface maps MIME types to <code>ContentHandler</code> objects. + * It consists of one method that, when passed a MIME type, returns a + * handler for that type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface ContentHandlerFactory +{ + /** + * This method is passed a MIME type as a string and is responsible for + * returning the appropriate <code>ContentHandler</code> object. + * + * @param mimeType The MIME type to map to a <code>ContentHandler</code> + * + * @return The <code>ContentHandler</code> for the passed in MIME type + */ + ContentHandler createContentHandler(String mimeType); +} // interface ContentHandlerFactory diff --git a/libjava/classpath/java/net/DatagramPacket.java b/libjava/classpath/java/net/DatagramPacket.java new file mode 100644 index 000000000..e642f889c --- /dev/null +++ b/libjava/classpath/java/net/DatagramPacket.java @@ -0,0 +1,391 @@ +/* DatagramPacket.java -- Class to model a packet to be sent via UDP + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class models a packet of data that is to be sent across the network + * using a connectionless protocol such as UDP. It contains the data + * to be send, as well as the destination address and port. Note that + * datagram packets can arrive in any order and are not guaranteed to be + * delivered at all. + * <p> + * This class can also be used for receiving data from the network. + * <p> + * Note that for all method below where the buffer length passed by the + * caller cannot exceed the actually length of the byte array passed as + * the buffer, if this condition is not true, then the method silently + * reduces the length value to maximum allowable value. + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aarom M. Renn (arenn@urbanophile.com) (Documentation comments) + * @date April 28, 1999. + */ +public final class DatagramPacket +{ + /** + * The data buffer to send + */ + private byte[] buffer; + + /** + * This is the offset into the buffer to start sending from or receiving to. + */ + private int offset; + + /** + * The length of the data buffer to send. + */ + int length; + + /** + * The maximal length of the buffer. + */ + int maxlen; + + /** + * The address to which the packet should be sent or from which it + * was received. + */ + private InetAddress address; + + /** + * The port to which the packet should be sent or from which it was + * was received. + */ + private int port; + + /** + * This method initializes a new instance of <code>DatagramPacket</code> + * which has the specified buffer, offset, and length. + * + * @param buf The buffer for holding the incoming datagram. + * @param offset The offset into the buffer to start writing. + * @param length The maximum number of bytes to read. + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length) + { + setData(buf, offset, length); + address = null; + port = -1; + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * receiving packets from the network. + * + * @param buf A buffer for storing the returned packet data + * @param length The length of the buffer (must be <= buf.length) + */ + public DatagramPacket(byte[] buf, int length) + { + this(buf, 0, length); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length, + InetAddress address, int port) + { + setData(buf, offset, length); + setAddress(address); + setPort(port); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + */ + public DatagramPacket(byte[] buf, int length, InetAddress address, int port) + { + this(buf, 0, length, address, port); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int offset, int length, + SocketAddress address) throws SocketException + { + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + setData(buf, offset, length); + setAddress(tmp.getAddress()); + setPort(tmp.getPort()); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int length, SocketAddress address) + throws SocketException + { + this(buf, 0, length, address); + } + + /** + * Returns the address that this packet is being sent to or, if it was used + * to receive a packet, the address that is was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * returns <code>null</code>. + * + * @return The address for this packet. + */ + public synchronized InetAddress getAddress() + { + return address; + } + + /** + * Returns the port number this packet is being sent to or, if it was used + * to receive a packet, the port that it was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * will return 0. + * + * @return The port number for this packet + */ + public synchronized int getPort() + { + return port; + } + + /** + * Returns the data buffer for this packet + * + * @return This packet's data buffer + */ + public synchronized byte[] getData() + { + return buffer; + } + + /** + * This method returns the current offset value into the data buffer + * where data will be sent from. + * + * @return The buffer offset. + * + * @since 1.2 + */ + public synchronized int getOffset() + { + return offset; + } + + /** + * Returns the length of the data in the buffer + * + * @return The length of the data + */ + public synchronized int getLength() + { + return length; + } + + /** + * This sets the address to which the data packet will be transmitted. + * + * @param address The destination address + * + * @since 1.1 + */ + public synchronized void setAddress(InetAddress address) + { + this.address = address; + } + + /** + * This sets the port to which the data packet will be transmitted. + * + * @param port The destination port + * + * @since 1.1 + */ + public synchronized void setPort(int port) + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + this.port = port; + } + + /** + * Sets the address of the remote host this package will be sent + * + * @param address The socket address of the remove host + * + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public void setSocketAddress(SocketAddress address) + throws IllegalArgumentException + { + if (address == null) + throw new IllegalArgumentException("address may not be null"); + + InetSocketAddress tmp = (InetSocketAddress) address; + this.address = tmp.getAddress(); + this.port = tmp.getPort(); + } + + /** + * Gets the socket address of the host this packet + * will be sent to/is coming from + * + * @return The socket address of the remote host + * + * @since 1.4 + */ + public SocketAddress getSocketAddress() + { + return new InetSocketAddress(address, port); + } + + /** + * Sets the data buffer for this packet. + * + * @param buf The new buffer for this packet + * + * @exception NullPointerException If the argument is null + * + * @since 1.1 + */ + public void setData(byte[] buf) + { + setData(buf, 0, buf.length); + } + + /** + * This method sets the data buffer for the packet. + * + * @param buf The byte array containing the data for this packet. + * @param offset The offset into the buffer to start reading data from. + * @param length The number of bytes of data in the buffer. + * + * @exception NullPointerException If the argument is null + * + * @since 1.2 + */ + public synchronized void setData(byte[] buf, int offset, int length) + { + // This form of setData must be used if offset is to be changed. + if (buf == null) + throw new NullPointerException("Null buffer"); + if (offset < 0) + throw new IllegalArgumentException("Invalid offset: " + offset); + + buffer = buf; + this.offset = offset; + setLength(length); + } + + /** + * Sets the length of the data in the buffer. + * + * @param length The new length. (Where len <= buf.length) + * + * @exception IllegalArgumentException If the length is negative or + * if the length is greater than the packet's data buffer length + * + * @since 1.1 + */ + public synchronized void setLength(int length) + { + if (length < 0) + throw new IllegalArgumentException("Invalid length: " + length); + if (offset + length > buffer.length) + throw new IllegalArgumentException("Potential buffer overflow - offset: " + + offset + " length: " + length); + + this.length = length; + this.maxlen = length; + } +} diff --git a/libjava/classpath/java/net/DatagramSocket.java b/libjava/classpath/java/net/DatagramSocket.java new file mode 100644 index 000000000..6ca9c42fe --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocket.java @@ -0,0 +1,968 @@ +/* DatagramSocket.java -- A class to model UDP sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; + +import gnu.java.net.PlainDatagramSocketImpl; +import gnu.java.nio.DatagramChannelImpl; + +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.IllegalBlockingModeException; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a connectionless datagram socket that sends + * individual packets of data across the network. In the TCP/IP world, + * this means UDP. Datagram packets do not have guaranteed delivery, + * or any guarantee about the order the data will be received on the + * remote host. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date May 3, 1999. + */ +public class DatagramSocket +{ + /** + * This is the user DatagramSocketImplFactory for this class. If this + * variable is null, a default factory is used. + */ + private static DatagramSocketImplFactory factory; + + /** + * This is the implementation object used by this socket. + */ + private DatagramSocketImpl impl; + + /** + * True if socket implementation was created. + */ + private boolean implCreated; + + /** + * This is the address we are "connected" to + */ + private InetAddress remoteAddress; + + /** + * This is the port we are "connected" to + */ + private int remotePort = -1; + + /** + * True if socket is bound. + */ + private boolean bound; + + /** + * Creates a <code>DatagramSocket</code> from a specified + * <code>DatagramSocketImpl</code> instance + * + * @param impl The <code>DatagramSocketImpl</code> the socket will be + * created from + * + * @since 1.4 + */ + protected DatagramSocket(DatagramSocketImpl impl) + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.remoteAddress = null; + this.remotePort = -1; + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * a random port and every address on the local machine. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its <code>checkListen</code> method doesn't allow the operation. + */ + public DatagramSocket() throws SocketException + { + this(new InetSocketAddress(0)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified port and every address on the local machine. + * + * @param port The local port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * <code>checkListen</code> method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port) throws SocketException + { + this(new InetSocketAddress(port)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified local port and address. + * + * @param port The local port number to bind to. + * @param addr The local address to bind to. + * + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port, InetAddress addr) throws SocketException + { + this(new InetSocketAddress(addr, port)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified local port and address. + * + * @param address The local address and port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * <code>checkListen</code> method doesn't allow the operation. + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public DatagramSocket(SocketAddress address) throws SocketException + { + String propVal = SystemProperties.getProperty("impl.prefix"); + if (propVal == null || propVal.equals("")) + { + if (factory != null) + impl = factory.createDatagramSocketImpl(); + else + { + try + { + impl = new PlainDatagramSocketImpl(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + } + else + try + { + impl = + (DatagramSocketImpl) Class.forName("java.net." + propVal + + "DatagramSocketImpl") + .newInstance(); + } + catch (Exception e) + { + System.err.println("Could not instantiate class: java.net." + + propVal + "DatagramSocketImpl"); + try + { + impl = new PlainDatagramSocketImpl(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + if (address != null) + bind(address); + } + + // This needs to be accessible from java.net.MulticastSocket + DatagramSocketImpl getImpl() throws SocketException + { + try + { + if (! implCreated) + { + impl.create(); + implCreated = true; + } + + return impl; + } + catch (IOException e) + { + SocketException se = new SocketException(); + se.initCause(e); + throw se; + } + } + + /** + * Closes this datagram socket. + */ + public void close() + { + if (isClosed()) + return; + + try + { + getImpl().close(); + } + catch (SocketException e) + { + // Ignore this case, just close the socket in finally clause. + } + finally + { + remoteAddress = null; + remotePort = -1; + impl = null; + } + + try + { + if (getChannel() != null) + getChannel().close(); + } + catch (IOException e) + { + // Do nothing. + } + } + + /** + * This method returns the remote address to which this socket is + * connected. If this socket is not connected, then this method will + * return <code>null</code>. + * + * @return The remote address. + * + * @since 1.2 + */ + public InetAddress getInetAddress() + { + return remoteAddress; + } + + /** + * This method returns the remote port to which this socket is + * connected. If this socket is not connected, then this method will + * return -1. + * + * @return The remote port. + * + * @since 1.2 + */ + public int getPort() + { + return remotePort; + } + + /** + * Returns the local address this datagram socket is bound to. + * + * @return The local address is the socket is bound or null + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return null; + + InetAddress localAddr; + + try + { + localAddr = + (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkConnect(localAddr.getHostAddress(), -1); + } + catch (SecurityException e) + { + localAddr = InetAddress.ANY_IF; + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + + return localAddr; + } + + /** + * Returns the local port this socket is bound to. + * + * @return The local port number. + */ + public int getLocalPort() + { + if (isClosed()) + return -1; + + try + { + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return 0; + } + } + + /** + * Returns the value of the socket's SO_TIMEOUT setting. If this method + * returns 0 then SO_TIMEOUT is disabled. + * + * @return The current timeout in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_TIMEOUT); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the value of the socket's SO_TIMEOUT value. A value of 0 will + * disable SO_TIMEOUT. Any other value is the number of milliseconds + * a socket read/write will block before timing out. + * + * @param timeout The new SO_TIMEOUT value in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("Invalid timeout: " + timeout); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method connects this socket to the specified address and port. + * When a datagram socket is connected, it will only send or receive + * packets to and from the host to which it is connected. A multicast + * socket that is connected may only send and not receive packets. + * + * @param address The address to connect this socket to. + * @param port The port to connect this socket to. + * + * @exception IllegalArgumentException If address or port are invalid. + * @exception SecurityException If the caller is not allowed to send + * datagrams to or receive from this address and port. + * + * @since 1.2 + */ + public void connect(InetAddress address, int port) + { + if (address == null) + throw new IllegalArgumentException("Connect address may not be null"); + + if ((port < 1) || (port > 65535)) + throw new IllegalArgumentException("Port number is illegal: " + port); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(address.getHostAddress(), port); + + try + { + getImpl().connect(address, port); + remoteAddress = address; + remotePort = port; + } + catch (SocketException e) + { + // This means simply not connected or connect not implemented. + } + } + + /** + * This method disconnects this socket from the address/port it was + * connected to. If the socket was not connected in the first place, + * this method does nothing. + * + * @since 1.2 + */ + public void disconnect() + { + if (! isConnected()) + return; + + try + { + getImpl().disconnect(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + finally + { + remoteAddress = null; + remotePort = -1; + } + } + + /** + * Reads a datagram packet from the socket. Note that this method + * will block until a packet is received from the network. On return, + * the passed in <code>DatagramPacket</code> is populated with the data + * received and all the other information about the packet. + * + * @param p A <code>DatagramPacket</code> for storing the data + * + * @exception IOException If an error occurs. + * @exception SocketTimeoutException If setSoTimeout was previously called + * and the timeout has expired. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + * @exception SecurityException If a security manager exists and its + * checkAccept method doesn't allow the receive. + */ + public synchronized void receive(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (remoteAddress != null && remoteAddress.isMulticastAddress()) + throw new IOException + ("Socket connected to a multicast address my not receive"); + + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + DatagramPacket p2 = new DatagramPacket(p.getData(), p.getOffset(), p.maxlen); + getImpl().receive(p2); + p.length = p2.length; + if (p2.getAddress() != null) + p.setAddress(p2.getAddress()); + if (p2.getPort() != -1) + p.setPort(p2.getPort()); + + SecurityManager s = System.getSecurityManager(); + if (s != null && isConnected()) + s.checkAccept(p.getAddress().getHostAddress(), p.getPort()); + } + + /** + * Sends the specified packet. The host and port to which the packet + * are to be sent should be set inside the packet. + * + * @param p The datagram packet to send. + * + * @exception IOException If an error occurs. + * @exception SecurityException If a security manager exists and its + * checkMulticast or checkConnect method doesn't allow the send. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + */ + public void send(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // JDK1.2: Don't do security checks if socket is connected; see jdk1.2 api. + SecurityManager s = System.getSecurityManager(); + if (s != null && ! isConnected()) + { + InetAddress addr = p.getAddress(); + if (addr.isMulticastAddress()) + s.checkMulticast(addr); + else + s.checkConnect(addr.getHostAddress(), p.getPort()); + } + + if (isConnected()) + { + if (p.getAddress() != null + && (remoteAddress != p.getAddress() || remotePort != p.getPort())) + throw new IllegalArgumentException + ("DatagramPacket address does not match remote address"); + } + + // FIXME: if this is a subclass of MulticastSocket, + // use getTimeToLive for TTL val. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + getImpl().send(p); + } + + /** + * Binds the socket to the given socket address. + * + * @param address The socket address to bind to. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its checkListen method doesn't allow the operation. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void bind(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (address == null) + address = new InetSocketAddress(InetAddress.ANY_IF, 0); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetAddress addr = ((InetSocketAddress) address).getAddress(); + int port = ((InetSocketAddress) address).getPort(); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + try + { + getImpl().bind(port, addr); + bound = true; + } + catch (SocketException exception) + { + getImpl().close(); + throw exception; + } + catch (RuntimeException exception) + { + getImpl().close(); + throw exception; + } + catch (Error error) + { + getImpl().close(); + throw error; + } + } + + /** + * Checks if the datagram socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + return impl == null; + } + + /** + * Returns the datagram channel assoziated with this datagram socket. + * + * @return The associated <code>DatagramChannel</code> object or null + * + * @since 1.4 + */ + public DatagramChannel getChannel() + { + return null; + } + + /** + * Connects the datagram socket to a specified socket address. + * + * @param address The socket address to connect to. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void connect(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + connect(tmp.getAddress(), tmp.getPort()); + } + + /** + * Returns the binding state of the socket. + * + * @return True if socket bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + return bound; + } + + /** + * Returns the connection state of the socket. + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + return remoteAddress != null; + } + + /** + * Returns the SocketAddress of the host this socket is conneted to + * or null if this socket is not connected. + * + * @return The socket address of the remote host if connected or null + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + return new InetSocketAddress(remoteAddress, remotePort); + } + + /** + * Returns the local SocketAddress this socket is bound to. + * + * @return The local SocketAddress or null if the socket is not bound. + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + return new InetSocketAddress(getLocalAddress(), getLocalPort()); + } + + /** + * Enables/Disables SO_REUSEADDR. + * + * @param on Whether or not to have SO_REUSEADDR turned on. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if SO_REUSEADDR is enabled. + * + * @return True if SO_REUSEADDR is set on the socket, false otherwise. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Enables/Disables SO_BROADCAST + * + * @param enable True if SO_BROADCAST should be enabled, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setBroadcast(boolean enable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(enable)); + } + + /** + * Checks if SO_BROADCAST is enabled + * + * @return Whether SO_BROADCAST is set + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getBroadcast() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_BROADCAST); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see DatagramSocket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, Integer.valueOf(tc)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @see DatagramSocket#setTrafficClass(int tc) + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_TOS); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the datagram socket implementation factory for the application + * + * @param fac The factory to set + * + * @exception IOException If an error occurs + * @exception SocketException If the factory is already defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) + throws IOException + { + if (factory != null) + throw new SocketException("DatagramSocketImplFactory already defined"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + factory = fac; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImpl.java b/libjava/classpath/java/net/DatagramSocketImpl.java new file mode 100644 index 000000000..4f51f9f93 --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImpl.java @@ -0,0 +1,296 @@ +/* DatagramSocketImpl.java -- Abstract class for UDP socket implementations + Copyright (C) 1998, 1999 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; + + +/** + * This abstract class models a datagram socket implementation. An + * actual implementation class would implement these methods, probably + * via redirecting them to native code. + * <p> + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * <p> + * Status: Believed complete and correct. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public abstract class DatagramSocketImpl implements SocketOptions +{ + /** + * The local port to which this socket is bound + */ + protected int localPort; + + /** + * The FileDescriptor object for this object. + */ + protected FileDescriptor fd; + + /** + * Default, no-argument constructor for subclasses to call. + */ + public DatagramSocketImpl() + { + } + + /** + * This method binds the socket to the specified local port and address. + * + * @param lport The port number to bind to + * @param laddr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected abstract void bind(int lport, InetAddress laddr) + throws SocketException; + + /** + * This methods closes the socket + */ + protected abstract void close(); + + /** + * Creates a new datagram socket. + * + * @exception SocketException If an error occurs + */ + protected abstract void create() throws SocketException; + + /** + * Takes a peek at the next packet received in order to retrieve the + * address of the sender + * + * @param i The <code>InetAddress</code> to fill in with the information + * about the sender if the next packet + * + * @return The port number of the sender of the packet + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract int peek(InetAddress i) throws IOException; + + /** + * Takes a peek at the next packet received. This packet is not consumed. + * With the next peekData/receive operation this packet will be read again. + * + * @param p The <code>DatagramPacket</code> to fill in with the data sent. + * + * @return The port number of the sender of the packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + * + * @since 1.4 + */ + protected abstract int peekData(DatagramPacket p) throws IOException; + + /** + * Transmits the specified packet of data to the network. The destination + * host and port should be encoded in the packet. + * + * @param p The packet to send + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void send(DatagramPacket p) throws IOException; + + /** + * Receives a packet of data from the network Will block until a packet + * arrives. The packet info in populated into the passed in + * <code>DatagramPacket</code> object. + * + * @param p A place to store the incoming packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void receive(DatagramPacket p) throws IOException; + + /** + * Connects the socket to a host specified by address and port. + * + * @param address The <code>InetAddress</code> of the host to connect to + * @param port The port number of the host to connect to + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + protected void connect(InetAddress address, int port) + throws SocketException + { + // This method has to be overwritten by real implementations + } + + /** + * Disconnects the socket. + * + * @since 1.4 + */ + protected void disconnect() + { + // This method has to be overwritten by real implementations + } + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. <b>Use <code>setTimeToLive(int)</code></b> instead. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + * @deprecated + */ + protected abstract void setTTL(byte ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. <b>Use <code>getTimeToLive()</code></b> instead. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + * + * @deprecated // FIXME: when ? + */ + protected abstract byte getTTL() throws IOException; + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + */ + protected abstract void setTimeToLive(int ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + */ + protected abstract int getTimeToLive() throws IOException; + + /** + * Causes this socket to join the specified multicast group + * + * @param inetaddr The multicast address to join with + * + * @exception IOException If an error occurs + */ + protected abstract void join(InetAddress inetaddr) throws IOException; + + /** + * Causes the socket to leave the specified multicast group. + * + * @param inetaddr The multicast address to leave + * + * @exception IOException If an error occurs + */ + protected abstract void leave(InetAddress inetaddr) throws IOException; + + /** + * Causes this socket to join the specified multicast group on a specified + * device + * + * @param mcastaddr The address to leave + * @param netIf The specified network interface to join the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void joinGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Leaves a multicast group + * + * @param mcastaddr The address to join + * @param netIf The specified network interface to leave the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void leaveGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Returns the FileDescriptor for this socket + * + * @return the file descriptor associated with this socket + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the local port this socket is bound to + * + * @return the local port + */ + protected int getLocalPort() + { + return localPort; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImplFactory.java b/libjava/classpath/java/net/DatagramSocketImplFactory.java new file mode 100644 index 000000000..014651aa8 --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImplFactory.java @@ -0,0 +1,60 @@ +/* DatagramSocketImplFactory.java -- + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.4 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a + * <code>DatagramSocketImpl</code> object. + * This should not be needed by ordinary applications. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.3 + */ +public interface DatagramSocketImplFactory +{ + /** + * This method returns an instance of the DatagramSocketImpl object + * + * @return A DatagramSocketImpl object + */ + DatagramSocketImpl createDatagramSocketImpl(); +} // interface DatagramSocketImplFactory diff --git a/libjava/classpath/java/net/FileNameMap.java b/libjava/classpath/java/net/FileNameMap.java new file mode 100644 index 000000000..6f1d25a6a --- /dev/null +++ b/libjava/classpath/java/net/FileNameMap.java @@ -0,0 +1,65 @@ +/* FileNameMap.java -- Maps filenames to MIME types + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface has one method which, when passed a filename, returns + * the MIME type associated with that filename. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public interface FileNameMap +{ + /** + * This method is passed a filename and is responsible for determining + * the appropriate MIME type for that file. + * + * @param filename The name of the file to generate a MIME type for. + * + * @return The MIME type for the filename passed in. + */ + String getContentTypeFor(String filename); +} // interface FileNameMap diff --git a/libjava/classpath/java/net/HttpURLConnection.java b/libjava/classpath/java/net/HttpURLConnection.java new file mode 100644 index 000000000..72dd67d91 --- /dev/null +++ b/libjava/classpath/java/net/HttpURLConnection.java @@ -0,0 +1,589 @@ +/* HttpURLConnection.java -- Subclass of communications links using + Hypertext Transfer Protocol. + Copyright (C) 1998, 1999, 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.security.Permission; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class provides a common abstract implementation for those + * URL connection classes that will connect using the HTTP protocol. + * In addition to the functionality provided by the URLConnection + * class, it defines constants for HTTP return code values and + * methods for setting the HTTP request method and determining whether + * or not to follow redirects. + * + * @since 1.1 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class HttpURLConnection extends URLConnection +{ + /* HTTP Success Response Codes */ + + /** + * Indicates that the client may continue with its request. This value + * is specified as part of RFC 2068 but was not included in Sun's JDK, so + * beware of using this value + */ + static final int HTTP_CONTINUE = 100; + + /** + * Indicates the request succeeded. + */ + public static final int HTTP_OK = 200; + + /** + * The requested resource has been created. + */ + public static final int HTTP_CREATED = 201; + + /** + * The request has been accepted for processing but has not completed. + * There is no guarantee that the requested action will actually ever + * be completed succesfully, but everything is ok so far. + */ + public static final int HTTP_ACCEPTED = 202; + + /** + * The meta-information returned in the header is not the actual data + * from the original server, but may be from a local or other copy. + * Normally this still indicates a successful completion. + */ + public static final int HTTP_NOT_AUTHORITATIVE = 203; + + /** + * The server performed the request, but there is no data to send + * back. This indicates that the user's display should not be changed. + */ + public static final int HTTP_NO_CONTENT = 204; + + /** + * The server performed the request, but there is no data to sent back, + * however, the user's display should be "reset" to clear out any form + * fields entered. + */ + public static final int HTTP_RESET = 205; + + /** + * The server completed the partial GET request for the resource. + */ + public static final int HTTP_PARTIAL = 206; + + /* HTTP Redirection Response Codes */ + + /** + * There is a list of choices available for the requested resource. + */ + public static final int HTTP_MULT_CHOICE = 300; + + /** + * The resource has been permanently moved to a new location. + */ + public static final int HTTP_MOVED_PERM = 301; + + /** + * The resource requested has been temporarily moved to a new location. + */ + public static final int HTTP_MOVED_TEMP = 302; + + /** + * The response to the request issued is available at another location. + */ + public static final int HTTP_SEE_OTHER = 303; + + /** + * The document has not been modified since the criteria specified in + * a conditional GET. + */ + public static final int HTTP_NOT_MODIFIED = 304; + + /** + * The requested resource needs to be accessed through a proxy. + */ + public static final int HTTP_USE_PROXY = 305; + + /* HTTP Client Error Response Codes */ + + /** + * The request was misformed or could not be understood. + */ + public static final int HTTP_BAD_REQUEST = 400; + + /** + * The request made requires user authorization. Try again with + * a correct authentication header. + */ + public static final int HTTP_UNAUTHORIZED = 401; + + /** + * Code reserved for future use - I hope way in the future. + */ + public static final int HTTP_PAYMENT_REQUIRED = 402; + + /** + * There is no permission to access the requested resource. + */ + public static final int HTTP_FORBIDDEN = 403; + + /** + * The requested resource was not found. + */ + public static final int HTTP_NOT_FOUND = 404; + + /** + * The specified request method is not allowed for this resource. + */ + public static final int HTTP_BAD_METHOD = 405; + + /** + * Based on the input headers sent, the resource returned in response + * to the request would not be acceptable to the client. + */ + public static final int HTTP_NOT_ACCEPTABLE = 406; + + /** + * The client must authenticate with a proxy prior to attempting this + * request. + */ + public static final int HTTP_PROXY_AUTH = 407; + + /** + * The request timed out. + */ + public static final int HTTP_CLIENT_TIMEOUT = 408; + + /** + * There is a conflict between the current state of the resource and the + * requested action. + */ + public static final int HTTP_CONFLICT = 409; + + /** + * The requested resource is no longer available. This ususally indicates + * a permanent condition. + */ + public static final int HTTP_GONE = 410; + + /** + * A Content-Length header is required for this request, but was not + * supplied. + */ + public static final int HTTP_LENGTH_REQUIRED = 411; + + /** + * A client specified pre-condition was not met on the server. + */ + public static final int HTTP_PRECON_FAILED = 412; + + /** + * The request sent was too large for the server to handle. + */ + public static final int HTTP_ENTITY_TOO_LARGE = 413; + + /** + * The name of the resource specified was too long. + */ + public static final int HTTP_REQ_TOO_LONG = 414; + + /** + * The request is in a format not supported by the requested resource. + */ + public static final int HTTP_UNSUPPORTED_TYPE = 415; + + /* HTTP Server Error Response Codes */ + + /** + * This error code indicates that some sort of server error occurred. + * + * @deprecated + */ + public static final int HTTP_SERVER_ERROR = 500; + + /** + * The server encountered an unexpected error (such as a CGI script crash) + * that prevents the request from being fulfilled. + */ + public static final int HTTP_INTERNAL_ERROR = 500; + + /** + * The server does not support the requested functionality. + * @since 1.3 + */ + public static final int HTTP_NOT_IMPLEMENTED = 501; + + /** + * The proxy encountered a bad response from the server it was proxy-ing for + */ + public static final int HTTP_BAD_GATEWAY = 502; + + /** + * The HTTP service is not availalble, such as because it is overloaded + * and does not want additional requests. + */ + public static final int HTTP_UNAVAILABLE = 503; + + /** + * The proxy timed out getting a reply from the remote server it was + * proxy-ing for. + */ + public static final int HTTP_GATEWAY_TIMEOUT = 504; + + /** + * This server does not support the protocol version requested. + */ + public static final int HTTP_VERSION = 505; + + // Non-HTTP response static variables + + /** + * Flag to indicate whether or not redirects should be automatically + * followed by default. + */ + private static boolean followRedirects = true; + + /** + * This is a list of valid request methods, separated by "|" characters. + */ + private static final String valid_methods = + "|GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE|"; + + // Instance Variables + + /** + * The requested method in use for this connection. Default is GET. + */ + protected String method = "GET"; + + /** + * The response code received from the server + */ + protected int responseCode = -1; + + /** + * The response message string received from the server. + */ + protected String responseMessage; + + /** + * If this instance should follow redirect requests. + */ + protected boolean instanceFollowRedirects = followRedirects; + + /** + * Whether we already got a valid response code for this connection. + * Used by <code>getResponseCode()</code> and + * <code>getResponseMessage()</code>. + */ + private boolean gotResponseVals; + + /** + * Create an HttpURLConnection for the specified URL + * + * @param url The URL to create this connection for. + */ + protected HttpURLConnection(URL url) + { + super(url); + } + + /** + * Closes the connection to the server. + */ + public abstract void disconnect(); + + /** + * Returns a boolean indicating whether or not this connection is going + * through a proxy + * + * @return true if through a proxy, false otherwise + */ + public abstract boolean usingProxy(); + + /** + * Sets whether HTTP redirects (requests with response code 3xx) should be + * automatically followed by this class. True by default + * + * @param set true if redirects should be followed, false otherwis. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setFollowRedirects(boolean set) + { + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + followRedirects = set; + } + + /** + * Returns a boolean indicating whether or not HTTP redirects will + * automatically be followed or not. + * + * @return true if redirects will be followed, false otherwise + */ + public static boolean getFollowRedirects() + { + return followRedirects; + } + + /** + * Returns the value of this HttpURLConnection's instanceFollowRedirects + * field + * + * @return true if following redirects is enabled, false otherwise + */ + public boolean getInstanceFollowRedirects() + { + return instanceFollowRedirects; + } + + /** + * Sets the value of this HttpURLConnection's instanceFollowRedirects field + * + * @param follow true to enable following redirects, false otherwise + */ + public void setInstanceFollowRedirects(boolean follow) + { + instanceFollowRedirects = follow; + } + + /** + * Set the method for the URL request, one of: + * GET POST HEAD OPTIONS PUT DELETE TRACE are legal + * + * @param method the method to use + * + * @exception ProtocolException If the method cannot be reset or if the + * requested method isn't valid for HTTP + */ + public void setRequestMethod(String method) throws ProtocolException + { + if (connected) + throw new ProtocolException("Already connected"); + + method = method.toUpperCase(); + if (valid_methods.indexOf("|" + method + "|") != -1) + this.method = method; + else + throw new ProtocolException("Invalid HTTP request method: " + method); + } + + /** + * The request method currently in use for this connection. + * + * @return The request method + */ + public String getRequestMethod() + { + return method; + } + + /** + * Gets the status code from an HTTP response message, or -1 if + * the response code could not be determined. + * Note that all valid response codes have class variables + * defined for them in this class. + * + * @return The response code + * + * @exception IOException If an error occurs + */ + public int getResponseCode() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseCode; + } + + /** + * Gets the HTTP response message, if any, returned along with the + * response code from a server. Null if no response message was set + * or an error occured while connecting. + * + * @return The response message + * + * @exception IOException If an error occurs + */ + public String getResponseMessage() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseMessage; + } + + private void getResponseVals() throws IOException + { + // getHeaderField() will connect for us, but do it here first in + // order to pick up IOExceptions. + if (! connected) + connect(); + + gotResponseVals = true; + + // If responseCode not yet explicitly set by subclass + if (responseCode == -1) + { + // Response is the first header received from the connection. + String respField = getHeaderField(0); + + if (respField == null || ! respField.startsWith("HTTP/")) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + return; + } + + int firstSpc; + int nextSpc; + firstSpc = respField.indexOf(' '); + nextSpc = respField.indexOf(' ', firstSpc + 1); + responseMessage = respField.substring(nextSpc + 1); + String codeStr = respField.substring(firstSpc + 1, nextSpc); + try + { + responseCode = Integer.parseInt(codeStr); + } + catch (NumberFormatException e) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + } + } + } + + /** + * Returns a permission object representing the permission necessary to make + * the connection represented by this object + * + * @return the permission necessary for this connection + * + * @exception IOException If an error occurs + */ + public Permission getPermission() throws IOException + { + URL url = getURL(); + String host = url.getHost(); + int port = url.getPort(); + if (port == -1) + port = 80; + + host = host + ":" + port; + + return new SocketPermission(host, "connect"); + } + + /** + * This method allows the caller to retrieve any data that might have + * been sent despite the fact that an error occurred. For example, the + * HTML page sent along with a 404 File Not Found error. If the socket + * is not connected, or if no error occurred or no data was returned, + * this method returns <code>null</code>. + * + * @return An <code>InputStream</code> for reading error data. + */ + public InputStream getErrorStream() + { + if (! connected) + return null; + + int code; + try + { + code = getResponseCode(); + } + catch (IOException e) + { + code = -1; + } + + if (code == -1) + return null; + + if (((code / 100) != 4) || ((code / 100) != 5)) + return null; + + try + { + PushbackInputStream pbis = new PushbackInputStream(getInputStream()); + + int i = pbis.read(); + if (i == -1) + return null; + + pbis.unread(i); + return pbis; + } + catch (IOException e) + { + return null; + } + } + + /** + * Returns the value of the named field parsed as date + * + * @param key the key of the header field + * @param value the default value if the header field is not present + * + * @return the value of the header field + */ + public long getHeaderFieldDate(String key, long value) + { + // FIXME: implement this correctly + // http://www.w3.org/Protocols/HTTP-NG/ng-notes.txt + return super.getHeaderFieldDate(key, value); + } +} diff --git a/libjava/classpath/java/net/Inet4Address.java b/libjava/classpath/java/net/Inet4Address.java new file mode 100644 index 000000000..e3cff7b13 --- /dev/null +++ b/libjava/classpath/java/net/Inet4Address.java @@ -0,0 +1,273 @@ +/* Inet4Address.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.ObjectStreamException; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt), + * RFC 1918 (http://www.ietf.org/rfc/rfc1918.txt), + * RFC 2365 (http://www.ietf.org/rfc/rfc2365.txt) + * + * @author Michael Koch + * @status Believed complete and correct. + */ +public final class Inet4Address extends InetAddress +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + static final long serialVersionUID = 3286316764910316507L; + + /** + * The address family of these addresses (used for serialization). + */ + private static final int AF_INET = 2; + + /** + * Inet4Address objects are serialized as InetAddress objects. + */ + private Object writeReplace() throws ObjectStreamException + { + return new InetAddress(addr, hostName, AF_INET); + } + + /** + * Initializes this object's addr instance variable from the passed in + * byte array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + * @param host The hostname of this IP address. + */ + Inet4Address(byte[] addr, String host) + { + super(addr, host, AF_INET); + } + + /** + * Checks if the address is a multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return (addr[0] & 0xf0) == 0xe0; + } + + /** + * Checks if this address is a loopback address + */ + public boolean isLoopbackAddress() + { + return (addr[0] & 0xff) == 0x7f; + } + + /** + * Checks if this address is a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + return equals(InetAddress.ANY_IF); + } + + /** + * Checks if this address is a link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return false; + } + + /** + * Checks if this address is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + // 10.0.0.0/8 + if ((addr[0] & 0xff) == 0x0a) + return true; + + // 172.16.0.0/12 + if ((addr[0] & 0xff) == 0xac && (addr[1] & 0xf0) == 0x10) + return true; + + // 192.168.0.0/16 + if ((addr[0] & 0xff) == 0xc0 && (addr[1] & 0xff) == 0xa8) + return true; + + return false; + } + + /** + * Checks if this multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + return false; + } + + /** + * Checks if this multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + return false; + } + + /** + * Checks if this multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + if (! isMulticastAddress()) + return false; + + return ((addr[0] & 0xff) == 0xe0 + && (addr[1] & 0xff) == 0x00 + && (addr[2] & 0xff) == 0x00); + } + + /** + * Checks if this multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + return false; + } + + /** + * Checks if this multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + return false; + } + + /** + * Returns the address of the current instance + */ + public byte[] getAddress() + { + return (byte[]) addr.clone(); + } + + /** + * Returns the address as string + * + * @since 1.0.2 + */ + public String getHostAddress() + { + CPStringBuilder sb = new CPStringBuilder(40); + + int len = addr.length; + int i = 0; + + for ( ; ; ) + { + sb.append(addr[i] & 0xff); + i++; + + if (i == len) + break; + + sb.append('.'); + } + + return sb.toString(); + } + + /** + * Computes the hashcode of the instance + */ + public int hashCode() + { + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xFF); + + return hash; + } + + /** + * Compare the current Inet4Address instance with obj + * + * @param obj Object to compare with + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + byte[] addr1 = addr; + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr1.length != addr2.length) + return false; + + for (int i = addr1.length; --i >= 0;) + if (addr1[i] != addr2[i]) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/net/Inet6Address.java b/libjava/classpath/java/net/Inet6Address.java new file mode 100644 index 000000000..60c406587 --- /dev/null +++ b/libjava/classpath/java/net/Inet6Address.java @@ -0,0 +1,429 @@ +/* Inet6Address.java -- + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Arrays; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt) + * + * @author Michael Koch + * @status Updated to 1.5. Serialization compatibility is tested. + */ +public final class Inet6Address extends InetAddress +{ + static final long serialVersionUID = 6880410070516793377L; + + /** + * Needed for serialization + */ + byte[] ipaddress; + + /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private int scope_id; + + /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private boolean scope_id_set; + + /** + * Whether ifname is set or not. + * @since 1.5 + * @serial + */ + private boolean scope_ifname_set; + + /** + * Name of the network interface, used only by the serialization methods + * @since 1.5 + * @serial + */ + private String ifname; + + /** + * Scope network interface, or <code>null</code>. + */ + private transient NetworkInterface nif; + + /** + * The address family of these addresses (used for serialization). + */ + private static final int AF_INET6 = 10; + + /** + * Create an Inet6Address object + * + * @param addr The IP address + * @param host The hostname + */ + Inet6Address(byte[] addr, String host) + { + super(addr, host, AF_INET6); + // Super constructor clones the addr. Get a reference to the clone. + this.ipaddress = this.addr; + ifname = null; + scope_ifname_set = scope_id_set = false; + scope_id = 0; + nif = null; + } + + /** + * Utility routine to check if the InetAddress is an IP multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return ipaddress[0] == (byte) 0xFF; + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + byte[] anylocal = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + return Arrays.equals(ipaddress, anylocal); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + byte[] loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + + return Arrays.equals(ipaddress, loopback); + } + + /** + * Utility routine to check if the InetAddress is an link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return ipaddress[0] == 0xFA; + } + + /** + * Utility routine to check if the InetAddress is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + return ipaddress[0] == 0xFB; + } + + /** + * Utility routine to check if the multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0xE; + } + + /** + * Utility routine to check if the multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x1; + } + + /** + * Utility routine to check if the multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x2; + } + + /** + * Utility routine to check if the multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x5; + } + + /** + * Utility routine to check if the multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x8; + } + + /** + * Returns the raw IP address of this InetAddress object. The result is in + * network byte order: the highest order byte of the address is i + * n getAddress()[0] + */ + public byte[] getAddress() + { + return (byte[]) ipaddress.clone(); + } + + /** + * Creates a scoped Inet6Address where the scope has an integer id. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + int scopeId) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.scope_id = scopeId; + ip.scope_id_set = true; + return ip; + } + + /** + * Creates a scoped Inet6Address where the scope is a given + * NetworkInterface. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + NetworkInterface nif) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.nif = nif; + + return ip; + } + + /** + * Returns the <code>NetworkInterface</code> of the address scope + * if it is a scoped address and the scope is given in the form of a + * NetworkInterface. + * (I.e. the address was created using the + * getByAddress(String, byte[], NetworkInterface) method) + * Otherwise this method returns <code>null</code>. + * @since 1.5 + */ + public NetworkInterface getScopedInterface() + { + return nif; + } + + /** + * Returns the scope ID of the address scope if it is a scoped adress using + * an integer to identify the scope. + * + * Otherwise this method returns 0. + * @since 1.5 + */ + public int getScopeId() + { + // check scope_id_set because some JDK-serialized objects seem to have + // scope_id set to a nonzero value even when scope_id_set == false + if( scope_id_set ) + return scope_id; + return 0; + } + + /** + * Returns the IP address string in textual presentation + */ + public String getHostAddress() + { + CPStringBuilder sbuf = new CPStringBuilder(40); + + for (int i = 0; i < 16; i += 2) + { + int x = ((ipaddress[i] & 0xFF) << 8) | (ipaddress[i + 1] & 0xFF); + + if (i > 0) + sbuf.append(':'); + + sbuf.append(Integer.toHexString(x)); + } + if( nif != null ) + sbuf.append( "%" + nif.getName() ); + else if( scope_id_set ) + sbuf.append( "%" + scope_id ); + + return sbuf.toString(); + } + + /** + * Returns a hashcode for this IP address + * (The hashcode is independent of scope) + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Compares this object against the specified object + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Inet6Address)) + return false; + + Inet6Address ip = (Inet6Address)obj; + if (ipaddress.length != ip.ipaddress.length) + return false; + + for (int i = 0; i < ip.ipaddress.length; i++) + if (ipaddress[i] != ip.ipaddress[i]) + return false; + + if( ip.nif != null && nif != null ) + return nif.equals( ip.nif ); + if( ip.nif != nif ) + return false; + if( ip.scope_id_set != scope_id_set ) + return false; + if( scope_id_set ) + return (scope_id == ip.scope_id); + return true; + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 compatible IPv6 address + * + * @since 1.4 + */ + public boolean isIPv4CompatibleAddress() + { + if (ipaddress[0] != 0x00 || ipaddress[1] != 0x00 || ipaddress[2] != 0x00 + || ipaddress[3] != 0x00 || ipaddress[4] != 0x00 + || ipaddress[5] != 0x00 || ipaddress[6] != 0x00 + || ipaddress[7] != 0x00 || ipaddress[8] != 0x00 + || ipaddress[9] != 0x00 || ipaddress[10] != 0x00 + || ipaddress[11] != 0x00) + return false; + + return true; + } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try + { + if( scope_ifname_set ) + nif = NetworkInterface.getByName( ifname ); + } + catch( SocketException se ) + { + // FIXME: Ignore this? or throw an IOException? + } + } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + if( nif != null ) + { + ifname = nif.getName(); + scope_ifname_set = true; + } + s.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/InetAddress.java b/libjava/classpath/java/net/InetAddress.java new file mode 100644 index 000000000..1a9dc6202 --- /dev/null +++ b/libjava/classpath/java/net/InetAddress.java @@ -0,0 +1,655 @@ +/* InetAddress.java -- Class to model an Internet address + Copyright (C) 1998, 1999, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** + * This class models an Internet address. It does not have a public + * constructor. Instead, new instances of this objects are created + * using the static methods getLocalHost(), getByName(), and + * getAllByName(). + * + * <p>This class fulfills the function of the C style functions gethostname(), + * gethostbyname(), and gethostbyaddr(). It resolves Internet DNS names + * into their corresponding numeric addresses and vice versa.</p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @author Gary Benson (gbenson@redhat.com) + * + * @specnote This class is not final since JDK 1.4 + */ +public class InetAddress implements Serializable +{ + private static final long serialVersionUID = 3286316764910316507L; + + /** + * Dummy InetAddress, used to bind socket to any (all) network interfaces. + */ + static InetAddress ANY_IF; + static + { + byte[] addr; + try + { + addr = VMInetAddress.lookupInaddrAny(); + } + catch (UnknownHostException e) + { + // Make one up and hope it works. + addr = new byte[] {0, 0, 0, 0}; + } + try + { + ANY_IF = getByAddress(addr); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + ANY_IF.hostName = ANY_IF.getHostName(); + } + + /** + * Stores static localhost address object. + */ + static InetAddress LOCALHOST; + static + { + try + { + LOCALHOST = getByAddress("localhost", new byte[] {127, 0, 0, 1}); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * The Serialized Form specifies that an int 'address' is saved/restored. + * This class uses a byte array internally so we'll just do the conversion + * at serialization time and leave the rest of the algorithm as is. + */ + private int address; + + /** + * An array of octets representing an IP address. + */ + transient byte[] addr; + + /** + * The name of the host for this address. + */ + String hostName; + + /** + * Needed for serialization. + */ + private int family; + + /** + * Constructor. Prior to the introduction of IPv6 support in 1.4, + * methods such as InetAddress.getByName() would return InetAddress + * objects. From 1.4 such methods returned either Inet4Address or + * Inet6Address objects, but for compatibility Inet4Address objects + * are serialized as InetAddresses. As such, there are only two + * places where it is appropriate to invoke this constructor: within + * subclasses constructors and within Inet4Address.writeReplace(). + * + * @param ipaddr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + * @param family The address family of this IP address. + */ + InetAddress(byte[] ipaddr, String hostname, int family) + { + addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone(); + hostName = hostname; + this.family = family; + } + + /** + * Returns true if this address is a multicast address, false otherwise. + * An address is multicast if the high four bits are "1110". These are + * also known as "Class D" addresses. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @return true if mulitcast, false if not + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a link local address + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a site local address + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a global multicast address + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a node local multicast address. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a link local multicast address. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a site local multicast address. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a organization local + * multicast address. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the hostname for this address. This will return the IP address + * as a String if there is no hostname available for this address + * + * @return The hostname for this address + */ + public String getHostName() + { + if (hostName == null) + hostName = getCanonicalHostName(); + + return hostName; + } + + /** + * Returns the canonical hostname represented by this InetAddress + */ + String internalGetCanonicalHostName() + { + try + { + return ResolverCache.getHostByAddr(addr); + } + catch (UnknownHostException e) + { + return getHostAddress(); + } + } + + /** + * Returns the canonical hostname represented by this InetAddress + * + * @since 1.4 + */ + public String getCanonicalHostName() + { + String hostname = internalGetCanonicalHostName(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + try + { + sm.checkConnect(hostname, -1); + } + catch (SecurityException e) + { + return getHostAddress(); + } + } + + return hostname; + } + + /** + * Returns the IP address of this object as a byte array. + * + * @return IP address + */ + public byte[] getAddress() + { + // An experiment shows that JDK1.2 returns a different byte array each + // time. This makes sense, in terms of security. + return (byte[]) addr.clone(); + } + + /** + * Returns the IP address of this object as a String. + * + * <p>This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.</p> + * + * @return The IP address of this object in String form + * + * @since 1.0.2 + */ + public String getHostAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a hash value for this address. Useful for creating hash + * tables. Overrides Object.hashCode() + * + * @return A hash value for this address. + */ + public int hashCode() + { + // There hashing algorithm is not specified, but a simple experiment + // shows that it is equal to the address, as a 32-bit big-endian integer. + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xff); + + return hash; + } + + /** + * Tests this address for equality against another InetAddress. The two + * addresses are considered equal if they contain the exact same octets. + * This implementation overrides Object.equals() + * + * @param obj The address to test for equality + * + * @return true if the passed in object's address is equal to this one's, + * false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + // "The Java Class Libraries" 2nd edition says "If a machine has + // multiple names instances of InetAddress for different name of + // that same machine are not equal. This is because they have + // different host names." This violates the description in the + // JDK 1.2 API documentation. A little experimentation + // shows that the latter is correct. + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr.length != addr2.length) + return false; + + for (int i = 0; i < addr.length; i++) + if (addr[i] != addr2[i]) + return false; + + return true; + } + + /** + * Converts this address to a String. This string contains the IP in + * dotted decimal form. For example: "127.0.0.1" This method is equivalent + * to getHostAddress() and overrides Object.toString() + * + * @return This address in String form + */ + public String toString() + { + String addr = getHostAddress(); + String host = (hostName != null) ? hostName : ""; + return host + "/" + addr; + } + + /** + * Returns an InetAddress object given the raw IP address. + * + * The argument is in network byte order: the highest order byte of the + * address is in getAddress()[0]. + * + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address has illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(byte[] addr) + throws UnknownHostException + { + return getByAddress(null, addr); + } + + /** + * Creates an InetAddress based on the provided host name and IP address. + * No name service is checked for the validity of the address. + * + * @param host The hostname of the InetAddress object to create + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address is of illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(String host, byte[] addr) + throws UnknownHostException + { + if (addr.length == 4) + return new Inet4Address(addr, host); + + if (addr.length == 16) + { + for (int i = 0; i < 12; i++) + { + if (addr[i] != (i < 10 ? 0 : (byte) 0xFF)) + return new Inet6Address(addr, host); + } + + byte[] ip4addr = new byte[4]; + ip4addr[0] = addr[12]; + ip4addr[1] = addr[13]; + ip4addr[2] = addr[14]; + ip4addr[3] = addr[15]; + return new Inet4Address(ip4addr, host); + } + + throw new UnknownHostException("IP address has illegal length"); + } + + /** + * Returns an InetAddress object representing the IP address of + * the given literal IP address in dotted decimal format such as + * "127.0.0.1". This is used by SocketPermission.setHostPort() + * to parse literal IP addresses without performing a DNS lookup. + * + * @param literal The literal IP address to create the InetAddress + * object from + * + * @return The address of the host as an InetAddress object, or + * null if the IP address is invalid. + */ + static InetAddress getByLiteral(String literal) + { + byte[] address = VMInetAddress.aton(literal); + if (address == null) + return null; + + try + { + return getByAddress(address); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * Returns an InetAddress object representing the IP address of the given + * hostname. This name can be either a hostname such as "www.urbanophile.com" + * or an IP address in dotted decimal format such as "127.0.0.1". If the + * hostname is null or "", the hostname of the local machine is supplied by + * default. This method is equivalent to returning the first element in + * the InetAddress array returned from GetAllByName. + * + * @param hostname The name of the desired host, or null for the local + * loopback address. + * + * @return The address of the host as an InetAddress object. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress getByName(String hostname) + throws UnknownHostException + { + InetAddress[] addresses = getAllByName(hostname); + return addresses[0]; + } + + /** + * Returns an array of InetAddress objects representing all the host/ip + * addresses of a given host, given the host's name. This name can be + * either a hostname such as "www.urbanophile.com" or an IP address in + * dotted decimal format such as "127.0.0.1". If the value is null, the + * hostname of the local machine is supplied by default. + * + * @param hostname The name of the desired host, or null for the + * local loopback address. + * + * @return All addresses of the host as an array of InetAddress objects. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress[] getAllByName(String hostname) + throws UnknownHostException + { + // If null or the empty string is supplied, the loopback address + // is returned. + if (hostname == null || hostname.length() == 0) + return new InetAddress[] {LOCALHOST}; + + // Check if hostname is an IP address + InetAddress address = getByLiteral(hostname); + if (address != null) + return new InetAddress[] {address}; + + // Perform security check before resolving + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(hostname, -1); + + // Resolve the hostname + byte[][] iplist = ResolverCache.getHostByName(hostname); + if (iplist.length == 0) + throw new UnknownHostException(hostname); + + InetAddress[] addresses = new InetAddress[iplist.length]; + for (int i = 0; i < iplist.length; i++) + addresses[i] = getByAddress(hostname, iplist[i]); + + return addresses; + } + + /** + * Returns an InetAddress object representing the address of the current + * host. + * + * @return The local host's address + * + * @exception UnknownHostException If no IP address for the host could + * be found + */ + public static InetAddress getLocalHost() throws UnknownHostException + { + String hostname = VMInetAddress.getLocalHostname(); + try + { + return getByName(hostname); + } + catch (SecurityException e) + { + return LOCALHOST; + } + } + + /** + * Inet4Address objects are serialized as InetAddress objects. + * This deserializes them back into Inet4Address objects. + */ + private Object readResolve() throws ObjectStreamException + { + return new Inet4Address(addr, hostName); + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + addr = new byte[4]; + addr[3] = (byte) address; + + for (int i = 2; i >= 0; --i) + addr[i] = (byte) (address >>= 8); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + // Build a 32 bit address from the last 4 bytes of a 4 byte IPv4 address + // or a 16 byte IPv6 address. + int len = addr.length; + int i = len - 4; + + for (; i < len; i++) + address = address << 8 | (addr[i] & 0xff); + + oos.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/InetSocketAddress.java b/libjava/classpath/java/net/InetSocketAddress.java new file mode 100644 index 000000000..2126f7703 --- /dev/null +++ b/libjava/classpath/java/net/InetSocketAddress.java @@ -0,0 +1,261 @@ +/* InetSocketAddress.java -- + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * InetSocketAddress instances represent socket addresses + * in the java.nio package. They encapsulate a InetAddress and + * a port number. + * + * @since 1.4 + */ +public class InetSocketAddress extends SocketAddress +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 5076001401234631237L; + + /** + * Name of host. + */ + private String hostname; + + /** + * Address of host. + */ + private InetAddress addr; + + /** + * Port of host. + */ + private int port; + + /** + * Constructs an InetSocketAddress instance. + * + * @param addr Address of the socket + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegel + */ + public InetSocketAddress(InetAddress addr, int port) + throws IllegalArgumentException + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + this.addr = addr; + this.port = port; + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegal + */ + public InetSocketAddress(int port) throws IllegalArgumentException + { + this((InetAddress) null, port); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + */ + public InetSocketAddress(String hostname, int port) + { + this(hostname, port, true); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * @param resolve <code>true</code> if the address has to be resolved, + * <code>false</code> otherwise + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + */ + private InetSocketAddress(String hostname, int port, boolean resolve) + { + if (hostname == null) + throw new IllegalArgumentException("Null host name value"); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + this.port = port; + this.hostname = hostname; + this.addr = null; + + if (resolve) + { + try + { + this.addr = InetAddress.getByName(hostname); + } + catch (Exception e) // UnknownHostException, SecurityException + { + // Do nothing here. this.addr is already set to null. + } + } + + } + + /** + * Creates an unresolved <code>InetSocketAddress</code> object. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + * + * @since 1.5 + */ + public static InetSocketAddress createUnresolved(String hostname, int port) + { + return new InetSocketAddress(hostname, port, false); + } + + /** + * Test if obj is a <code>InetSocketAddress</code> and + * has the same address and port + * + * @param obj The obj to compare this address with. + * + * @return True if obj is equal. + */ + public final boolean equals(Object obj) + { + // InetSocketAddress objects are equal when addr and port are equal. + // The hostname may differ. + if (obj instanceof InetSocketAddress) + { + InetSocketAddress sa = (InetSocketAddress) obj; + + if (addr == null && sa.addr != null) + return false; + else if (addr == null && sa.addr == null) // we know hostname != null + return hostname.equals(sa.hostname) && sa.port == port; + else + return addr.equals(sa.addr) && sa.port == port; + } + + return false; + } + + /** + * Returns the <code>InetAddress</code> or + * <code>null</code> if its unresolved + * + * @return The IP address of this address. + */ + public final InetAddress getAddress() + { + return addr; + } + + /** + * Returns <code>hostname</code> + * + * @return The hostname of this address. + */ + public final String getHostName() + { + if (hostname == null) // we know addr != null + hostname = addr.getHostName(); + + return hostname; + } + + /** + * Returns the <code>port</code> + * + * @return The port of this address. + */ + public final int getPort() + { + return port; + } + + /** + * Returns the hashcode of the <code>InetSocketAddress</code> + * + * @return The hashcode for this address. + */ + public final int hashCode() + { + return port + addr.hashCode(); + } + + /** + * Checks wether the address has been resolved or not + * + * @return True if address is unresolved. + */ + public final boolean isUnresolved() + { + return addr == null; + } + + /** + * Returns the <code>InetSocketAddress</code> as string + * + * @return A string representation of this address. + */ + public String toString() + { + // Note: if addr is null, then hostname != null. + return (addr == null ? hostname : addr.toString()) + ":" + port; + } +} diff --git a/libjava/classpath/java/net/JarURLConnection.java b/libjava/classpath/java/net/JarURLConnection.java new file mode 100644 index 000000000..0ffd1afac --- /dev/null +++ b/libjava/classpath/java/net/JarURLConnection.java @@ -0,0 +1,229 @@ +/* JarURLConnection.java -- Class for manipulating remote jar files + Copyright (C) 1998, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + + +/** + * This abstract class represents a common superclass for implementations + * of jar URL's. A jar URL is a special type of URL that allows JAR + * files on remote systems to be accessed. It has the form: + * <p> + * jar:<standard URL pointing to jar filei>!/file/within/jarfile + * <p> for example: + * <p> + * jar:http://www.urbanophile.com/java/foo.jar!/com/urbanophile/bar.class + * <p> + * That example URL points to the file /com/urbanophile/bar.class in the + * remote JAR file http://www.urbanophile.com/java/foo.jar. The HTTP + * protocol is used only as an example. Any supported remote protocol + * can be used. + * <p> + * This class currently works by retrieving the entire jar file into a + * local cache file, then performing standard jar operations on it. + * (At least this is true for the default protocol implementation). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Kresten Krab Thorup (krab@gnu.org) + * @date Aug 10, 1999. + * + * @since 1.2 + */ +public abstract class JarURLConnection extends URLConnection +{ + /** + * This is the actual URL that points the remote jar file. This is parsed + * out of the jar URL by the constructor. + */ + private final URL jarFileURL; + + /** + * The connection to the jar file itself. A JarURLConnection + * can represent an entry in a jar file or an entire jar file. In + * either case this describes just the jar file itself. + */ + protected URLConnection jarFileURLConnection; + + /** + * This is the jar file "entry name" or portion after the "!/" in the + * URL which represents the pathname inside the actual jar file. + */ + private final String entryName; + + /** + * Creates a JarURLConnection from an URL object + * + * @param url The URL object for this connection. + * + * @exception MalformedURLException If url is invalid + * + * @specnote This constructor is protected since JDK 1.4 + */ + protected JarURLConnection(URL url) throws MalformedURLException + { + super(url); + + if (! url.getProtocol().equals("jar")) + throw new MalformedURLException(url + ": Not jar protocol."); + + String spec = url.getFile(); + int bang = spec.indexOf("!/"); + if (bang == -1) + throw new MalformedURLException(url + ": No `!/' in spec."); + + // Extract the url for the jar itself. + jarFileURL = new URL(spec.substring(0, bang)); + + // Get the name of the entry, if any. + entryName = spec.length() == (bang + 2) ? null : spec.substring(bang + 2); + } + + /** + * This method returns the "real" URL where the JarFile is located. + * //****Is this right?***** + * + * @return The remote URL + */ + public URL getJarFileURL() + { + return jarFileURL; + } + + /** + * Returns the "entry name" portion of the jar URL. This is the portion + * after the "!/" in the jar URL that represents the pathname inside the + * actual jar file. + * + * @return The entry name. + */ + public String getEntryName() + { + return entryName; + } + + /** + * Returns the entry in this jar file specified by the URL. + * + * @return The jar entry + * + * @exception IOException If an error occurs + */ + public JarEntry getJarEntry() throws IOException + { + if (entryName == null) + return null; + JarFile jarFile = getJarFile(); + return jarFile != null ? jarFile.getJarEntry(entryName) : null; + } + + /** + * Returns a read-only JarFile object for the remote jar file + * + * @return The JarFile object + * + * @exception IOException If an error occurs + */ + public abstract JarFile getJarFile() throws IOException; + + /** + * Returns an array of Certificate objects for the jar file entry specified + * by this URL or null if there are none + * + * @return A Certificate array + * + * @exception IOException If an error occurs + */ + public Certificate[] getCertificates() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getCertificates() : null; + } + + /** + * Returns the main Attributes for the jar file specified in the URL or + * null if there are none + * + * @return The main Attributes for the JAR file for this connection + * + * @exception IOException If an error occurs + */ + public Attributes getMainAttributes() throws IOException + { + Manifest manifest = getManifest(); + + return manifest != null ? manifest.getMainAttributes() : null; + } + + /** + * Returns the Attributes for the Jar entry specified by the URL or null + * if none + * + * @return The Attributes object for this connection if the URL for it points + * to a JAR file entry, null otherwise + * + * @exception IOException If an error occurs + */ + public Attributes getAttributes() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getAttributes() : null; + } + + /** + * Returns a Manifest object for this jar file, or null if there is no + * manifest. + * + * @return The Manifest for this connection, or null if none + * + * @exception IOException If an error occurs + */ + public Manifest getManifest() throws IOException + { + JarFile file = getJarFile(); + + return file != null ? file.getManifest() : null; + } +} diff --git a/libjava/classpath/java/net/MalformedURLException.java b/libjava/classpath/java/net/MalformedURLException.java new file mode 100644 index 000000000..27e201886 --- /dev/null +++ b/libjava/classpath/java/net/MalformedURLException.java @@ -0,0 +1,74 @@ +/* MalformedURLException.java -- A URL was not in a valid format + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a URL passed to an object was not in a + * valid format. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class MalformedURLException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -182787522200415866L; + + /** + * Create a new instance without a descriptive error message. + */ + public MalformedURLException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public MalformedURLException(String message) + { + super(message); + } +} // class MalformedURLException diff --git a/libjava/classpath/java/net/MimeTypeMapper.java b/libjava/classpath/java/net/MimeTypeMapper.java new file mode 100644 index 000000000..3e4379291 --- /dev/null +++ b/libjava/classpath/java/net/MimeTypeMapper.java @@ -0,0 +1,346 @@ +/* MimeTypeMapper.java -- A class for mapping file names to MIME types + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; + +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import java.util.TreeMap; + + +/** + * This non-public class is used to implement the FileNameMap interface + * which defines a mechanism for mapping filenames to MIME types. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class MimeTypeMapper implements FileNameMap +{ + /** + * This array of strings is used to identify a MIME type based on a file + * extension. This is list is based on the Apache mime.types file. + */ + protected static final String[][] mime_strings = + { + { "ai", "application/postscript" } + , { "aif", "audio/x-aiff" } + , { "aifc", "audio/x-aiff" } + , { "aiff", "audio/x-aiff" } + , { "asc", "text/plain" } + , { "au", "audio/basic" } + , { "avi", "video/x-msvideo" } + , { "bcpio", "application/x-bcpio" } + , { "bin", "application/octet-stream" } + , { "bmp", "image/bmp" } + , { "bz2", "application/x-bzip2" } + , { "cdf", "application/x-netcdf" } + , { "chrt", "application/x-kchart" } + , { "class", "application/octet-stream" } + , { "cpio", "application/x-cpio" } + , { "cpt", "application/mac-compactpro" } + , { "csh", "application/x-csh" } + , { "css", "text/css" } + , { "dcr", "application/x-director" } + , { "dir", "application/x-director" } + , { "djv", "image/vnd.djvu" } + , { "djvu", "image/vnd.djvu" } + , { "dll", "application/octet-stream" } + , { "dms", "application/octet-stream" } + , { "doc", "application/msword" } + , { "dvi", "application/x-dvi" } + , { "dxr", "application/x-director" } + , { "eps", "application/postscript" } + , { "etx", "text/x-setext" } + , { "exe", "application/octet-stream" } + , { "ez", "application/andrew-inset" } + , { "gif", "image/gif" } + , { "gtar", "application/x-gtar" } + , { "gz", "application/x-gzip" } + , { "hdf", "application/x-hdf" } + , { "hqx", "application/mac-binhex40" } + , { "htm", "text/html" } + , { "html", "text/html" } + , { "ice", "x-conference/x-cooltalk" } + , { "ief", "image/ief" } + , { "iges", "model/iges" } + , { "igs", "model/iges" } + , { "img", "application/octet-stream" } + , { "iso", "application/octet-stream" } + , { "jpe", "image/jpeg" } + , { "jpeg", "image/jpeg" } + , { "jpg", "image/jpeg" } + , { "js", "application/x-javascript" } + , { "kar", "audio/midi" } + , { "kil", "application/x-killustrator" } + , { "kpr", "application/x-kpresenter" } + , { "kpt", "application/x-kpresenter" } + , { "ksp", "application/x-kspread" } + , { "kwd", "application/x-kword" } + , { "kwt", "application/x-kword" } + , { "latex", "application/x-latex" } + , { "lha", "application/octet-stream" } + , { "lzh", "application/octet-stream" } + , { "m3u", "audio/x-mpegurl" } + , { "man", "application/x-troff-man" } + , { "me", "application/x-troff-me" } + , { "mesh", "model/mesh" } + , { "mid", "audio/midi" } + , { "midi", "audio/midi" } + , { "mif", "application/vnd.mif" } + , { "mov", "video/quicktime" } + , { "movie", "video/x-sgi-movie" } + , { "mp2", "audio/mpeg" } + , { "mp3", "audio/mpeg" } + , { "mpe", "video/mpeg" } + , { "mpeg", "video/mpeg" } + , { "mpg", "video/mpeg" } + , { "mpga", "audio/mpeg" } + , { "ms", "application/x-troff-ms" } + , { "msh", "model/mesh" } + , { "mxu", "video/vnd.mpegurl" } + , { "nc", "application/x-netcdf" } + , { "ogg", "application/ogg" } + , { "pbm", "image/x-portable-bitmap" } + , { "pdb", "chemical/x-pdb" } + , { "pdf", "application/pdf" } + , { "pgm", "image/x-portable-graymap" } + , { "pgn", "application/x-chess-pgn" } + , { "png", "image/png" } + , { "pnm", "image/x-portable-anymap" } + , { "ppm", "image/x-portable-pixmap" } + , { "ppt", "application/vnd.ms-powerpoint" } + , { "ps", "application/postscript" } + , { "qt", "video/quicktime" } + , { "ra", "audio/x-realaudio" } + , { "ram", "audio/x-pn-realaudio" } + , { "ras", "image/x-cmu-raster" } + , { "rgb", "image/x-rgb" } + , { "rm", "audio/x-pn-realaudio" } + , { "roff", "application/x-troff" } + , { "rpm", "application/x-rpm" } + , { "rtf", "text/rtf" } + , { "rtx", "text/richtext" } + , { "sgm", "text/sgml" } + , { "sgml", "text/sgml" } + , { "sh", "application/x-sh" } + , { "shar", "application/x-shar" } + , { "silo", "model/mesh" } + , { "sit", "application/x-stuffit" } + , { "skd", "application/x-koan" } + , { "skm", "application/x-koan" } + , { "skp", "application/x-koan" } + , { "skt", "application/x-koan" } + , { "smi", "application/smil" } + , { "smil", "application/smil" } + , { "snd", "audio/basic" } + , { "so", "application/octet-stream" } + , { "spl", "application/x-futuresplash" } + , { "src", "application/x-wais-source" } + , { "stc", "application/vnd.sun.xml.calc.template" } + , { "std", "application/vnd.sun.xml.draw.template" } + , { "sti", "application/vnd.sun.xml.impress.template" } + , { "stw", "application/vnd.sun.xml.writer.template" } + , { "sv4cpio", "application/x-sv4cpio" } + , { "sv4crc", "application/x-sv4crc" } + , { "swf", "application/x-shockwave-flash" } + , { "sxc", "application/vnd.sun.xml.calc" } + , { "sxd", "application/vnd.sun.xml.draw" } + , { "sxg", "application/vnd.sun.xml.writer.global" } + , { "sxi", "application/vnd.sun.xml.impress" } + , { "sxm", "application/vnd.sun.xml.math" } + , { "sxw", "application/vnd.sun.xml.writer" } + , { "t", "application/x-troff" } + , { "tar", "application/x-tar" } + , { "tcl", "application/x-tcl" } + , { "tex", "application/x-tex" } + , { "texi", "application/x-texinfo" } + , { "texinfo", "application/x-texinfo" } + , { "tgz", "application/x-gzip" } + , { "tif", "image/tiff" } + , { "tiff", "image/tiff" } + , { "torrent", "application/x-bittorrent" } + , { "tr", "application/x-troff" } + , { "tsv", "text/tab-separated-values" } + , { "txt", "text/plain" } + , { "ustar", "application/x-ustar" } + , { "vcd", "application/x-cdlink" } + , { "vrml", "model/vrml" } + , { "wav", "audio/x-wav" } + , { "wbmp", "image/vnd.wap.wbmp" } + , { "wbxml", "application/vnd.wap.wbxml" } + , { "wml", "text/vnd.wap.wml" } + , { "wmlc", "application/vnd.wap.wmlc" } + , { "wmls", "text/vnd.wap.wmlscript" } + , { "wmlsc", "application/vnd.wap.wmlscriptc" } + , { "wrl", "model/vrml" } + , { "xbm", "image/x-xbitmap" } + , { "xht", "application/xhtml+xml" } + , { "xhtml", "application/xhtml+xml" } + , { "xls", "application/vnd.ms-excel" } + , { "xml", "text/xml" } + , { "xpm", "image/x-xpixmap" } + , { "xsl", "text/xml" } + , { "xwd", "image/x-xwindowdump" } + , { "xyz", "chemical/x-xyz" } + , { "zip", "application/zip" } + }; + + /** + * The MIME types above are put into this Hashtable for faster lookup. + */ + private Hashtable<String, String> mime_types + = new Hashtable<String, String>(150); + + /** + * Create a new <code>MimeTypeMapper</code> object. + */ + public MimeTypeMapper() + { + for (int i = 0; i < mime_strings.length; i++) + mime_types.put(mime_strings[i][0], mime_strings[i][1]); + + // Now read from the system mime database, if it exists. Entries found + // here override our internal ones. + try + { + // On Linux this usually means /etc/mime.types. + String file + = SystemProperties.getProperty("gnu.classpath.mime.types.file"); + if (file != null) + fillFromFile(mime_types, file); + } + catch (IOException ignore) + { + } + } + + public static void fillFromFile (Map<String, String> table, String fname) + throws IOException + { + LineNumberReader reader = + new LineNumberReader (new FileReader (fname)); + + while (reader.ready ()) + { + StringTokenizer tokenizer = + new StringTokenizer (reader.readLine ()); + + try + { + String t = tokenizer.nextToken (); + + if (! t.startsWith ("#")) + { + while (true) + { + // Read the next extension + String e = tokenizer.nextToken (); + if ((e != null) && (! e.startsWith ("#"))) + table.put (e, t); + else + break; + } + } + } + catch (NoSuchElementException ex) + { + // Do nothing. + } + } + } + + /** + * The method returns the MIME type of the filename passed as an argument. + * The value returned is based on the extension of the filename. The + * default content type returned if this method cannot determine the + * actual content type is "application/octet-stream" + * + * @param filename The name of the file to return the MIME type for + * + * @return The MIME type + */ + public String getContentTypeFor(String filename) + { + int index = filename.lastIndexOf("."); + if (index != -1) + { + if (index == filename.length()) + return "application/octet-stream"; + else + filename = filename.substring(index + 1); + } + + String type = (String) mime_types.get(filename); + if (type == null) + return "application/octet-stream"; + else + return type; + } + + /** + * Run this class as a program to create a new mime_strings table. + */ + public static void main(String[] args) throws IOException + { + TreeMap<String, String> map = new TreeMap<String, String>(); + // It is fine to hard-code the name here. This is only ever + // used by maintainers, who can hack it if they need to re-run + // it. + fillFromFile(map, "/etc/mime.types"); + Iterator<String> it = map.keySet().iterator(); + boolean first = true; + while (it.hasNext()) + { + String key = it.next(); + String value = map.get(key); + // Put the "," first since it is easier to make correct syntax this way. + System.out.println(" " + (first ? " " : ", ") + + "{ \"" + key + "\", \"" + value + "\" }"); + first = false; + } + } +} diff --git a/libjava/classpath/java/net/MulticastSocket.java b/libjava/classpath/java/net/MulticastSocket.java new file mode 100644 index 000000000..5014b6a8e --- /dev/null +++ b/libjava/classpath/java/net/MulticastSocket.java @@ -0,0 +1,518 @@ +/* MulticastSocket.java -- Class for using multicast sockets + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.util.Enumeration; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a multicast UDP socket. A multicast address is a + * class D internet address (one whose most significant bits are 1110). + * A multicast group consists of a multicast address and a well known + * port number. All members of the group listening on that address and + * port will receive all the broadcasts to the group. + * <p> + * Please note that applets are not allowed to use multicast sockets + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (Documentation comments) + * @since 1.1 + * @date May 18, 1999. + */ +public class MulticastSocket extends DatagramSocket +{ + /** + * Create a MulticastSocket that this not bound to any address + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket() throws IOException + { + this(new InetSocketAddress(0)); + } + + /** + * Create a multicast socket bound to the specified port + * + * @param port The port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket(int port) throws IOException + { + this(new InetSocketAddress(port)); + } + + /** + * Create a multicast socket bound to the specified SocketAddress. + * + * @param address The SocketAddress the multicast socket will be bound to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public MulticastSocket(SocketAddress address) throws IOException + { + super((SocketAddress) null); + setReuseAddress(true); + if (address != null) + bind(address); + } + + /** + * Returns the interface being used for multicast packets + * + * @return The multicast interface + * + * @exception SocketException If an error occurs + */ + public InetAddress getInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". This method id + * deprecated. Use <code>getTimeToLive</code> instead. + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by getTimeToLive() + * + * @see MulticastSocket#getTimeToLive() + */ + public byte getTTL() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use getTTL here rather than getTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // getTimeToLive yet. + return getImpl().getTTL(); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public int getTimeToLive() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return getImpl().getTimeToLive(); + } + + /** + * Sets the interface to use for sending multicast packets. + * + * @param addr The new interface to use. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setInterface(InetAddress addr) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, addr); + } + + /** + * Sets the local network interface used to send multicast messages + * + * @param netIf The local network interface used to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#getNetworkInterface() + * + * @since 1.4 + */ + public void setNetworkInterface(NetworkInterface netIf) + throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetAddress address; + if (netIf != null) + out: + { + Enumeration e = netIf.getInetAddresses(); + if (getLocalAddress() instanceof Inet4Address) + { + // Search for a IPv4 address. + while (e.hasMoreElements()) + { + address = (InetAddress) e.nextElement(); + if (address instanceof Inet4Address) + break out; + } + throw new SocketException("interface " + netIf.getName() + " has no IPv6 address"); + } + else if (getLocalAddress() instanceof Inet6Address) + { + // Search for a IPv6 address. + while (e.hasMoreElements()) + { + address = (InetAddress) e.nextElement(); + if (address instanceof Inet6Address) + break out; + } + throw new SocketException("interface " + netIf.getName() + " has no IPv6 address"); + } + else + throw new SocketException("interface " + netIf.getName() + " has no suitable IP address"); + } + else + address = InetAddress.ANY_IF; + + + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, address); + } + + /** + * Gets the local network interface which is used to send multicast messages + * + * @return The local network interface to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public NetworkInterface getNetworkInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetAddress address = + (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + + if (address.isAnyLocalAddress()) + return NetworkInterface.createAnyInterface(); + + NetworkInterface netIf = NetworkInterface.getByInetAddress(address); + + return netIf; + } + + /** + * Disable/Enable local loopback of multicast packets. The option is used by + * the platform's networking code as a hint for setting whether multicast + * data will be looped back to the local socket. + * + * Because this option is a hint, applications that want to verify what + * loopback mode is set to should call #getLoopbackMode + * + * @param disable True to disable loopback mode + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setLoopbackMode(boolean disable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, + Boolean.valueOf(disable)); + } + + /** + * Checks if local loopback mode is enabled + * + * @return true if loopback mode is enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getLoopbackMode() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 1 and 255. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by <code>setTimeToLive</code> + * + * @see MulticastSocket#setTimeToLive(int ttl) + */ + public void setTTL(byte ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use setTTL here rather than setTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // setTimeToLive yet. + getImpl().setTTL(ttl); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 0 and 255, inclusive. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public void setTimeToLive(int ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (ttl < 0 || ttl > 255) + throw new IllegalArgumentException("Invalid ttl: " + ttl); + + getImpl().setTimeToLive(ttl); + } + + /** + * Joins the specified multicast group. + * + * @param mcastaddr The address of the group to join + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void joinGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().join(mcastaddr); + } + + /** + * Leaves the specified multicast group + * + * @param mcastaddr The address of the group to leave + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void leaveGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().leave(mcastaddr); + } + + /** + * Joins the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to join + * @param netIf The local network interface to receive the multicast + * messages on or null to defer the interface set by #setInterface or + * #setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("SocketAddress type not supported"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().joinGroup(mcastaddr, netIf); + } + + /** + * Leaves the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to leave + * @param netIf The local networki interface or null to defer to the + * interface set by setInterface or setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().leaveGroup(mcastaddr, netIf); + } + + /** + * Sends a packet of data to a multicast address with a TTL that is + * different from the default TTL on this socket. The default TTL for + * the socket is not changed. + * + * @param packet The packet of data to send + * @param ttl The TTL for this packet + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect or checkMulticast method doesn't allow the operation + * + * @deprecated + */ + public synchronized void send(DatagramPacket packet, byte ttl) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + InetAddress addr = packet.getAddress(); + if (addr.isMulticastAddress()) + s.checkPermission(new SocketPermission(addr.getHostName() + + packet.getPort(), + "accept,connect")); + else + s.checkConnect(addr.getHostAddress(), packet.getPort()); + } + + int oldttl = getImpl().getTimeToLive(); + getImpl().setTimeToLive(((int) ttl) & 0xFF); + getImpl().send(packet); + getImpl().setTimeToLive(oldttl); + } +} diff --git a/libjava/classpath/java/net/NetPermission.java b/libjava/classpath/java/net/NetPermission.java new file mode 100644 index 000000000..cabe54e06 --- /dev/null +++ b/libjava/classpath/java/net/NetPermission.java @@ -0,0 +1,90 @@ +/* NetPermission.java -- A class for basic miscellaneous network permission + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.security.BasicPermission; + + +/** + * This class is used to model miscellaneous network permissions. It is + * a subclass of <code>BasicPermission</code>. This means that it models a + * "boolean" permission. One that you either have or do not have. Thus + * there is no permitted action list associated with this object. + * + * The following permission names are defined for this class: + * + * <ul> + * <li>setDefaultAuthenticator - Grants the ability to install a facility + * to collect username and password information when requested by a + * web site or proxy server.</li> + * <li>requestPasswordAuthentication - Grants the ability to ask the + * authentication facility for the user's password.</li> + * <li>specifyStreamHandler - Grants the permission to specify the + * stream handler class used when loading from a URL.</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class NetPermission extends BasicPermission +{ + static final long serialVersionUID = -8343910153355041693L; + + /** + * Initializes a new instance of <code>NetPermission</code> with the + * specified name. + * + * @param name The name of this permission. + */ + public NetPermission(String name) + { + super(name); + } + + /** + * Initializes a new instance of <code>NetPermission</code> with the + * specified name and perms. Note that the perms field is irrelevant and is + * ignored. This constructor should never need to be used. + * + * @param name The name of this permission + * @param perms The permitted actions of this permission (ignored) + */ + public NetPermission(String name, String perms) + { + super(name); + } +} diff --git a/libjava/classpath/java/net/NetworkInterface.java b/libjava/classpath/java/net/NetworkInterface.java new file mode 100644 index 000000000..127dfba78 --- /dev/null +++ b/libjava/classpath/java/net/NetworkInterface.java @@ -0,0 +1,316 @@ +/* NetworkInterface.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Vector; + +/** + * This class models a network interface on the host computer. A network + * interface contains a name (typically associated with a specific + * hardware adapter) and a list of addresses that are bound to it. + * For example, an ethernet interface may be named "eth0" and have the + * address 192.168.1.101 assigned to it. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public final class NetworkInterface +{ + private final VMNetworkInterface netif; + + private NetworkInterface(VMNetworkInterface netif) + { + this.netif = netif; + } + + /** Creates an NetworkInterface instance which + * represents any interface in the system. Its only + * address is <code>0.0.0.0/0.0.0.0</code>. This + * method is needed by {@link MulticastSocket#getNetworkInterface} + */ + static NetworkInterface createAnyInterface() + { + return new NetworkInterface(new VMNetworkInterface()); + } + + /** + * Returns the name of the network interface + * + * @return The name of the interface. + */ + public String getName() + { + return netif.name; + } + + /** + * Returns all available addresses of the network interface + * + * If a @see SecurityManager is available all addresses are checked + * with @see SecurityManager::checkConnect() if they are available. + * Only <code>InetAddresses</code> are returned where the security manager + * doesn't throw an exception. + * + * @return An enumeration of all addresses. + */ + public Enumeration<InetAddress> getInetAddresses() + { + SecurityManager s = System.getSecurityManager(); + Vector<InetAddress> inetAddresses + = new Vector<InetAddress>(netif.addresses); + + if (s == null) + return inetAddresses.elements(); + + Vector<InetAddress> tmpInetAddresses = new Vector<InetAddress>(1, 1); + + for (Enumeration<InetAddress> addresses = inetAddresses.elements(); + addresses.hasMoreElements();) + { + InetAddress addr = addresses.nextElement(); + try + { + s.checkConnect(addr.getHostAddress(), -1); + tmpInetAddresses.add(addr); + } + catch (SecurityException e) + { + // Ignore. + } + } + + return tmpInetAddresses.elements(); + } + + /** + * Returns the display name of the interface + * + * @return The display name of the interface + */ + public String getDisplayName() + { + return netif.name; + } + + /** + * Returns an network interface by name + * + * @param name The name of the interface to return + * + * @return a <code>NetworkInterface</code> object representing the interface, + * or null if there is no interface with that name. + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified name is null + */ + public static NetworkInterface getByName(String name) + throws SocketException + { + if (name == null) + throw new NullPointerException(); + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + for (int i = 0; i < netifs.length; i++) + { + if (netifs[i].name.equals(name)) + return new NetworkInterface(netifs[i]); + } + return null; + } + + /** + * Return a network interface by its address + * + * @param addr The address of the interface to return + * + * @return the interface, or <code>null</code> if none found + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified addess is null + */ + public static NetworkInterface getByInetAddress(InetAddress addr) + throws SocketException + { + if (addr == null) + throw new NullPointerException(); + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + for (int i = 0; i < netifs.length; i++) + { + if (netifs[i].addresses.contains(addr)) + return new NetworkInterface(netifs[i]); + } + return null; + } + + /** + * Return an <code>Enumeration</code> of all available network interfaces + * + * @return all interfaces + * + * @exception SocketException If an error occurs + */ + public static Enumeration<NetworkInterface> getNetworkInterfaces() + throws SocketException + { + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + Vector<NetworkInterface> networkInterfaces = + new Vector<NetworkInterface>(netifs.length); + for (int i = 0; i < netifs.length; i++) + { + if (!netifs[i].addresses.isEmpty()) + networkInterfaces.add(new NetworkInterface(netifs[i])); + } + return networkInterfaces.elements(); + } + + /** + * Checks if the current instance is equal to obj + * + * @param obj The object to compare with + * + * @return <code>true</code> if equal, <code>false</code> otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof NetworkInterface)) + return false; + + NetworkInterface tmp = (NetworkInterface) obj; + + if (netif.name == null) + return tmp.netif.name == null; + + return (netif.name.equals(tmp.netif.name) + && (netif.addresses.equals(tmp.netif.addresses))); + } + + /** + * Returns the hashcode of the current instance + * + * @return the hashcode + */ + public int hashCode() + { + // FIXME: hash correctly + int hc = netif.addresses.hashCode(); + + if (netif.name != null) + hc += netif.name.hashCode(); + + return hc; + } + + /** + * Returns a string representation of the interface + * + * @return the string + */ + public String toString() + { + // FIXME: check if this is correct + CPStringBuilder result; + String separator = SystemProperties.getProperty("line.separator"); + + result = new CPStringBuilder(); + + result.append("name: "); + result.append(getDisplayName()); + result.append(" (").append(getName()).append(") addresses:"); + result.append(separator); + + for (Iterator it = netif.addresses.iterator(); it.hasNext(); ) + { + InetAddress address = (InetAddress) it.next(); + result.append(address.toString()).append(";").append(separator); + } + + return result.toString(); + } + + /** + * Determines whether this interface is ready to transfer data. + * + * @return whether the interface is up + */ + public boolean isUp() + throws SocketException + { + return VMNetworkInterface.isUp(netif.name); + } + + /** + * Determines whether this interface does point to point + * transmission. + * + * @return whether the interface does point to point transmission + */ + public boolean isPointToPoint() + throws SocketException + { + return VMNetworkInterface.isPointToPoint(netif.name); + } + + /** + * Determines whether this interface is the loopback interface. + * + * @return whether the interface is the loopback interface + */ + public boolean isLoopback() + throws SocketException + { + return VMNetworkInterface.isLoopback(netif.name); + } + + /** + * Determines whether this interface supports multicast transmission. + * + * @return whether the interface supports multicast transmission. + */ + public boolean supportsMulticast() + throws SocketException + { + return VMNetworkInterface.supportsMulticast(netif.name); + } + +} diff --git a/libjava/classpath/java/net/NoRouteToHostException.java b/libjava/classpath/java/net/NoRouteToHostException.java new file mode 100644 index 000000000..48c3a8e60 --- /dev/null +++ b/libjava/classpath/java/net/NoRouteToHostException.java @@ -0,0 +1,74 @@ +/* NoRouteToHostException.java -- Cannot connect to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that there is no TCP/IP route to the requested + * host. This is often due to a misconfigured routing table. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoRouteToHostException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1897550894873493790L; + + /** + * Create an instance without a descriptive error message. + */ + public NoRouteToHostException() + { + } + + /** + * Create an instance with a descriptive error message, such as the text + * from strerror(3). + * + * @param message a message describing the error that occurred + */ + public NoRouteToHostException(String message) + { + super(message); + } +} // class NoRouteToHostException diff --git a/libjava/classpath/java/net/PasswordAuthentication.java b/libjava/classpath/java/net/PasswordAuthentication.java new file mode 100644 index 000000000..1d4ec8961 --- /dev/null +++ b/libjava/classpath/java/net/PasswordAuthentication.java @@ -0,0 +1,92 @@ +/* PasswordAuthentication.java -- Container class for username/password pairs + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This class serves a container for username/password pairs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @since 1.2 + */ +public final class PasswordAuthentication +{ + /** + * The username + */ + private String username; + + /** + * The password + */ + private char[] password; + + /** + * Creates a new <code>PasswordAuthentication</code> object from the + * specified username and password. + * + * @param username The username for this object + * @param password The password for this object + */ + public PasswordAuthentication(String username, char[] password) + { + this.username = username; + this.password = password; + } + + /** + * Returns the username associated with this object + * + * @return The username + */ + public String getUserName() + { + return username; + } + + /** + * Returns the password associated with this object + * + * @return The password + */ + public char[] getPassword() + { + return password; + } +} diff --git a/libjava/classpath/java/net/PortUnreachableException.java b/libjava/classpath/java/net/PortUnreachableException.java new file mode 100644 index 000000000..49a8c9ea1 --- /dev/null +++ b/libjava/classpath/java/net/PortUnreachableException.java @@ -0,0 +1,72 @@ +/* PortUnreachableException.java -- received an ICMP port unreachable datagram + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception signals that an ICMP port unreachable datagram has been + * received. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class PortUnreachableException extends SocketException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8462541992376507323L; + + /** + * Create a new instance without a descriptive error message. + */ + public PortUnreachableException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public PortUnreachableException(String message) + { + super(message); + } +} // class PortUnreachableException diff --git a/libjava/classpath/java/net/ProtocolException.java b/libjava/classpath/java/net/ProtocolException.java new file mode 100644 index 000000000..27718a979 --- /dev/null +++ b/libjava/classpath/java/net/ProtocolException.java @@ -0,0 +1,75 @@ +/* ProtocolException.java -- a low level protocol error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that some sort of low level protocol + * exception occurred. Look in the descriptive message (if any) for + * details on what went wrong. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ProtocolException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6098449442062388080L; + + /** + * Create a new instance without a descriptive error message. + */ + public ProtocolException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public ProtocolException(String message) + { + super(message); + } +} // class ProtocolException diff --git a/libjava/classpath/java/net/Proxy.java b/libjava/classpath/java/net/Proxy.java new file mode 100644 index 000000000..36a25fa80 --- /dev/null +++ b/libjava/classpath/java/net/Proxy.java @@ -0,0 +1,139 @@ +/* Proxy.java -- Represends a proxy for a network connection + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Defines a proxy setting. This setting contains a type (https, socks, + * direct) and a socket address. + * + * @since 1.5 + */ +public class Proxy +{ + /** + * Represents the proxy type. + */ + public enum Type + { + DIRECT, HTTP, SOCKS; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -2231209257930100533L; + } + + public static final Proxy NO_PROXY = new Proxy(Type.DIRECT, null); + + private Type type; + private SocketAddress address; + + /** + * Creates a new <code>Proxy</code> object. + * + * @param type The type for this proxy + * @param address The address of this proxy + */ + public Proxy(Type type, SocketAddress address) + { + this.type = type; + this.address = address; + } + + /** + * Returns the socket address for this proxy object. + * + * @return the socket address + */ + public SocketAddress address() + { + return address; + } + + /** + * Returns the of this proxy instance. + * + * @return the type + * + * @see Type + */ + public Type type() + { + return type; + } + + /** + * Compares the given object with this object. + * + * @return <code>true</code> if both objects or equals, + * <code>false</code> otherwise. + */ + public final boolean equals(Object obj) + { + if (! (obj instanceof Proxy)) + return false; + + Proxy tmp = (Proxy) obj; + + return (type.equals(tmp.type) + && (address == null ? tmp.address == null + : address.equals(tmp.address))); + } + + /** + * Returns the hashcode for this <code>Proxy</code> object. + * + * @return the hashcode + */ + public final int hashCode() + { + return type.hashCode() ^ (address == null ? 0 : address.hashCode()); + } + + /** + * Returns a string representation of this <code>Proxy</code> object. + * + * @return the string + */ + public String toString() + { + return type.toString() + (address == null ? "" + : (":" + address.toString())); + } +} diff --git a/libjava/classpath/java/net/ProxySelector.java b/libjava/classpath/java/net/ProxySelector.java new file mode 100644 index 000000000..6c85f99c7 --- /dev/null +++ b/libjava/classpath/java/net/ProxySelector.java @@ -0,0 +1,117 @@ +/* ProxySelector.java -- A proxy selector class + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.DefaultProxySelector; + +import java.io.IOException; +import java.util.List; + +/** + * Class for handling proxies for different connections. + * + * @since 1.5 + */ +public abstract class ProxySelector +{ + /** + * Default proxy selector. + */ + private static ProxySelector defaultSelector = new DefaultProxySelector(); + + /** + * Creates a new <code>ProxySelector</code> object. + */ + public ProxySelector() + { + // Do nothing here. + } + + /** + * Returns the default proxy selector. + * + * @return the default proxy selector + * + * @throws SecurityException If a security manager is installed and it + * denies NetPermission("getProxySelector") + */ + public static ProxySelector getDefault() + { + SecurityManager sm = System.getSecurityManager(); + + if (sm != null) + sm.checkPermission(new NetPermission("getProxySelector")); + + return defaultSelector; + } + + /** + * Sets the default proxy selector. + * + * @param selector the defualt proxy selector + * + * @throws SecurityException If a security manager is installed and it + * denies NetPermission("setProxySelector") + */ + public static void setDefault(ProxySelector selector) + { + SecurityManager sm = System.getSecurityManager(); + + if (sm != null) + sm.checkPermission(new NetPermission("setProxySelector")); + + defaultSelector = selector; + } + + /** + * Signals to the selector that a proxy was no available. + * + * @throws IllegalArgumentException If one argument is null + */ + public abstract void connectFailed(URI uri, SocketAddress address, + IOException exception); + + /** + * Returns the list of proxy settings for a given URI. + * + * @return list of proxy settings + * + * @throws IllegalArgumentException If uri is null + */ + public abstract List<Proxy> select(URI uri); +} diff --git a/libjava/classpath/java/net/ResolverCache.java b/libjava/classpath/java/net/ResolverCache.java new file mode 100644 index 000000000..d7e5ed407 --- /dev/null +++ b/libjava/classpath/java/net/ResolverCache.java @@ -0,0 +1,269 @@ +/* ResolverCache.java -- A cache of resolver lookups for InetAddress. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.security.Security; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * This class provides a cache of name service resolutions. By + * default successful resolutions are cached forever to guard + * against DNS spoofing attacks and failed resolutions are cached + * for 10 seconds to improve performance. The length of time that + * results remain in the cache is determined by the following + * security properties: + * <dl> + * <dt><code>networkaddress.cache.ttl</code></dt> + * <dd> + * This property specifies the length of time in seconds that + * successful resolutions remain in the cache. The default is + * -1, indicating to cache forever. + * </dd> + * <dt><code>networkaddress.cache.negative.ttl</code></dt> + * <dd> + * This property specifies the length of time in seconds that + * unsuccessful resolutions remain in the cache. The default + * is 10, indicating to cache for 10 seconds. + * </dd> + * In both cases, a value of -1 indicates to cache forever and a + * value of 0 indicates not to cache. + * + * @author Gary Benson (gbenson@redhat.com) + */ +class ResolverCache +{ + /** + * The time in seconds for which successful lookups are cached. + */ + private static final int POSITIVE_TTL = + getTTL("networkaddress.cache.ttl", -1); + + /** + * The time in seconds for which unsuccessful lookups are cached. + */ + private static final int NEGATIVE_TTL = + getTTL("networkaddress.cache.negative.ttl", 10); + + /** + * Helper function to set the TTLs. + */ + private static int getTTL(String propName, int defaultValue) + { + String propValue = Security.getProperty(propName); + if (propValue == null) + return defaultValue; + + return Integer.parseInt(propValue); + } + + /** + * The cache itself. + */ + private static HashMap<Object, Entry> cache = new HashMap<Object, Entry>(); + + /** + * List of entries which may expire. + */ + private static LinkedList<Entry> killqueue = new LinkedList<Entry>(); + + /** + * Return the hostname for the specified IP address. + * + * @param addr The IP address as a byte array + * + * @return The hostname + * + * @exception UnknownHostException If the reverse lookup fails + */ + public static String getHostByAddr(byte[] addr) throws UnknownHostException + { + Object key = makeHashableAddress(addr); + Entry entry = get(key); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (String) entry.value; + } + + try + { + String hostname = VMInetAddress.getHostByAddr(addr); + put(new Entry(key, hostname)); + return hostname; + } + catch (UnknownHostException e) + { + put(new Entry(key, null)); + throw e; + } + } + + /** + * Return a list of all IP addresses for the specified hostname. + * + * @param hostname The hostname + * + * @return An list of IP addresses as byte arrays + * + * @exception UnknownHostException If the lookup fails + */ + public static byte[][] getHostByName(String hostname) + throws UnknownHostException + { + Entry entry = get(hostname); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (byte[][]) entry.value; + } + + try + { + byte[][] addrs = VMInetAddress.getHostByName(hostname); + put(new Entry(hostname, addrs)); + return addrs; + } + catch (UnknownHostException e) + { + put(new Entry(hostname, null)); + throw e; + } + } + + /** + * Convert an IP address expressed as a byte array into something + * we can use as a hashtable key. + */ + private static Object makeHashableAddress(byte[] addr) + { + char[] chars = new char[addr.length]; + for (int i = 0; i < addr.length; i++) + chars[i] = (char) addr[i]; + return new String(chars); + } + + /** + * Return the entry in the cache associated with the supplied key, + * or <code>null</code> if the cache does not contain an entry + * associated with this key. + */ + private static synchronized Entry get(Object key) + { + reap(); + return (Entry) cache.get(key); + } + + /** + * Insert the supplied entry into the cache. + */ + private static synchronized void put(Entry entry) + { + reap(); + if (entry.expires != 0) + { + if (entry.expires != -1) + killqueue.add(entry); + cache.put(entry.key, entry); + } + } + + /** + * Clear expired entries. This method is not synchronized, so + * it must only be called by methods that are. + */ + private static void reap() + { + if (!killqueue.isEmpty()) + { + long now = System.currentTimeMillis(); + + Iterator iter = killqueue.iterator(); + while (iter.hasNext()) + { + Entry entry = (Entry) iter.next(); + if (entry.expires > now) + break; + cache.remove(entry.key); + iter.remove(); + } + } + } + + /** + * An entry in the cache. + */ + private static class Entry + { + /** + * The key by which this entry is referenced. + */ + public final Object key; + + /** + * The entry itself. A null value indicates a failed lookup. + */ + public final Object value; + + /** + * The time when this cache entry expires. If set to -1 then + * this entry will never expire. If set to 0 then this entry + * expires immediately and will not be inserted into the cache. + */ + public final long expires; + + /** + * Constructor. + */ + public Entry(Object key, Object value) + { + this.key = key; + this.value = value; + + int ttl = value != null ? POSITIVE_TTL : NEGATIVE_TTL; + if (ttl < 1) + expires = ttl; + else + expires = System.currentTimeMillis() + ttl * 1000; + } + } +} diff --git a/libjava/classpath/java/net/STATUS b/libjava/classpath/java/net/STATUS new file mode 100644 index 000000000..25dff963e --- /dev/null +++ b/libjava/classpath/java/net/STATUS @@ -0,0 +1,48 @@ +X ContentHandlerFactory +X FileNameMap +X SocketImplFactory +X URLStreamHandlerFactory +* Authenticator +* ContentHandler ++ DatagramPacket ++ DatagramSocket ++ DatagramSocketImpl ++ HttpURLConnection ++ InetAddress +* JarURLConnection ++ MulticastSocket +* NetPermission +* PasswordAuthentication ++ PlainDatagramSocketImpl ++ PlainSocketImpl (internal) ++ ServerSocket ++ Socket ++ SocketImpl ++ SocketInputStream (internal) ++ SocketOptions (internal) ++ SocketOutputStream (internal) +* SocketPermission ++ URL + URLClassLoader ++ URLConnection +* URLEncoder ++ URLStreamHandler +X BindException +X ConnectException +X MalformedURLException +X NoRouteToHostException +X ProtocolException +X SocketException +X UnknownHostException +X UnknownServiceException + +--------------- ++ Native InetAddress ++ Native SocketImpl ++ Native DatagramSocketImpl ++ Protocol Handler for HTTP + Protocol Handler for FTP ++ ContentHandler for text + ContentHandler for gif + ContentHandler for jpeg + diff --git a/libjava/classpath/java/net/ServerSocket.java b/libjava/classpath/java/net/ServerSocket.java new file mode 100644 index 000000000..1dbd7636c --- /dev/null +++ b/libjava/classpath/java/net/ServerSocket.java @@ -0,0 +1,627 @@ +/* ServerSocket.java -- Class for implementing server side sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ServerSocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models server side sockets. The basic model is that the + * server socket is created and bound to some well known port. It then + * listens for and accepts connections. At that point the client and + * server sockets are ready to communicate with one another utilizing + * whatever application layer protocol they desire. + * + * As with the <code>Socket</code> class, most instance methods of this class + * simply redirect their calls to an implementation class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class ServerSocket +{ + /** + * This is the user defined SocketImplFactory, if one is supplied + */ + private static SocketImplFactory factory; + + /** + * This is the SocketImp object to which most instance methods in this + * class are redirected + */ + private SocketImpl impl; + + /** + * We need to retain the local address even after the socket is closed. + */ + private InetSocketAddress local; + private int port; + + /* + * This constructor is only used by java.nio. + */ + + ServerSocket(PlainSocketImpl impl) throws IOException + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.impl.create(true); + setReuseAddress(true); + } + + /* + * This method is only used by java.nio. + */ + + SocketImpl getImpl() + { + return impl; + } + + /** + * Constructor that simply sets the implementation. + * + * @exception IOException If an error occurs + * + * @specnote This constructor is public since JDK 1.4 + */ + public ServerSocket() throws IOException + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + + impl.create(true); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to 50. + * + * @param port The port number to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port) throws IOException + { + this(port, 50); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * arg2. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port, int backlog) throws IOException + { + this(port, backlog, null); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * backlog. The third argument specifies a particular local address to + * bind t or null to bind to all local address. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * @param bindAddr The address to bind to, or null to bind to all addresses + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.1 + */ + public ServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException + { + this(); + + // bind/listen socket + bind(new InetSocketAddress(bindAddr, port), backlog); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint) throws IOException + { + bind(endpoint, 50); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint, int backlog) + throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (isBound()) + throw new SocketException("Already bound"); + + InetAddress addr; + int port; + + if (endpoint == null) + { + addr = InetAddress.ANY_IF; + port = 0; + } + else if (! (endpoint instanceof InetSocketAddress)) + { + throw new IllegalArgumentException("Address type not supported"); + } + else + { + InetSocketAddress tmp = (InetSocketAddress) endpoint; + if (tmp.isUnresolved()) + throw new SocketException("Unresolved address"); + addr = tmp.getAddress(); + port = tmp.getPort(); + } + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(port); + + try + { + impl.bind(addr, port); + impl.listen(backlog); + this.port = port; + local = new InetSocketAddress( + (InetAddress) impl.getOption(SocketOptions.SO_BINDADDR), + impl.getLocalPort()); + } + finally + { + try + { + if (local == null) + close(); + } + catch (IOException _) + { + } + } + } + + /** + * This method returns the local address to which this socket is bound + * + * @return The socket's local address + */ + public InetAddress getInetAddress() + { + if (local == null) + return null; + + return local.getAddress(); + } + + /** + * This method returns the local port number to which this socket is bound + * + * @return The socket's port number + */ + public int getLocalPort() + { + if (local == null) + return -1; + + return local.getPort(); + } + + /** + * Returns the local socket address + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + return local; + } + + /** + * Accepts a new connection and returns a connected <code>Socket</code> + * instance representing that connection. This method will block until a + * connection is available. + * + * @return socket object for the just accepted connection + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If a timeout was previously set with + * setSoTimeout and the timeout has been reached + */ + public Socket accept() throws IOException + { + Socket socket = new Socket(); + + try + { + implAccept(socket); + } + catch (IOException e) + { + try + { + socket.close(); + } + catch (IOException e2) + { + // Ignore. + } + + throw e; + } + catch (SecurityException e) + { + try + { + socket.close(); + } + catch (IOException e2) + { + // Ignore. + } + + throw e; + } + + return socket; + } + + /** + * This protected method is used to help subclasses override + * <code>ServerSocket.accept()</code>. The passed in socket will be + * connected when this method returns. + * + * @param socket The socket that is used for the accepted connection + * + * @exception IOException If an error occurs + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.1 + */ + protected final void implAccept(Socket socket) throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + impl.accept(socket.impl); + socket.bound = true; + socket.implCreated = true; + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkAccept(socket.getInetAddress().getHostAddress(), + socket.getPort()); + } + + /** + * Closes this socket and stops listening for connections + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + if (impl != null) + { + impl.close(); + impl = null; + } + } + + /** + * Returns the unique <code>ServerSocketChannel</code> object + * associated with this socket, if any. + * + * <p>The socket only has a <code>ServerSocketChannel</code> if its created + * by <code>ServerSocketChannel.open()</code>.</p> + * + * @return the associated socket channel, null if none exists + * + * @since 1.4 + */ + public ServerSocketChannel getChannel() + { + return null; + } + + /** + * Returns true when the socket is bound, otherwise false + * + * @return true if socket is bound, false otherwise + * + * @since 1.4 + */ + public boolean isBound() + { + return local != null; + } + + /** + * Returns true if the socket is closed, otherwise false + * + * @return true if socket is closed, false otherwise + * + * @since 1.4 + */ + public boolean isClosed() + { + ServerSocketChannel channel = getChannel(); + return impl == null || (channel != null && ! channel.isOpen()); + } + + /** + * Sets the value of SO_TIMEOUT. A value of 0 implies that SO_TIMEOUT is + * disabled (ie, operations never time out). This is the number of + * milliseconds a socket operation can block before an + * InterruptedIOException is thrown. + * + * @param timeout The new SO_TIMEOUT value + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + public void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * Retrieves the current value of the SO_TIMEOUT setting. A value of 0 + * implies that SO_TIMEOUT is disabled (ie, operations never time out). + * This is the number of milliseconds a socket operation can block before + * an InterruptedIOException is thrown. + * + * @return The value of SO_TIMEOUT + * + * @exception IOException If an error occurs + * + * @since 1.1 + */ + public int getSoTimeout() throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object timeout = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (! (timeout instanceof Integer)) + throw new IOException("Internal Error"); + + return ((Integer) timeout).intValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param on true if SO_REUSEADDR should be enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return true if SO_REUSEADDR is set, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object reuseaddr = impl.getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.4 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.4 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object buf = impl.getOption(SocketOptions.SO_RCVBUF); + + if (! (buf instanceof Integer)) + throw new SocketException("Internal Error: Unexpected type"); + + return ((Integer) buf).intValue(); + } + + /** + * Returns the value of this socket as a <code>String</code>. + * + * @return This socket represented as a <code>String</code>. + */ + public String toString() + { + if (! isBound()) + return "ServerSocket[unbound]"; + + return ("ServerSocket[addr=" + getInetAddress() + ",port=" + + port + ",localport=" + getLocalPort() + "]"); + } + + /** + * Sets the <code>SocketImplFactory</code> for all + * <code>ServerSocket</code>'s. This may only be done + * once per virtual machine. Subsequent attempts will generate an + * exception. Note that a <code>SecurityManager</code> check is made prior + * to setting the factory. If insufficient privileges exist to set the + * factory, an exception will be thrown + * + * @param fac the factory to set + * + * @exception SecurityException If this operation is not allowed by the + * <code>SecurityManager</code>. + * @exception SocketException If the factory object is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketFactory(SocketImplFactory fac) + throws IOException + { + if (factory != null) + throw new SocketException("SocketFactory already defined"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + factory = fac; + } +} diff --git a/libjava/classpath/java/net/Socket.java b/libjava/classpath/java/net/Socket.java new file mode 100644 index 000000000..d61e81f5e --- /dev/null +++ b/libjava/classpath/java/net/Socket.java @@ -0,0 +1,1288 @@ +/* Socket.java -- Client socket implementation + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models a client site socket. A socket is a TCP/IP endpoint + * for network communications conceptually similar to a file handle. + * <p> + * This class does not actually do any work. Instead, it redirects all of + * its calls to a socket implementation object which implements the + * <code>SocketImpl</code> interface. The implementation class is + * instantiated by factory class that implements the + * <code>SocketImplFactory interface</code>. A default + * factory is provided, however the factory may be set by a call to + * the <code>setSocketImplFactory</code> method. Note that this may only be + * done once per virtual machine. If a subsequent attempt is made to set the + * factory, a <code>SocketException</code> will be thrown. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class Socket +{ + /** + * This is the user SocketImplFactory for this class. If this variable is + * null, a default factory is used. + */ + static SocketImplFactory factory; + + /** + * The implementation object to which calls are redirected + */ + // package-private because ServerSocket.implAccept() needs to access it. + SocketImpl impl; + + /** + * True if impl.create() has been called. + */ + // package-private because ServerSocket.implAccept() needs to access it. + boolean implCreated; + + /** + * True if the socket is bound. + * Package private so it can be set from ServerSocket when accept is called. + */ + boolean bound; + + /** + * True if input is shutdown. + */ + private boolean inputShutdown; + + /** + * True if output is shutdown. + */ + private boolean outputShutdown; + + /** + * Initializes a new instance of <code>Socket</code> object without + * connecting to a remote host. This useful for subclasses of socket that + * might want this behavior. + * + * @specnote This constructor is public since JDK 1.4 + * @since 1.1 + */ + public Socket() + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + } + + /** + * Initializes a new instance of <code>Socket</code> object without + * connecting to a remote host. This is useful for subclasses of socket + * that might want this behavior. + * <p> + * Additionally, this socket will be created using the supplied + * implementation class instead the default class or one returned by a + * factory. If this value is <code>null</code>, the default Socket + * implementation is used. + * + * @param impl The <code>SocketImpl</code> to use for this + * <code>Socket</code> + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + protected Socket(SocketImpl impl) throws SocketException + { + if (impl == null) + this.impl = new PlainSocketImpl(); + else + this.impl = impl; + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * hostname and port specified as arguments. + * + * @param host The name of the host to connect to + * @param port The port number to connect to + * + * @exception UnknownHostException If the hostname cannot be resolved to a + * network address. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(String host, int port) + throws UnknownHostException, IOException + { + this(InetAddress.getByName(host), port, null, 0, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments. + * + * @param address The address to connect to + * @param port The port number to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(InetAddress address, int port) throws IOException + { + this(address, port, null, 0, true); + } + + /** + * Initializes a new instance of <code>Socket</code> that connects to the + * named host on the specified port and binds to the specified local address + * and port. + * + * @param host The name of the remote host to connect to. + * @param port The remote port to connect to. + * @param localAddr The local address to bind to. + * @param localPort The local port to bind to. + * + * @exception SecurityException If the <code>SecurityManager</code> + * exists and does not allow a connection to the specified host/port or + * binding to the specified local host/port. + * @exception IOException If a connection error occurs. + * + * @since 1.1 + */ + public Socket(String host, int port, InetAddress localAddr, int localPort) + throws IOException + { + this(InetAddress.getByName(host), port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments, plus binds to the + * specified local address and port. + * + * @param address The remote address to connect to + * @param port The remote port to connect to + * @param localAddr The local address to connect to + * @param localPort The local port to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @since 1.1 + */ + public Socket(InetAddress address, int port, InetAddress localAddr, + int localPort) throws IOException + { + this(address, port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * hostname and port specified as arguments. If the stream argument is set + * to <code>true</code>, then a stream socket is created. If it is + * <code>false</code>, a datagram socket is created. + * + * @param host The name of the host to connect to + * @param port The port to connect to + * @param stream <code>true</code> for a stream socket, <code>false</code> + * for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the <code>DatagramSocket</code> class to create + * datagram oriented sockets. + */ + public Socket(String host, int port, boolean stream) + throws IOException + { + this(InetAddress.getByName(host), port, null, 0, stream); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments. If the stream param is + * <code>true</code>, a stream socket will be created, otherwise a datagram + * socket is created. + * + * @param host The address to connect to + * @param port The port number to connect to + * @param stream <code>true</code> to create a stream socket, + * <code>false</code> to create a datagram socket. + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the <code>DatagramSocket</code> class to create + * datagram oriented sockets. + */ + public Socket(InetAddress host, int port, boolean stream) + throws IOException + { + this(host, port, null, 0, stream); + } + + /** + * This constructor is where the real work takes place. Connect to the + * specified address and port. Use default local values if not specified, + * otherwise use the local host and port passed in. Create as stream or + * datagram based on "stream" argument. + * <p> + * + * @param raddr The remote address to connect to + * @param rport The remote port to connect to + * @param laddr The local address to connect to + * @param lport The local port to connect to + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + private Socket(InetAddress raddr, int rport, InetAddress laddr, int lport, + boolean stream) throws IOException + { + this(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(raddr.getHostAddress(), rport); + + // bind socket + SocketAddress bindaddr = + laddr == null ? null : new InetSocketAddress(laddr, lport); + bind(bindaddr); + + // Connect socket in case of Exceptions we must close the socket + // because an exception in the constructor means that the caller will + // not have a reference to this instance. + // Note: You may have the idea that the exception treatment + // should be moved into connect() but there is a Mauve test which + // shows that a failed connect should not close the socket. + try + { + connect(new InetSocketAddress(raddr, rport)); + } + catch (IOException ioe) + { + impl.close(); + throw ioe; + } + catch (RuntimeException re) + { + impl.close(); + throw re; + } + + // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port, + // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as + // that default. JDK 1.2 doc infers not to do a bind. + } + + private SocketImpl getImpl() throws SocketException + { + if (! implCreated) + { + try + { + impl.create(true); + } + catch (IOException x) + { + throw (SocketException) new SocketException().initCause(x); + } + implCreated = true; + } + return impl; + } + + /** + * Binds the socket to the given local address/port + * + * @param bindpoint The address/port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * @exception IllegalArgumentException If the address type is not supported + * + * @since 1.4 + */ + public void bind(SocketAddress bindpoint) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // XXX: JDK 1.4.1 API documentation says that if bindpoint is null the + // socket will be bound to an ephemeral port and a valid local address. + if (bindpoint == null) + bindpoint = new InetSocketAddress(InetAddress.ANY_IF, 0); + + if (! (bindpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException(); + + InetSocketAddress tmp = (InetSocketAddress) bindpoint; + + // bind to address/port + try + { + getImpl().bind(tmp.getAddress(), tmp.getPort()); + bound = true; + } + catch (IOException exception) + { + close(); + throw exception; + } + catch (RuntimeException exception) + { + close(); + throw exception; + } + catch (Error error) + { + close(); + throw error; + } + } + + /** + * Connects the socket with a remote address. + * + * @param endpoint The address to connect to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the addess type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint) throws IOException + { + connect(endpoint, 0); + } + + /** + * Connects the socket with a remote address. A timeout of zero is + * interpreted as an infinite timeout. The connection will then block + * until established or an error occurs. + * + * @param endpoint The address to connect to + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the address type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If the timeout is reached + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint, int timeout) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + if (! isBound()) + bind(null); + + getImpl().connect(endpoint, timeout); + } + + /** + * Returns the address of the remote end of the socket. If this socket + * is not connected, then <code>null</code> is returned. + * + * @return The remote address this socket is connected to + */ + public InetAddress getInetAddress() + { + if (! isConnected()) + return null; + + try + { + return getImpl().getInetAddress(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return null; + } + + /** + * Returns the local address to which this socket is bound. If this socket + * is not connected, then a wildcard address, for which + * @see InetAddress#isAnyLocalAddress() is <code>true</code>, is returned. + * + * @return The local address + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return InetAddress.ANY_IF; + + InetAddress addr = null; + + if (impl instanceof PlainSocketImpl) + addr = ((PlainSocketImpl) impl).getLocalAddress().getAddress(); + + if (addr == null) + { + try + { + addr = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + } + catch (SocketException e) + { + // (hopefully) shouldn't happen + // throw new java.lang.InternalError + // ("Error in PlainSocketImpl.getOption"); + return null; + } + } + + // FIXME: According to libgcj, checkConnect() is supposed to be called + // before performing this operation. Problems: 1) We don't have the + // addr until after we do it, so we do a post check. 2). The docs I + // see don't require this in the Socket case, only DatagramSocket, but + // we'll assume they mean both. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(addr.getHostName(), getLocalPort()); + + return addr; + } + + /** + * Returns the port number of the remote end of the socket connection. If + * this socket is not connected, then 0 is returned. + * + * @return The remote port this socket is connected to + */ + public int getPort() + { + if (! isConnected()) + return 0; + + try + { + return getImpl().getPort(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return 0; + } + + /** + * Returns the local port number to which this socket is bound. If this + * socket is not connected, then -1 is returned. + * + * @return The local port + */ + public int getLocalPort() + { + if (! isBound()) + return -1; + + try + { + if (getImpl() != null) + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + } + + return -1; + } + + /** + * Returns local socket address. + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + InetAddress addr = getLocalAddress(); + + try + { + return new InetSocketAddress(addr, getImpl().getLocalPort()); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + } + + /** + * Returns the remote socket address. + * + * @return the remote socket address, null of not connected + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + try + { + return new InetSocketAddress(getImpl().getInetAddress(), + getImpl().getPort()); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + return null; + } + } + + /** + * Returns an InputStream for reading from this socket. + * + * @return The InputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public InputStream getInputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getInputStream(); + } + + /** + * Returns an OutputStream for writing to this socket. + * + * @return The OutputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public OutputStream getOutputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getOutputStream(); + } + + /** + * Sets the TCP_NODELAY option on the socket. + * + * @param on true to enable, false to disable + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public void setTcpNoDelay(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); + } + + /** + * Tests whether or not the TCP_NODELAY option is set on the socket. + * Returns true if enabled, false if disabled. When on it disables the + * Nagle algorithm which means that packets are always send immediatly and + * never merged together to reduce network trafic. + * + * @return Whether or not TCP_NODELAY is set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public boolean getTcpNoDelay() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object on = getImpl().getOption(SocketOptions.TCP_NODELAY); + + if (on instanceof Boolean) + return (((Boolean) on).booleanValue()); + else + throw new SocketException("Internal Error"); + } + + /** + * Sets the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. The linger interval is specified in hundreths of a second + * (platform specific?) + * + * @param on true to enable SO_LINGER, false to disable + * @param linger The SO_LINGER timeout in hundreths of a second or -1 if + * SO_LINGER not set. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If linger is negative + * + * @since 1.1 + */ + public void setSoLinger(boolean on, int linger) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (on) + { + if (linger < 0) + throw new IllegalArgumentException("SO_LINGER must be >= 0"); + + if (linger > 65535) + linger = 65535; + + getImpl().setOption(SocketOptions.SO_LINGER, Integer.valueOf(linger)); + } + else + getImpl().setOption(SocketOptions.SO_LINGER, Integer.valueOf(-1)); + } + + /** + * Returns the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. This method either returns the timeouts (in hundredths of + * of a second (platform specific?)) if SO_LINGER is set, or -1 if + * SO_LINGER is not set. + * + * @return The SO_LINGER timeout in hundreths of a second or -1 + * if SO_LINGER not set + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public int getSoLinger() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object linger = getImpl().getOption(SocketOptions.SO_LINGER); + + if (linger instanceof Integer) + return (((Integer) linger).intValue()); + else + return -1; + } + + /** + * Sends urgent data through the socket + * + * @param data The data to send. + * Only the lowest eight bits of data are sent + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + public void sendUrgentData(int data) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().sendUrgentData(data); + } + + /** + * Enables/disables the SO_OOBINLINE option + * + * @param on True if SO_OOBLINE should be enabled + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setOOBInline(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on)); + } + + /** + * Returns the current setting of the SO_OOBINLINE option for this socket + * + * @return True if SO_OOBINLINE is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getOOBInline() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_OOBINLINE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Sets the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in milliseconds. + * + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * Returns the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in thousandths of a second (implementation specific?). + * + * @return The length of the timeout in thousandth's of a second or 0 + * if not set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object timeout = getImpl().getOption(SocketOptions.SO_TIMEOUT); + if (timeout instanceof Integer) + return (((Integer) timeout).intValue()); + else + return 0; + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_SNDBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs or socket not connected + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the socket level socket option + * SO_KEEPALIVE. + * + * @param on True if SO_KEEPALIVE should be enabled + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public void setKeepAlive(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on)); + } + + /** + * This method returns the value of the socket level socket option + * SO_KEEPALIVE. + * + * @return The setting + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public boolean getKeepAlive() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_KEEPALIVE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Closes the socket. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + if (isClosed()) + return; + + impl.close(); + impl = null; + } + + /** + * Converts this <code>Socket</code> to a <code>String</code>. + * + * @return The <code>String</code> representation of this <code>Socket</code> + */ + public String toString() + { + try + { + if (isConnected()) + return (super.toString() + + " [addr=" + getImpl().getInetAddress() + ",port=" + + getImpl().getPort() + ",localport=" + + getImpl().getLocalPort() + "]"); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return super.toString() + " [unconnected]"; + } + + /** + * Sets the <code>SocketImplFactory</code>. This may be done only once per + * virtual machine. Subsequent attempts will generate a + * <code>SocketException</code>. Note that a <code>SecurityManager</code> + * check is made prior to setting the factory. If + * insufficient privileges exist to set the factory, then an + * <code>IOException</code> will be thrown. + * + * @param fac the factory to set + * + * @exception SecurityException If the <code>SecurityManager</code> does + * not allow this operation. + * @exception SocketException If the SocketImplFactory is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketImplFactory(SocketImplFactory fac) + throws IOException + { + // See if already set + if (factory != null) + throw new SocketException("SocketImplFactory already defined"); + + // Check permissions + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + if (fac == null) + throw new SocketException("SocketImplFactory cannot be null"); + + factory = fac; + } + + /** + * Closes the input side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownInput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownInput(); + inputShutdown = true; + } + + /** + * Closes the output side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownOutput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownOutput(); + outputShutdown = true; + } + + /** + * Returns the socket channel associated with this socket. + * + * @return the associated socket channel, + * null if no associated channel exists + * + * @since 1.4 + */ + public SocketChannel getChannel() + { + return null; + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return True if SO_REUSEADDR is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object reuseaddr = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param reuseAddress true if SO_REUSEADDR should be enabled, + * false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean reuseAddress) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, + Boolean.valueOf(reuseAddress)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @exception SocketException If an error occurs + * + * @see Socket#setTrafficClass(int tc) + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object obj = getImpl().getOption(SocketOptions.IP_TOS); + + if (obj instanceof Integer) + return ((Integer) obj).intValue(); + else + throw new SocketException("Unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see Socket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, Integer.valueOf(tc)); + } + + /** + * Checks if the socket is connected + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + if (impl == null) + return false; + + return impl.getInetAddress() != null; + } + + /** + * Checks if the socket is already bound. + * + * @return True if socket is bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + if (isClosed()) + return false; + if (impl instanceof PlainSocketImpl) + { + InetSocketAddress addr = ((PlainSocketImpl) impl).getLocalAddress(); + return addr != null && addr.getAddress() != null; + } + return bound; + } + + /** + * Checks if the socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + SocketChannel channel = getChannel(); + + return impl == null || (channel != null && ! channel.isOpen()); + } + + /** + * Checks if the socket's input stream is shutdown + * + * @return True if input is shut down. + * + * @since 1.4 + */ + public boolean isInputShutdown() + { + return inputShutdown; + } + + /** + * Checks if the socket's output stream is shutdown + * + * @return True if output is shut down. + * + * @since 1.4 + */ + public boolean isOutputShutdown() + { + return outputShutdown; + } +} diff --git a/libjava/classpath/java/net/SocketAddress.java b/libjava/classpath/java/net/SocketAddress.java new file mode 100644 index 000000000..48ab01009 --- /dev/null +++ b/libjava/classpath/java/net/SocketAddress.java @@ -0,0 +1,63 @@ +/* SocketAddress.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.Serializable; + + +/** + * Abstract base class for InetSocketAddress. + * InetSocketAddress is to my knowledge the only derived + * class. [Ronald] + * + * @since 1.4 + */ +public abstract class SocketAddress implements Serializable +{ + /** + * Compatible with JDK 1.4+ + */ + static final long serialVersionUID = 5215720748342549866L; + + /** + * Initializes the socket address. + */ + public SocketAddress() + { + } +} diff --git a/libjava/classpath/java/net/SocketException.java b/libjava/classpath/java/net/SocketException.java new file mode 100644 index 000000000..37b2f6fba --- /dev/null +++ b/libjava/classpath/java/net/SocketException.java @@ -0,0 +1,75 @@ +/* SocketException.java -- An exception occurred while performing a socket op + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a generic error occurred related to an + * operation on a socket. Check the descriptive message (if any) for + * details on the nature of this error + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class SocketException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5935874303556886934L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketException(String message) + { + super(message); + } +} // class SocketException diff --git a/libjava/classpath/java/net/SocketImpl.java b/libjava/classpath/java/net/SocketImpl.java new file mode 100644 index 000000000..77f470be3 --- /dev/null +++ b/libjava/classpath/java/net/SocketImpl.java @@ -0,0 +1,321 @@ +/* SocketImpl.java -- Abstract socket implementation class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Believed complete and correct. + */ + +/** + * This abstract class serves as the parent class for socket implementations. + * The implementation class serves an intermediary to native routines that + * perform system specific socket operations. + * <p> + * A default implementation is provided by the system, but this can be + * changed via installing a <code>SocketImplFactory</code> (through a call + * to the static method <code>Socket.setSocketImplFactory</code>). A + * subclass of <code>Socket</code> can also pass in a <code>SocketImpl</code> + * to the <code>Socket(SocketImpl)</code> constructor to use an + * implementation different from the system default without installing + * a factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class SocketImpl implements SocketOptions +{ + /** + * The address of the remote end of the socket connection + */ + protected InetAddress address; + + /** + * A FileDescriptor object representing this socket connection. + */ + protected FileDescriptor fd; + + /** + * The port number the socket is bound to locally + */ + protected int localport = -1; + + /** + * The port number of the remote end of the socket connection + */ + protected int port; + + /** + * Default, no-argument constructor for use by subclasses. + */ + public SocketImpl() + { + } + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + */ + protected abstract void create(boolean stream) throws IOException; + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param host The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(String host, int port) + throws IOException; + + /** + * Connects to the remote address and port specified as arguments. + * + * @param host The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(InetAddress host, int port) + throws IOException; + + /** + * Connects to the socket to the host specified in address. This + * method blocks until successful connected or the timeout occurs. + * A timeout of zero means no timout. + * + * @param address Data of remote host + * @param timeout time to wait to stop connecting + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void connect(SocketAddress address, int timeout) + throws IOException; + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. + * <p> + * Note that it is unspecified how to bind to all interfaces on the localhost + * (INADDR_ANY). + * + * @param host The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ + protected abstract void bind(InetAddress host, int port) + throws IOException; + + /** + * Starts listening for connections on a socket. The backlog parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + */ + protected abstract void listen(int backlog) throws IOException; + + /** + * Accepts a connection on this socket. + * + * @param s The implementation object for the accepted connection. + * + * @exception IOException If an error occurs + */ + protected abstract void accept(SocketImpl s) throws IOException; + + /** + * Returns an <code>InputStream</code> object for reading from this socket. + * + * @return An <code>InputStream</code> for reading from this socket. + * + * @exception IOException If an error occurs + */ + protected abstract InputStream getInputStream() throws IOException; + + /** + * Returns an <code>OutputStream</code> object for writing to this socket + * + * @return An <code>OutputStream</code> for writing to this socket. + * + * @exception IOException If an error occurs. + */ + protected abstract OutputStream getOutputStream() throws IOException; + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ + protected abstract int available() throws IOException; + + /** + * Closes the socket. This will normally cause any resources, such as the + * InputStream, OutputStream and associated file descriptors to be freed. + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ + protected abstract void close() throws IOException; + + /** + * Returns the FileDescriptor objects for this socket. + * + * @return A FileDescriptor for this socket. + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the remote address this socket is connected to + * + * @return The remote address + */ + protected InetAddress getInetAddress() + { + return address; + } + + /** + * Returns the remote port this socket is connected to + * + * @return The remote port + */ + protected int getPort() + { + return port; + } + + /** + * Returns true or false when this socket supports sending urgent data + * or not. + * + * @return true if the socket implementation supports sending urgent data, + * false otherwise + * + * @since 1.4 + */ + protected boolean supportsUrgentData() + { + // This method has to be overwritten by socket classes that support + // sending urgend data. + return false; + } + + /** + * Sends one byte of urgent data to the socket. + * + * @param data The byte to send, the low eight bits of it + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void sendUrgentData(int data) throws IOException; + + /** + * Returns the local port this socket is bound to + * + * @return The local port + */ + protected int getLocalPort() + { + return localport; + } + + /** + * Returns a <code>String</code> representing the remote host and port of + * this socket. + * + * @return A <code>String</code> for this socket. + */ + public String toString() + { + return "[addr=" + + ((address == null) ? "0.0.0.0/0.0.0.0" : address.toString()) + + ",port=" + port + ",localport=" + localport + "]"; + } + + /** + * Shut down the input side of this socket. Subsequent reads will + * return end-of-file. + * + * @exception IOException if an error occurs + */ + protected void shutdownInput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } + + /** + * Shut down the output side of this socket. Subsequent writes will + * fail with an IOException. + * + * @exception IOException if an error occurs + */ + protected void shutdownOutput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } +} diff --git a/libjava/classpath/java/net/SocketImplFactory.java b/libjava/classpath/java/net/SocketImplFactory.java new file mode 100644 index 000000000..b7cb10ca6 --- /dev/null +++ b/libjava/classpath/java/net/SocketImplFactory.java @@ -0,0 +1,59 @@ +/* SocketImplFactory.java -- Interface to create a SocketImpl object + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a <code>SocketImpl</code> + * object. This should not be needed by ordinary applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public interface SocketImplFactory +{ + /** + * This method returns an instance of the <code>SocketImpl</code> object + * + * @return A <code>SocketImpl</code> object + */ + SocketImpl createSocketImpl(); +} // interface SocketImplFactory diff --git a/libjava/classpath/java/net/SocketOptions.java b/libjava/classpath/java/net/SocketOptions.java new file mode 100644 index 000000000..659bf750c --- /dev/null +++ b/libjava/classpath/java/net/SocketOptions.java @@ -0,0 +1,166 @@ +/* SocketOptions.java -- Implements options for sockets (duh!) + Copyright (C) 1998, 1999, 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface is used by <code>SocketImpl</code> and + * <code>DatagramSocketImpl</code> to implement options + * on sockets. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status should be completely JDK 1.4 compatible + */ +public interface SocketOptions +{ + /** + * Option id for the SO_KEEPALIVE value + * @since 1.3 + */ + int SO_KEEPALIVE = 0x8; + + /** + * Option id for the SO_LINGER value + */ + int SO_LINGER = 0x80; // 128 + + /** + * Option id for the SO_TIMEOUT value + */ + int SO_TIMEOUT = 0x1006; // 4102 + + /** + * Retrieve the local address to which the socket is bound. + */ + int SO_BINDADDR = 0x0F; // 15 + + /** + * Option id for the send buffer size + * @since 1.2 + */ + int SO_SNDBUF = 0x1001; // 4097 + + /** + * Option id for the receive buffer size + * @since 1.2 + */ + int SO_RCVBUF = 0x1002; // 4098 + + /** + * Sets the SO_REUSEADDR parameter on a socket + */ + int SO_REUSEADDR = 0x04; // 4 + + /** + * Sets SO_BROADCAST for a socket + * @since 1.4 + */ + int SO_BROADCAST = 0x20; // 32 + + /** + * Sets SO_OOBINLINE for a socket + * @since 1.4 + */ + int SO_OOBINLINE = 0x1003; // 4099 + + /** + * Option id for the TCP_NODELAY value + */ + int TCP_NODELAY = 0x01; // 1 + + /** + * Options id for the IP_MULTICAST_IF value + */ + int IP_MULTICAST_IF = 0x10; // 16 + + /** + * same as above + * @since 1.4 + */ + int IP_MULTICAST_IF2 = 0x1F; // 31 + + /** + * This option enables or disables local loopback of multicast datagrams. + * @since 1.4 + */ + int IP_MULTICAST_LOOP = 0x12; // 18 + + /** + * This option sets the type-of-service or traffic class field in the + * IP header for a TCP or UDP socket. + * @since 1.4 + */ + int IP_TOS = 0x03; // 3 + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * <code>Integer</code>. For options that are set to on or off, the + * value passed will be a <code>Boolean</code>. The <code>optionId</code> + * parameter is one of the defined constants in this interface. + * + * @param optionId The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ + void setOption(int optionId, Object val) throws SocketException; + + /** + * Returns the current setting of the specified option. The + * <code>Object</code> returned will be an <code>Integer</code> for options + * that have integer values. For options that are set to on or off, a + * <code>Boolean</code> will be returned. The <code>optionId</code> + * parameter is one of the defined constants in this interface. + * + * @param optionId The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ + Object getOption(int optionId) throws SocketException; +} // interface SocketOptions diff --git a/libjava/classpath/java/net/SocketPermission.java b/libjava/classpath/java/net/SocketPermission.java new file mode 100644 index 000000000..ce16a79a0 --- /dev/null +++ b/libjava/classpath/java/net/SocketPermission.java @@ -0,0 +1,636 @@ +/* SocketPermission.java -- Class modeling permissions for socket operations + Copyright (C) 1998, 2000, 2001, 2002, 2004, 2006 Free Software + Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.StringTokenizer; + + +/** + * This class models a specific set of permssions for connecting to a + * host. There are two elements to this, the host/port combination and + * the permission list. + * <p> + * The host/port combination is specified as followed + * <p> + * <pre> + * hostname[:[-]port[-[port]]] + * </pre> + * <p> + * The hostname portion can be either a hostname or IP address. If it is + * a hostname, a wildcard is allowed in hostnames. This wildcard is a "*" + * and matches one or more characters. Only one "*" may appear in the + * host and it must be the leftmost character. For example, + * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain. + * <p> + * The port portion can be either a single value, or a range of values + * treated as inclusive. The first or the last port value in the range + * can be omitted in which case either the minimum or maximum legal + * value for a port (respectively) is used by default. Here are some + * examples: + * <p><ul> + * <li>8080 - Represents port 8080 only</li> + * <li>2000-3000 - Represents ports 2000 through 3000 inclusive</li> + * <li>-4000 - Represents ports 0 through 4000 inclusive</li> + * <li>1024- - Represents ports 1024 through 65535 inclusive</li> + * </ul><p> + * The permission list is a comma separated list of individual permissions. + * These individual permissions are: + * <p> + * <pre> + * accept + * connect + * listen + * resolve + * </pre> + * <p> + * The "listen" permission is only relevant if the host is localhost. If + * any permission at all is specified, then resolve permission is implied to + * exist. + * <p> + * Here are a variety of examples of how to create SocketPermission's + * <p><pre> + * SocketPermission("www.urbanophile.com", "connect"); + * Can connect to any port on www.urbanophile.com + * SocketPermission("www.urbanophile.com:80", "connect,accept"); + * Can connect to or accept connections from www.urbanophile.com on port 80 + * SocketPermission("localhost:1024-", "listen,accept,connect"); + * Can connect to, accept from, an listen on any local port number 1024 + * and up. + * SocketPermission("*.edu", "connect"); + * Can connect to any host in the edu domain + * SocketPermission("197.197.20.1", "accept"); + * Can accept connections from 197.197.20.1 + * </pre><p> + * + * This class also supports IPv6 addresses. These should be specified + * in either RFC 2732 format or in full uncompressed form. + * + * @since 1.2 + * + * @author Written by Aaron M. Renn (arenn@urbanophile.com) + * @author Extensively modified by Gary Benson (gbenson@redhat.com) + */ +public final class SocketPermission extends Permission implements Serializable +{ + static final long serialVersionUID = -7204263841984476862L; + + /** + * A hostname (possibly wildcarded). Will be set if and only if + * this object was initialized with a hostname. + */ + private transient String hostname = null; + + /** + * An IP address (IPv4 or IPv6). Will be set if and only if this + * object was initialized with a single literal IP address. + */ + private transient InetAddress address = null; + + /** + * A range of ports. + */ + private transient int minport; + private transient int maxport; + + /** + * Values used for minimum and maximum ports when one or both bounds + * are omitted. This class is essentially independent of the + * networking code it describes, so we do not limit ports to the + * usual network limits of 1 and 65535. + */ + private static final int MIN_PORT = 0; + private static final int MAX_PORT = Integer.MAX_VALUE; + + /** + * The actions for which we have permission. This field is present + * to make the serialized form correct and should not be used by + * anything other than writeObject: everything else should use + * actionmask. + */ + private String actions; + + /** + * A bitmask representing the actions for which we have permission. + */ + private transient int actionmask; + + /** + * The available actions, in the canonical order required for getActions(). + */ + private static final String[] ACTIONS = new String[] { + "connect", "listen", "accept", "resolve"}; + + /** + * Initializes a new instance of <code>SocketPermission</code> with the + * specified host/port combination and actions string. + * + * @param hostport The hostname/port number combination + * @param actions The actions string + */ + public SocketPermission(String hostport, String actions) + { + super(processHostport(hostport)); + + setHostPort(getName()); + setActions(actions); + } + + /** + * There are two cases in which hostport needs rewriting before + * being passed to the superclass constructor. If hostport is an + * empty string then it is substituted with "localhost". And if + * the host part of hostport is a literal IPv6 address in the full + * uncompressed form not enclosed with "[" and "]" then we enclose + * it with them. + */ + private static String processHostport(String hostport) + { + if (hostport.length() == 0) + return "localhost"; + + if (hostport.charAt(0) == '[') + return hostport; + + int colons = 0; + boolean colon_allowed = true; + for (int i = 0; i < hostport.length(); i++) + { + if (hostport.charAt(i) == ':') + { + if (!colon_allowed) + throw new IllegalArgumentException("Ambiguous hostport part"); + colons++; + colon_allowed = false; + } + else + colon_allowed = true; + } + + switch (colons) + { + case 0: + case 1: + // a hostname or IPv4 address + return hostport; + + case 7: + // an IPv6 address with no ports + return "[" + hostport + "]"; + + case 8: + // an IPv6 address with ports + int last_colon = hostport.lastIndexOf(':'); + return "[" + hostport.substring(0, last_colon) + "]" + + hostport.substring(last_colon); + + default: + throw new IllegalArgumentException("Ambiguous hostport part"); + } + } + + /** + * Parse the hostport argument to the constructor. + */ + private void setHostPort(String hostport) + { + // Split into host and ports + String host, ports; + if (hostport.charAt(0) == '[') + { + // host is a bracketed IPv6 address + int end = hostport.indexOf("]"); + if (end == -1) + throw new IllegalArgumentException("Unmatched '['"); + host = hostport.substring(1, end); + + address = InetAddress.getByLiteral(host); + if (address == null) + throw new IllegalArgumentException("Bad IPv6 address"); + + if (end == hostport.length() - 1) + ports = ""; + else if (hostport.charAt(end + 1) == ':') + ports = hostport.substring(end + 2); + else + throw new IllegalArgumentException("Bad character after ']'"); + } + else + { + // host is a hostname or IPv4 address + int sep = hostport.indexOf(":"); + if (sep == -1) + { + host = hostport; + ports = ""; + } + else + { + host = hostport.substring(0, sep); + ports = hostport.substring(sep + 1); + } + + address = InetAddress.getByLiteral(host); + if (address == null) + { + if (host.lastIndexOf('*') > 0) + throw new IllegalArgumentException("Bad hostname"); + + hostname = host; + } + } + + // Parse and validate the ports + if (ports.length() == 0) + { + minport = MIN_PORT; + maxport = MAX_PORT; + } + else + { + int sep = ports.indexOf("-"); + if (sep == -1) + { + // a single port + minport = maxport = Integer.parseInt(ports); + } + else + { + if (ports.indexOf("-", sep + 1) != -1) + throw new IllegalArgumentException("Unexpected '-'"); + + if (sep == 0) + { + // an upper bound + minport = MIN_PORT; + maxport = Integer.parseInt(ports.substring(1)); + } + else if (sep == ports.length() - 1) + { + // a lower bound + minport = + Integer.parseInt(ports.substring(0, ports.length() - 1)); + maxport = MAX_PORT; + } + else + { + // a range with two bounds + minport = Integer.parseInt(ports.substring(0, sep)); + maxport = Integer.parseInt(ports.substring(sep + 1)); + } + } + } + } + + /** + * Parse the actions argument to the constructor. + */ + private void setActions(String actionstring) + { + actionmask = 0; + + boolean resolve_needed = false; + boolean resolve_present = false; + + StringTokenizer t = new StringTokenizer(actionstring, ","); + while (t.hasMoreTokens()) + { + String action = t.nextToken(); + action = action.trim().toLowerCase(); + setAction(action); + + if (action.equals("resolve")) + resolve_present = true; + else + resolve_needed = true; + } + + if (resolve_needed && !resolve_present) + setAction("resolve"); + } + + /** + * Parse one element of the actions argument to the constructor. + */ + private void setAction(String action) + { + for (int i = 0; i < ACTIONS.length; i++) + { + if (action.equals(ACTIONS[i])) + { + actionmask |= 1 << i; + return; + } + } + throw new IllegalArgumentException("Unknown action " + action); + } + + /** + * Tests this object for equality against another. This will be true if + * and only if the passed object is an instance of + * <code>SocketPermission</code> and both its hostname/port combination + * and permissions string are identical. + * + * @param obj The object to test against for equality + * + * @return <code>true</code> if object is equal to this object, + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + SocketPermission p; + + if (obj instanceof SocketPermission) + p = (SocketPermission) obj; + else + return false; + + if (p.actionmask != actionmask || + p.minport != minport || + p.maxport != maxport) + return false; + + if (address != null) + { + if (p.address == null) + return false; + else + return p.address.equals(address); + } + else + { + if (p.hostname == null) + return false; + else + return p.hostname.equals(hostname); + } + } + + /** + * Returns a hash code value for this object. Overrides the + * <code>Permission.hashCode()</code>. + * + * @return A hash code + */ + public int hashCode() + { + int code = actionmask + minport + maxport; + if (address != null) + code += address.hashCode(); + else + code += hostname.hashCode(); + return code; + } + + /** + * Returns the list of permission actions in this object in canonical + * order. The canonical order is "connect,listen,accept,resolve" + * + * @return The permitted action string. + */ + public String getActions() + { + CPStringBuilder sb = new CPStringBuilder(""); + + for (int i = 0; i < ACTIONS.length; i++) + { + if ((actionmask & (1 << i)) != 0) + { + if (sb.length() != 0) + sb.append(","); + sb.append(ACTIONS[i]); + } + } + + return sb.toString(); + } + + /** + * Returns a new <code>PermissionCollection</code> object that can hold + * <code>SocketPermission</code>'s. + * + * @return A new <code>PermissionCollection</code>. + */ + public PermissionCollection newPermissionCollection() + { + // FIXME: Implement + + return null; + } + + /** + * Returns an array of all IP addresses represented by this object. + */ + private InetAddress[] getAddresses() + { + if (address != null) + return new InetAddress[] {address}; + + try + { + return InetAddress.getAllByName(hostname); + } + catch (UnknownHostException e) + { + return new InetAddress[0]; + } + } + + /** + * Returns the canonical hostname represented by this object, + * or null if this object represents a wildcarded domain. + */ + private String getCanonicalHostName() + { + if (address != null) + return address.internalGetCanonicalHostName(); + if (hostname.charAt(0) == '*') + return null; + try + { + return InetAddress.getByName(hostname).internalGetCanonicalHostName(); + } + catch (UnknownHostException e) + { + return null; + } + } + + /** + * Returns true if the permission object passed it is implied by the + * this permission. This will be true if: + * + * <ul> + * <li>The argument is of type <code>SocketPermission</code></li> + * <li>The actions list of the argument are in this object's actions</li> + * <li>The port range of the argument is within this objects port range</li> + * <li>The hostname is equal to or a subset of this objects hostname</li> + * </ul> + * + * <p>The argument's hostname will be a subset of this object's hostname if:</p> + * + * <ul> + * <li>The argument's hostname or IP address is equal to this object's.</li> + * <li>The argument's canonical hostname is equal to this object's.</li> + * <li>The argument's canonical name matches this domains hostname with + * wildcards</li> + * </ul> + * + * @param perm The <code>Permission</code> to check against + * + * @return <code>true</code> if the <code>Permission</code> is implied by + * this object, <code>false</code> otherwise. + */ + public boolean implies(Permission perm) + { + SocketPermission p; + + // First make sure we are the right object type + if (perm instanceof SocketPermission) + p = (SocketPermission) perm; + else + return false; + + // If p was initialised with an empty hostname then we do not + // imply it. This is not part of the spec, but it seems necessary. + if (p.hostname != null && p.hostname.length() == 0) + return false; + + // Next check the actions + if ((p.actionmask & actionmask) != p.actionmask) + return false; + + // Then check the ports + if ((p.minport < minport) || (p.maxport > maxport)) + return false; + + // Finally check the hosts + String p_canon = null; + + // Return true if this object was initialized with a single + // IP address which one of p's IP addresses is equal to. + if (address != null) + { + InetAddress[] addrs = p.getAddresses(); + for (int i = 0; i < addrs.length; i++) + { + if (address.equals(addrs[i])) + return true; + } + } + + // Return true if this object is a wildcarded domain that + // p's canonical name matches. + if (hostname != null && hostname.charAt(0) == '*') + { + p_canon = p.getCanonicalHostName(); + if (p_canon != null && p_canon.endsWith(hostname.substring(1))) + return true; + + } + + // Return true if this one of this object's IP addresses + // is equal to one of p's. + if (address == null) + { + InetAddress[] addrs = p.getAddresses(); + InetAddress[] p_addrs = p.getAddresses(); + + for (int i = 0; i < addrs.length; i++) + { + for (int j = 0; j < p_addrs.length; j++) + { + if (addrs[i].equals(p_addrs[j])) + return true; + } + } + } + + // Return true if this object's canonical name equals p's. + String canon = getCanonicalHostName(); + if (canon != null) + { + if (p_canon == null) + p_canon = p.getCanonicalHostName(); + if (p_canon != null && canon.equals(p_canon)) + return true; + } + + // Didn't make it + return false; + } + + /** + * Deserializes a <code>SocketPermission</code> object from + * an input stream. + * + * @param input the input stream. + * @throws IOException if an I/O error occurs in the stream. + * @throws ClassNotFoundException if the class of the + * serialized object could not be found. + */ + private void readObject(ObjectInputStream input) + throws IOException, ClassNotFoundException + { + input.defaultReadObject(); + setHostPort(getName()); + setActions(actions); + } + + /** + * Serializes a <code>SocketPermission</code> object to an + * output stream. + * + * @param output the output stream. + * @throws IOException if an I/O error occurs in the stream. + */ + private void writeObject(ObjectOutputStream output) + throws IOException + { + actions = getActions(); + output.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/SocketTimeoutException.java b/libjava/classpath/java/net/SocketTimeoutException.java new file mode 100644 index 000000000..21b0dcd86 --- /dev/null +++ b/libjava/classpath/java/net/SocketTimeoutException.java @@ -0,0 +1,73 @@ +/* SocketTimeoutException.java -- the socket timed out + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.InterruptedIOException; + + +/** + * This exception signals that a socket read or accept timed out. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class SocketTimeoutException extends InterruptedIOException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -8846654841826352300L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketTimeoutException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketTimeoutException(String message) + { + super(message); + } +} // class SocketTimeoutException diff --git a/libjava/classpath/java/net/TODO b/libjava/classpath/java/net/TODO new file mode 100644 index 000000000..e48969d3a --- /dev/null +++ b/libjava/classpath/java/net/TODO @@ -0,0 +1,24 @@ +-- DNS cache purging. + +-- Implement ContentHandler chaining (non-JDK feature) + +-- Implement MIME type by file determination chaining using external + disk files. (non-JDK feature) + +-- Implement determining MIME type from an InputStream + +-- Datagram peek()'s + +-- Finalize methods for sockets + +-- HTTP - caching (supported by JDK?) + +-- HTTP - all protocol support beyond basic GET functionality + +-- Fix call to Date(String) in URLConnection.getHeaderFieldDate() when + I figure out why DateFormat isn't working. + +-- Finish off all JDK 1.2 permissions stuff + +-- Write URLClassLoader + diff --git a/libjava/classpath/java/net/URI.java b/libjava/classpath/java/net/URI.java new file mode 100644 index 000000000..b5fb9654f --- /dev/null +++ b/libjava/classpath/java/net/URI.java @@ -0,0 +1,1450 @@ +/* URI.java -- An URI class + Copyright (C) 2002, 2004, 2005, 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <p> + * A URI instance represents that defined by + * <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC3986</a>, + * with some deviations. + * </p> + * <p> + * At its highest level, a URI consists of: + * </p> + * <code>[<em>scheme</em><strong>:</strong>]<em>scheme-specific-part</em> + * [<strong>#</strong><em>fragment</em>]</code> + * </p> + * <p> + * where <strong>#</strong> and <strong>:</strong> are literal characters, + * and those parts enclosed in square brackets are optional. + * </p> + * <p> + * There are two main types of URI. An <em>opaque</em> URI is one + * which just consists of the above three parts, and is not further + * defined. An example of such a URI would be <em>mailto:</em> URI. + * In contrast, <em>hierarchical</em> URIs give further definition + * to the scheme-specific part, so as represent some part of a hierarchical + * structure. + * </p> + * <p> + * <code>[<strong>//</strong><em>authority</em>][<em>path</em>] + * [<strong>?</strong><em>query</em>]</code> + * </p> + * <p> + * with <strong>/</strong> and <strong>?</strong> being literal characters. + * When server-based, the authority section is further subdivided into: + * </p> + * <p> + * <code>[<em>user-info</em><strong>@</strong>]<em>host</em> + * [<strong>:</strong><em>port</em>]</code> + * </p> + * <p> + * with <strong>@</strong> and <strong>:</strong> as literal characters. + * Authority sections that are not server-based are said to be registry-based. + * </p> + * <p> + * Hierarchical URIs can be either relative or absolute. Absolute URIs + * always start with a `<strong>/</strong>', while relative URIs don't + * specify a scheme. Opaque URIs are always absolute. + * </p> + * <p> + * Each part of the URI may have one of three states: undefined, empty + * or containing some content. The former two of these are represented + * by <code>null</code> and the empty string in Java, respectively. + * The scheme-specific part may never be undefined. It also follows from + * this that the path sub-part may also not be undefined, so as to ensure + * the former. + * </p> + * <h2>Character Escaping and Quoting</h2> + * <p> + * The characters that can be used within a valid URI are restricted. + * There are two main classes of characters which can't be used as is + * within the URI: + * </p> + * <ol> + * <li><strong>Characters outside the US-ASCII character set</strong>. + * These have to be <strong>escaped</strong> in order to create + * an RFC-compliant URI; this means replacing the character with the + * appropriate hexadecimal value, preceded by a `%'.</li> + * <li><strong>Illegal characters</strong> (e.g. space characters, + * control characters) are quoted, which results in them being encoded + * in the same way as non-US-ASCII characters.</li> + * </ol> + * <p> + * The set of valid characters differs depending on the section of the URI: + * </p> + * <ul> + * <li><strong>Scheme</strong>: Must be an alphanumeric, `-', `.' or '+'.</li> + * <li><strong>Authority</strong>:Composed of the username, host, port, `@' + * and `:'.</li> + * <li><strong>Username</strong>: Allows unreserved or percent-encoded + * characters, sub-delimiters and `:'.</li> + * <li><strong>Host</strong>: Allows unreserved or percent-encoded + * characters, sub-delimiters and square brackets (`[' and `]') for IPv6 + * addresses.</li> + * <li><strong>Port</strong>: Digits only.</li> + * <li><strong>Path</strong>: Allows the path characters and `/'. + * <li><strong>Query</strong>: Allows the path characters, `?' and '/'. + * <li><strong>Fragment</strong>: Allows the path characters, `?' and '/'. + * </ul> + * <p> + * These definitions reference the following sets of characters: + * </p> + * <ul> + * <li><strong>Unreserved characters</strong>: The alphanumerics plus + * `-', `.', `_', and `~'.</li> + * <li><strong>Sub-delimiters</strong>: `!', `$', `&', `(', `)', `*', + * `+', `,', `;', `=' and the single-quote itself.</li> + * <li><strong>Path characters</strong>: Unreserved and percent-encoded + * characters and the sub-delimiters along with `@' and `:'.</li> + * </ul> + * <p> + * The constructors and accessor methods allow the use and retrieval of + * URI components which contain non-US-ASCII characters directly. + * They are only escaped when the <code>toASCIIString()</code> method + * is used. In contrast, illegal characters are always quoted, with the + * exception of the return values of the non-raw accessors. + * </p> + * + * @author Ito Kazumitsu (ito.kazumitsu@hitachi-cable.co.jp) + * @author Dalibor Topic (robilad@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public final class URI + implements Comparable<URI>, Serializable +{ + /** + * For serialization compatability. + */ + static final long serialVersionUID = -6052424284110960213L; + + /** + * Regular expression for parsing URIs. + * + * Taken from RFC 2396, Appendix B. + * This expression doesn't parse IPv6 addresses. + */ + private static final String URI_REGEXP = + "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?"; + + /** + * Regular expression for parsing the authority segment. + */ + private static final String AUTHORITY_REGEXP = + "(([^?#]*)@)?([^?#:]*)(:([0-9]*))?"; + + /** + * Valid characters (taken from rfc2396/3986) + */ + private static final String RFC2396_DIGIT = "0123456789"; + private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz"; + private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String RFC2396_ALPHA = + RFC2396_LOWALPHA + RFC2396_UPALPHA; + private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA; + private static final String RFC3986_UNRESERVED = RFC2396_ALPHANUM + "-._~"; + private static final String RFC3986_SUBDELIMS = "!$&'()*+,;="; + private static final String RFC3986_REG_NAME = + RFC3986_UNRESERVED + RFC3986_SUBDELIMS + "%"; + private static final String RFC3986_PCHAR = RFC3986_UNRESERVED + + RFC3986_SUBDELIMS + ":@%"; + private static final String RFC3986_SEGMENT = RFC3986_PCHAR; + private static final String RFC3986_PATH_SEGMENTS = RFC3986_SEGMENT + "/"; + private static final String RFC3986_SSP = RFC3986_PCHAR + "?/"; + private static final String RFC3986_HOST = RFC3986_REG_NAME + "[]"; + private static final String RFC3986_USERINFO = RFC3986_REG_NAME + ":"; + + /** + * Index of scheme component in parsed URI. + */ + private static final int SCHEME_GROUP = 2; + + /** + * Index of scheme-specific-part in parsed URI. + */ + private static final int SCHEME_SPEC_PART_GROUP = 3; + + /** + * Index of authority component in parsed URI. + */ + private static final int AUTHORITY_GROUP = 5; + + /** + * Index of path component in parsed URI. + */ + private static final int PATH_GROUP = 6; + + /** + * Index of query component in parsed URI. + */ + private static final int QUERY_GROUP = 8; + + /** + * Index of fragment component in parsed URI. + */ + private static final int FRAGMENT_GROUP = 10; + + /** + * Index of userinfo component in parsed authority section. + */ + private static final int AUTHORITY_USERINFO_GROUP = 2; + + /** + * Index of host component in parsed authority section. + */ + private static final int AUTHORITY_HOST_GROUP = 3; + + /** + * Index of port component in parsed authority section. + */ + private static final int AUTHORITY_PORT_GROUP = 5; + + /** + * The compiled version of the URI regular expression. + */ + private static final Pattern URI_PATTERN; + + /** + * The compiled version of the authority regular expression. + */ + private static final Pattern AUTHORITY_PATTERN; + + /** + * The set of valid hexadecimal characters. + */ + private static final String HEX = "0123456789ABCDEF"; + + private transient String scheme; + private transient String rawSchemeSpecificPart; + private transient String schemeSpecificPart; + private transient String rawAuthority; + private transient String authority; + private transient String rawUserInfo; + private transient String userInfo; + private transient String rawHost; + private transient String host; + private transient int port = -1; + private transient String rawPath; + private transient String path; + private transient String rawQuery; + private transient String query; + private transient String rawFragment; + private transient String fragment; + private String string; + + /** + * Static initializer to pre-compile the regular expressions. + */ + static + { + URI_PATTERN = Pattern.compile(URI_REGEXP); + AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEXP); + } + + private void readObject(ObjectInputStream is) + throws ClassNotFoundException, IOException + { + this.string = (String) is.readObject(); + try + { + parseURI(this.string); + } + catch (URISyntaxException x) + { + // Should not happen. + throw new RuntimeException(x); + } + } + + private void writeObject(ObjectOutputStream os) throws IOException + { + if (string == null) + string = toString(); + os.writeObject(string); + } + + /** + * <p> + * Returns the string content of the specified group of the supplied + * matcher. The returned value is modified according to the following: + * </p> + * <ul> + * <li>If the resulting string has a length greater than 0, then + * that string is returned.</li> + * <li>If a string of zero length, is matched, then the content + * of the preceding group is considered. If this is also an empty + * string, then <code>null</code> is returned to indicate an undefined + * value. Otherwise, the value is truly the empty string and this is + * the returned value.</li> + * </ul> + * <p> + * This method is used for matching against all parts of the URI + * that may be either undefined or empty (i.e. all those but the + * scheme-specific part and the path). In each case, the preceding + * group is the content of the original group, along with some + * additional distinguishing feature. For example, the preceding + * group for the query includes the preceding question mark, + * while that of the fragment includes the hash symbol. The presence + * of these features enables disambiguation between the two cases + * of a completely unspecified value and a simple non-existant value. + * The scheme differs in that it will never return an empty string; + * the delimiter follows the scheme rather than preceding it, so + * it becomes part of the following section. The same is true + * of the user information. + * </p> + * + * @param match the matcher, which contains the results of the URI + * matched against the URI regular expression. + * @return either the matched content, <code>null</code> for undefined + * values, or an empty string for a URI part with empty content. + */ + private static String getURIGroup(Matcher match, int group) + { + String matched = match.group(group); + if (matched == null || matched.length() == 0) + { + String prevMatched = match.group(group -1); + if (prevMatched == null || prevMatched.length() == 0) + return null; + else + return ""; + } + return matched; + } + + /** + * Sets fields of this URI by parsing the given string. + * + * @param str The string to parse + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + private void parseURI(String str) throws URISyntaxException + { + Matcher matcher = URI_PATTERN.matcher(str); + + if (matcher.matches()) + { + scheme = getURIGroup(matcher, SCHEME_GROUP); + rawSchemeSpecificPart = matcher.group(SCHEME_SPEC_PART_GROUP); + schemeSpecificPart = unquote(rawSchemeSpecificPart); + if (!isOpaque()) + { + rawAuthority = getURIGroup(matcher, AUTHORITY_GROUP); + rawPath = matcher.group(PATH_GROUP); + rawQuery = getURIGroup(matcher, QUERY_GROUP); + } + rawFragment = getURIGroup(matcher, FRAGMENT_GROUP); + } + else + throw new URISyntaxException(str, + "doesn't match URI regular expression"); + parseServerAuthority(); + + // We must eagerly unquote the parts, because this is the only time + // we may throw an exception. + authority = unquote(rawAuthority); + userInfo = unquote(rawUserInfo); + host = unquote(rawHost); + path = unquote(rawPath); + query = unquote(rawQuery); + fragment = unquote(rawFragment); + } + + /** + * Unquote "%" + hex quotes characters + * + * @param str The string to unquote or null. + * + * @return The unquoted string or null if str was null. + * + * @exception URISyntaxException If the given string contains invalid + * escape sequences. + */ + private static String unquote(String str) throws URISyntaxException + { + if (str == null) + return null; + byte[] buf = new byte[str.length()]; + int pos = 0; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (c == '%') + { + if (i + 2 >= str.length()) + throw new URISyntaxException(str, "Invalid quoted character"); + int hi = Character.digit(str.charAt(++i), 16); + int lo = Character.digit(str.charAt(++i), 16); + if (lo < 0 || hi < 0) + throw new URISyntaxException(str, "Invalid quoted character"); + buf[pos++] = (byte) (hi * 16 + lo); + } + else + buf[pos++] = (byte) c; + } + try + { + return new String(buf, 0, pos, "utf-8"); + } + catch (java.io.UnsupportedEncodingException x2) + { + throw (Error) new InternalError().initCause(x2); + } + } + + /** + * Quote characters illegal in URIs in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quote(String str) + { + return quote(str, RFC3986_SSP); + } + + /** + * Quote characters illegal in URI authorities in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteAuthority(String str) + { + // Technically, we should be using RFC2396_AUTHORITY, but + // it contains no additional characters. + return quote(str, RFC3986_REG_NAME); + } + + /** + * Quotes the characters in the supplied string that are not part of + * the specified set of legal characters. + * + * @param str the string to quote + * @param legalCharacters the set of legal characters + * + * @return the quoted string. + */ + private static String quote(String str, String legalCharacters) + { + CPStringBuilder sb = new CPStringBuilder(str.length()); + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if ((legalCharacters.indexOf(c) == -1) + && (c <= 127)) + { + sb.append('%'); + sb.append(HEX.charAt(c / 16)); + sb.append(HEX.charAt(c % 16)); + } + else + sb.append(c); + } + return sb.toString(); + } + + /** + * Quote characters illegal in URI hosts in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteHost(String str) + { + return quote(str, RFC3986_HOST); + } + + /** + * Quote characters illegal in URI paths in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quotePath(String str) + { + // Technically, we should be using RFC2396_PATH, but + // it contains no additional characters. + return quote(str, RFC3986_PATH_SEGMENTS); + } + + /** + * Quote characters illegal in URI user infos in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteUserInfo(String str) + { + return quote(str, RFC3986_USERINFO); + } + + /** + * Creates an URI from the given string + * + * @param str The string to create the URI from + * + * @exception URISyntaxException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public URI(String str) throws URISyntaxException + { + this.string = str; + parseURI(str); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param userInfo The username and authorization info + * @param host The hostname + * @param port The port number + * @param path The path + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String userInfo, String host, int port, + String path, String query, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (userInfo == null && host == null && port == -1 ? "" : "//") + + (userInfo == null ? "" : quoteUserInfo(userInfo) + "@") + + (host == null ? "" : quoteHost(host)) + + (port == -1 ? "" : ":" + String.valueOf(port)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param authority The authority + * @param path The apth + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String authority, String path, String query, + String fragment) throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (authority == null ? "" : "//" + quoteAuthority(authority)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param host The hostname + * @param path The path + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String host, String path, String fragment) + throws URISyntaxException + { + this(scheme, null, host, -1, path, null, fragment); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param ssp The scheme specific part + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String ssp, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (ssp == null ? "" : quote(ssp)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given string + * + * @param str The string to create the URI from + * + * @exception IllegalArgumentException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public static URI create(String str) + { + try + { + return new URI(str); + } + catch (URISyntaxException e) + { + throw (IllegalArgumentException) new IllegalArgumentException() + .initCause(e); + } + } + + /** + * Attempts to parse this URI's authority component, if defined, + * into user-information, host, and port components. The purpose + * of this method was to disambiguate between some authority sections, + * which form invalid server-based authories, but valid registry + * based authorities. In the updated RFC 3986, the authority section + * is defined differently, with registry-based authorities part of + * the host section. Thus, this method is now simply an explicit + * way of parsing any authority section. + * + * @return the URI, with the authority section parsed into user + * information, host and port components. + * @throws URISyntaxException if the given string violates RFC 2396 + */ + public URI parseServerAuthority() throws URISyntaxException + { + if (rawAuthority != null) + { + Matcher matcher = AUTHORITY_PATTERN.matcher(rawAuthority); + + if (matcher.matches()) + { + rawUserInfo = getURIGroup(matcher, AUTHORITY_USERINFO_GROUP); + rawHost = getURIGroup(matcher, AUTHORITY_HOST_GROUP); + + String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP); + + if (portStr != null && ! portStr.isEmpty()) + try + { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) + { + URISyntaxException use = + new URISyntaxException + (string, "doesn't match URI regular expression"); + use.initCause(e); + throw use; + } + } + else + throw new URISyntaxException(string, + "doesn't match URI regular expression"); + } + return this; + } + + /** + * <p> + * Returns a normalized version of the URI. If the URI is opaque, + * or its path is already in normal form, then this URI is simply + * returned. Otherwise, the following transformation of the path + * element takes place: + * </p> + * <ol> + * <li>All `.' segments are removed.</li> + * <li>Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.</li> + * <li>A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.</li> + * </ol> + * <p> + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + * </p> + * + * @return the normalized URI. + */ + public URI normalize() + { + if (isOpaque() || path.indexOf("/./") == -1 && path.indexOf("/../") == -1) + return this; + try + { + return new URI(scheme, authority, normalizePath(path), query, + fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Normalized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * <p> + * Normalize the given path. The following transformation takes place: + * </p> + * <ol> + * <li>All `.' segments are removed.</li> + * <li>Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.</li> + * <li>A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.</li> + * </ol> + * <p> + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + * </p> + * + * @param relativePath the relative path to be normalized. + * @return the normalized path. + */ + private String normalizePath(String relativePath) + { + /* + This follows the algorithm in section 5.2.4. of RFC3986, + but doesn't modify the input buffer. + */ + CPStringBuilder input = new CPStringBuilder(relativePath); + CPStringBuilder output = new CPStringBuilder(); + int start = 0; + while (start < input.length()) + { + /* A */ + if (input.indexOf("../",start) == start) + { + start += 3; + continue; + } + if (input.indexOf("./",start) == start) + { + start += 2; + continue; + } + /* B */ + if (input.indexOf("/./",start) == start) + { + start += 2; + continue; + } + if (input.indexOf("/.",start) == start + && input.charAt(start + 2) != '.') + { + start += 1; + input.setCharAt(start,'/'); + continue; + } + /* C */ + if (input.indexOf("/../",start) == start) + { + start += 3; + removeLastSegment(output); + continue; + } + if (input.indexOf("/..",start) == start) + { + start += 2; + input.setCharAt(start,'/'); + removeLastSegment(output); + continue; + } + /* D */ + if (start == input.length() - 1 && input.indexOf(".",start) == start) + { + input.delete(0,1); + continue; + } + if (start == input.length() - 2 && input.indexOf("..",start) == start) + { + input.delete(0,2); + continue; + } + /* E */ + int indexOfSlash = input.indexOf("/",start); + while (indexOfSlash == start) + { + output.append("/"); + ++start; + indexOfSlash = input.indexOf("/",start); + } + if (indexOfSlash == -1) + indexOfSlash = input.length(); + output.append(input.substring(start, indexOfSlash)); + start = indexOfSlash; + } + return output.toString(); + } + + /** + * Removes the last segment of the path from the specified buffer. + * + * @param buffer the buffer containing the path. + */ + private void removeLastSegment(CPStringBuilder buffer) + { + int lastSlash = buffer.lastIndexOf("/"); + if (lastSlash == -1) + buffer.setLength(0); + else + buffer.setLength(lastSlash); + } + + /** + * Resolves the given URI against this URI + * + * @param uri The URI to resolve against this URI + * + * @return The resulting URI, or null when it couldn't be resolved + * for some reason. + * + * @throws NullPointerException if uri is null + */ + public URI resolve(URI uri) + { + if (uri.isAbsolute()) + return uri; + if (uri.isOpaque()) + return uri; + + String scheme = uri.getScheme(); + String schemeSpecificPart = uri.getSchemeSpecificPart(); + String authority = uri.getAuthority(); + String path = uri.getPath(); + String query = uri.getQuery(); + String fragment = uri.getFragment(); + + try + { + if (fragment != null && path != null && path.equals("") + && scheme == null && authority == null && query == null) + return new URI(this.scheme, this.schemeSpecificPart, fragment); + + if (authority == null) + { + authority = this.authority; + if (path == null) + path = ""; + if (! (path.startsWith("/"))) + { + CPStringBuilder basepath = new CPStringBuilder(this.path); + int i = this.path.lastIndexOf('/'); + + if (i >= 0) + basepath.delete(i + 1, basepath.length()); + + basepath.append(path); + path = normalizePath(basepath.toString()); + } + } + return new URI(this.scheme, authority, path, query, fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Resolved URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Resolves the given URI string against this URI + * + * @param str The URI as string to resolve against this URI + * + * @return The resulting URI + * + * @throws IllegalArgumentException If the given URI string + * violates RFC 2396 + * @throws NullPointerException If uri is null + */ + public URI resolve(String str) throws IllegalArgumentException + { + return resolve(create(str)); + } + + /** + * <p> + * Relativizes the given URI against this URI. The following + * algorithm is used: + * </p> + * <ul> + * <li>If either URI is opaque, the given URI is returned.</li> + * <li>If the schemes of the URIs differ, the given URI is returned.</li> + * <li>If the authority components of the URIs differ, then the given + * URI is returned.</li> + * <li>If the path of this URI is not a prefix of the supplied URI, + * then the given URI is returned.</li> + * <li>If all the above conditions hold, a new URI is created using the + * query and fragment components of the given URI, along with a path + * computed by removing the path of this URI from the start of the path + * of the supplied URI.</li> + * </ul> + * + * @param uri the URI to relativize agsint this URI + * @return the resulting URI + * @throws NullPointerException if the uri is null + */ + public URI relativize(URI uri) + { + if (isOpaque() || uri.isOpaque()) + return uri; + if (scheme == null && uri.getScheme() != null) + return uri; + if (scheme != null && !(scheme.equals(uri.getScheme()))) + return uri; + if (rawAuthority == null && uri.getRawAuthority() != null) + return uri; + if (rawAuthority != null && !(rawAuthority.equals(uri.getRawAuthority()))) + return uri; + String basePath = rawPath; + if (!(uri.getRawPath().equals(rawPath))) + { + if (!(basePath.endsWith("/"))) + basePath = basePath.concat("/"); + if (!(uri.getRawPath().startsWith(basePath))) + return uri; + } + try + { + return new URI(null, null, + uri.getRawPath().substring(basePath.length()), + uri.getRawQuery(), uri.getRawFragment()); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Relativized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Creates an URL from an URI + * + * @throws MalformedURLException If a protocol handler for the URL could + * not be found, or if some other error occurred while constructing the URL + * @throws IllegalArgumentException If the URI is not absolute + */ + public URL toURL() throws IllegalArgumentException, MalformedURLException + { + if (isAbsolute()) + return new URL(this.toString()); + + throw new IllegalArgumentException("not absolute"); + } + + /** + * Returns the scheme of the URI + */ + public String getScheme() + { + return scheme; + } + + /** + * Tells whether this URI is absolute or not + */ + public boolean isAbsolute() + { + return scheme != null; + } + + /** + * Tell whether this URI is opaque or not + */ + public boolean isOpaque() + { + return ((scheme != null) && ! (schemeSpecificPart.startsWith("/"))); + } + + /** + * Returns the raw scheme specific part of this URI. + * The scheme-specific part is never undefined, though it may be empty + */ + public String getRawSchemeSpecificPart() + { + return rawSchemeSpecificPart; + } + + /** + * Returns the decoded scheme specific part of this URI. + */ + public String getSchemeSpecificPart() + { + return schemeSpecificPart; + } + + /** + * Returns the raw authority part of this URI + */ + public String getRawAuthority() + { + return rawAuthority; + } + + /** + * Returns the decoded authority part of this URI + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the raw user info part of this URI + */ + public String getRawUserInfo() + { + return rawUserInfo; + } + + /** + * Returns the decoded user info part of this URI + */ + public String getUserInfo() + { + return userInfo; + } + + /** + * Returns the hostname of the URI + */ + public String getHost() + { + return host; + } + + /** + * Returns the port number of the URI + */ + public int getPort() + { + return port; + } + + /** + * Returns the raw path part of this URI + */ + public String getRawPath() + { + return rawPath; + } + + /** + * Returns the path of the URI + */ + public String getPath() + { + return path; + } + + /** + * Returns the raw query part of this URI + */ + public String getRawQuery() + { + return rawQuery; + } + + /** + * Returns the query of the URI + */ + public String getQuery() + { + return query; + } + + /** + * Return the raw fragment part of this URI + */ + public String getRawFragment() + { + return rawFragment; + } + + /** + * Returns the fragment of the URI + */ + public String getFragment() + { + return fragment; + } + + /** + * <p> + * Compares the URI with the given object for equality. If the + * object is not a <code>URI</code>, then the method returns false. + * Otherwise, the following criteria are observed: + * </p> + * <ul> + * <li>The scheme of the URIs must either be null (undefined) in both cases, + * or equal, ignorant of case.</li> + * <li>The raw fragment of the URIs must either be null (undefined) in both + * cases, or equal, ignorant of case.</li> + * <li>Both URIs must be of the same type (opaque or hierarchial)</li> + * <li><strong>For opaque URIs:</strong></li> + * <ul> + * <li>The raw scheme-specific parts must be equal.</li> + * </ul> + * <li>For hierarchical URIs:</li> + * <ul> + * <li>The raw paths must be equal, ignorant of case.</li> + * <li>The raw queries are either both undefined or both equal, ignorant + * of case.</li> + * <li>The raw authority sections are either both undefined or:</li> + * <li><strong>For registry-based authorities:</strong></li> + * <ul><li>they are equal.</li></ul> + * <li><strong>For server-based authorities:</strong></li> + * <ul> + * <li>the hosts are equal, ignoring case</li> + * <li>the ports are equal</li> + * <li>the user information components are equal</li> + * </ul> + * </ul> + * </ul> + * + * @param obj the obj to compare the URI with. + * @return <code>true</code> if the objects are equal, according to + * the specification above. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof URI)) + return false; + URI uriObj = (URI) obj; + if (scheme == null) + { + if (uriObj.getScheme() != null) + return false; + } + else + if (!(scheme.equalsIgnoreCase(uriObj.getScheme()))) + return false; + if (rawFragment == null) + { + if (uriObj.getRawFragment() != null) + return false; + } + else + if (!(rawFragment.equalsIgnoreCase(uriObj.getRawFragment()))) + return false; + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uriObj.isOpaque(); + if (opaqueThis && opaqueObj) + return rawSchemeSpecificPart.equals(uriObj.getRawSchemeSpecificPart()); + else if (!opaqueThis && !opaqueObj) + { + boolean common = rawPath.equalsIgnoreCase(uriObj.getRawPath()) + && ((rawQuery == null && uriObj.getRawQuery() == null) + || rawQuery.equalsIgnoreCase(uriObj.getRawQuery())); + if (rawAuthority == null && uriObj.getRawAuthority() == null) + return common; + if (host == null) + return common + && rawAuthority.equalsIgnoreCase(uriObj.getRawAuthority()); + return common + && host.equalsIgnoreCase(uriObj.getHost()) + && port == uriObj.getPort() + && (rawUserInfo == null ? + uriObj.getRawUserInfo() == null : + rawUserInfo.equalsIgnoreCase(uriObj.getRawUserInfo())); + } + else + return false; + } + + /** + * Computes the hashcode of the URI + */ + public int hashCode() + { + return (getScheme() == null ? 0 : 13 * getScheme().hashCode()) + + 17 * getRawSchemeSpecificPart().hashCode() + + (getRawFragment() == null ? 0 : 21 + getRawFragment().hashCode()); + } + + /** + * Compare the URI with another URI. + * Undefined components are taken to be less than any other component. + * The following criteria are observed: + * </p> + * <ul> + * <li>Two URIs with different schemes are compared according to their + * scheme, regardless of case.</li> + * <li>A hierarchical URI is less than an opaque URI with the same + * scheme.</li> + * <li><strong>For opaque URIs:</strong></li> + * <ul> + * <li>URIs with differing scheme-specific parts are ordered according + * to the ordering of the scheme-specific part.</li> + * <li>URIs with the same scheme-specific part are ordered by the + * raw fragment.</li> + * </ul> + * <li>For hierarchical URIs:</li> + * <ul> + * <li>URIs are ordered according to their raw authority sections, + * if they are unequal.</li> + * <li><strong>For registry-based authorities:</strong></li> + * <ul><li>they are ordered according to the ordering of the authority + * component.</li></ul> + * <li><strong>For server-based authorities:</strong></li> + * <ul> + * <li>URIs are ordered according to the raw user information.</li> + * <li>URIs with the same user information are ordered by the host, + * ignoring case.</li> + * <lI>URIs with the same host are ordered by the port.</li> + * </ul> + * <li>URIs with the same authority section are ordered by the raw path.</li> + * <li>URIs with the same path are ordered by their raw query.</li> + * <li>URIs with the same query are ordered by their raw fragments.</li> + * </ul> + * </ul> + * + * @param uri The other URI to compare this URI with + * @return a negative integer, zero or a positive integer depending + * on whether this URI is less than, equal to or greater + * than that supplied, respectively. + */ + public int compareTo(URI uri) + throws ClassCastException + { + if (scheme == null && uri.getScheme() != null) + return -1; + if (scheme != null) + { + int sCompare = scheme.compareToIgnoreCase(uri.getScheme()); + if (sCompare != 0) + return sCompare; + } + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uri.isOpaque(); + if (opaqueThis && !opaqueObj) + return 1; + if (!opaqueThis && opaqueObj) + return -1; + if (opaqueThis) + { + int ssCompare = + rawSchemeSpecificPart.compareTo(uri.getRawSchemeSpecificPart()); + if (ssCompare == 0) + return compareFragments(uri); + else + return ssCompare; + } + if (rawAuthority == null && uri.getRawAuthority() != null) + return -1; + if (rawAuthority != null) + { + int aCompare = rawAuthority.compareTo(uri.getRawAuthority()); + if (aCompare != 0) + { + if (host == null) + return aCompare; + if (rawUserInfo == null && uri.getRawUserInfo() != null) + return -1; + int uCompare = rawUserInfo.compareTo(uri.getRawUserInfo()); + if (uCompare != 0) + return uCompare; + if (host == null && uri.getHost() != null) + return -1; + int hCompare = host.compareTo(uri.getHost()); + if (hCompare != 0) + return hCompare; + int uriPort = uri.getPort(); + return (uriPort == port) ? 0 : (uriPort > port) ? -1 : 1; + } + } + if (rawPath == null && uri.getRawPath() != null) + return -1; + if (rawPath != null) + { + int pCompare = rawPath.compareTo(uri.getRawPath()); + if (pCompare != 0) + return pCompare; + } + if (rawQuery == null && uri.getRawQuery() != null) + return -1; + if (rawQuery != null) + { + int qCompare = rawQuery.compareTo(uri.getRawQuery()); + if (qCompare != 0) + return qCompare; + } + return compareFragments(uri); + } + + /** + * Compares the fragment of this URI with that of the supplied URI. + * + * @param uri the URI to compare with this one. + * @return a negative integer, zero or a positive integer depending + * on whether this uri's fragment is less than, equal to + * or greater than the fragment of the uri supplied, respectively. + */ + private int compareFragments(URI uri) + { + if (rawFragment == null && uri.getRawFragment() != null) + return -1; + else if (rawFragment == null) + return 0; + else + return rawFragment.compareTo(uri.getRawFragment()); + } + + /** + * Returns the URI as a String. If the URI was created using a constructor, + * then this will be the same as the original input string. + * + * @return a string representation of the URI. + */ + public String toString() + { + return (scheme == null ? "" : scheme + ":") + + rawSchemeSpecificPart + + (rawFragment == null ? "" : "#" + rawFragment); + } + + /** + * Returns the URI as US-ASCII string. This is the same as the result + * from <code>toString()</code> for URIs that don't contain any non-US-ASCII + * characters. Otherwise, the non-US-ASCII characters are replaced + * by their percent-encoded representations. + * + * @return a string representation of the URI, containing only US-ASCII + * characters. + */ + public String toASCIIString() + { + String strRep = toString(); + boolean inNonAsciiBlock = false; + CPStringBuilder buffer = new CPStringBuilder(); + CPStringBuilder encBuffer = null; + for (int i = 0; i < strRep.length(); i++) + { + char c = strRep.charAt(i); + if (c <= 127) + { + if (inNonAsciiBlock) + { + buffer.append(escapeCharacters(encBuffer.toString())); + inNonAsciiBlock = false; + } + buffer.append(c); + } + else + { + if (!inNonAsciiBlock) + { + encBuffer = new CPStringBuilder(); + inNonAsciiBlock = true; + } + encBuffer.append(c); + } + } + return buffer.toString(); + } + + /** + * Converts the non-ASCII characters in the supplied string + * to their equivalent percent-encoded representations. + * That is, they are replaced by "%" followed by their hexadecimal value. + * + * @param str a string including non-ASCII characters. + * @return the string with the non-ASCII characters converted to their + * percent-encoded representations. + */ + private static String escapeCharacters(String str) + { + try + { + CPStringBuilder sb = new CPStringBuilder(); + // this is far from optimal, but it works + byte[] utf8 = str.getBytes("utf-8"); + for (int j = 0; j < utf8.length; j++) + { + sb.append('%'); + sb.append(HEX.charAt((utf8[j] & 0xff) / 16)); + sb.append(HEX.charAt((utf8[j] & 0xff) % 16)); + } + return sb.toString(); + } + catch (java.io.UnsupportedEncodingException x) + { + throw (Error) new InternalError("Escaping error").initCause(x); + } + } + +} diff --git a/libjava/classpath/java/net/URISyntaxException.java b/libjava/classpath/java/net/URISyntaxException.java new file mode 100644 index 000000000..27a70bdb7 --- /dev/null +++ b/libjava/classpath/java/net/URISyntaxException.java @@ -0,0 +1,144 @@ +/* URISyntaxException.java -- a string could not be parsed as a URI + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception is thrown when a String cannot be parsed as a URI. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see URI + * @since 1.4 + * @status updated to 1.4 + */ +public class URISyntaxException extends Exception +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 2137979680897488891L; + + /** + * The failed input. + * + * @serial the bad URI + */ + private final String input; + + /** + * The index of failure. + * + * @serial the location of the problem + */ + private final int index; + + /** + * Create an exception from the invalid string, with the index set to -1. + * + * @param input the bad URI + * @param msg the descriptive error message + * @throws NullPointerException if input or msg are null + */ + public URISyntaxException(String input, String msg) + { + this(input, msg, -1); + } + + /** + * Create an exception from the invalid string, with the index of the + * point of failure. + * + * @param input the bad URI + * @param msg the descriptive error message + * @param index the index of the parse error, or -1 + * @throws NullPointerException if input or msg are null + * @throws IllegalArgumentException if index < -1 + */ + public URISyntaxException(String input, String msg, int index) + { + // The toString() hack checks for null. + super(msg.toString()); + this.input = input.toString(); + this.index = index; + if (index < -1) + throw new IllegalArgumentException(); + } + + /** + * Returns the bad input string. + * + * @return the bad URI, guaranteed non-null + */ + public String getInput() + { + return input; + } + + /** + * Returns the reason for the failure. + * + * @return the message, guaranteed non-null + */ + public String getReason() + { + return super.getMessage(); + } + + /** + * Returns the index of the failure, or -1. + * + * @return the index of failure + */ + public int getIndex() + { + return index; + } + + /** + * Returns a message describing the parse error, as if by + * <code>getReason() + (getIndex() >= 0 ? " at index " + getIndex() : "") + * + ": " + getInput()</code>. + * + * @return the message string + */ + public String getMessage() + { + return (super.getMessage() + (index >= 0 ? " at index " + index : "") + + ": " + input); + } +} diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java new file mode 100644 index 000000000..1b778b40a --- /dev/null +++ b/libjava/classpath/java/net/URL.java @@ -0,0 +1,1010 @@ +/* URL.java -- Uniform Resource Locator Class + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; +import gnu.java.net.URLParseError; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.StringTokenizer; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This final class represents an Internet Uniform Resource Locator (URL). + * For details on the syntax of URL's and what they can be used for, + * refer to RFC 1738, available from <a + * href="http://ds.internic.net/rfcs/rfc1738.txt"> + * http://ds.internic.net/rfcs/rfc1738.txt</a> + * <p> + * There are a great many protocols supported by URL's such as "http", + * "ftp", and "file". This object can handle any arbitrary URL for which + * a URLStreamHandler object can be written. Default protocol handlers + * are provided for the "http" and "ftp" protocols. Additional protocols + * handler implementations may be provided in the future. In any case, + * an application or applet can install its own protocol handlers that + * can be "chained" with other protocol hanlders in the system to extend + * the base functionality provided with this class. (Note, however, that + * unsigned applets cannot access properties by default or install their + * own protocol handlers). + * <p> + * This chaining is done via the system property java.protocol.handler.pkgs + * If this property is set, it is assumed to be a "|" separated list of + * package names in which to attempt locating protocol handlers. The + * protocol handler is searched for by appending the string + * ".<protocol>.Handler" to each packed in the list until a hander is + * found. If a protocol handler is not found in this list of packages, or if + * the property does not exist, then the default protocol handler of + * "gnu.java.net.<protocol>.Handler" is tried. If this is + * unsuccessful, a MalformedURLException is thrown. + * <p> + * All of the constructor methods of URL attempt to load a protocol + * handler and so any needed protocol handlers must be installed when + * the URL is constructed. + * <p> + * Here is an example of how URL searches for protocol handlers. Assume + * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the + * URL is "news://comp.lang.java.programmer". URL would looking the + * following places for protocol handlers: + * <p><pre> + * com.foo.news.Handler + * com.bar.news.Handler + * gnu.java.net.news.Handler + * </pre><p> + * If the protocol handler is not found in any of those locations, a + * MalformedURLException would be thrown. + * <p> + * Please note that a protocol handler must be a subclass of + * URLStreamHandler. + * <p> + * Normally, this class caches protocol handlers. Once it finds a handler + * for a particular protocol, it never tries to look up a new handler + * again. However, if the system property + * gnu.java.net.nocache_protocol_handlers is set, then this + * caching behavior is disabled. This property is specific to this + * implementation. Sun's JDK may or may not do protocol caching, but it + * almost certainly does not examine this property. + * <p> + * Please also note that an application can install its own factory for + * loading protocol handlers (see setURLStreamHandlerFactory). If this is + * done, then the above information is superseded and the behavior of this + * class in loading protocol handlers is dependent on that factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URLStreamHandler + */ +public final class URL implements Serializable +{ + private static final String DEFAULT_SEARCH_PATH = + "gnu.java.net.protocol|gnu.inet"; + + // Cached System ClassLoader + private static ClassLoader systemClassLoader; + + /** + * The name of the protocol for this URL. + * The protocol is always stored in lower case. + */ + private String protocol; + + /** + * The "authority" portion of the URL. + */ + private String authority; + + /** + * The hostname or IP address of this protocol. + * This includes a possible user. For example <code>joe@some.host.net</code>. + */ + private String host; + + /** + * The user information necessary to establish the connection. + */ + private String userInfo; + + /** + * The port number of this protocol or -1 if the port number used is + * the default for this protocol. + */ + private int port = -1; // Initialize for constructor using context. + + /** + * The "file" portion of the URL. It is defined as <code>path[?query]</code>. + */ + private String file; + + /** + * The anchor portion of the URL. + */ + private String ref; + + /** + * This is the hashCode for this URL + */ + private int hashCode; + + /** + * The protocol handler in use for this URL + */ + transient URLStreamHandler ph; + + /** + * If an application installs its own protocol handler factory, this is + * where we keep track of it. + */ + private static URLStreamHandlerFactory factory; + private static final long serialVersionUID = -7627629688361524110L; + + /** + * This a table where we cache protocol handlers to avoid the overhead + * of looking them up each time. + */ + private static HashMap<String, URLStreamHandler> ph_cache + = new HashMap<String, URLStreamHandler>(); + + /** + * Whether or not to cache protocol handlers. + */ + private static boolean cache_handlers; + + static + { + String s = SystemProperties.getProperty("gnu.java.net.nocache_protocol_handlers"); + + if (s == null) + cache_handlers = true; + else + cache_handlers = false; + } + + /** + * Constructs a URL and loads a protocol handler for the values passed as + * arguments. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's + * default port + * @param file The "file" portion of the URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, int port, String file) + throws MalformedURLException + { + this(protocol, host, port, file, null); + } + + /** + * Constructs a URL and loads a protocol handler for the values passed in + * as arugments. Uses the default port for the protocol. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address for this URL + * @param file The "file" portion of this URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, String file) + throws MalformedURLException + { + this(protocol, host, -1, file, null); + } + + /** + * This method initializes a new instance of <code>URL</code> with the + * specified protocol, host, port, and file. Additionally, this method + * allows the caller to specify a protocol handler to use instead of + * the default. If this handler is specified, the caller must have + * the "specifyStreamHandler" permission (see <code>NetPermission</code>) + * or a <code>SecurityException</code> will be thrown. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's default + * port + * @param file The "file" portion of the URL. + * @param ph The protocol handler to use with this URL. + * + * @exception MalformedURLException If no protocol handler can be loaded + * for the specified protocol. + * @exception SecurityException If the <code>SecurityManager</code> exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(String protocol, String host, int port, String file, + URLStreamHandler ph) throws MalformedURLException + { + if (protocol == null) + throw new MalformedURLException("null protocol"); + protocol = protocol.toLowerCase(); + this.protocol = protocol; + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + this.host = host; + this.port = port; + this.authority = (host != null) ? host : ""; + if (port >= 0 && host != null) + this.authority += ":" + port; + + int hashAt = file.indexOf('#'); + if (hashAt < 0) + { + this.file = file; + this.ref = null; + } + else + { + this.file = file.substring(0, hashAt); + this.ref = file.substring(hashAt + 1); + } + hashCode = hashCode(); // Used for serialization. + } + + /** + * Initializes a URL from a complete string specification such as + * "http://www.urbanophile.com/arenn/". First the protocol name is parsed + * out of the string. Then a handler is located for that protocol and + * the parseURL() method of that protocol handler is used to parse the + * remaining fields. + * + * @param spec The complete String representation of a URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + */ + public URL(String spec) throws MalformedURLException + { + this((URL) null, spec != null ? spec : "", (URLStreamHandler) null, + false); + } + + /** + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any + * fields not present the URL are inheritied from the context URL. + * This allows relative URL's to be easily constructed. If the + * context argument is null, then a complete URL must be specified + * in the URL string. If the protocol parsed out of the URL is + * different from the context URL's protocol, then then URL String + * is also expected to be a complete URL. + * + * @param context The context on which to parse the specification + * @param spec The string to parse an URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * for the URL cannot be parsed + */ + public URL(URL context, String spec) throws MalformedURLException + { + this(context, spec, + (context == null) ? (URLStreamHandler) null : context.ph, + false); + } + + /** + * Creates an URL from given arguments + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any fields + * not present the URL are inheritied from the context URL. This allows + * relative URL's to be easily constructed. If the context argument is + * null, then a complete URL must be specified in the URL string. + * If the protocol parsed out of the URL is different + * from the context URL's protocol, then then URL String is also + * expected to be a complete URL. + * <p> + * Additionally, this method allows the caller to specify a protocol handler + * to use instead of the default. If this handler is specified, the caller + * must have the "specifyStreamHandler" permission + * (see <code>NetPermission</code>) or a <code>SecurityException</code> + * will be thrown. + * + * @param context The context in which to parse the specification + * @param spec The string to parse as an URL + * @param ph The stream handler for the URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + * @exception SecurityException If the <code>SecurityManager</code> exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(URL context, String spec, URLStreamHandler ph) + throws MalformedURLException + { + this(context, spec, ph, true); + } + + /** + * Private constructor called by all other constructors taking + * a context and spec. + * + * @param context The context in which to parse the specification + * @param spec The string to parse as an URL + * @param ph The stream handler for the URL + * @param phFromUser Whether or not the user supplied the URLStreamHandler + * + */ + private URL(URL context, String spec, URLStreamHandler ph, + boolean phFromUser) + throws MalformedURLException + { + /* A protocol is defined by the doc as the substring before a ':' + * as long as the ':' occurs before any '/'. + * + * If context is null, then spec must be an absolute URL. + * + * The relative URL need not specify all the components of a URL. + * If the protocol, host name, or port number is missing, the value + * is inherited from the context. A bare file component is appended + * to the context's file. The optional anchor is not inherited. + */ + + // If this is an absolute URL, then ignore context completely. + // An absolute URL must have chars prior to "://" but cannot have a colon + // right after the "://". The second colon is for an optional port value + // and implies that the host from the context is used if available. + int colon; + int slash = spec.indexOf('/'); + if ((colon = spec.indexOf("://", 1)) > 0 + && ((colon < slash || slash < 0)) + && ! spec.regionMatches(colon, "://:", 0, 4)) + { + context = null; + if (! phFromUser) + ph = null; + } + + boolean protocolSpecified = false; + + if ((colon = spec.indexOf(':')) > 0 + && (colon < slash || slash < 0)) + { + // Protocol may have been specified in spec string. + protocolSpecified = true; + protocol = spec.substring(0, colon).toLowerCase(); + if (context != null) + { + if (context.protocol.equals(protocol)) + { + // The 1.2 doc specifically says these are copied to the new URL. + host = context.host; + port = context.port; + userInfo = context.userInfo; + authority = context.authority; + } + else + { + // There was a colon in the spec. Check to see if + // what precedes it is a valid protocol. If it was + // not, assume that it is relative to the context. + URLStreamHandler specPh = getURLStreamHandler(protocol.trim()); + if (null == specPh) + protocolSpecified = false; + } + } + } + + if (!protocolSpecified) + { + if (context != null) + { + // Protocol NOT specified in spec string. + // Use context fields (except ref) as a foundation for relative URLs. + colon = -1; + protocol = context.protocol; + host = context.host; + port = context.port; + userInfo = context.userInfo; + if (spec.indexOf(":/", 1) < 0) + { + file = context.file; + if (file == null || file.length() == 0) + file = "/"; + } + authority = context.authority; + } + else // Protocol NOT specified in spec. and no context available. + throw new MalformedURLException("Absolute URL required with null" + + " context: " + spec); + } + + protocol = protocol.trim(); + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null && phFromUser) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + // JDK 1.2 doc for parseURL specifically states that any '#' ref + // is to be excluded by passing the 'limit' as the indexOf the '#' + // if one exists, otherwise pass the end of the string. + int hashAt = spec.indexOf('#', colon + 1); + + try + { + this.ph.parseURL(this, spec, colon + 1, + hashAt < 0 ? spec.length() : hashAt); + } + catch (URLParseError e) + { + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } + catch (RuntimeException e) + { + // This isn't documented, but the JDK also catches + // RuntimeExceptions here. + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } + + if (hashAt >= 0) + ref = spec.substring(hashAt + 1); + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Test another URL for equality with this one. This will be true only if + * the argument is non-null and all of the fields in the URL's match + * exactly (ie, protocol, host, port, file, and ref). Overrides + * Object.equals(), implemented by calling the equals method of the handler. + * + * @param obj The URL to compare with + * + * @return true if the URL is equal, false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof URL)) + return false; + + return ph.equals(this, (URL) obj); + } + + /** + * Returns the contents of this URL as an object by first opening a + * connection, then calling the getContent() method against the connection + * + * @return A content object for this URL + * @exception IOException If opening the connection or getting the + * content fails. + * + * @since 1.3 + */ + public Object getContent() throws IOException + { + return openConnection().getContent(); + } + + /** + * Gets the contents of this URL + * + * @param classes The allow classes for the content object. + * + * @return a context object for this URL. + * + * @exception IOException If an error occurs + */ + public Object getContent(Class[] classes) throws IOException + { + return openConnection().getContent(classes); + } + + /** + * Returns the file portion of the URL. + * Defined as <code>path[?query]</code>. + * Returns the empty string if there is no file portion. + * + * @return The filename specified in this URL, or an empty string if empty. + */ + public String getFile() + { + return file == null ? "" : file; + } + + /** + * Returns the path of the URL. This is the part of the file before any '?' + * character. + * + * @return The path specified in this URL, or null if empty. + * + * @since 1.3 + */ + public String getPath() + { + // The spec says we need to return an empty string, but some + // applications depends on receiving null when the path is empty. + if (file == null) + return null; + int quest = file.indexOf('?'); + return quest < 0 ? getFile() : file.substring(0, quest); + } + + /** + * Returns the authority of the URL + * + * @return The authority specified in this URL. + * + * @since 1.3 + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the host of the URL + * + * @return The host specified in this URL. + */ + public String getHost() + { + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? host : host.substring(at + 1, host.length()); + } + + /** + * Returns the port number of this URL or -1 if the default port number is + * being used. + * + * @return The port number + * + * @see #getDefaultPort() + */ + public int getPort() + { + return port; + } + + /** + * Returns the default port of the URL. If the StreamHandler for the URL + * protocol does not define a default port it returns -1. + * + * @return The default port of the current protocol. + */ + public int getDefaultPort() + { + return ph.getDefaultPort(); + } + + /** + * Returns the protocol of the URL + * + * @return The specified protocol. + */ + public String getProtocol() + { + return protocol; + } + + /** + * Returns the ref (sometimes called the "# reference" or "anchor") portion + * of the URL. + * + * @return The ref + */ + public String getRef() + { + return ref; + } + + /** + * Returns the user information of the URL. This is the part of the host + * name before the '@'. + * + * @return the user at a particular host or null when no user defined. + */ + public String getUserInfo() + { + if (userInfo != null) + return userInfo; + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? null : host.substring(0, at); + } + + /** + * Returns the query of the URL. This is the part of the file before the + * '?'. + * + * @return the query part of the file, or null when there is no query part. + */ + public String getQuery() + { + int quest = (file == null) ? -1 : file.indexOf('?'); + return quest < 0 ? null : file.substring(quest + 1, file.length()); + } + + /** + * Returns a hashcode computed by the URLStreamHandler of this URL + * + * @return The hashcode for this URL. + */ + public int hashCode() + { + if (hashCode != 0) + return hashCode; // Use cached value if available. + else + return ph.hashCode(this); + } + + /** + * Returns a URLConnection object that represents a connection to the remote + * object referred to by the URL. The URLConnection is created by calling the + * openConnection() method of the protocol handler + * + * @return A URLConnection for this URL + * + * @exception IOException If an error occurs + */ + public URLConnection openConnection() throws IOException + { + return ph.openConnection(this); + } + + /** + * Opens a connection to this URL and returns an InputStream for reading + * from that connection + * + * @return An <code>InputStream</code> for this URL. + * + * @exception IOException If an error occurs + */ + public InputStream openStream() throws IOException + { + return openConnection().getInputStream(); + } + + /** + * Tests whether or not another URL refers to the same "file" as this one. + * This will be true if and only if the passed object is not null, is a + * URL, and matches all fields but the ref (ie, protocol, host, port, + * and file); + * + * @param url The URL object to test with + * + * @return true if URL matches this URL's file, false otherwise + */ + public boolean sameFile(URL url) + { + return ph.sameFile(this, url); + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. This might be called + * by the <code>parseURL()</code> method in that class. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL + * @param host The hostname or IP address for this URL + * @param port The port number of this URL + * @param file The "file" portion of this URL. + * @param ref The anchor portion of this URL. + */ + protected void set(String protocol, String host, int port, String file, + String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.authority = ""; + this.port = port; + this.host = host; + this.file = file; + this.ref = ref; + + if (host != null) + this.authority += host; + if (port >= 0) + this.authority += ":" + port; + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL. + * @param host The hostname or IP address for this URL. + * @param port The port number of this URL. + * @param authority The authority of this URL. + * @param userInfo The user and password (if needed) of this URL. + * @param path The "path" portion of this URL. + * @param query The query of this URL. + * @param ref The anchor portion of this URL. + * + * @since 1.3 + */ + protected void set(String protocol, String host, int port, String authority, + String userInfo, String path, String query, String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.host = host; + this.userInfo = userInfo; + this.port = port; + this.authority = authority; + if (query == null) + this.file = path; + else + this.file = path + "?" + query; + this.ref = ref; + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the URLStreamHandlerFactory for this class. This factory is + * responsible for returning the appropriate protocol handler for + * a given URL. + * + * @param fac The URLStreamHandlerFactory class to use + * + * @exception Error If the factory is alread set. + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) + { + if (factory != null) + throw new Error("URLStreamHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + factory = fac; + } + + /** + * Returns a String representing this URL. The String returned is + * created by calling the protocol handler's toExternalForm() method. + * + * @return A string for this URL + */ + public String toExternalForm() + { + // Identical to toString(). + return ph.toExternalForm(this); + } + + /** + * Returns a String representing this URL. Identical to toExternalForm(). + * The value returned is created by the protocol handler's + * toExternalForm method. Overrides Object.toString() + * + * @return A string for this URL + */ + public String toString() + { + // Identical to toExternalForm(). + return ph.toExternalForm(this); + } + + /** + * This internal method is used in two different constructors to load + * a protocol handler for this URL. + * + * @param protocol The protocol to load a handler for + * + * @return A URLStreamHandler for this protocol, or null when not found. + */ + private static synchronized URLStreamHandler getURLStreamHandler(String protocol) + { + URLStreamHandler ph = null; + + // First, see if a protocol handler is in our cache. + if (cache_handlers) + { + if ((ph = ph_cache.get(protocol)) != null) + return ph; + } + + // If a non-default factory has been set, use it to find the protocol. + if (factory != null) + { + ph = factory.createURLStreamHandler(protocol); + } + + // Non-default factory may have returned null or a factory wasn't set. + // Use the default search algorithm to find a handler for this protocol. + if (ph == null) + { + // Get the list of packages to check and append our default handler + // to it, along with the JDK specified default as a last resort. + // Except in very unusual environments the JDK specified one shouldn't + // ever be needed (or available). + String ph_search_path = + SystemProperties.getProperty("java.protocol.handler.pkgs"); + + // Tack our default package on at the ends. + if (ph_search_path != null) + ph_search_path += "|" + DEFAULT_SEARCH_PATH; + else + ph_search_path = DEFAULT_SEARCH_PATH; + + // Finally loop through our search path looking for a match. + StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|"); + + // Cache the systemClassLoader + if (systemClassLoader == null) + { + systemClassLoader = AccessController.doPrivileged + (new PrivilegedAction<ClassLoader>() { + public ClassLoader run() + { + return ClassLoader.getSystemClassLoader(); + } + }); + } + + do + { + try + { + // Try to get a class from the system/application + // classloader, initialize it, make an instance + // and try to cast it to a URLStreamHandler. + String clsName = + (pkgPrefix.nextToken() + "." + protocol + ".Handler"); + Class c = Class.forName(clsName, true, systemClassLoader); + ph = (URLStreamHandler) c.newInstance(); + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Ignored. + } + } + while (ph == null && pkgPrefix.hasMoreTokens()); + } + + // Update the hashtable with the new protocol handler. + if (ph != null && cache_handlers) + ph_cache.put(protocol, ph); + else + ph = null; + + return ph; + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + this.ph = getURLStreamHandler(protocol); + if (this.ph == null) + throw new IOException("Handler for protocol " + protocol + " not found"); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + } + + /** + * Returns the equivalent <code>URI</code> object for this <code>URL</code>. + * This is the same as calling <code>new URI(this.toString())</code>. + * RFC2396-compliant URLs are guaranteed a successful conversion to + * a <code>URI</code> instance. However, there are some values which + * form valid URLs, but which do not also form RFC2396-compliant URIs. + * + * @throws URISyntaxException if this URL is not RFC2396-compliant, + * and thus can not be successfully converted to a URI. + */ + public URI toURI() + throws URISyntaxException + { + return new URI(toString()); + } + +} diff --git a/libjava/classpath/java/net/URLClassLoader.java b/libjava/classpath/java/net/URLClassLoader.java new file mode 100644 index 000000000..418ee77f3 --- /dev/null +++ b/libjava/classpath/java/net/URLClassLoader.java @@ -0,0 +1,860 @@ +/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.net.loader.FileURLLoader; +import gnu.java.net.loader.JarURLLoader; +import gnu.java.net.loader.RemoteURLLoader; +import gnu.java.net.loader.Resource; +import gnu.java.net.loader.URLLoader; +import gnu.java.net.loader.URLStreamHandlerCache; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.PrivilegedAction; +import java.security.SecureClassLoader; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + + +/** + * A secure class loader that can load classes and resources from + * multiple locations. Given an array of <code>URL</code>s this class + * loader will retrieve classes and resources by fetching them from + * possible remote locations. Each <code>URL</code> is searched in + * order in which it was added. If the file portion of the + * <code>URL</code> ends with a '/' character then it is interpreted + * as a base directory, otherwise it is interpreted as a jar file from + * which the classes/resources are resolved. + * + * <p>New instances can be created by two static + * <code>newInstance()</code> methods or by three public + * contructors. Both ways give the option to supply an initial array + * of <code>URL</code>s and (optionally) a parent classloader (that is + * different from the standard system class loader).</p> + * + * <p>Normally creating a <code>URLClassLoader</code> throws a + * <code>SecurityException</code> if a <code>SecurityManager</code> is + * installed and the <code>checkCreateClassLoader()</code> method does + * not return true. But the <code>newInstance()</code> methods may be + * used by any code as long as it has permission to acces the given + * <code>URL</code>s. <code>URLClassLoaders</code> created by the + * <code>newInstance()</code> methods also explicitly call the + * <code>checkPackageAccess()</code> method of + * <code>SecurityManager</code> if one is installed before trying to + * load a class. Note that only subclasses of + * <code>URLClassLoader</code> can add new URLs after the + * URLClassLoader had been created. But it is always possible to get + * an array of all URLs that the class loader uses to resolve classes + * and resources by way of the <code>getURLs()</code> method.</p> + * + * <p>Open issues: + * <ul> + * + * <li>Should the URLClassLoader actually add the locations found in + * the manifest or is this the responsibility of some other + * loader/(sub)class? (see <a + * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html"> + * Extension Mechanism Architecture - Bundles Extensions</a>)</li> + * + * <li>How does <code>definePackage()</code> and sealing work + * precisely?</li> + * + * <li>We save and use the security context (when a created by + * <code>newInstance()</code> but do we have to use it in more + * places?</li> + * + * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li> + * + * </ul> + * </p> + * + * @since 1.2 + * + * @author Mark Wielaard (mark@klomp.org) + * @author Wu Gansha (gansha.wu@intel.com) + */ +public class URLClassLoader extends SecureClassLoader +{ + // Class Variables + + /** + * A cache to store mappings between handler factory and its + * private protocol handler cache (also a HashMap), so we can avoid + * creating handlers each time the same protocol comes. + */ + private static URLStreamHandlerCache factoryCache + = new URLStreamHandlerCache(); + + /** + * The prefix for URL loaders. + */ + private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_"; + + // Instance variables + + /** Locations to load classes from */ + private final Vector<URL> urls = new Vector<URL>(); + + /** + * Store pre-parsed information for each url into this vector: each + * element is a URL loader. A jar file has its own class-path + * attribute which adds to the URLs that will be searched, but this + * does not add to the list of urls. + */ + private final Vector<URLLoader> urlinfos = new Vector<URLLoader>(); + + /** Factory used to get the protocol handlers of the URLs */ + private final URLStreamHandlerFactory factory; + + /** + * The security context when created from <code>newInstance()</code> + * or null when created through a normal constructor or when no + * <code>SecurityManager</code> was installed. + */ + private final AccessControlContext securityContext; + + // Helper classes + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (<code>SecureClassLoader</code>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the default parent ClassLoader). + * + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls) throws SecurityException + { + super(); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + /** + * Creates a <code>URLClassLoader</code> that gets classes from the supplied + * <code>URL</code>s. + * To determine if this classloader may be created the constructor of + * the super class (<code>SecureClassLoader</code>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent) + throws SecurityException + { + super(parent); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + // Package-private to avoid a trampoline constructor. + /** + * Package-private constructor used by the static + * <code>newInstance(URL[])</code> method. Creates an + * <code>URLClassLoader</code> with the given parent but without any + * <code>URL</code>s yet. This is used to bypass the normal security + * check for creating classloaders, but remembers the security + * context which will be used when defining classes. The + * <code>URL</code>s to load from must be added by the + * <code>newInstance()</code> method in the security context of the + * caller. + * + * @param securityContext the security context of the unprivileged code. + */ + URLClassLoader(ClassLoader parent, AccessControlContext securityContext) + { + super(parent); + this.factory = null; + this.securityContext = securityContext; + } + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (<CODE>SecureClassLoader</CODE>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the + * protocol handlers of the supplied URLs. + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @param factory Used to get the protocol handler for the URLs. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent, + URLStreamHandlerFactory factory) + throws SecurityException + { + super(parent); + this.securityContext = null; + this.factory = factory; + // If this factory is not yet in factoryCache, add it. + factoryCache.add(factory); + addURLs(urls); + } + + // Methods + + /** + * Adds a new location to the end of the internal URL store. + * @param newUrl the location to add + */ + protected void addURL(URL newUrl) + { + urls.add(newUrl); + addURLImpl(newUrl); + } + + private void addURLImpl(URL newUrl) + { + synchronized (this) + { + if (newUrl == null) + return; // Silently ignore... + + // Reset the toString() value. + thisString = null; + + // Create a loader for this URL. + URLLoader loader = null; + String file = newUrl.getFile(); + String protocol = newUrl.getProtocol(); + + // If we have a file: URL, we want to make it absolute + // here, before we decide whether it is really a jar. + URL absoluteURL; + if ("file".equals (protocol)) + { + File dir = new File(file); + try + { + absoluteURL = dir.getCanonicalFile().toURL(); + } + catch (IOException ignore) + { + try + { + absoluteURL = dir.getAbsoluteFile().toURL(); + } + catch (MalformedURLException _) + { + // This really should not happen. + absoluteURL = newUrl; + } + } + } + else + { + // This doesn't hurt, and it simplifies the logic a + // little. + absoluteURL = newUrl; + } + + // First see if we can find a handler with the correct name. + try + { + Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol); + Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class, + URLStreamHandlerCache.class, + URLStreamHandlerFactory.class, + URL.class, + URL.class }; + Constructor k = handler.getDeclaredConstructor(argTypes); + loader + = (URLLoader) k.newInstance(new Object[] { this, + factoryCache, + factory, + newUrl, + absoluteURL }); + } + catch (ClassNotFoundException ignore) + { + // Fall through. + } + catch (NoSuchMethodException nsme) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't find URLLoader constructor"); + vme.initCause(nsme); + throw vme; + } + catch (InstantiationException inste) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't instantiate URLLoader"); + vme.initCause(inste); + throw vme; + } + catch (InvocationTargetException ite) + { + // Programming error in the class library. + InternalError vme + = new InternalError("error instantiating URLLoader"); + vme.initCause(ite); + throw vme; + } + catch (IllegalAccessException illae) + { + // Programming error in the class library. + InternalError vme + = new InternalError("invalid access to URLLoader"); + vme.initCause(illae); + throw vme; + } + + if (loader == null) + { + // If it is not a directory, use the jar loader. + if (! (file.endsWith("/") || file.endsWith(File.separator))) + loader = new JarURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else if ("file".equals(protocol)) + loader = new FileURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else + loader = new RemoteURLLoader(this, factoryCache, factory, + newUrl); + } + + urlinfos.add(loader); + ArrayList<URLLoader> extra = loader.getClassPath(); + if (extra != null) + urlinfos.addAll(extra); + } + } + + /** + * Adds an array of new locations to the end of the internal URL + * store. Called from the the constructors. Should not call to the + * protected addURL() method since that can be overridden and + * subclasses are not yet in a good state at this point. + * jboss 4.0.3 for example depends on this. + * + * @param newUrls the locations to add + */ + private void addURLs(URL[] newUrls) + { + for (int i = 0; i < newUrls.length; i++) + { + urls.add(newUrls[i]); + addURLImpl(newUrls[i]); + } + } + + /** + * Look in both Attributes for a given value. The first Attributes + * object, if not null, has precedence. + */ + private String getAttributeValue(Attributes.Name name, Attributes first, + Attributes second) + { + String result = null; + if (first != null) + result = first.getValue(name); + if (result == null) + result = second.getValue(name); + return result; + } + + /** + * Defines a Package based on the given name and the supplied manifest + * information. The manifest indicates the title, version and + * vendor information of the specification and implementation and whether the + * package is sealed. If the Manifest indicates that the package is sealed + * then the Package will be sealed with respect to the supplied URL. + * + * @param name The name of the package + * @param manifest The manifest describing the specification, + * implementation and sealing details of the package + * @param url the code source url to seal the package + * @return the defined Package + * @throws IllegalArgumentException If this package name already exists + * in this class loader + */ + protected Package definePackage(String name, Manifest manifest, URL url) + throws IllegalArgumentException + { + // Compute the name of the package as it may appear in the + // Manifest. + CPStringBuilder xform = new CPStringBuilder(name); + for (int i = xform.length () - 1; i >= 0; --i) + if (xform.charAt(i) == '.') + xform.setCharAt(i, '/'); + xform.append('/'); + String xformName = xform.toString(); + + Attributes entryAttr = manifest.getAttributes(xformName); + Attributes attr = manifest.getMainAttributes(); + + String specTitle + = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE, + entryAttr, attr); + String specVersion + = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION, + entryAttr, attr); + String specVendor + = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR, + entryAttr, attr); + String implTitle + = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE, + entryAttr, attr); + String implVersion + = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION, + entryAttr, attr); + String implVendor + = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR, + entryAttr, attr); + + // Look if the Manifest indicates that this package is sealed + // XXX - most likely not completely correct! + // Shouldn't we also check the sealed attribute of the complete jar? + // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled + // But how do we get that jar manifest here? + String sealed = attr.getValue(Attributes.Name.SEALED); + if ("false".equals(sealed)) + // make sure that the URL is null so the package is not sealed + url = null; + + return definePackage(name, + specTitle, specVendor, specVersion, + implTitle, implVendor, implVersion, + url); + } + + /** + * Finds (the first) class by name from one of the locations. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param className the classname to find + * @exception ClassNotFoundException when the class could not be found or + * loaded + * @return a Class object representing the found class + */ + protected Class<?> findClass(final String className) + throws ClassNotFoundException + { + // Just try to find the resource by the (almost) same name + String resourceName = className.replace('.', '/') + ".class"; + int max = urlinfos.size(); + Resource resource = null; + for (int i = 0; i < max && resource == null; i++) + { + URLLoader loader = (URLLoader)urlinfos.elementAt(i); + if (loader == null) + continue; + + Class k = loader.getClass(className); + if (k != null) + return k; + + resource = loader.getResource(resourceName); + } + if (resource == null) + throw new ClassNotFoundException(className + " not found in " + this); + + // Try to read the class data, create the CodeSource, Package and + // construct the class (and watch out for those nasty IOExceptions) + try + { + byte[] data; + InputStream in = resource.getInputStream(); + try + { + int length = resource.getLength(); + if (length != -1) + { + // We know the length of the data. + // Just try to read it in all at once + data = new byte[length]; + int pos = 0; + while (length - pos > 0) + { + int len = in.read(data, pos, length - pos); + if (len == -1) + throw new EOFException("Not enough data reading from: " + + in); + pos += len; + } + } + else + { + // We don't know the data length. + // Have to read it in chunks. + ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + byte[] b = new byte[4096]; + int l = 0; + while (l != -1) + { + l = in.read(b); + if (l != -1) + out.write(b, 0, l); + } + data = out.toByteArray(); + } + } + finally + { + in.close(); + } + final byte[] classData = data; + + // Now get the CodeSource + final CodeSource source = resource.getCodeSource(); + + // Find out package name + String packageName = null; + int lastDot = className.lastIndexOf('.'); + if (lastDot != -1) + packageName = className.substring(0, lastDot); + + if (packageName != null && getPackage(packageName) == null) + { + // define the package + Manifest manifest = resource.getLoader().getManifest(); + if (manifest == null) + definePackage(packageName, null, null, null, null, null, null, + null); + else + definePackage(packageName, manifest, + resource.getLoader().getBaseURL()); + } + + // And finally construct the class! + SecurityManager sm = System.getSecurityManager(); + Class result = null; + if (sm != null && securityContext != null) + { + result = AccessController.doPrivileged + (new PrivilegedAction<Class>() + { + public Class run() + { + return defineClass(className, classData, + 0, classData.length, + source); + } + }, securityContext); + } + else + result = defineClass(className, classData, 0, classData.length, source); + + // Avoid NullPointerExceptions. + Certificate[] resourceCertificates = resource.getCertificates(); + if(resourceCertificates != null) + super.setSigners(result, resourceCertificates); + + return result; + } + catch (IOException ioe) + { + throw new ClassNotFoundException(className + " not found in " + this, ioe); + } + } + + // Cached String representation of this URLClassLoader + private String thisString; + + /** + * Returns a String representation of this URLClassLoader giving the + * actual Class name, the URLs that are searched and the parent + * ClassLoader. + */ + public String toString() + { + synchronized (this) + { + if (thisString == null) + { + CPStringBuilder sb = new CPStringBuilder(); + sb.append(this.getClass().getName()); + sb.append("{urls=[" ); + URL[] thisURLs = getURLs(); + for (int i = 0; i < thisURLs.length; i++) + { + sb.append(thisURLs[i]); + if (i < thisURLs.length - 1) + sb.append(','); + } + sb.append(']'); + sb.append(", parent="); + sb.append(getParent()); + sb.append('}'); + thisString = sb.toString(); + } + return thisString; + } + } + + /** + * Finds the first occurrence of a resource that can be found. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param resourceName the resource name to look for + * @return the URLResource for the resource if found, null otherwise + */ + private Resource findURLResource(String resourceName) + { + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + if (loader == null) + continue; + + Resource resource = loader.getResource(resourceName); + if (resource != null) + return resource; + } + return null; + } + + /** + * Finds the first occurrence of a resource that can be found. + * + * @param resourceName the resource name to look for + * @return the URL if found, null otherwise + */ + public URL findResource(String resourceName) + { + Resource resource = findURLResource(resourceName); + if (resource != null) + return resource.getURL(); + + // Resource not found + return null; + } + + /** + * Finds all the resources with a particular name from all the locations. + * + * @param resourceName the name of the resource to lookup + * @return a (possible empty) enumeration of URLs where the resource can be + * found + * @exception IOException when an error occurs accessing one of the + * locations + */ + public Enumeration<URL> findResources(String resourceName) + throws IOException + { + Vector<URL> resources = new Vector<URL>(); + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + Resource resource = loader.getResource(resourceName); + if (resource != null) + resources.add(resource.getURL()); + } + return resources.elements(); + } + + /** + * Returns the permissions needed to access a particular code + * source. These permissions includes those returned by + * <code>SecureClassLoader.getPermissions()</code> and the actual + * permissions to access the objects referenced by the URL of the + * code source. The extra permissions added depend on the protocol + * and file portion of the URL in the code source. If the URL has + * the "file" protocol ends with a '/' character then it must be a + * directory and a file Permission to read everything in that + * directory and all subdirectories is added. If the URL had the + * "file" protocol and doesn't end with a '/' character then it must + * be a normal file and a file permission to read that file is + * added. If the <code>URL</code> has any other protocol then a + * socket permission to connect and accept connections from the host + * portion of the URL is added. + * + * @param source The codesource that needs the permissions to be accessed + * @return the collection of permissions needed to access the code resource + * @see java.security.SecureClassLoader#getPermissions(CodeSource) + */ + protected PermissionCollection getPermissions(CodeSource source) + { + // XXX - This implementation does exactly as the Javadoc describes. + // But maybe we should/could use URLConnection.getPermissions()? + // First get the permissions that would normally be granted + PermissionCollection permissions = super.getPermissions(source); + + // Now add any extra permissions depending on the URL location. + URL url = source.getLocation(); + String protocol = url.getProtocol(); + if (protocol.equals("file")) + { + String file = url.getFile(); + + // If the file end in / it must be an directory. + if (file.endsWith("/") || file.endsWith(File.separator)) + { + // Grant permission to read everything in that directory and + // all subdirectories. + permissions.add(new FilePermission(file + "-", "read")); + } + else + { + // It is a 'normal' file. + // Grant permission to access that file. + permissions.add(new FilePermission(file, "read")); + } + } + else + { + // Grant permission to connect to and accept connections from host + String host = url.getHost(); + if (host != null) + permissions.add(new SocketPermission(host, "connect,accept")); + } + + return permissions; + } + + /** + * Returns all the locations that this class loader currently uses the + * resolve classes and resource. This includes both the initially supplied + * URLs as any URLs added later by the loader. + * @return All the currently used URLs + */ + public URL[] getURLs() + { + return (URL[]) urls.toArray(new URL[urls.size()]); + } + + /** + * Creates a new instance of a <code>URLClassLoader</code> that gets + * classes from the supplied <code>URL</code>s. This class loader + * will have as parent the standard system class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given <code>URL</code>s + */ + public static URLClassLoader newInstance(URL[] urls) + throws SecurityException + { + return newInstance(urls, null); + } + + /** + * Creates a new instance of a <code>URLClassLoader</code> that gets + * classes from the supplied <code>URL</code>s and with the supplied + * loader as parent class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * @param parent the parent class loader + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given <code>URL</code>s + */ + public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) + return new URLClassLoader(urls, parent); + else + { + final Object securityContext = sm.getSecurityContext(); + + // XXX - What to do with anything else then an AccessControlContext? + if (! (securityContext instanceof AccessControlContext)) + throw new SecurityException("securityContext must be AccessControlContext: " + + securityContext); + + URLClassLoader loader = + AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() + { + public URLClassLoader run() + { + return new URLClassLoader(parent, + (AccessControlContext) securityContext); + } + }); + loader.addURLs(urls); + return loader; + } + } +} diff --git a/libjava/classpath/java/net/URLConnection.java b/libjava/classpath/java/net/URLConnection.java new file mode 100644 index 000000000..e9365a3e0 --- /dev/null +++ b/libjava/classpath/java/net/URLConnection.java @@ -0,0 +1,1150 @@ +/* URLConnection.java -- Abstract superclass for reading from URL's + Copyright (C) 1998, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.classpath.SystemProperties; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AllPermission; +import java.security.Permission; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: One guessContentTypeFrom... methods not implemented. + * getContent method assumes content type from response; see comment there. + */ +/** + * This class models a connection that retrieves the information pointed + * to by a URL object. This is typically a connection to a remote node + * on the network, but could be a simple disk read. + * <p> + * A URLConnection object is normally created by calling the openConnection() + * method of a URL object. This method is somewhat misnamed because it does + * not actually open the connection. Instead, it return an unconnected + * instance of this object. The caller then has the opportunity to set + * various connection options prior to calling the actual connect() method. + * <p> + * After the connection has been opened, there are a number of methods in + * this class that access various attributes of the data, typically + * represented by headers sent in advance of the actual data itself. + * <p> + * Also of note are the getInputStream and getContent() methods which allow + * the caller to retrieve the actual data from the connection. Note that + * for some types of connections, writing is also allowed. The setDoOutput() + * method must be called prior to connecing in order to enable this, then + * the getOutputStream method called after the connection in order to + * obtain a stream to write the output to. + * <p> + * The getContent() method is of particular note. This method returns an + * Object that encapsulates the data returned. There is no way do determine + * the type of object that will be returned in advance. This is determined + * by the actual content handlers as described in the description of that + * method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class URLConnection +{ + /** + * This is an object that maps filenames to MIME types. The interface + * to do this is implemented by this class, so just create an empty + * instance and store it here. + */ + private static FileNameMap fileNameMap; + + /** + * This is the ContentHandlerFactory set by the caller, if any + */ + private static ContentHandlerFactory factory; + + /** + * This is the default value that will be used to determine whether or + * not user interaction should be allowed. + */ + private static boolean defaultAllowUserInteraction; + + /** + * This is the default flag indicating whether or not to use caches to + * store the data returned from a server + */ + private static boolean defaultUseCaches = true; + + /** + * Default internal content handler factory. + */ + private static ContentHandlerFactory defaultFactory + = new gnu.java.net.DefaultContentHandlerFactory(); + + /** + * This variable determines whether or not interaction is allowed with + * the user. For example, to prompt for a username and password. + */ + protected boolean allowUserInteraction; + + /** + * Indicates whether or not a connection has been established to the + * destination specified in the URL + */ + protected boolean connected; + + /** + * Indicates whether or not input can be read from this URL + */ + protected boolean doInput = true; + + /** + * Indicates whether or not output can be sent to this URL + */ + protected boolean doOutput; + + /** + * If this flag is set, the protocol is allowed to cache data whenever + * it can (caching is not guaranteed). If it is not set, the protocol + * must a get a fresh copy of the data. + * <p> + * This field is set by the setUseCaches method and returned by the + * getUseCaches method. + * + * Its default value is that determined by the last invocation of + * setDefaultUseCaches + */ + protected boolean useCaches; + + /** + * If this value is non-zero, then the connection will only attempt to + * fetch the document pointed to by the URL if the document has been + * modified more recently than the date set in this variable. That date + * should be specified as the number of seconds since 1/1/1970 GMT. + */ + protected long ifModifiedSince; + + /** + * This is the URL associated with this connection + */ + protected URL url; + + private static SimpleDateFormat[] dateFormats; + private static boolean dateformats_initialized; + + /** + * The connection timeout period. + */ + private int connectTimeout; + + /** + * The read timeout period. + */ + private int readTimeout; + + /* Cached ParsePosition, used when parsing dates. */ + private ParsePosition position; + + /** + * Creates a URL connection to a given URL. A real connection is not made. + * Use <code>connect()</code> to do this. + * + * @param url The Object to create the URL connection to + * + * @see URLConnection#connect() + */ + protected URLConnection(URL url) + { + // Set up all our instance variables + this.url = url; + allowUserInteraction = defaultAllowUserInteraction; + useCaches = defaultUseCaches; + } + + /** + * Establishes the actual connection to the URL associated with this + * connection object + * + * @exception IOException if an error occurs + */ + public abstract void connect() throws IOException; + + /** + * Returns the URL object associated with this connection + * + * @return The URL for this connection. + */ + public URL getURL() + { + return url; + } + + /** + * Returns the connection timeout speed, in milliseconds, or zero if + * the timeout is infinite or not set. + * + * @return The timeout. + * + * @since 1.5 + */ + public int getConnectTimeout() + { + return connectTimeout; + } + + /** + * Set the connection timeout speed, in milliseconds, or zero if the timeout + * is to be considered infinite. Note that in certain socket + * implementations/platforms this method may not have any effect. + * + * Throws an <code>IllegalArgumentException</code> if timeout < 0. + * + * @param timeout the timeout, in milliseconds. + * + * @since 1.5 + */ + public void setConnectTimeout(int timeout) + throws IllegalArgumentException + { + if( timeout < 0 ) + throw new IllegalArgumentException("Timeout must be 0 or positive."); + connectTimeout = timeout; + } + + /** + * Returns the read timeout, in milliseconds, or zero if the timeout + * is infinite or not set. + * + * @return The timeout. + * + * @see #setReadTimeout + * + * @since 1.5 + */ + public int getReadTimeout() + { + return readTimeout; + } + + /** + * Set the read timeout, in milliseconds, or zero if the timeout + * is to be considered infinite. Note that in certain socket + * implementations/platforms this method may not have any effect. + * + * Throws an <code>IllegalArgumentException</code> if timeout < 0. + * + * @param timeout - The timeout, in milliseconds. + * + * @throws IllegalArgumentException if timeout is negative. + * + * @see #getReadTimeout + * + * @since 1.5 + */ + public void setReadTimeout(int timeout) + throws IllegalArgumentException + { + if( timeout < 0 ) + throw new IllegalArgumentException("Timeout must be 0 or positive."); + readTimeout = timeout; + } + + /** + * Returns the value of the content-length header field or -1 if the value + * is not known or not present. + * + * @return The content-length field + */ + public int getContentLength() + { + return getHeaderFieldInt("content-length", -1); + } + + /** + * Returns the the content-type of the data pointed to by the URL. This + * method first tries looking for a content-type header. If that is not + * present, it attempts to use the file name to determine the content's + * MIME type. If that is unsuccessful, the method returns null. The caller + * may then still attempt to determine the MIME type by a call to + * guessContentTypeFromStream() + * + * @return The content MIME type + */ + public String getContentType() + { + return getHeaderField("content-type"); + } + + /** + * Returns the value of the content-encoding field or null if it is not + * known or not present. + * + * @return The content-encoding field + */ + public String getContentEncoding() + { + return getHeaderField("content-encoding"); + } + + /** + * Returns the value of the expires header or 0 if not known or present. + * If populated, the return value is number of seconds since midnight + * on 1/1/1970 GMT. + * + * @return The expiration time. + */ + public long getExpiration() + { + return getHeaderFieldDate("expires", 0L); + } + + /** + * Returns the date of the document pointed to by the URL as reported in + * the date field of the header or 0 if the value is not present or not + * known. If populated, the return value is number of seconds since + * midnight on 1/1/1970 GMT. + * + * @return The document date + */ + public long getDate() + { + return getHeaderFieldDate("date", 0L); + } + + /** + * Returns the value of the last-modified header field or 0 if not known known + * or not present. If populated, the return value is the number of seconds + * since midnight on 1/1/1970. + * + * @return The last modified time + */ + public long getLastModified() + { + return getHeaderFieldDate("last-modified", 0L); + } + + /** + * Return a String representing the header value at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * {@link #getHeaderField(int)} method allows access to the corresponding + * key for this header field + * + * @param index The index into the header field list to retrieve the value for + * + * @return The header value or null if index is past the end of the headers + */ + public String getHeaderField(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns a String representing the value of the header field having + * the named key. Returns null if the header field does not exist. + * + * @param name The key of the header field + * + * @return The value of the header field as a String + */ + public String getHeaderField(String name) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns an unmodifiable Map containing all sent header fields. + * + * @return The map of header fields. The map consists of String keys with + * an unmodifiable List of String objects as value. + * + * @since 1.4 + */ + public Map<String,List<String>> getHeaderFields() + { + // Subclasses for specific protocols override this. + return Collections.emptyMap(); + } + + /** + * Returns the value of the named header field as an int. If the field + * is not present or cannot be parsed as an integer, the default value + * will be returned. + * + * @param name The header field key to lookup + * @param defaultValue The defaule value if the header field is not found + * or can't be parsed. + * + * @return The value of the header field or the default value if the field + * is missing or malformed + */ + public int getHeaderFieldInt(String name, int defaultValue) + { + String value = getHeaderField(name); + + if (value == null) + return defaultValue; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + /** + * Returns the value of the named header field as a date. This date will + * be the number of seconds since midnight 1/1/1970 GMT or the default + * value if the field is not present or cannot be converted to a date. + * + * @param name The name of the header field + * @param defaultValue The default date if the header field is not found + * or can't be converted. + * + * @return The date value of the header filed or the default value + * if the field is missing or malformed + */ + public long getHeaderFieldDate(String name, long defaultValue) + { + if (! dateformats_initialized) + initializeDateFormats(); + + if (position == null) + position = new ParsePosition(0); + + long result = defaultValue; + String str = getHeaderField(name); + + if (str != null) + { + for (int i = 0; i < dateFormats.length; i++) + { + SimpleDateFormat df = dateFormats[i]; + position.setIndex(0); + position.setErrorIndex(0); + Date date = df.parse(str, position); + if (date != null) + return date.getTime(); + } + } + + return result; + } + + /** + * Returns a String representing the header key at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * {@link #getHeaderField(int)} method allows access to the corresponding + * value for this tag. + * + * @param index The index into the header field list to retrieve the key for. + * + * @return The header field key or null if index is past the end + * of the headers. + */ + public String getHeaderFieldKey(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * This method returns the content of the document pointed to by the + * URL as an Object. The type of object depends on the MIME type of + * the object and particular content hander loaded. Most text type + * content handlers will return a subclass of + * <code>InputStream</code>. Images usually return a class that + * implements <code>ImageProducer</code>. There is not guarantee + * what type of object will be returned, however. + * + * <p>This class first determines the MIME type of the content, then + * creates a ContentHandler object to process the input. If the + * <code>ContentHandlerFactory</code> is set, then that object is + * called to load a content handler, otherwise a class called + * gnu.java.net.content.<content_type> is tried. If this + * handler does not exist, the method will simple return the + * <code>InputStream</code> returned by + * <code>getInputStream()</code>. Note that the default + * implementation of <code>getInputStream()</code> throws a + * <code>UnknownServiceException</code> so subclasses are encouraged + * to override this method.</p> + * + * @return the content + * + * @exception IOException If an error with the connection occurs. + * @exception UnknownServiceException If the protocol does not support the + * content type at all. + */ + public Object getContent() throws IOException + { + if (!connected) + connect(); + + // FIXME: Doc indicates that other criteria should be applied as + // heuristics to determine the true content type, e.g. see + // guessContentTypeFromName() and guessContentTypeFromStream methods + // as well as FileNameMap class & fileNameMap field & get/set methods. + String type = getContentType(); + ContentHandler ch = getContentHandler(type); + + if (ch != null) + return ch.getContent(this); + + return getInputStream(); + } + + /** + * Retrieves the content of this URLConnection + * + * @param classes The allowed classes for the content + * + * @return the content + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support the + * content type + */ + public Object getContent(Class[] classes) + throws IOException + { + if (! connected) + connect(); + String type = getContentType(); + ContentHandler ch = getContentHandler(type); + if (ch != null) + return ch.getContent(this, classes); + throw new UnknownServiceException("protocol does not support the content type"); + } + + /** + * This method returns a <code>Permission</code> object representing the + * permissions required to access this URL. This method returns + * <code>java.security.AllPermission</code> by default. Subclasses should + * override it to return a more specific permission. For example, an + * HTTP URL should return an instance of <code>SocketPermission</code> + * for the appropriate host and port. + * <p> + * Note that because of items such as HTTP redirects, the permission + * object returned might be different before and after connecting. + * + * @return A Permission object + * + * @exception IOException If the computation of the permission requires + * network or file I/O and an exception occurs while computing it + */ + public Permission getPermission() throws IOException + { + // Subclasses may override this. + return new AllPermission(); + } + + /** + * Returns an InputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An InputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support input + */ + public InputStream getInputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support input."); + } + + /** + * Returns an OutputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An OutputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support output + */ + public OutputStream getOutputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support output."); + } + + /** + * The methods prints the value of this object as a String by calling the + * toString() method of its associated URL. Overrides Object.toString() + * + * @return A String representation of this object + */ + public String toString() + { + return this.getClass().getName() + ":" + url.toString(); + } + + /** + * Sets the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @param input <code>true</code> if input is to be done, + * <code>false</code> otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoInput(boolean input) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doInput = input; + } + + /** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @return true if input is to be done, false otherwise + */ + public boolean getDoInput() + { + return doInput; + } + + /** + * Sets a boolean flag indicating whether or not output will be done + * on this connection. The default value is false, so this method can + * be used to override the default + * + * @param output ture if output is to be done, false otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoOutput(boolean output) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doOutput = output; + } + + /** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. This defaults to false. + * + * @return true if output is to be done, false otherwise + */ + public boolean getDoOutput() + { + return doOutput; + } + + /** + * Sets a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @param allow true if user interaction should be allowed, false otherwise. + * + * @exception IllegalStateException If already connected + */ + public void setAllowUserInteraction(boolean allow) + { + if (connected) + throw new IllegalStateException("Already connected"); + + allowUserInteraction = allow; + } + + /** + * Returns a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @return true if user interaction is allowed, false otherwise + */ + public boolean getAllowUserInteraction() + { + return allowUserInteraction; + } + + /** + * Sets the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @param allow true to allow user interaction, false otherwise + */ + public static void setDefaultAllowUserInteraction(boolean allow) + { + defaultAllowUserInteraction = allow; + } + + /** + * Returns the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @return true if user interaction is allowed, false otherwise + */ + public static boolean getDefaultAllowUserInteraction() + { + return defaultAllowUserInteraction; + } + + /** + * Sets a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @param usecaches The new value + * + * @exception IllegalStateException If already connected + */ + public void setUseCaches(boolean usecaches) + { + if (connected) + throw new IllegalStateException("Already connected"); + + useCaches = usecaches; + } + + /** + * Returns a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @return true if caching should be used if possible, false otherwise + */ + public boolean getUseCaches() + { + return useCaches; + } + + /** + * Sets the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * passed should be 0 if this feature is to be disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise. + * + * @param ifmodifiedsince The new value in milliseconds + * since January 1, 1970 GMT + * + * @exception IllegalStateException If already connected + */ + public void setIfModifiedSince(long ifmodifiedsince) + { + if (connected) + throw new IllegalStateException("Already connected"); + + ifModifiedSince = ifmodifiedsince; + } + + /** + * Returns the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * returned will be 0 if this feature is disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise + * + * @return The ifModifiedSince value + */ + public long getIfModifiedSince() + { + return ifModifiedSince; + } + + /** + * Returns the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @return true if caches will be used, false otherwise + */ + public boolean getDefaultUseCaches() + { + return defaultUseCaches; + } + + /** + * Sets the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @param use true to use caches if possible by default, false otherwise + */ + public void setDefaultUseCaches(boolean use) + { + defaultUseCaches = use; + } + + /** + * Sets the value of the named request property. + * This method does overwrite the value of existing properties with + * the new value. + * + * @param key The name of the property + * @param value The value of the property + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String key) + * @see URLConnection#addRequestProperty(String key, String value) + * + * @since 1.4 + */ + public void setRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support setting + // header fields in the request. + } + + /** + * Adds a new request property by a key/value pair. + * This method does not overwrite existing properties with the same key. + * + * @param key Key of the property to add + * @param value Value of the Property to add + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String) + * @see URLConnection#setRequestProperty(String, String) + * + * @since 1.4 + */ + public void addRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support adding + // header fields in the request. + } + + /** + * Returns the value of the named request property. + * + * @param key The name of the property + * + * @return Value of the property, or <code>null</code> if key is null. + * + * @exception IllegalStateException If already connected + * + * @see URLConnection#setRequestProperty(String, String) + * @see URLConnection#addRequestProperty(String, String) + */ + public String getRequestProperty(String key) + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return null; + } + + /** + * Returns an unmodifiable Map containing the request properties. + * + * @return The map of properties. The map consists of String keys with an + * unmodifiable List of String objects as value. + * + * @exception IllegalStateException If already connected + * + * @since 1.4 + */ + public Map<String,List<String>> getRequestProperties() + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return Collections.emptyMap(); + } + + /** + * Sets the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property name the default is being set for + * @param value The value to set the default to + * + * @deprecated 1.3 The method setRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#setRequestProperty(String, String) + */ + public static void setDefaultRequestProperty(String key, String value) + { + // This method does nothing since JDK 1.3. + } + + /** + * Returns the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property to return the default value of + * + * @return The value of the default property or null if not available + * + * @deprecated 1.3 The method getRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#getRequestProperty(String) + */ + public static String getDefaultRequestProperty(String key) + { + // This method does nothing since JDK 1.3. + return null; + } + + /** + * Sets the ContentHandlerFactory for an application. This can be called + * once and only once. If it is called again, then an Error is thrown. + * Unlike for other set factory methods, this one does not do a security + * check prior to setting the factory. + * + * @param factory The ContentHandlerFactory for this application + * + * @exception Error If the factory has already been defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory) + { + if (URLConnection.factory != null) + throw new Error("ContentHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + URLConnection.factory = factory; + } + + /** + * Returns the MIME type of a file based on the name of the file. This + * works by searching for the file's extension in a list of file extensions + * and returning the MIME type associated with it. If no type is found, + * then a MIME type of "application/octet-stream" will be returned. + * + * @param filename The filename to determine the MIME type for + * + * @return The MIME type String + * + * @specnote public since JDK 1.4 + */ + public static String guessContentTypeFromName(String filename) + { + return getFileNameMap().getContentTypeFor(filename.toLowerCase()); + } + + /** + * Returns the MIME type of a stream based on the first few characters + * at the beginning of the stream. This routine can be used to determine + * the MIME type if a server is believed to be returning an incorrect + * MIME type. This method returns "application/octet-stream" if it + * cannot determine the MIME type. + * <p> + * NOTE: Overriding MIME types sent from the server can be obnoxious + * to user's. See Internet Exploder 4 if you don't believe me. + * + * @param is The InputStream to determine the MIME type from + * + * @return The MIME type + * + * @exception IOException If an error occurs + */ + public static String guessContentTypeFromStream(InputStream is) + throws IOException + { + String result = VMURLConnection.guessContentTypeFromStream(is); + if (result == null) + return "application/octet-stream"; + return result; + } + + /** + * This method returns the <code>FileNameMap</code> object being used + * to decode MIME types by file extension. + * + * @return The <code>FileNameMap</code>. + * + * @since 1.2 + */ + public static synchronized FileNameMap getFileNameMap() + { + // Delayed initialization. + if (fileNameMap == null) + fileNameMap = new MimeTypeMapper(); + + return fileNameMap; + } + + /** + * This method sets the <code>FileNameMap</code> object being used + * to decode MIME types by file extension. + * + * @param map The <code>FileNameMap</code>. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + * + * @since 1.2 + */ + public static synchronized void setFileNameMap(FileNameMap map) + { + // Throw an exception if an extant security manager precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + fileNameMap = map; + } + + private ContentHandler getContentHandler(String contentType) + { + // No content type so just handle it as the default. + if (contentType == null || contentType.equals("")) + return null; + + ContentHandler handler = null; + + // If a non-default factory has been set, use it. + if (factory != null) + handler = factory.createContentHandler(contentType); + + // Now try default factory. Using this factory to instantiate built-in + // content handlers is preferable + if (handler == null) + handler = defaultFactory.createContentHandler(contentType); + + // User-set factory has not returned a handler. Use the default search + // algorithm. + if (handler == null) + { + // Get the list of packages to check and append our default handler + // to it, along with the JDK specified default as a last resort. + // Except in very unusual environments the JDK specified one shouldn't + // ever be needed (or available). + String propVal = SystemProperties.getProperty("java.content.handler.pkgs"); + propVal = (((propVal == null) ? "" : (propVal + "|")) + + "gnu.java.net.content|sun.net.www.content"); + + // Deal with "Content-Type: text/html; charset=ISO-8859-1". + int parameterBegin = contentType.indexOf(';'); + if (parameterBegin >= 1) + contentType = contentType.substring(0, parameterBegin); + contentType = contentType.trim(); + + // Replace the '/' character in the content type with '.' and + // all other non-alphabetic, non-numeric characters with '_'. + char[] cArray = contentType.toCharArray(); + for (int i = 0; i < cArray.length; i++) + { + if (cArray[i] == '/') + cArray[i] = '.'; + else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') || + (cArray[i] >= 'a' && cArray[i] <= 'z') || + (cArray[i] >= '0' && cArray[i] <= '9'))) + cArray[i] = '_'; + } + String contentClass = new String(cArray); + + // See if a class of this content type exists in any of the packages. + StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|"); + do + { + String facName = pkgPrefix.nextToken() + "." + contentClass; + try + { + handler = + (ContentHandler) Class.forName(facName).newInstance(); + } + catch (Exception e) + { + // Can't instantiate; handler still null, go on to next element. + } + } while (handler == null && pkgPrefix.hasMoreTokens()); + } + + return handler; + } + + // We don't put these in a static initializer, because it creates problems + // with initializer co-dependency: SimpleDateFormat's constructors + // eventually depend on URLConnection (via the java.text.*Symbols classes). + private static synchronized void initializeDateFormats() + { + if (dateformats_initialized) + return; + + Locale locale = new Locale("En", "Us", "Unix"); + dateFormats = new SimpleDateFormat[3]; + dateFormats[0] = + new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale); + dateFormats[1] = + new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale); + dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale); + dateformats_initialized = true; + } +} diff --git a/libjava/classpath/java/net/URLDecoder.java b/libjava/classpath/java/net/URLDecoder.java new file mode 100644 index 000000000..73cedea5c --- /dev/null +++ b/libjava/classpath/java/net/URLDecoder.java @@ -0,0 +1,182 @@ +/* URLDecoder.java -- Class to decode URL's from encoded form. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.UnsupportedEncodingException; + + +/** + * This utility class contains static methods that converts a + * string encoded in the x-www-form-urlencoded format to the original + * text. The x-www-form-urlencoded format replaces certain disallowed + * characters with encoded equivalents. All upper case and lower case + * letters in the US alphabet remain as is, the space character (' ') + * is replaced with '+' sign, and all other characters are converted to a + * "%XX" format where XX is the hexadecimal representation of that character + * in a given character encoding (default is "UTF-8"). + * <p> + * This method is very useful for decoding strings sent to CGI scripts + * + * Written using on-line Java Platform 1.2/1.4 API Specification. + * Status: Believed complete and correct. + * + * @since 1.2 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (documentation comments) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLDecoder +{ + /** + * Public contructor. Note that this class has only static methods. + */ + public URLDecoder() + { + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the default encoding "UTF-8" to decode the hex encoded + * unsafe characters. + * + * @param s the String to convert + * + * @return the converted String + * + * @deprecated + */ + public static String decode(String s) + { + try + { + return decode(s, "UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since UTF-8 encoding should always be supported + return s; + } + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the given character encoding to decode the hex encoded + * unsafe characters. + * + * This implementation will decode the string even if it contains + * unsafe characters (characters that should have been encoded) or if the + * two characters following a % do not represent a hex encoded byte. + * In those cases the unsafe character or the % character will be added + * verbatim to the decoded result. + * + * @param s the String to convert + * @param encoding the character encoding to use the decode the hex encoded + * unsafe characters + * + * @return the converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String decode(String s, String encoding) + throws UnsupportedEncodingException + { + // First convert all '+' characters to spaces. + String str = s.replace('+', ' '); + + // Then go through the whole string looking for byte encoded characters + int i; + int start = 0; + byte[] bytes = null; + int length = str.length(); + CPStringBuilder result = new CPStringBuilder(length); + while ((i = str.indexOf('%', start)) >= 0) + { + // Add all non-encoded characters to the result buffer + result.append(str.substring(start, i)); + start = i; + + // Get all consecutive encoded bytes + while ((i + 2 < length) && (str.charAt(i) == '%')) + i += 3; + + // Decode all these bytes + if ((bytes == null) || (bytes.length < ((i - start) / 3))) + bytes = new byte[((i - start) / 3)]; + + int index = 0; + try + { + while (start < i) + { + String sub = str.substring(start + 1, start + 3); + bytes[index] = (byte) Integer.parseInt(sub, 16); + index++; + start += 3; + } + } + catch (NumberFormatException nfe) + { + // One of the hex encoded strings was bad + } + + // Add the bytes as characters according to the given encoding + result.append(new String(bytes, 0, index, encoding)); + + // Make sure we skip to just after a % sign + // There might not have been enough encoded characters after the % + // or the hex chars were not actually hex chars (NumberFormatException) + if (start < length && s.charAt(start) == '%') + { + result.append('%'); + start++; + } + } + + // Add any characters left + if (start < str.length()) + result.append(str.substring(start)); + + return result.toString(); + } +} // class URLDecoder diff --git a/libjava/classpath/java/net/URLEncoder.java b/libjava/classpath/java/net/URLEncoder.java new file mode 100644 index 000000000..09c0f3de0 --- /dev/null +++ b/libjava/classpath/java/net/URLEncoder.java @@ -0,0 +1,186 @@ +/* URLEncoder.java -- Class to convert strings to a properly encoded URL + Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.UnsupportedEncodingException; + + +/* + * Written using on-line Java Platform 1.2/1.4 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This utility class contains static methods that converts a + * string into a fully encoded URL string in x-www-form-urlencoded + * format. This format replaces certain disallowed characters with + * encoded equivalents. All upper case and lower case letters in the + * US alphabet remain as is, the space character (' ') is replaced with + * '+' sign, and all other characters are converted to a "%XX" format + * where XX is the hexadecimal representation of that character in a + * certain encoding (by default, the platform encoding, though the + * standard is "UTF-8"). + * <p> + * This method is very useful for encoding strings to be sent to CGI scripts + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLEncoder +{ + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the default encoding. The standard encoding is + * "UTF-8", and the two-argument form of this method should be used + * instead. + * + * @param s The String to convert + * + * @return The converted String + * + * @deprecated + */ + public static String encode(String s) + { + try + { + // We default to 8859_1 for compatibility with the same + // default elsewhere in the library. + return encode(s, System.getProperty("file.encoding", "8859_1")); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since default should always be supported + return s; + } + } + + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the character encoding to hex-encode the unsafe characters. + * + * @param s The String to convert + * @param encoding The encoding to use for unsafe characters + * + * @return The converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String encode(String s, String encoding) + throws UnsupportedEncodingException + { + int length = s.length(); + int start = 0; + int i = 0; + + CPStringBuilder result = new CPStringBuilder(length); + while (true) + { + while (i < length && isSafe(s.charAt(i))) + i++; + + // Safe character can just be added + result.append(s.substring(start, i)); + + // Are we done? + if (i >= length) + return result.toString(); + else if (s.charAt(i) == ' ') + { + result.append('+'); // Replace space char with plus symbol. + i++; + } + else + { + // Get all unsafe characters + start = i; + char c; + while (i < length && (c = s.charAt(i)) != ' ' && ! isSafe(c)) + i++; + + // Convert them to %XY encoded strings + String unsafe = s.substring(start, i); + byte[] bytes = unsafe.getBytes(encoding); + for (int j = 0; j < bytes.length; j++) + { + result.append('%'); + int val = bytes[j]; + result.append(hex.charAt((val & 0xf0) >> 4)); + result.append(hex.charAt(val & 0x0f)); + } + } + start = i; + } + } + + /** + * Private static method that returns true if the given char is either + * a uppercase or lowercase letter from 'a' till 'z', or a digit froim + * '0' till '9', or one of the characters '-', '_', '.' or '*'. Such + * 'safe' character don't have to be url encoded. + */ + private static boolean isSafe(char c) + { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' + || c == '*'); + } + + /** + * Private constructor that does nothing. Included to avoid a default + * public constructor being created by the compiler. + */ + private URLEncoder() + { + } + + /** + * Used to convert to hex. We don't use Integer.toHexString, since + * it converts to lower case (and the Sun docs pretty clearly + * specify upper case here), and because it doesn't provide a + * leading 0. + */ + private static final String hex = "0123456789ABCDEF"; +} diff --git a/libjava/classpath/java/net/URLStreamHandler.java b/libjava/classpath/java/net/URLStreamHandler.java new file mode 100644 index 000000000..5433aedd9 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandler.java @@ -0,0 +1,541 @@ +/* URLStreamHandler.java -- Abstract superclass for all protocol handlers + Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.File; +import java.io.IOException; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class is the superclass of all URL protocol handlers. The URL + * class loads the appropriate protocol handler to establish a connection + * to a (possibly) remote service (eg, "http", "ftp") and to do protocol + * specific parsing of URL's. Refer to the URL class documentation for + * details on how that class locates and loads protocol handlers. + * <p> + * A protocol handler implementation should override the openConnection() + * method, and optionally override the parseURL() and toExternalForm() + * methods if necessary. (The default implementations will parse/write all + * URL's in the same form as http URL's). A protocol specific subclass + * of URLConnection will most likely need to be created as well. + * <p> + * Note that the instance methods in this class are called as if they + * were static methods. That is, a URL object to act on is passed with + * every call rather than the caller assuming the URL is stored in an + * instance variable of the "this" object. + * <p> + * The methods in this class are protected and accessible only to subclasses. + * URLStreamConnection objects are intended for use by the URL class only, + * not by other classes (unless those classes are implementing protocols). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URL + */ +public abstract class URLStreamHandler +{ + /** + * Creates a URLStreamHander + */ + public URLStreamHandler() + { + } + + /** + * Returns a URLConnection for the passed in URL. Note that this should + * not actually create the connection to the (possibly) remote host, but + * rather simply return a URLConnection object. The connect() method of + * URL connection is used to establish the actual connection, possibly + * after the caller sets up various connection options. + * + * @param url The URL to get a connection object for + * + * @return A URLConnection object for the given URL + * + * @exception IOException If an error occurs + */ + protected abstract URLConnection openConnection(URL url) + throws IOException; + + /** + * This method parses the string passed in as a URL and set's the + * instance data fields in the URL object passed in to the various values + * parsed out of the string. The start parameter is the position to start + * scanning the string. This is usually the position after the ":" which + * terminates the protocol name. The end parameter is the position to + * stop scanning. This will be either the end of the String, or the + * position of the "#" character, which separates the "file" portion of + * the URL from the "anchor" portion. + * <p> + * This method assumes URL's are formatted like http protocol URL's, so + * subclasses that implement protocols with URL's the follow a different + * syntax should override this method. The lone exception is that if + * the protocol name set in the URL is "file", this method will accept + * an empty hostname (i.e., "file:///"), which is legal for that protocol + * + * @param url The URL object in which to store the results + * @param spec The String-ized URL to parse + * @param start The position in the string to start scanning from + * @param end The position in the string to stop scanning + */ + protected void parseURL(URL url, String spec, int start, int end) + { + String host = url.getHost(); + int port = url.getPort(); + String file = url.getFile(); + String ref = url.getRef(); + String userInfo = url.getUserInfo(); + String authority = url.getAuthority(); + String query = null; + + // On Windows we need to change \ to / for file URLs + char separator = File.separatorChar; + if (url.getProtocol().equals("file") && separator != '/') + { + file = file.replace(separator, '/'); + spec = spec.replace(separator, '/'); + } + + if (spec.regionMatches(start, "//", 0, 2)) + { + String genuineHost; + int hostEnd; + int colon; + int at_host; + + start += 2; + int slash = spec.indexOf('/', start); + if (slash >= 0) + hostEnd = slash; + else + hostEnd = end; + + authority = host = spec.substring(start, hostEnd); + + // We first need a genuine host name (with userinfo). + // So we check for '@': if it's present check the port in the + // section after '@' in the other case check it in the full string. + // P.S.: We don't care having '@' at the beginning of the string. + if ((at_host = host.indexOf('@')) >= 0) + { + genuineHost = host.substring(at_host); + userInfo = host.substring(0, at_host); + } + else + genuineHost = host; + + // Look for optional port number. It is valid for the non-port + // part of the host name to be null (e.g. a URL "http://:80"). + // TBD: JDK 1.2 in this case sets host to null rather than ""; + // this is undocumented and likely an unintended side effect in 1.2 + // so we'll be simple here and stick with "". Note that + // "http://" or "http:///" produce a "" host in JDK 1.2. + if ((colon = genuineHost.indexOf(':')) >= 0) + { + try + { + port = Integer.parseInt(genuineHost.substring(colon + 1)); + } + catch (NumberFormatException e) + { + // Ignore invalid port values; port is already set to u's + // port. + } + + // Now we must cut the port number in the original string. + if (at_host >= 0) + host = host.substring(0, at_host + colon); + else + host = host.substring(0, colon); + } + file = null; + start = hostEnd; + } + else if (host == null) + host = ""; + + if (file == null || file.length() == 0 + || (start < end && spec.charAt(start) == '/')) + { + // No file context available; just spec for file. + // Or this is an absolute path name; ignore any file context. + file = spec.substring(start, end); + ref = null; + } + else if (start < end) + { + // Context is available, but only override it if there is a new file. + int lastSlash = file.lastIndexOf('/'); + if (lastSlash < 0) + file = spec.substring(start, end); + else + file = (file.substring(0, lastSlash) + + '/' + spec.substring(start, end)); + + // For URLs constructed relative to a context, we + // need to canonicalise the file path. + file = canonicalizeFilename(file); + + ref = null; + } + + if (ref == null) + { + // Normally there should be no '#' in the file part, + // but we are nice. + int hash = file.indexOf('#'); + if (hash != -1) + { + ref = file.substring(hash + 1, file.length()); + file = file.substring(0, hash); + } + } + + // We care about the query tag only if there is no reference at all. + if (ref == null) + { + int queryTag = file.indexOf('?'); + if (queryTag != -1) + { + query = file.substring(queryTag + 1); + file = file.substring(0, queryTag); + } + } + + // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on + // the file part. It seems like overhead, but supposedly there is some + // benefit in windows based systems (it also lowercased the string). + setURL(url, url.getProtocol(), host, port, authority, userInfo, file, query, ref); + } + + /* + * Canonicalize a filename. + */ + private static String canonicalizeFilename(String file) + { + // XXX - GNU Classpath has an implementation that might be more appropriate + // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm) + int index; + + // Replace "/./" with "/". This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/./")) >= 0) + file = file.substring(0, index) + file.substring(index + 2); + + // Process "/../" correctly. This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/../")) >= 0) + { + // Strip of the previous directory - if it exists. + int previous = file.lastIndexOf('/', index - 1); + if (previous >= 0) + file = file.substring(0, previous) + file.substring(index + 3); + else + break; + } + return file; + } + + /** + * Compares two URLs, excluding the fragment component + * + * @param url1 The first url + * @param url2 The second url to compare with the first + * + * @return True if both URLs point to the same file, false otherwise. + * + * @specnote Now protected + */ + protected boolean sameFile(URL url1, URL url2) + { + if (url1 == url2) + return true; + + // This comparison is very conservative. It assumes that any + // field can be null. + if (url1 == null || url2 == null) + return false; + int p1 = url1.getPort(); + if (p1 == -1) + p1 = url1.ph.getDefaultPort(); + int p2 = url2.getPort(); + if (p2 == -1) + p2 = url2.ph.getDefaultPort(); + if (p1 != p2) + return false; + String s1; + String s2; + s1 = url1.getProtocol(); + s2 = url2.getProtocol(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = url1.getHost(); + s2 = url2.getHost(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = canonicalizeFilename(url1.getFile()); + s2 = canonicalizeFilename(url2.getFile()); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + return true; + } + + /** + * This methods sets the instance variables representing the various fields + * of the URL to the values passed in. + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to et + * @param port The port number to set + * @param file The filename to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + * + * @deprecated 1.2 Please use + * #setURL(URL,String,String,int,String,String,String,String); + */ + protected void setURL(URL u, String protocol, String host, int port, + String file, String ref) + { + u.set(protocol, host, port, file, ref); + } + + /** + * Sets the fields of the URL argument to the indicated values + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to set + * @param port The port number to set + * @param authority The authority to set + * @param userInfo The user information to set + * @param path The path/filename to set + * @param query The query part to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + */ + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) + { + u.set(protocol, host, port, authority, userInfo, path, query, ref); + } + + /** + * This is the default method for computing whether two URLs are + * equivalent. This method assumes that neither URL is null. + * + * @param url1 An URL object + * @param url2 Another URL object + * + * @return True if both given URLs are equal, false otherwise. + */ + protected boolean equals(URL url1, URL url2) + { + // This comparison is very conservative. It assumes that any + // field can be null. + int port1 = url1.getPort(); + if (port1 == -1) + port1 = url1.getDefaultPort(); + int port2 = url2.getPort(); + if (port2 == -1) + port2 = url2.getDefaultPort(); + // Note that we don't bother checking the 'authority'; it is + // redundant. + return (port1 == port2 + && ((url1.getProtocol() == null && url2.getProtocol() == null) + || (url1.getProtocol() != null + && url1.getProtocol().equals(url2.getProtocol()))) + && ((url1.getUserInfo() == null && url2.getUserInfo() == null) + || (url1.getUserInfo() != null + && url1.getUserInfo().equals(url2.getUserInfo()))) + && ((url1.getHost() == null && url2.getHost() == null) + || (url1.getHost() != null && url1.getHost().equals(url2.getHost()))) + && ((url1.getPath() == null && url2.getPath() == null) + || (url1.getPath() != null && url1.getPath().equals(url2.getPath()))) + && ((url1.getQuery() == null && url2.getQuery() == null) + || (url1.getQuery() != null + && url1.getQuery().equals(url2.getQuery()))) + && ((url1.getRef() == null && url2.getRef() == null) + || (url1.getRef() != null && url1.getRef().equals(url2.getRef())))); + } + + /** + * Compares the host components of two URLs. + * + * @param url1 The first URL. + * @param url2 The second URL. + * + * @return True if both URLs contain the same host. + */ + protected boolean hostsEqual(URL url1, URL url2) + { + InetAddress addr1 = getHostAddress(url1); + InetAddress addr2 = getHostAddress(url2); + + if (addr1 != null && addr2 != null) + return addr1.equals(addr2); + + String host1 = url1.getHost(); + String host2 = url2.getHost(); + + if (host1 != null && host2 != null) + return host1.equalsIgnoreCase(host2); + + return host1 == null && host2 == null; + } + + /** + * Get the IP address of our host. An empty host field or a DNS failure will + * result in a null return. + * + * @param url The URL to return the host address for. + * + * @return The address of the hostname in url. + */ + protected InetAddress getHostAddress(URL url) + { + String hostname = url.getHost(); + + if (hostname.equals("")) + return null; + + try + { + return InetAddress.getByName(hostname); + } + catch (UnknownHostException e) + { + return null; + } + } + + /** + * Returns the default port for a URL parsed by this handler. This method is + * meant to be overidden by handlers with default port numbers. + * + * @return The default port number. + */ + protected int getDefaultPort() + { + return -1; + } + + /** + * Provides the default hash calculation. May be overidden by handlers for + * other protocols that have different requirements for hashCode calculation. + * + * @param url The URL to calc the hashcode for. + * + * @return The hashcode for the given URL. + */ + protected int hashCode(URL url) + { + return url.getProtocol().hashCode() + + ((url.getHost() == null) ? 0 : url.getHost().hashCode()) + + url.getFile().hashCode() + url.getPort(); + } + + /** + * This method converts a URL object into a String. This method creates + * Strings in the mold of http URL's, so protocol handlers which use URL's + * that have a different syntax should override this method + * + * @param url The URL object to convert + * + * @return A string representation of the url + */ + protected String toExternalForm(URL url) + { + String protocol; + String file; + String ref; + String authority; + + protocol = url.getProtocol(); + authority = url.getAuthority(); + if (authority == null) + authority = ""; + + file = url.getFile(); + ref = url.getRef(); + + // Guess a reasonable size for the string buffer so we have to resize + // at most once. + int size = protocol.length() + authority.length() + file.length() + 24; + CPStringBuilder sb = new CPStringBuilder(size); + + if (protocol.length() > 0) + { + sb.append(protocol); + sb.append(":"); + } + + // If we have superfluous leading slashes (that means, at least 2) + // we always add the authority component ("//" + host) to + // avoid ambiguity. Otherwise we would generate an URL like + // proto://home/foo + // where we meant: + // host: <empty> - file: //home/foo + // but URL spec says it is: + // host: home - file: /foo + if (authority.length() != 0 || file.startsWith("//") ) + sb.append("//").append(authority).append(file); + else + sb.append(file); + + if (ref != null) + sb.append('#').append(ref); + + return sb.toString(); + } +} diff --git a/libjava/classpath/java/net/URLStreamHandlerFactory.java b/libjava/classpath/java/net/URLStreamHandlerFactory.java new file mode 100644 index 000000000..c92c71fb2 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandlerFactory.java @@ -0,0 +1,65 @@ +/* URLStreamHandlerFactory.java -- Maps protocols to URLStreamHandlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface contains one method which maps the protocol portion of + * a URL (eg, "http" in "http://www.urbanophile.com/arenn/") to a + * <code>URLStreamHandler</code> object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface URLStreamHandlerFactory +{ + /** + * This method maps the protocol portion of a URL to a + * <code>URLStreamHandler</code> object. + * + * @param protocol The protocol name to map ("http", "ftp", etc). + * + * @return The <code>URLStreamHandler</code> for the specified protocol + */ + URLStreamHandler createURLStreamHandler(String protocol); +} // interface URLStreamHandlerFactory diff --git a/libjava/classpath/java/net/UnknownHostException.java b/libjava/classpath/java/net/UnknownHostException.java new file mode 100644 index 000000000..c5ba18330 --- /dev/null +++ b/libjava/classpath/java/net/UnknownHostException.java @@ -0,0 +1,77 @@ +/* UnknownHostException.java -- The hostname is unknown + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that an attempt was made to reference a hostname + * or IP address that is not valid. This could possibly indicate that a + * DNS problem has occurred, but most often means that the host was not + * correctly specified. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class UnknownHostException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4639126076052875403L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownHostException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * name of the host that could not be resolved. + * + * @param message a message describing the error that occurred + */ + public UnknownHostException(String message) + { + super(message); + } +} // class UnknownHostException diff --git a/libjava/classpath/java/net/UnknownServiceException.java b/libjava/classpath/java/net/UnknownServiceException.java new file mode 100644 index 000000000..65cc8f592 --- /dev/null +++ b/libjava/classpath/java/net/UnknownServiceException.java @@ -0,0 +1,76 @@ +/* UnknownServiceException.java -- A service error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * Contrary to what you might think, this does not indicate that the + * TCP/IP service name specified was invalid. Instead it indicates that + * the MIME type returned from a URL could not be determined or that an + * attempt was made to write to a read-only URL. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class UnknownServiceException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4169033248853639508L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownServiceException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public UnknownServiceException(String message) + { + super(message); + } +} // class UnknownServiceException diff --git a/libjava/classpath/java/net/package.html b/libjava/classpath/java/net/package.html new file mode 100644 index 000000000..133ee716f --- /dev/null +++ b/libjava/classpath/java/net/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.net package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.net</title></head> + +<body> +<p>Network communication through TCP and UDP sockets or URLs.</p> + +</body> +</html> |