feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 1997, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.log;
import java.io.*;
import sun.rmi.server.MarshalOutputStream;
import sun.rmi.server.MarshalInputStream;
/**
* A LogHandler represents snapshots and update records as serializable
* objects.
*
* This implementation does not know how to create an initial snaphot or
* apply an update to a snapshot. The client must specifiy these methods
* via a subclass.
*
* @see ReliableLog
*
* @author Ann Wollrath
*/
public abstract
class LogHandler {
/**
* Creates a LogHandler for a ReliableLog.
*/
public LogHandler() {}
/**
* Creates and returns the initial state of data structure that needs
* to be stably stored. This method is called when a ReliableLog is
* created.
* @return the initial state
* @exception Exception can raise any exception
*/
public abstract
Object initialSnapshot() throws Exception;
/**
* Writes the snapshot object to a stream. This callback is
* invoked when the client calls the snaphot method of ReliableLog.
* @param out the output stream
* @param value the snapshot
* @exception Exception can raise any exception
*/
public
void snapshot(OutputStream out, Object value) throws Exception {
MarshalOutputStream s = new MarshalOutputStream(out);
s.writeObject(value);
s.flush();
}
/**
* Read the snapshot object from a stream and returns the snapshot.
* This callback is invoked when the client calls the recover method
* of ReliableLog.
* @param in the input stream
* @return the state (snapshot)
* @exception Exception can raise any exception
*/
public
Object recover(InputStream in) throws Exception {
MarshalInputStream s = new MarshalInputStream(in);
return s.readObject();
}
/**
* Writes the representation (a serializable object) of an update
* to a stream. This callback is invoked when the client calls the
* update method of ReliableLog.
* @param out the output stream
* @param value the snapshot
* @exception Exception can raise any exception
*/
public
void writeUpdate(LogOutputStream out, Object value) throws Exception {
MarshalOutputStream s = new MarshalOutputStream(out);
s.writeObject(value);
s.flush();
}
/**
* Reads a stably logged update (a serializable object) from a
* stream. This callback is invoked during recovery, once for
* every record in the log. After reading the update, this method
* invokes the applyUpdate (abstract) method in order to obtain
* the new snapshot value. It then returns the new snapshot.
*
* @param in the input stream
* @param state the current state
* @return the new state
* @exception Exception can raise any exception
*/
public
Object readUpdate(LogInputStream in, Object state) throws Exception {
MarshalInputStream s = new MarshalInputStream(in);
return applyUpdate(s.readObject(), state);
}
/**
* Reads a stably logged update (a serializable object) from a stream.
* This callback is invoked during recovery, once for every record in the
* log. After reading the update, this method is invoked in order to
* obtain the new snapshot value. The method should apply the update
* object to the current state <code>state</code> and return the new
* state (the new snapshot value).
* @param update the update object
* @param state the current state
* @return the new state
* @exception Exception can raise any exception
*/
public abstract
Object applyUpdate(Object update, Object state) throws Exception;
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.log;
import java.io.*;
public
class LogInputStream extends InputStream {
private InputStream in;
private int length;
/**
* Creates a log input file with the specified system dependent
* file descriptor.
* @param fd the system dependent file descriptor
* @param length the total number of bytes allowed to be read
* @exception IOException If an I/O error has occurred.
*/
public LogInputStream(InputStream in, int length) throws IOException {
this.in = in;
this.length = length;
}
/**
* Reads a byte of data. This method will block if no input is
* available.
* @return the byte read, or -1 if the end of the log or end of the
* stream is reached.
* @exception IOException If an I/O error has occurred.
*/
public int read() throws IOException {
if (length == 0)
return -1;
int c = in.read();
length = (c != -1) ? length - 1 : 0;
return c;
}
/**
* Reads data into an array of bytes.
* This method blocks until some input is available.
* @param b the buffer into which the data is read
* @return the actual number of bytes read, or -1 if the end of the log
* or end of the stream is reached.
* @exception IOException If an I/O error has occurred.
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads data into an array of bytes.
* This method blocks until some input is available.
* @param b the buffer into which the data is read
* @param off the start offset of the data
* @param len the maximum number of bytes read
* @return the actual number of bytes read, or -1 if the end of the log or
* end of the stream is reached.
* @exception IOException If an I/O error has occurred.
*/
public int read(byte b[], int off, int len) throws IOException {
if (length == 0)
return -1;
len = (length < len) ? length : len;
int n = in.read(b, off, len);
length = (n != -1) ? length - n : 0;
return n;
}
/**
* Skips n bytes of input.
* @param n the number of bytes to be skipped
* @return the actual number of bytes skipped.
* @exception IOException If an I/O error has occurred.
*/
public long skip(long n) throws IOException {
if (n > Integer.MAX_VALUE)
throw new IOException("Too many bytes to skip - " + n);
if (length == 0)
return 0;
n = (length < n) ? length : n;
n = in.skip(n);
length -= n;
return n;
}
/**
* Returns the number of bytes that can be read without blocking.
* @return the number of available bytes, which is initially
* equal to the file size.
*/
public int available() throws IOException {
int avail = in.available();
return (length < avail) ? length : avail;
}
/**
* Closes the input stream. No further input can be read.
* the stream.
*/
public void close() {
length = 0;
}
/**
* Closes the stream when garbage is collected.
*/
protected void finalize() throws IOException {
close();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.log;
import java.io.*;
public
class LogOutputStream extends OutputStream {
private RandomAccessFile raf;
/**
* Creates an output file with the specified system dependent
* file descriptor.
* @param fd the system dependent file descriptor
* @exception IOException If an I/O error has occurred.
*/
public LogOutputStream(RandomAccessFile raf) throws IOException {
this.raf = raf;
}
/**
* Writes a byte of data. This method will block until the byte is
* actually written.
* @param b the byte to be written
* @exception IOException If an I/O error has occurred.
*/
public void write(int b) throws IOException {
raf.write(b);
}
/**
* Writes an array of bytes. Will block until the bytes
* are actually written.
* @param b the data to be written
* @exception IOException If an I/O error has occurred.
*/
public void write(byte b[]) throws IOException {
raf.write(b);
}
/**
* Writes a sub array of bytes.
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
* @exception IOException If an I/O error has occurred.
*/
public void write(byte b[], int off, int len) throws IOException {
raf.write(b, off, len);
}
/**
* Can not close a LogOutputStream, so this does nothing.
* @exception IOException If an I/O error has occurred.
*/
public final void close() throws IOException {
}
}

View File

@@ -0,0 +1,824 @@
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.log;
import java.io.*;
import java.lang.reflect.Constructor;
import java.rmi.server.RMIClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
/**
* This class is a simple implementation of a reliable Log. The
* client of a ReliableLog must provide a set of callbacks (via a
* LogHandler) that enables a ReliableLog to read and write
* checkpoints and log records. This implementation ensures that the
* current value of the data stored (via a ReliableLog) is recoverable
* after a system crash. <p>
*
* The secondary storage strategy is to record values in files using a
* representation of the caller's choosing. Two sorts of files are
* kept: snapshots and logs. At any instant, one snapshot is current.
* The log consists of a sequence of updates that have occurred since
* the current snapshot was taken. The current stable state is the
* value of the snapshot, as modified by the sequence of updates in
* the log. From time to time, the client of a ReliableLog instructs
* the package to make a new snapshot and clear the log. A ReliableLog
* arranges disk writes such that updates are stable (as long as the
* changes are force-written to disk) and atomic : no update is lost,
* and each update either is recorded completely in the log or not at
* all. Making a new snapshot is also atomic. <p>
*
* Normal use for maintaining the recoverable store is as follows: The
* client maintains the relevant data structure in virtual memory. As
* updates happen to the structure, the client informs the ReliableLog
* (all it "log") by calling log.update. Periodically, the client
* calls log.snapshot to provide the current value of the data
* structure. On restart, the client calls log.recover to obtain the
* latest snapshot and the following sequences of updates; the client
* applies the updates to the snapshot to obtain the state that
* existed before the crash. <p>
*
* The current logfile format is: <ol>
* <li> a format version number (two 4-octet integers, major and
* minor), followed by
* <li> a sequence of log records. Each log record contains, in
* order, <ol>
* <li> a 4-octet integer representing the length of the following log
* data,
* <li> the log data (variable length). </ol> </ol> <p>
*
* @see LogHandler
*
* @author Ann Wollrath
*
*/
public class ReliableLog {
public final static int PreferredMajorVersion = 0;
public final static int PreferredMinorVersion = 2;
// sun.rmi.log.debug=false
private boolean Debug = false;
private static String snapshotPrefix = "Snapshot.";
private static String logfilePrefix = "Logfile.";
private static String versionFile = "Version_Number";
private static String newVersionFile = "New_Version_Number";
private static int intBytes = 4;
private static long diskPageSize = 512;
private File dir; // base directory
private int version = 0; // current snapshot and log version
private String logName = null;
private LogFile log = null;
private long snapshotBytes = 0;
private long logBytes = 0;
private int logEntries = 0;
private long lastSnapshot = 0;
private long lastLog = 0;
//private long padBoundary = intBytes;
private LogHandler handler;
private final byte[] intBuf = new byte[4];
// format version numbers read from/written to this.log
private int majorFormatVersion = 0;
private int minorFormatVersion = 0;
/**
* Constructor for the log file. If the system property
* sun.rmi.log.class is non-null and the class specified by this
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
* public two-arg constructor (String, String), ReliableLog uses the
* constructor to construct the LogFile.
**/
private static final Constructor<? extends LogFile>
logClassConstructor = getLogClassConstructor();
/**
* Creates a ReliableLog to handle checkpoints and logging in a
* stable storage directory.
*
* @param dirPath path to the stable storage directory
* @param logCl the closure object containing callbacks for logging and
* recovery
* @param pad ignored
* @exception IOException If a directory creation error has
* occurred or if initialSnapshot callback raises an exception or
* if an exception occurs during invocation of the handler's
* snapshot method or if other IOException occurs.
*/
public ReliableLog(String dirPath,
LogHandler handler,
boolean pad)
throws IOException
{
super();
this.Debug = AccessController.doPrivileged(
new GetBooleanAction("sun.rmi.log.debug")).booleanValue();
dir = new File(dirPath);
if (!(dir.exists() && dir.isDirectory())) {
// create directory
if (!dir.mkdir()) {
throw new IOException("could not create directory for log: " +
dirPath);
}
}
//padBoundary = (pad ? diskPageSize : intBytes);
this.handler = handler;
lastSnapshot = 0;
lastLog = 0;
getVersion();
if (version == 0) {
try {
snapshot(handler.initialSnapshot());
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException("initial snapshot failed with " +
"exception: " + e);
}
}
}
/**
* Creates a ReliableLog to handle checkpoints and logging in a
* stable storage directory.
*
* @param dirPath path to the stable storage directory
* @param logCl the closure object containing callbacks for logging and
* recovery
* @exception IOException If a directory creation error has
* occurred or if initialSnapshot callback raises an exception
*/
public ReliableLog(String dirPath,
LogHandler handler)
throws IOException
{
this(dirPath, handler, false);
}
/* public methods */
/**
* Returns an object which is the value recorded in the current
* snapshot. This snapshot is recovered by calling the client
* supplied callback "recover" and then subsequently invoking
* the "readUpdate" callback to apply any logged updates to the state.
*
* @exception IOException If recovery fails due to serious log
* corruption, read update failure, or if an exception occurs
* during the recover callback
*/
public synchronized Object recover()
throws IOException
{
if (Debug)
System.err.println("log.debug: recover()");
if (version == 0)
return null;
Object snapshot;
String fname = versionName(snapshotPrefix);
File snapshotFile = new File(fname);
InputStream in =
new BufferedInputStream(new FileInputStream(snapshotFile));
if (Debug)
System.err.println("log.debug: recovering from " + fname);
try {
try {
snapshot = handler.recover(in);
} catch (IOException e) {
throw e;
} catch (Exception e) {
if (Debug)
System.err.println("log.debug: recovery failed: " + e);
throw new IOException("log recover failed with " +
"exception: " + e);
}
snapshotBytes = snapshotFile.length();
} finally {
in.close();
}
return recoverUpdates(snapshot);
}
/**
* Records this update in the log file (does not force update to disk).
* The update is recorded by calling the client's "writeUpdate" callback.
* This method must not be called until this log's recover method has
* been invoked (and completed).
*
* @param value the object representing the update
* @exception IOException If an exception occurred during a
* writeUpdate callback or if other I/O error has occurred.
*/
public synchronized void update(Object value) throws IOException {
update(value, true);
}
/**
* Records this update in the log file. The update is recorded by
* calling the client's writeUpdate callback. This method must not be
* called until this log's recover method has been invoked
* (and completed).
*
* @param value the object representing the update
* @param forceToDisk ignored; changes are always forced to disk
* @exception IOException If force-write to log failed or an
* exception occurred during the writeUpdate callback or if other
* I/O error occurs while updating the log.
*/
public synchronized void update(Object value, boolean forceToDisk)
throws IOException
{
// avoid accessing a null log field.
if (log == null) {
throw new IOException("log is inaccessible, " +
"it may have been corrupted or closed");
}
/*
* If the entry length field spans a sector boundary, write
* the high order bit of the entry length, otherwise write zero for
* the entry length.
*/
long entryStart = log.getFilePointer();
boolean spansBoundary = log.checkSpansBoundary(entryStart);
writeInt(log, spansBoundary? 1<<31 : 0);
/*
* Write update, and sync.
*/
try {
handler.writeUpdate(new LogOutputStream(log), value);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw (IOException)
new IOException("write update failed").initCause(e);
}
log.sync();
long entryEnd = log.getFilePointer();
int updateLen = (int) ((entryEnd - entryStart) - intBytes);
log.seek(entryStart);
if (spansBoundary) {
/*
* If length field spans a sector boundary, then
* the next two steps are required (see 4652922):
*
* 1) Write actual length with high order bit set; sync.
* 2) Then clear high order bit of length; sync.
*/
writeInt(log, updateLen | 1<<31);
log.sync();
log.seek(entryStart);
log.writeByte(updateLen >> 24);
log.sync();
} else {
/*
* Write actual length; sync.
*/
writeInt(log, updateLen);
log.sync();
}
log.seek(entryEnd);
logBytes = entryEnd;
lastLog = System.currentTimeMillis();
logEntries++;
}
/**
* Returns the constructor for the log file if the system property
* sun.rmi.log.class is non-null and the class specified by the
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
* public two-arg constructor (String, String); otherwise returns null.
**/
private static Constructor<? extends LogFile>
getLogClassConstructor() {
String logClassName = AccessController.doPrivileged(
new GetPropertyAction("sun.rmi.log.class"));
if (logClassName != null) {
try {
ClassLoader loader =
AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return ClassLoader.getSystemClassLoader();
}
});
Class<? extends LogFile> cl =
loader.loadClass(logClassName).asSubclass(LogFile.class);
return cl.getConstructor(String.class, String.class);
} catch (Exception e) {
System.err.println("Exception occurred:");
e.printStackTrace();
}
}
return null;
}
/**
* Records this value as the current snapshot by invoking the client
* supplied "snapshot" callback and then empties the log.
*
* @param value the object representing the new snapshot
* @exception IOException If an exception occurred during the
* snapshot callback or if other I/O error has occurred during the
* snapshot process
*/
public synchronized void snapshot(Object value)
throws IOException
{
int oldVersion = version;
incrVersion();
String fname = versionName(snapshotPrefix);
File snapshotFile = new File(fname);
FileOutputStream out = new FileOutputStream(snapshotFile);
try {
try {
handler.snapshot(out, value);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException("snapshot failed", e);
}
lastSnapshot = System.currentTimeMillis();
} finally {
out.close();
snapshotBytes = snapshotFile.length();
}
openLogFile(true);
writeVersionFile(true);
commitToNewVersion();
deleteSnapshot(oldVersion);
deleteLogFile(oldVersion);
}
/**
* Close the stable storage directory in an orderly manner.
*
* @exception IOException If an I/O error occurs when the log is
* closed
*/
public synchronized void close() throws IOException {
if (log == null) return;
try {
log.close();
} finally {
log = null;
}
}
/**
* Returns the size of the snapshot file in bytes;
*/
public long snapshotSize() {
return snapshotBytes;
}
/**
* Returns the size of the log file in bytes;
*/
public long logSize() {
return logBytes;
}
/* private methods */
/**
* Write an int value in single write operation. This method
* assumes that the caller is synchronized on the log file.
*
* @param out output stream
* @param val int value
* @throws IOException if any other I/O error occurs
*/
private void writeInt(DataOutput out, int val)
throws IOException
{
intBuf[0] = (byte) (val >> 24);
intBuf[1] = (byte) (val >> 16);
intBuf[2] = (byte) (val >> 8);
intBuf[3] = (byte) val;
out.write(intBuf);
}
/**
* Generates a filename prepended with the stable storage directory path.
*
* @param name the leaf name of the file
*/
private String fName(String name) {
return dir.getPath() + File.separator + name;
}
/**
* Generates a version 0 filename prepended with the stable storage
* directory path
*
* @param name version file name
*/
private String versionName(String name) {
return versionName(name, 0);
}
/**
* Generates a version filename prepended with the stable storage
* directory path with the version number as a suffix.
*
* @param name version file name
* @thisversion a version number
*/
private String versionName(String prefix, int ver) {
ver = (ver == 0) ? version : ver;
return fName(prefix) + String.valueOf(ver);
}
/**
* Increments the directory version number.
*/
private void incrVersion() {
do { version++; } while (version==0);
}
/**
* Delete a file.
*
* @param name the name of the file
* @exception IOException If new version file couldn't be removed
*/
private void deleteFile(String name) throws IOException {
File f = new File(name);
if (!f.delete())
throw new IOException("couldn't remove file: " + name);
}
/**
* Removes the new version number file.
*
* @exception IOException If an I/O error has occurred.
*/
private void deleteNewVersionFile() throws IOException {
deleteFile(fName(newVersionFile));
}
/**
* Removes the snapshot file.
*
* @param ver the version to remove
* @exception IOException If an I/O error has occurred.
*/
private void deleteSnapshot(int ver) throws IOException {
if (ver == 0) return;
deleteFile(versionName(snapshotPrefix, ver));
}
/**
* Removes the log file.
*
* @param ver the version to remove
* @exception IOException If an I/O error has occurred.
*/
private void deleteLogFile(int ver) throws IOException {
if (ver == 0) return;
deleteFile(versionName(logfilePrefix, ver));
}
/**
* Opens the log file in read/write mode. If file does not exist, it is
* created.
*
* @param truncate if true and file exists, file is truncated to zero
* length
* @exception IOException If an I/O error has occurred.
*/
private void openLogFile(boolean truncate) throws IOException {
try {
close();
} catch (IOException e) { /* assume this is okay */
}
logName = versionName(logfilePrefix);
try {
log = (logClassConstructor == null ?
new LogFile(logName, "rw") :
logClassConstructor.newInstance(logName, "rw"));
} catch (Exception e) {
throw (IOException) new IOException(
"unable to construct LogFile instance").initCause(e);
}
if (truncate) {
initializeLogFile();
}
}
/**
* Creates a new log file, truncated and initialized with the format
* version number preferred by this implementation.
* <p>Environment: inited, synchronized
* <p>Precondition: valid: log, log contains nothing useful
* <p>Postcondition: if successful, log is initialised with the format
* version number (Preferred{Major,Minor}Version), and logBytes is
* set to the resulting size of the updatelog, and logEntries is set to
* zero. Otherwise, log is in an indeterminate state, and logBytes
* is unchanged, and logEntries is unchanged.
*
* @exception IOException If an I/O error has occurred.
*/
private void initializeLogFile()
throws IOException
{
log.setLength(0);
majorFormatVersion = PreferredMajorVersion;
writeInt(log, PreferredMajorVersion);
minorFormatVersion = PreferredMinorVersion;
writeInt(log, PreferredMinorVersion);
logBytes = intBytes * 2;
logEntries = 0;
}
/**
* Writes out version number to file.
*
* @param newVersion if true, writes to a new version file
* @exception IOException If an I/O error has occurred.
*/
private void writeVersionFile(boolean newVersion) throws IOException {
String name;
if (newVersion) {
name = newVersionFile;
} else {
name = versionFile;
}
try (FileOutputStream fos = new FileOutputStream(fName(name));
DataOutputStream out = new DataOutputStream(fos)) {
writeInt(out, version);
}
}
/**
* Creates the initial version file
*
* @exception IOException If an I/O error has occurred.
*/
private void createFirstVersion() throws IOException {
version = 0;
writeVersionFile(false);
}
/**
* Commits (atomically) the new version.
*
* @exception IOException If an I/O error has occurred.
*/
private void commitToNewVersion() throws IOException {
writeVersionFile(false);
deleteNewVersionFile();
}
/**
* Reads version number from a file.
*
* @param name the name of the version file
* @return the version
* @exception IOException If an I/O error has occurred.
*/
private int readVersion(String name) throws IOException {
try (DataInputStream in = new DataInputStream
(new FileInputStream(name))) {
return in.readInt();
}
}
/**
* Sets the version. If version file does not exist, the initial
* version file is created.
*
* @exception IOException If an I/O error has occurred.
*/
private void getVersion() throws IOException {
try {
version = readVersion(fName(newVersionFile));
commitToNewVersion();
} catch (IOException e) {
try {
deleteNewVersionFile();
}
catch (IOException ex) {
}
try {
version = readVersion(fName(versionFile));
}
catch (IOException ex) {
createFirstVersion();
}
}
}
/**
* Applies outstanding updates to the snapshot.
*
* @param state the most recent snapshot
* @exception IOException If serious log corruption is detected or
* if an exception occurred during a readUpdate callback or if
* other I/O error has occurred.
* @return the resulting state of the object after all updates
*/
private Object recoverUpdates(Object state)
throws IOException
{
logBytes = 0;
logEntries = 0;
if (version == 0) return state;
String fname = versionName(logfilePrefix);
InputStream in =
new BufferedInputStream(new FileInputStream(fname));
DataInputStream dataIn = new DataInputStream(in);
if (Debug)
System.err.println("log.debug: reading updates from " + fname);
try {
majorFormatVersion = dataIn.readInt(); logBytes += intBytes;
minorFormatVersion = dataIn.readInt(); logBytes += intBytes;
} catch (EOFException e) {
/* This is a log which was corrupted and/or cleared (by
* fsck or equivalent). This is not an error.
*/
openLogFile(true); // create and truncate
in = null;
}
/* A new major version number is a catastrophe (it means
* that the file format is incompatible with older
* clients, and we'll only be breaking things by trying to
* use the log). A new minor version is no big deal for
* upward compatibility.
*/
if (majorFormatVersion != PreferredMajorVersion) {
if (Debug) {
System.err.println("log.debug: major version mismatch: " +
majorFormatVersion + "." + minorFormatVersion);
}
throw new IOException("Log file " + logName + " has a " +
"version " + majorFormatVersion +
"." + minorFormatVersion +
" format, and this implementation " +
" understands only version " +
PreferredMajorVersion + "." +
PreferredMinorVersion);
}
try {
while (in != null) {
int updateLen = 0;
try {
updateLen = dataIn.readInt();
} catch (EOFException e) {
if (Debug)
System.err.println("log.debug: log was sync'd cleanly");
break;
}
if (updateLen <= 0) {/* crashed while writing last log entry */
if (Debug) {
System.err.println(
"log.debug: last update incomplete, " +
"updateLen = 0x" +
Integer.toHexString(updateLen));
}
break;
}
// this is a fragile use of available() which relies on the
// twin facts that BufferedInputStream correctly consults
// the underlying stream, and that FileInputStream returns
// the number of bytes remaining in the file (via FIONREAD).
if (in.available() < updateLen) {
/* corrupted record at end of log (can happen since we
* do only one fsync)
*/
if (Debug)
System.err.println("log.debug: log was truncated");
break;
}
if (Debug)
System.err.println("log.debug: rdUpdate size " + updateLen);
try {
state = handler.readUpdate(new LogInputStream(in, updateLen),
state);
} catch (IOException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("read update failed with " +
"exception: " + e);
}
logBytes += (intBytes + updateLen);
logEntries++;
} /* while */
} finally {
if (in != null)
in.close();
}
if (Debug)
System.err.println("log.debug: recovered updates: " + logEntries);
/* reopen log file at end */
openLogFile(false);
// avoid accessing a null log field
if (log == null) {
throw new IOException("rmid's log is inaccessible, " +
"it may have been corrupted or closed");
}
log.seek(logBytes);
log.setLength(logBytes);
return state;
}
/**
* ReliableLog's log file implementation. This implementation
* is subclassable for testing purposes.
*/
public static class LogFile extends RandomAccessFile {
private final FileDescriptor fd;
/**
* Constructs a LogFile and initializes the file descriptor.
**/
public LogFile(String name, String mode)
throws FileNotFoundException, IOException
{
super(name, mode);
this.fd = getFD();
}
/**
* Invokes sync on the file descriptor for this log file.
*/
protected void sync() throws IOException {
fd.sync();
}
/**
* Returns true if writing 4 bytes starting at the specified file
* position, would span a 512 byte sector boundary; otherwise returns
* false.
**/
protected boolean checkSpansBoundary(long fp) {
return fp % 512 > 508;
}
}
}

View File

@@ -0,0 +1,562 @@
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.registry;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.io.FilePermission;
import java.net.*;
import java.rmi.*;
import java.rmi.server.ObjID;
import java.rmi.server.ServerNotActiveException;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Policy;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.Security;
import java.text.MessageFormat;
import sun.misc.ObjectInputFilter;
import sun.rmi.runtime.Log;
import sun.rmi.server.UnicastRef;
import sun.rmi.server.UnicastServerRef;
import sun.rmi.server.UnicastServerRef2;
import sun.rmi.transport.LiveRef;
/**
* A "registry" exists on every node that allows RMI connections to
* servers on that node. The registry on a particular node contains a
* transient database that maps names to remote objects. When the
* node boots, the registry database is empty. The names stored in the
* registry are pure and are not parsed. A service storing itself in
* the registry may want to prefix its name of the service by a package
* name (although not required), to reduce name collisions in the
* registry.
*
* The LocateRegistry class is used to obtain registry for different hosts.
* <p>
* The default RegistryImpl exported restricts access to clients on the local host
* for the methods {@link #bind}, {@link #rebind}, {@link #unbind} by checking
* the client host in the skeleton.
*
* @see java.rmi.registry.LocateRegistry
*/
public class RegistryImpl extends java.rmi.server.RemoteServer
implements Registry
{
/* indicate compatibility with JDK 1.1.x version of class */
private static final long serialVersionUID = 4666870661827494597L;
private Hashtable<String, Remote> bindings
= new Hashtable<>(101);
private static Hashtable<InetAddress, InetAddress> allowedAccessCache
= new Hashtable<>(3);
private static RegistryImpl registry;
private static ObjID id = new ObjID(ObjID.REGISTRY_ID);
private static ResourceBundle resources = null;
/**
* Property name of the RMI Registry serial filter to augment
* the built-in list of allowed types.
* Setting the property in the {@code lib/security/java.security} file
* will enable the augmented filter.
*/
private static final String REGISTRY_FILTER_PROPNAME = "sun.rmi.registry.registryFilter";
/** Registry max depth of remote invocations. **/
private static final int REGISTRY_MAX_DEPTH = 20;
/** Registry maximum array size in remote invocations. **/
private static final int REGISTRY_MAX_ARRAY_SIZE = 1_000_000;
/**
* The registryFilter created from the value of the {@code "sun.rmi.registry.registryFilter"}
* property.
*/
private static final ObjectInputFilter registryFilter =
AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)RegistryImpl::initRegistryFilter);
/**
* Initialize the registryFilter from the security properties or system property; if any
* @return an ObjectInputFilter, or null
*/
private static ObjectInputFilter initRegistryFilter() {
ObjectInputFilter filter = null;
String props = System.getProperty(REGISTRY_FILTER_PROPNAME);
if (props == null) {
props = Security.getProperty(REGISTRY_FILTER_PROPNAME);
}
if (props != null) {
filter = ObjectInputFilter.Config.createFilter2(props);
Log regLog = Log.getLog("sun.rmi.registry", "registry", -1);
if (regLog.isLoggable(Log.BRIEF)) {
regLog.log(Log.BRIEF, "registryFilter = " + filter);
}
}
return filter;
}
/**
* Construct a new RegistryImpl on the specified port with the
* given custom socket factory pair.
*/
public RegistryImpl(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws RemoteException
{
this(port, csf, ssf, RegistryImpl::registryFilter);
}
/**
* Construct a new RegistryImpl on the specified port with the
* given custom socket factory pair and ObjectInputFilter.
*/
public RegistryImpl(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf,
ObjectInputFilter serialFilter)
throws RemoteException
{
if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
// grant permission for default port only.
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws RemoteException {
LiveRef lref = new LiveRef(id, port, csf, ssf);
setup(new UnicastServerRef2(lref, serialFilter));
return null;
}
}, null, new SocketPermission("localhost:"+port, "listen,accept"));
} catch (PrivilegedActionException pae) {
throw (RemoteException)pae.getException();
}
} else {
LiveRef lref = new LiveRef(id, port, csf, ssf);
setup(new UnicastServerRef2(lref, serialFilter));
}
}
/**
* Construct a new RegistryImpl on the specified port.
*/
public RegistryImpl(int port)
throws RemoteException
{
if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
// grant permission for default port only.
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws RemoteException {
LiveRef lref = new LiveRef(id, port);
setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
return null;
}
}, null, new SocketPermission("localhost:"+port, "listen,accept"));
} catch (PrivilegedActionException pae) {
throw (RemoteException)pae.getException();
}
} else {
LiveRef lref = new LiveRef(id, port);
setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
}
}
/*
* Create the export the object using the parameter
* <code>uref</code>
*/
private void setup(UnicastServerRef uref)
throws RemoteException
{
/* Server ref must be created and assigned before remote
* object 'this' can be exported.
*/
ref = uref;
uref.exportObject(this, null, true);
}
/**
* Returns the remote object for specified name in the registry.
* @exception RemoteException If remote operation failed.
* @exception NotBoundException If name is not currently bound.
*/
public Remote lookup(String name)
throws RemoteException, NotBoundException
{
synchronized (bindings) {
Remote obj = bindings.get(name);
if (obj == null)
throw new NotBoundException(name);
return obj;
}
}
/**
* Binds the name to the specified remote object.
* @exception RemoteException If remote operation failed.
* @exception AlreadyBoundException If name is already bound.
*/
public void bind(String name, Remote obj)
throws RemoteException, AlreadyBoundException, AccessException
{
// The access check preventing remote access is done in the skeleton
// and is not applicable to local access.
synchronized (bindings) {
Remote curr = bindings.get(name);
if (curr != null)
throw new AlreadyBoundException(name);
bindings.put(name, obj);
}
}
/**
* Unbind the name.
* @exception RemoteException If remote operation failed.
* @exception NotBoundException If name is not currently bound.
*/
public void unbind(String name)
throws RemoteException, NotBoundException, AccessException
{
// The access check preventing remote access is done in the skeleton
// and is not applicable to local access.
synchronized (bindings) {
Remote obj = bindings.get(name);
if (obj == null)
throw new NotBoundException(name);
bindings.remove(name);
}
}
/**
* Rebind the name to a new object, replaces any existing binding.
* @exception RemoteException If remote operation failed.
*/
public void rebind(String name, Remote obj)
throws RemoteException, AccessException
{
// The access check preventing remote access is done in the skeleton
// and is not applicable to local access.
bindings.put(name, obj);
}
/**
* Returns an enumeration of the names in the registry.
* @exception RemoteException If remote operation failed.
*/
public String[] list()
throws RemoteException
{
String[] names;
synchronized (bindings) {
int i = bindings.size();
names = new String[i];
Enumeration<String> enum_ = bindings.keys();
while ((--i) >= 0)
names[i] = enum_.nextElement();
}
return names;
}
/**
* Check that the caller has access to perform indicated operation.
* The client must be on same the same host as this server.
*/
public static void checkAccess(String op) throws AccessException {
try {
/*
* Get client host that this registry operation was made from.
*/
final String clientHostName = getClientHost();
InetAddress clientHost;
try {
clientHost = java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<InetAddress>() {
public InetAddress run()
throws java.net.UnknownHostException
{
return InetAddress.getByName(clientHostName);
}
});
} catch (PrivilegedActionException pae) {
throw (java.net.UnknownHostException) pae.getException();
}
// if client not yet seen, make sure client allowed access
if (allowedAccessCache.get(clientHost) == null) {
if (clientHost.isAnyLocalAddress()) {
throw new AccessException(
op + " disallowed; origin unknown");
}
try {
final InetAddress finalClientHost = clientHost;
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
public Void run() throws java.io.IOException {
/*
* if a ServerSocket can be bound to the client's
* address then that address must be local
*/
(new ServerSocket(0, 10, finalClientHost)).close();
allowedAccessCache.put(finalClientHost,
finalClientHost);
return null;
}
});
} catch (PrivilegedActionException pae) {
// must have been an IOException
throw new AccessException(
op + " disallowed; origin " +
clientHost + " is non-local host");
}
}
} catch (ServerNotActiveException ex) {
/*
* Local call from this VM: allow access.
*/
} catch (java.net.UnknownHostException ex) {
throw new AccessException(op + " disallowed; origin is unknown host");
}
}
public static ObjID getID() {
return id;
}
/**
* Retrieves text resources from the locale-specific properties file.
*/
private static String getTextResource(String key) {
if (resources == null) {
try {
resources = ResourceBundle.getBundle(
"sun.rmi.registry.resources.rmiregistry");
} catch (MissingResourceException mre) {
}
if (resources == null) {
// throwing an Error is a bit extreme, methinks
return ("[missing resource file: " + key + "]");
}
}
String val = null;
try {
val = resources.getString(key);
} catch (MissingResourceException mre) {
}
if (val == null) {
return ("[missing resource: " + key + "]");
} else {
return (val);
}
}
/**
* ObjectInputFilter to filter Registry input objects.
* The list of acceptable classes is limited to classes normally
* stored in a registry.
*
* @param filterInfo access to the class, array length, etc.
* @return {@link ObjectInputFilter.Status#ALLOWED} if allowed,
* {@link ObjectInputFilter.Status#REJECTED} if rejected,
* otherwise {@link ObjectInputFilter.Status#UNDECIDED}
*/
private static ObjectInputFilter.Status registryFilter(ObjectInputFilter.FilterInfo filterInfo) {
if (registryFilter != null) {
ObjectInputFilter.Status status = registryFilter.checkInput(filterInfo);
if (status != ObjectInputFilter.Status.UNDECIDED) {
// The Registry filter can override the built-in white-list
return status;
}
}
if (filterInfo.depth() > REGISTRY_MAX_DEPTH) {
return ObjectInputFilter.Status.REJECTED;
}
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
if (clazz.isArray()) {
// Arrays are REJECTED only if they exceed the limit
return (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > REGISTRY_MAX_ARRAY_SIZE)
? ObjectInputFilter.Status.REJECTED
: ObjectInputFilter.Status.UNDECIDED;
}
if (String.class == clazz
|| java.lang.Number.class.isAssignableFrom(clazz)
|| Remote.class.isAssignableFrom(clazz)
|| java.lang.reflect.Proxy.class.isAssignableFrom(clazz)
|| UnicastRef.class.isAssignableFrom(clazz)
|| RMIClientSocketFactory.class.isAssignableFrom(clazz)
|| RMIServerSocketFactory.class.isAssignableFrom(clazz)
|| java.rmi.activation.ActivationID.class.isAssignableFrom(clazz)
|| java.rmi.server.UID.class.isAssignableFrom(clazz)) {
return ObjectInputFilter.Status.ALLOWED;
} else {
return ObjectInputFilter.Status.REJECTED;
}
}
return ObjectInputFilter.Status.UNDECIDED;
}
/**
* Main program to start a registry. <br>
* The port number can be specified on the command line.
*/
public static void main(String args[])
{
// Create and install the security manager if one is not installed
// already.
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
/*
* Fix bugid 4147561: When JDK tools are executed, the value of
* the CLASSPATH environment variable for the shell in which they
* were invoked is no longer incorporated into the application
* class path; CLASSPATH's only effect is to be the value of the
* system property "env.class.path". To preserve the previous
* (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its
* CLASSPATH should still be considered when resolving classes
* being unmarshalled. To effect this old behavior, a class
* loader that loads from the file path specified in the
* "env.class.path" property is created and set to be the context
* class loader before the remote object is exported.
*/
String envcp = System.getProperty("env.class.path");
if (envcp == null) {
envcp = "."; // preserve old default behavior
}
URL[] urls = sun.misc.URLClassPath.pathToURLs(envcp);
ClassLoader cl = new URLClassLoader(urls);
/*
* Fix bugid 4242317: Classes defined by this class loader should
* be annotated with the value of the "java.rmi.server.codebase"
* property, not the "file:" URLs for the CLASSPATH elements.
*/
sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl);
Thread.currentThread().setContextClassLoader(cl);
final int regPort = (args.length >= 1) ? Integer.parseInt(args[0])
: Registry.REGISTRY_PORT;
try {
registry = AccessController.doPrivileged(
new PrivilegedExceptionAction<RegistryImpl>() {
public RegistryImpl run() throws RemoteException {
return new RegistryImpl(regPort);
}
}, getAccessControlContext(regPort));
} catch (PrivilegedActionException ex) {
throw (RemoteException) ex.getException();
}
// prevent registry from exiting
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
}
}
} catch (NumberFormatException e) {
System.err.println(MessageFormat.format(
getTextResource("rmiregistry.port.badnumber"),
args[0] ));
System.err.println(MessageFormat.format(
getTextResource("rmiregistry.usage"),
"rmiregistry" ));
} catch (Exception e) {
e.printStackTrace();
}
System.exit(1);
}
/**
* Generates an AccessControlContext with minimal permissions.
* The approach used here is taken from the similar method
* getAccessControlContext() in the sun.applet.AppletPanel class.
*/
private static AccessControlContext getAccessControlContext(int port) {
// begin with permissions granted to all code in current policy
PermissionCollection perms = AccessController.doPrivileged(
new java.security.PrivilegedAction<PermissionCollection>() {
public PermissionCollection run() {
CodeSource codesource = new CodeSource(null,
(java.security.cert.Certificate[]) null);
Policy p = java.security.Policy.getPolicy();
if (p != null) {
return p.getPermissions(codesource);
} else {
return new Permissions();
}
}
});
/*
* Anyone can connect to the registry and the registry can connect
* to and possibly download stubs from anywhere. Downloaded stubs and
* related classes themselves are more tightly limited by RMI.
*/
perms.add(new SocketPermission("*", "connect,accept"));
perms.add(new SocketPermission("localhost:"+port, "listen,accept"));
perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*"));
perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*"));
perms.add(new FilePermission("<<ALL FILES>>", "read"));
/*
* Create an AccessControlContext that consists of a single
* protection domain with only the permissions calculated above.
*/
ProtectionDomain pd = new ProtectionDomain(
new CodeSource(null,
(java.security.cert.Certificate[]) null), perms);
return new AccessControlContext(new ProtectionDomain[] { pd });
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.registry;
import java.io.IOException;
import java.io.ObjectInputStream;
import sun.misc.SharedSecrets;
import sun.rmi.transport.StreamRemoteCall;
/**
* Skeleton to dispatch RegistryImpl methods.
* Originally generated by RMIC but frozen to match the stubs.
*/
@SuppressWarnings({"deprecation", "serial"})
public final class RegistryImpl_Skel
implements java.rmi.server.Skeleton {
private static final java.rmi.server.Operation[] operations = {
new java.rmi.server.Operation("void bind(java.lang.String, java.rmi.Remote)"),
new java.rmi.server.Operation("java.lang.String list()[]"),
new java.rmi.server.Operation("java.rmi.Remote lookup(java.lang.String)"),
new java.rmi.server.Operation("void rebind(java.lang.String, java.rmi.Remote)"),
new java.rmi.server.Operation("void unbind(java.lang.String)")
};
private static final long interfaceHash = 4905912898345647071L;
public java.rmi.server.Operation[] getOperations() {
return operations.clone();
}
public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall remoteCall, int opnum, long hash)
throws java.lang.Exception {
if (opnum < 0) {
if (hash == 7583982177005850366L) {
opnum = 0;
} else if (hash == 2571371476350237748L) {
opnum = 1;
} else if (hash == -7538657168040752697L) {
opnum = 2;
} else if (hash == -8381844669958460146L) {
opnum = 3;
} else if (hash == 7305022919901907578L) {
opnum = 4;
} else {
throw new java.rmi.UnmarshalException("invalid method hash");
}
} else {
if (hash != interfaceHash)
throw new java.rmi.server.SkeletonMismatchException("interface hash mismatch");
}
sun.rmi.registry.RegistryImpl server = (sun.rmi.registry.RegistryImpl) obj;
StreamRemoteCall call = (StreamRemoteCall) remoteCall;
switch (opnum) {
case 0: // bind(String, Remote)
{
// Check access before reading the arguments
RegistryImpl.checkAccess("Registry.bind");
java.lang.String $param_String_1;
java.rmi.Remote $param_Remote_2;
try {
ObjectInputStream in = (ObjectInputStream)call.getInputStream();
$param_String_1 =
SharedSecrets.getJavaObjectInputStreamReadString().readString(in);
$param_Remote_2 = (java.rmi.Remote) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
server.bind($param_String_1, $param_Remote_2);
try {
call.getResultStream(true);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
case 1: // list()
{
call.releaseInputStream();
java.lang.String[] $result = server.list();
try {
java.io.ObjectOutput out = call.getResultStream(true);
out.writeObject($result);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
case 2: // lookup(String)
{
java.lang.String $param_String_1;
try {
ObjectInputStream in = (ObjectInputStream)call.getInputStream();
$param_String_1 =
SharedSecrets.getJavaObjectInputStreamReadString().readString(in);
} catch (ClassCastException | IOException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
java.rmi.Remote $result = server.lookup($param_String_1);
try {
java.io.ObjectOutput out = call.getResultStream(true);
out.writeObject($result);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
case 3: // rebind(String, Remote)
{
// Check access before reading the arguments
RegistryImpl.checkAccess("Registry.rebind");
java.lang.String $param_String_1;
java.rmi.Remote $param_Remote_2;
try {
ObjectInputStream in = (ObjectInputStream)call.getInputStream();
$param_String_1 =
SharedSecrets.getJavaObjectInputStreamReadString().readString(in);
$param_Remote_2 = (java.rmi.Remote) in.readObject();
} catch (ClassCastException | IOException | java.lang.ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
server.rebind($param_String_1, $param_Remote_2);
try {
call.getResultStream(true);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
case 4: // unbind(String)
{
// Check access before reading the arguments
RegistryImpl.checkAccess("Registry.unbind");
java.lang.String $param_String_1;
try {
ObjectInputStream in = (ObjectInputStream)call.getInputStream();
$param_String_1 =
SharedSecrets.getJavaObjectInputStreamReadString().readString(in);
} catch (ClassCastException | IOException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
server.unbind($param_String_1);
try {
call.getResultStream(true);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
default:
throw new java.rmi.UnmarshalException("invalid method number");
}
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.registry;
import java.io.IOException;
import sun.rmi.transport.StreamRemoteCall;
/**
* Stubs to invoke RegistryImpl remote methods.
* Originally generated from RMIC but frozen to match RegistryImpl_Skel.
*/
@SuppressWarnings({"deprecation", "serial"})
public final class RegistryImpl_Stub
extends java.rmi.server.RemoteStub
implements java.rmi.registry.Registry, java.rmi.Remote {
private static final java.rmi.server.Operation[] operations = {
new java.rmi.server.Operation("void bind(java.lang.String, java.rmi.Remote)"),
new java.rmi.server.Operation("java.lang.String list()[]"),
new java.rmi.server.Operation("java.rmi.Remote lookup(java.lang.String)"),
new java.rmi.server.Operation("void rebind(java.lang.String, java.rmi.Remote)"),
new java.rmi.server.Operation("void unbind(java.lang.String)")
};
private static final long interfaceHash = 4905912898345647071L;
// constructors
public RegistryImpl_Stub() {
super();
}
public RegistryImpl_Stub(java.rmi.server.RemoteRef ref) {
super(ref);
}
// methods from remote interfaces
// implementation of bind(String, Remote)
public void bind(java.lang.String $param_String_1, java.rmi.Remote $param_Remote_2)
throws java.rmi.AccessException, java.rmi.AlreadyBoundException, java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 0, interfaceHash);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_String_1);
out.writeObject($param_Remote_2);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
ref.done(call);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.rmi.AlreadyBoundException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
// implementation of list()
public java.lang.String[] list()
throws java.rmi.AccessException, java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 1, interfaceHash);
ref.invoke(call);
java.lang.String[] $result;
try {
java.io.ObjectInput in = call.getInputStream();
$result = (java.lang.String[]) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling return", e);
} finally {
ref.done(call);
}
return $result;
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
// implementation of lookup(String)
public java.rmi.Remote lookup(java.lang.String $param_String_1)
throws java.rmi.AccessException, java.rmi.NotBoundException, java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 2, interfaceHash);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_String_1);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
java.rmi.Remote $result;
try {
java.io.ObjectInput in = call.getInputStream();
$result = (java.rmi.Remote) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling return", e);
} finally {
ref.done(call);
}
return $result;
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.rmi.NotBoundException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
// implementation of rebind(String, Remote)
public void rebind(java.lang.String $param_String_1, java.rmi.Remote $param_Remote_2)
throws java.rmi.AccessException, java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 3, interfaceHash);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_String_1);
out.writeObject($param_Remote_2);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
ref.done(call);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
// implementation of unbind(String)
public void unbind(java.lang.String $param_String_1)
throws java.rmi.AccessException, java.rmi.NotBoundException, java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 4, interfaceHash);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_String_1);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
ref.done(call);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.rmi.NotBoundException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
}

View File

@@ -0,0 +1,445 @@
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*****************************************************************************/
/* Copyright (c) IBM Corporation 1998 */
/* */
/* (C) Copyright IBM Corp. 1998 */
/* */
/*****************************************************************************/
package sun.rmi.rmic;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import sun.tools.java.ClassPath;
/**
* BatchEnvironment for rmic extends javac's version in four ways:
* 1. It overrides errorString() to handle looking for rmic-specific
* error messages in rmic's resource bundle
* 2. It provides a mechanism for recording intermediate generated
* files so that they can be deleted later.
* 3. It holds a reference to the Main instance so that generators
* can refer to it.
* 4. It provides access to the ClassPath passed to the constructor.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public class BatchEnvironment extends sun.tools.javac.BatchEnvironment {
/** instance of Main which created this environment */
private Main main;
/**
* Create a ClassPath object for rmic from a class path string.
*/
public static ClassPath createClassPath(String classPathString) {
ClassPath[] paths = classPaths(null, classPathString, null, null);
return paths[1];
}
/**
* Create a ClassPath object for rmic from the relevant command line
* options for class path, boot class path, and extension directories.
*/
public static ClassPath createClassPath(String classPathString,
String sysClassPathString,
String extDirsString)
{
/**
* Previously, this method delegated to the
* sun.tools.javac.BatchEnvironment.classPaths method in order
* to supply default values for paths not specified on the
* command line, expand extensions directories into specific
* JAR files, and construct the ClassPath object-- but as part
* of the fix for 6473331, which adds support for Class-Path
* manifest entries in JAR files, those steps are now handled
* here directly, with the help of a Path utility class copied
* from the new javac implementation (see below).
*/
Path path = new Path();
if (sysClassPathString == null) {
sysClassPathString = System.getProperty("sun.boot.class.path");
}
if (sysClassPathString != null) {
path.addFiles(sysClassPathString);
}
/*
* Class-Path manifest entries are supported for JAR files
* everywhere except in the boot class path.
*/
path.expandJarClassPaths(true);
if (extDirsString == null) {
extDirsString = System.getProperty("java.ext.dirs");
}
if (extDirsString != null) {
path.addDirectories(extDirsString);
}
/*
* In the application class path, an empty element means
* the current working directory.
*/
path.emptyPathDefault(".");
if (classPathString == null) {
// The env.class.path property is the user's CLASSPATH
// environment variable, and it set by the wrapper (ie,
// javac.exe).
classPathString = System.getProperty("env.class.path");
if (classPathString == null) {
classPathString = ".";
}
}
path.addFiles(classPathString);
return new ClassPath(path.toArray(new String[path.size()]));
}
/**
* Create a BatchEnvironment for rmic with the given class path,
* stream for messages and Main.
*/
public BatchEnvironment(OutputStream out, ClassPath path, Main main) {
super(out, new ClassPath(""), path);
// use empty "sourcePath" (see 4666958)
this.main = main;
}
/**
* Get the instance of Main which created this environment.
*/
public Main getMain() {
return main;
}
/**
* Get the ClassPath.
*/
public ClassPath getClassPath() {
return binaryPath;
}
/** list of generated source files created in this environment */
private Vector<File> generatedFiles = new Vector<>();
/**
* Remember a generated source file generated so that it
* can be removed later, if appropriate.
*/
public void addGeneratedFile(File file) {
generatedFiles.addElement(file);
}
/**
* Delete all the generated source files made during the execution
* of this environment (those that have been registered with the
* "addGeneratedFile" method).
*/
public void deleteGeneratedFiles() {
synchronized(generatedFiles) {
Enumeration<File> enumeration = generatedFiles.elements();
while (enumeration.hasMoreElements()) {
File file = enumeration.nextElement();
file.delete();
}
generatedFiles.removeAllElements();
}
}
/**
* Release resources, if any.
*/
public void shutdown() {
main = null;
generatedFiles = null;
super.shutdown();
}
/**
* Return the formatted, localized string for a named error message
* and supplied arguments. For rmic error messages, with names that
* being with "rmic.", look up the error message in rmic's resource
* bundle; otherwise, defer to java's superclass method.
*/
public String errorString(String err,
Object arg0, Object arg1, Object arg2)
{
if (err.startsWith("rmic.") || err.startsWith("warn.rmic.")) {
String result = Main.getText(err,
(arg0 != null ? arg0.toString() : null),
(arg1 != null ? arg1.toString() : null),
(arg2 != null ? arg2.toString() : null));
if (err.startsWith("warn.")) {
result = "warning: " + result;
}
return result;
} else {
return super.errorString(err, arg0, arg1, arg2);
}
}
public void reset() {
}
/**
* Utility for building paths of directories and JAR files. This
* class was copied from com.sun.tools.javac.util.Paths as part of
* the fix for 6473331, which adds support for Class-Path manifest
* entries in JAR files. Diagnostic code is simply commented out
* because rmic silently ignored these conditions historically.
*/
private static class Path extends LinkedHashSet<String> {
private static final long serialVersionUID = 0;
private static final boolean warn = false;
private static class PathIterator implements Collection<String> {
private int pos = 0;
private final String path;
private final String emptyPathDefault;
public PathIterator(String path, String emptyPathDefault) {
this.path = path;
this.emptyPathDefault = emptyPathDefault;
}
public PathIterator(String path) { this(path, null); }
public Iterator<String> iterator() {
return new Iterator<String>() {
public boolean hasNext() {
return pos <= path.length();
}
public String next() {
int beg = pos;
int end = path.indexOf(File.pathSeparator, beg);
if (end == -1)
end = path.length();
pos = end + 1;
if (beg == end && emptyPathDefault != null)
return emptyPathDefault;
else
return path.substring(beg, end);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
// required for Collection.
public int size() {
throw new UnsupportedOperationException();
}
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
public boolean contains(Object o) {
throw new UnsupportedOperationException();
}
public Object[] toArray() {
throw new UnsupportedOperationException();
}
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException();
}
public boolean add(String o) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public boolean addAll(Collection<? extends String> c) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
public boolean equals(Object o) {
throw new UnsupportedOperationException();
}
public int hashCode() {
throw new UnsupportedOperationException();
}
}
/** Is this the name of a zip file? */
private static boolean isZip(String name) {
return new File(name).isFile();
}
private boolean expandJarClassPaths = false;
public Path expandJarClassPaths(boolean x) {
expandJarClassPaths = x;
return this;
}
/** What to use when path element is the empty string */
private String emptyPathDefault = null;
public Path emptyPathDefault(String x) {
emptyPathDefault = x;
return this;
}
public Path() { super(); }
public Path addDirectories(String dirs, boolean warn) {
if (dirs != null)
for (String dir : new PathIterator(dirs))
addDirectory(dir, warn);
return this;
}
public Path addDirectories(String dirs) {
return addDirectories(dirs, warn);
}
private void addDirectory(String dir, boolean warn) {
if (! new File(dir).isDirectory()) {
// if (warn)
// log.warning(Position.NOPOS,
// "dir.path.element.not.found", dir);
return;
}
for (String direntry : new File(dir).list()) {
String canonicalized = direntry.toLowerCase();
if (canonicalized.endsWith(".jar") ||
canonicalized.endsWith(".zip"))
addFile(dir + File.separator + direntry, warn);
}
}
public Path addFiles(String files, boolean warn) {
if (files != null)
for (String file : new PathIterator(files, emptyPathDefault))
addFile(file, warn);
return this;
}
public Path addFiles(String files) {
return addFiles(files, warn);
}
private void addFile(String file, boolean warn) {
if (contains(file)) {
/* Discard duplicates and avoid infinite recursion */
return;
}
File ele = new File(file);
if (! ele.exists()) {
/* No such file or directory exist */
if (warn)
// log.warning(Position.NOPOS,
// "path.element.not.found", file);
return;
}
if (ele.isFile()) {
/* File is an ordinay file */
String arcname = file.toLowerCase();
if (! (arcname.endsWith(".zip") ||
arcname.endsWith(".jar"))) {
/* File name don't have right extension */
// if (warn)
// log.warning(Position.NOPOS,
// "invalid.archive.file", file);
return;
}
}
/* Now what we have left is either a directory or a file name
confirming to archive naming convention */
super.add(file);
if (expandJarClassPaths && isZip(file))
addJarClassPath(file, warn);
}
// Adds referenced classpath elements from a jar's Class-Path
// Manifest entry. In some future release, we may want to
// update this code to recognize URLs rather than simple
// filenames, but if we do, we should redo all path-related code.
private void addJarClassPath(String jarFileName, boolean warn) {
try {
String jarParent = new File(jarFileName).getParent();
JarFile jar = new JarFile(jarFileName);
try {
Manifest man = jar.getManifest();
if (man == null) return;
Attributes attr = man.getMainAttributes();
if (attr == null) return;
String path = attr.getValue(Attributes.Name.CLASS_PATH);
if (path == null) return;
for (StringTokenizer st = new StringTokenizer(path);
st.hasMoreTokens();) {
String elt = st.nextToken();
if (jarParent != null)
elt = new File(jarParent, elt).getCanonicalPath();
addFile(elt, warn);
}
} finally {
jar.close();
}
} catch (IOException e) {
// log.error(Position.NOPOS,
// "error.reading.file", jarFileName,
// e.getLocalizedMessage());
}
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic;
import sun.tools.java.Identifier;
/**
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public interface Constants extends sun.tools.java.Constants {
/*
* Identifiers potentially useful for all Generators
*/
public static final Identifier idRemote =
Identifier.lookup("java.rmi.Remote");
public static final Identifier idRemoteException =
Identifier.lookup("java.rmi.RemoteException");
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic;
import java.io.File;
import sun.tools.java.ClassDefinition;
/**
* Generator defines the protocol for back-end implementations to be added
* to rmic. See the rmic.properties file for a description of the format for
* adding new Generators to rmic.
* <p>
* Classes implementing this interface must have a public default constructor
* which should set any required arguments to their defaults. When Main
* encounters a command line argument which maps to a specific Generator
* subclass, it will instantiate one and call parseArgs(...). At some later
* point, Main will invoke the generate(...) method once for _each_ class passed
* on the command line.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Bryan Atsatt
*/
public interface Generator {
/**
* Examine and consume command line arguments.
* @param argv The command line arguments. Ignore null
* and unknown arguments. Set each consumed argument to null.
* @param main Report any errors using the main.error() methods.
* @return true if no errors, false otherwise.
*/
public boolean parseArgs(String argv[], Main main);
/**
* Generate output. Any source files created which need compilation should
* be added to the compiler environment using the addGeneratedFile(File)
* method.
*
* @param env The compiler environment
* @param cdef The definition for the implementation class or interface from
* which to generate output
* @param destDir The directory for the root of the package hierarchy
* for generated files. May be null.
*/
public void generate(BatchEnvironment env, ClassDefinition cdef, File destDir);
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*****************************************************************************/
/* Copyright (c) IBM Corporation 1998 */
/* */
/* (C) Copyright IBM Corp. 1998 */
/* */
/*****************************************************************************/
package sun.rmi.rmic;
import java.io.Writer;
import java.io.BufferedWriter;
import java.io.IOException;
/**
* IndentingWriter is a BufferedWriter subclass that supports automatic
* indentation of lines of text written to the underlying Writer.
*
* Methods are provided for compact, convenient indenting, writing text,
* and writing lines in various combinations.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public class IndentingWriter extends BufferedWriter {
/** true if the next character written is the first on a line */
private boolean beginningOfLine = true;
/** current number of spaces to prepend to lines */
private int currentIndent = 0;
/** number of spaces to change indent when indenting in or out */
private int indentStep = 4;
/** number of spaces to convert into tabs. Use MAX_VALUE to disable */
private int tabSize = 8;
/**
* Create a new IndentingWriter that writes indented text to the
* given Writer. Use the default indent step of four spaces.
*/
public IndentingWriter(Writer out) {
super(out);
}
/**
* Create a new IndentingWriter that writes indented text to the
* given Writer and uses the supplied indent step.
*/
public IndentingWriter(Writer out, int step) {
this(out);
if (indentStep < 0)
throw new IllegalArgumentException("negative indent step");
indentStep = step;
}
/**
* Create a new IndentingWriter that writes indented text to the
* given Writer and uses the supplied indent step and tab size.
*/
public IndentingWriter(Writer out, int step, int tabSize) {
this(out);
if (indentStep < 0)
throw new IllegalArgumentException("negative indent step");
indentStep = step;
this.tabSize = tabSize;
}
/**
* Write a single character.
*/
public void write(int c) throws IOException {
checkWrite();
super.write(c);
}
/**
* Write a portion of an array of characters.
*/
public void write(char[] cbuf, int off, int len) throws IOException {
if (len > 0) {
checkWrite();
}
super.write(cbuf, off, len);
}
/**
* Write a portion of a String.
*/
public void write(String s, int off, int len) throws IOException {
if (len > 0) {
checkWrite();
}
super.write(s, off, len);
}
/**
* Write a line separator. The next character written will be
* preceded by an indent.
*/
public void newLine() throws IOException {
super.newLine();
beginningOfLine = true;
}
/**
* Check if an indent needs to be written before writing the next
* character.
*
* The indent generation is optimized (and made consistent with
* certain coding conventions) by condensing groups of eight spaces
* into tab characters.
*/
protected void checkWrite() throws IOException {
if (beginningOfLine) {
beginningOfLine = false;
int i = currentIndent;
while (i >= tabSize) {
super.write('\t');
i -= tabSize;
}
while (i > 0) {
super.write(' ');
-- i;
}
}
}
/**
* Increase the current indent by the indent step.
*/
protected void indentIn() {
currentIndent += indentStep;
}
/**
* Decrease the current indent by the indent step.
*/
protected void indentOut() {
currentIndent -= indentStep;
if (currentIndent < 0)
currentIndent = 0;
}
/**
* Indent in.
*/
public void pI() {
indentIn();
}
/**
* Indent out.
*/
public void pO() {
indentOut();
}
/**
* Write string.
*/
public void p(String s) throws IOException {
write(s);
}
/**
* End current line.
*/
public void pln() throws IOException {
newLine();
}
/**
* Write string; end current line.
*/
public void pln(String s) throws IOException {
p(s);
pln();
}
/**
* Write string; end current line; indent in.
*/
public void plnI(String s) throws IOException {
p(s);
pln();
pI();
}
/**
* Indent out; write string.
*/
public void pO(String s) throws IOException {
pO();
p(s);
}
/**
* Indent out; write string; end current line.
*/
public void pOln(String s) throws IOException {
pO(s);
pln();
}
/**
* Indent out; write string; end current line; indent in.
*
* This method is useful for generating lines of code that both
* end and begin nested blocks, like "} else {".
*/
public void pOlnI(String s) throws IOException {
pO(s);
pln();
pI();
}
/**
* Write Object.
*/
public void p(Object o) throws IOException {
write(o.toString());
}
/**
* Write Object; end current line.
*/
public void pln(Object o) throws IOException {
p(o.toString());
pln();
}
/**
* Write Object; end current line; indent in.
*/
public void plnI(Object o) throws IOException {
p(o.toString());
pln();
pI();
}
/**
* Indent out; write Object.
*/
public void pO(Object o) throws IOException {
pO();
p(o.toString());
}
/**
* Indent out; write Object; end current line.
*/
public void pOln(Object o) throws IOException {
pO(o.toString());
pln();
}
/**
* Indent out; write Object; end current line; indent in.
*
* This method is useful for generating lines of code that both
* end and begin nested blocks, like "} else {".
*/
public void pOlnI(Object o) throws IOException {
pO(o.toString());
pln();
pI();
}
}

View File

@@ -0,0 +1,897 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic;
import java.util.Vector;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.MissingResourceException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;
import sun.tools.java.ClassFile;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.ClassNotFound;
import sun.tools.java.Identifier;
import sun.tools.java.ClassPath;
import sun.tools.javac.SourceClass;
import sun.tools.util.CommandLine;
import java.lang.reflect.Constructor;
import java.util.Properties;
/**
* Main "rmic" program.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public class Main implements sun.rmi.rmic.Constants {
String sourcePathArg;
String sysClassPathArg;
String extDirsArg;
String classPathString;
File destDir;
int flags;
long tm;
Vector<String> classes;
boolean nowrite;
boolean nocompile;
boolean keepGenerated;
boolean status;
String[] generatorArgs;
Vector<Generator> generators;
Class<? extends BatchEnvironment> environmentClass =
BatchEnvironment.class;
boolean iiopGeneration = false;
/**
* Name of the program.
*/
String program;
/**
* The stream where error message are printed.
*/
OutputStream out;
/**
* Constructor.
*/
public Main(OutputStream out, String program) {
this.out = out;
this.program = program;
}
/**
* Output a message.
*/
public void output(String msg) {
PrintStream out =
this.out instanceof PrintStream ? (PrintStream)this.out
: new PrintStream(this.out, true);
out.println(msg);
}
/**
* Top level error message. This method is called when the
* environment could not be set up yet.
*/
public void error(String msg) {
output(getText(msg));
}
public void error(String msg, String arg1) {
output(getText(msg, arg1));
}
public void error(String msg, String arg1, String arg2) {
output(getText(msg, arg1, arg2));
}
/**
* Usage
*/
public void usage() {
error("rmic.usage", program);
}
/**
* Run the compiler
*/
public synchronized boolean compile(String argv[]) {
/*
* Handle internal option to use the new (and incomplete) rmic
* implementation. This option is handled here, rather than
* in parseArgs, so that none of the arguments will be nulled
* before delegating to the new implementation.
*/
for (int i = 0; i < argv.length; i++) {
if (argv[i].equals("-Xnew")) {
return (new sun.rmi.rmic.newrmic.Main(out,
program)).compile(argv);
}
}
if (!parseArgs(argv)) {
return false;
}
if (classes.size() == 0) {
usage();
return false;
}
if ((flags & F_WARNINGS) != 0) {
for (Generator g : generators) {
if (g instanceof RMIGenerator) {
output(getText("rmic.jrmp.stubs.deprecated", program));
break;
}
}
}
return doCompile();
}
/**
* Get the destination directory.
*/
public File getDestinationDir() {
return destDir;
}
/**
* Parse the arguments for compile.
*/
public boolean parseArgs(String argv[]) {
sourcePathArg = null;
sysClassPathArg = null;
extDirsArg = null;
classPathString = null;
destDir = null;
flags = F_WARNINGS;
tm = System.currentTimeMillis();
classes = new Vector<>();
nowrite = false;
nocompile = false;
keepGenerated = false;
generatorArgs = getArray("generator.args",true);
if (generatorArgs == null) {
return false;
}
generators = new Vector<>();
// Pre-process command line for @file arguments
try {
argv = CommandLine.parse(argv);
} catch (FileNotFoundException e) {
error("rmic.cant.read", e.getMessage());
return false;
} catch (IOException e) {
e.printStackTrace(out instanceof PrintStream ?
(PrintStream) out :
new PrintStream(out, true));
return false;
}
// Parse arguments
for (int i = 0 ; i < argv.length ; i++) {
if (argv[i] != null) {
if (argv[i].equals("-g")) {
flags &= ~F_OPT;
flags |= F_DEBUG_LINES | F_DEBUG_VARS;
argv[i] = null;
} else if (argv[i].equals("-O")) {
flags &= ~F_DEBUG_LINES;
flags &= ~F_DEBUG_VARS;
flags |= F_OPT | F_DEPENDENCIES;
argv[i] = null;
} else if (argv[i].equals("-nowarn")) {
flags &= ~F_WARNINGS;
argv[i] = null;
} else if (argv[i].equals("-debug")) {
flags |= F_DUMP;
argv[i] = null;
} else if (argv[i].equals("-depend")) {
flags |= F_DEPENDENCIES;
argv[i] = null;
} else if (argv[i].equals("-verbose")) {
flags |= F_VERBOSE;
argv[i] = null;
} else if (argv[i].equals("-nowrite")) {
nowrite = true;
argv[i] = null;
} else if (argv[i].equals("-Xnocompile")) {
nocompile = true;
keepGenerated = true;
argv[i] = null;
} else if (argv[i].equals("-keep") ||
argv[i].equals("-keepgenerated")) {
keepGenerated = true;
argv[i] = null;
} else if (argv[i].equals("-show")) {
error("rmic.option.unsupported", "-show");
usage();
return false;
} else if (argv[i].equals("-classpath")) {
if ((i + 1) < argv.length) {
if (classPathString != null) {
error("rmic.option.already.seen", "-classpath");
usage();
return false;
}
argv[i] = null;
classPathString = argv[++i];
argv[i] = null;
} else {
error("rmic.option.requires.argument", "-classpath");
usage();
return false;
}
} else if (argv[i].equals("-sourcepath")) {
if ((i + 1) < argv.length) {
if (sourcePathArg != null) {
error("rmic.option.already.seen", "-sourcepath");
usage();
return false;
}
argv[i] = null;
sourcePathArg = argv[++i];
argv[i] = null;
} else {
error("rmic.option.requires.argument", "-sourcepath");
usage();
return false;
}
} else if (argv[i].equals("-bootclasspath")) {
if ((i + 1) < argv.length) {
if (sysClassPathArg != null) {
error("rmic.option.already.seen", "-bootclasspath");
usage();
return false;
}
argv[i] = null;
sysClassPathArg = argv[++i];
argv[i] = null;
} else {
error("rmic.option.requires.argument", "-bootclasspath");
usage();
return false;
}
} else if (argv[i].equals("-extdirs")) {
if ((i + 1) < argv.length) {
if (extDirsArg != null) {
error("rmic.option.already.seen", "-extdirs");
usage();
return false;
}
argv[i] = null;
extDirsArg = argv[++i];
argv[i] = null;
} else {
error("rmic.option.requires.argument", "-extdirs");
usage();
return false;
}
} else if (argv[i].equals("-d")) {
if ((i + 1) < argv.length) {
if (destDir != null) {
error("rmic.option.already.seen", "-d");
usage();
return false;
}
argv[i] = null;
destDir = new File(argv[++i]);
argv[i] = null;
if (!destDir.exists()) {
error("rmic.no.such.directory", destDir.getPath());
usage();
return false;
}
} else {
error("rmic.option.requires.argument", "-d");
usage();
return false;
}
} else {
if (!checkGeneratorArg(argv,i)) {
usage();
return false;
}
}
}
}
// Now that all generators have had a chance at the args,
// scan what's left for classes and illegal args...
for (int i = 0; i < argv.length; i++) {
if (argv[i] != null) {
if (argv[i].startsWith("-")) {
error("rmic.no.such.option", argv[i]);
usage();
return false;
} else {
classes.addElement(argv[i]);
}
}
}
// If the generators vector is empty, add the default generator...
if (generators.size() == 0) {
addGenerator("default");
}
return true;
}
/**
* If this argument is for a generator, instantiate it, call
* parseArgs(...) and add generator to generators vector.
* Returns false on error.
*/
protected boolean checkGeneratorArg(String[] argv, int currentIndex) {
boolean result = true;
if (argv[currentIndex].startsWith("-")) {
String arg = argv[currentIndex].substring(1).toLowerCase(); // Remove '-'
for (int i = 0; i < generatorArgs.length; i++) {
if (arg.equalsIgnoreCase(generatorArgs[i])) {
// Got a match, add Generator and call parseArgs...
Generator gen = addGenerator(arg);
if (gen == null) {
return false;
}
result = gen.parseArgs(argv,this);
break;
}
}
}
return result;
}
/**
* Instantiate and add a generator to the generators array.
*/
protected Generator addGenerator(String arg) {
Generator gen;
// Create an instance of the generator and add it to
// the array...
String className = getString("generator.class." + arg);
if (className == null) {
error("rmic.missing.property",arg);
return null;
}
try {
gen = (Generator) Class.forName(className).newInstance();
} catch (Exception e) {
error("rmic.cannot.instantiate",className);
return null;
}
generators.addElement(gen);
// Get the environment required by this generator...
Class<?> envClass = BatchEnvironment.class;
String env = getString("generator.env." + arg);
if (env != null) {
try {
envClass = Class.forName(env);
// Is the new class a subclass of the current one?
if (environmentClass.isAssignableFrom(envClass)) {
// Yes, so switch to the new one...
environmentClass = envClass.asSubclass(BatchEnvironment.class);
} else {
// No. Is the current class a subclass of the
// new one?
if (!envClass.isAssignableFrom(environmentClass)) {
// No, so it's a conflict...
error("rmic.cannot.use.both",environmentClass.getName(),envClass.getName());
return null;
}
}
} catch (ClassNotFoundException e) {
error("rmic.class.not.found",env);
return null;
}
}
// If this is the iiop stub generator, cache
// that fact for the jrmp generator...
if (arg.equals("iiop")) {
iiopGeneration = true;
}
return gen;
}
/**
* Grab a resource string and parse it into an array of strings. Assumes
* comma separated list.
* @param name The resource name.
* @param mustExist If true, throws error if resource does not exist. If
* false and resource does not exist, returns zero element array.
*/
protected String[] getArray(String name, boolean mustExist) {
String[] result = null;
String value = getString(name);
if (value == null) {
if (mustExist) {
error("rmic.resource.not.found",name);
return null;
} else {
return new String[0];
}
}
StringTokenizer parser = new StringTokenizer(value,", \t\n\r", false);
int count = parser.countTokens();
result = new String[count];
for (int i = 0; i < count; i++) {
result[i] = parser.nextToken();
}
return result;
}
/**
* Get the correct type of BatchEnvironment
*/
public BatchEnvironment getEnv() {
ClassPath classPath =
BatchEnvironment.createClassPath(classPathString,
sysClassPathArg,
extDirsArg);
BatchEnvironment result = null;
try {
Class<?>[] ctorArgTypes = {OutputStream.class,ClassPath.class,Main.class};
Object[] ctorArgs = {out,classPath,this};
Constructor<? extends BatchEnvironment> constructor =
environmentClass.getConstructor(ctorArgTypes);
result = constructor.newInstance(ctorArgs);
result.reset();
}
catch (Exception e) {
error("rmic.cannot.instantiate",environmentClass.getName());
}
return result;
}
/**
* Do the compile with the switches and files already supplied
*/
public boolean doCompile() {
// Create batch environment
BatchEnvironment env = getEnv();
env.flags |= flags;
// Set the classfile version numbers
// Compat and 1.1 stubs must retain the old version number.
env.majorVersion = 45;
env.minorVersion = 3;
// Preload the "out of memory" error string just in case we run
// out of memory during the compile.
String noMemoryErrorString = getText("rmic.no.memory");
String stackOverflowErrorString = getText("rmic.stack.overflow");
try {
/** Load the classes on the command line
* Replace the entries in classes with the ClassDefinition for the class
*/
for (int i = classes.size()-1; i >= 0; i-- ) {
Identifier implClassName =
Identifier.lookup(classes.elementAt(i));
/*
* Fix bugid 4049354: support using '.' as an inner class
* qualifier on the command line (previously, only mangled
* inner class names were understood, like "pkg.Outer$Inner").
*
* The following method, also used by "javap", resolves the
* given unmangled inner class name to the appropriate
* internal identifier. For example, it translates
* "pkg.Outer.Inner" to "pkg.Outer. Inner".
*/
implClassName = env.resolvePackageQualifiedName(implClassName);
/*
* But if we use such an internal inner class name identifier
* to load the class definition, the Java compiler will notice
* if the impl class is a "private" inner class and then deny
* skeletons (needed unless "-v1.2" is used) the ability to
* cast to it. To work around this problem, we mangle inner
* class name identifiers to their binary "outer" class name:
* "pkg.Outer. Inner" becomes "pkg.Outer$Inner".
*/
implClassName = Names.mangleClass(implClassName);
ClassDeclaration decl = env.getClassDeclaration(implClassName);
try {
ClassDefinition def = decl.getClassDefinition(env);
for (int j = 0; j < generators.size(); j++) {
Generator gen = generators.elementAt(j);
gen.generate(env, def, destDir);
}
} catch (ClassNotFound ex) {
env.error(0, "rmic.class.not.found", implClassName);
}
}
// compile all classes that need compilation
if (!nocompile) {
compileAllClasses(env);
}
} catch (OutOfMemoryError ee) {
// The compiler has run out of memory. Use the error string
// which we preloaded.
env.output(noMemoryErrorString);
return false;
} catch (StackOverflowError ee) {
env.output(stackOverflowErrorString);
return false;
} catch (Error ee) {
// We allow the compiler to take an exception silently if a program
// error has previously been detected. Presumably, this makes the
// compiler more robust in the face of bad error recovery.
if (env.nerrors == 0 || env.dump()) {
env.error(0, "fatal.error");
ee.printStackTrace(out instanceof PrintStream ?
(PrintStream) out :
new PrintStream(out, true));
}
} catch (Exception ee) {
if (env.nerrors == 0 || env.dump()) {
env.error(0, "fatal.exception");
ee.printStackTrace(out instanceof PrintStream ?
(PrintStream) out :
new PrintStream(out, true));
}
}
env.flushErrors();
boolean status = true;
if (env.nerrors > 0) {
String msg = "";
if (env.nerrors > 1) {
msg = getText("rmic.errors", env.nerrors);
} else {
msg = getText("rmic.1error");
}
if (env.nwarnings > 0) {
if (env.nwarnings > 1) {
msg += ", " + getText("rmic.warnings", env.nwarnings);
} else {
msg += ", " + getText("rmic.1warning");
}
}
output(msg);
status = false;
} else {
if (env.nwarnings > 0) {
if (env.nwarnings > 1) {
output(getText("rmic.warnings", env.nwarnings));
} else {
output(getText("rmic.1warning"));
}
}
}
// last step is to delete generated source files
if (!keepGenerated) {
env.deleteGeneratedFiles();
}
// We're done
if (env.verbose()) {
tm = System.currentTimeMillis() - tm;
output(getText("rmic.done_in", Long.toString(tm)));
}
// Shutdown the environment object and release our resources.
// Note that while this is unneccessary when rmic is invoked
// the command line, there are environments in which rmic
// from is invoked within a server process, so resource
// reclamation is important...
env.shutdown();
sourcePathArg = null;
sysClassPathArg = null;
extDirsArg = null;
classPathString = null;
destDir = null;
classes = null;
generatorArgs = null;
generators = null;
environmentClass = null;
program = null;
out = null;
return status;
}
/*
* Compile all classes that need to be compiled.
*/
public void compileAllClasses (BatchEnvironment env)
throws ClassNotFound,
IOException,
InterruptedException {
ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);
boolean done;
do {
done = true;
for (Enumeration<?> e = env.getClasses() ; e.hasMoreElements() ; ) {
ClassDeclaration c = (ClassDeclaration)e.nextElement();
done = compileClass(c,buf,env);
}
} while (!done);
}
/*
* Compile a single class.
* Fallthrough is intentional
*/
@SuppressWarnings("fallthrough")
public boolean compileClass (ClassDeclaration c,
ByteArrayOutputStream buf,
BatchEnvironment env)
throws ClassNotFound,
IOException,
InterruptedException {
boolean done = true;
env.flushErrors();
SourceClass src;
switch (c.getStatus()) {
case CS_UNDEFINED:
{
if (!env.dependencies()) {
break;
}
// fall through
}
case CS_SOURCE:
{
done = false;
env.loadDefinition(c);
if (c.getStatus() != CS_PARSED) {
break;
}
// fall through
}
case CS_PARSED:
{
if (c.getClassDefinition().isInsideLocal()) {
break;
}
// If we get to here, then compilation is going
// to occur. If the -Xnocompile switch is set
// then fail. Note that this check is required
// here because this method is called from
// generators, not just from within this class...
if (nocompile) {
throw new IOException("Compilation required, but -Xnocompile option in effect");
}
done = false;
src = (SourceClass)c.getClassDefinition(env);
src.check(env);
c.setDefinition(src, CS_CHECKED);
// fall through
}
case CS_CHECKED:
{
src = (SourceClass)c.getClassDefinition(env);
// bail out if there were any errors
if (src.getError()) {
c.setDefinition(src, CS_COMPILED);
break;
}
done = false;
buf.reset();
src.compile(buf);
c.setDefinition(src, CS_COMPILED);
src.cleanup(env);
if (src.getError() || nowrite) {
break;
}
String pkgName = c.getName().getQualifier().toString().replace('.', File.separatorChar);
String className = c.getName().getFlatName().toString().replace('.', SIGC_INNERCLASS) + ".class";
File file;
if (destDir != null) {
if (pkgName.length() > 0) {
file = new File(destDir, pkgName);
if (!file.exists()) {
file.mkdirs();
}
file = new File(file, className);
} else {
file = new File(destDir, className);
}
} else {
ClassFile classfile = (ClassFile)src.getSource();
if (classfile.isZipped()) {
env.error(0, "cant.write", classfile.getPath());
break;
}
file = new File(classfile.getPath());
file = new File(file.getParent(), className);
}
// Create the file
try {
FileOutputStream out = new FileOutputStream(file.getPath());
buf.writeTo(out);
out.close();
if (env.verbose()) {
output(getText("rmic.wrote", file.getPath()));
}
} catch (IOException ee) {
env.error(0, "cant.write", file.getPath());
}
}
}
return done;
}
/**
* Main program
*/
public static void main(String argv[]) {
Main compiler = new Main(System.out, "rmic");
System.exit(compiler.compile(argv) ? 0 : 1);
}
/**
* Return the string value of a named resource in the rmic.properties
* resource bundle. If the resource is not found, null is returned.
*/
public static String getString(String key) {
if (!resourcesInitialized) {
initResources();
}
// To enable extensions, search the 'resourcesExt'
// bundle first, followed by the 'resources' bundle...
if (resourcesExt != null) {
try {
return resourcesExt.getString(key);
} catch (MissingResourceException e) {}
}
try {
return resources.getString(key);
} catch (MissingResourceException ignore) {
}
return null;
}
private static boolean resourcesInitialized = false;
private static ResourceBundle resources;
private static ResourceBundle resourcesExt = null;
private static void initResources() {
try {
resources =
ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic");
resourcesInitialized = true;
try {
resourcesExt =
ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext");
} catch (MissingResourceException e) {}
} catch (MissingResourceException e) {
throw new Error("fatal: missing resource bundle: " +
e.getClassName());
}
}
public static String getText(String key) {
String message = getString(key);
if (message == null) {
message = "no text found: \"" + key + "\"";
}
return message;
}
public static String getText(String key, int num) {
return getText(key, Integer.toString(num), null, null);
}
public static String getText(String key, String arg0) {
return getText(key, arg0, null, null);
}
public static String getText(String key, String arg0, String arg1) {
return getText(key, arg0, arg1, null);
}
public static String getText(String key,
String arg0, String arg1, String arg2)
{
String format = getString(key);
if (format == null) {
format = "no text found: key = \"" + key + "\", " +
"arguments = \"{0}\", \"{1}\", \"{2}\"";
}
String[] args = new String[3];
args[0] = (arg0 != null ? arg0 : "null");
args[1] = (arg1 != null ? arg1 : "null");
args[2] = (arg2 != null ? arg2 : "null");
return java.text.MessageFormat.format(format, (Object[]) args);
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic;
import sun.tools.java.Identifier;
/**
* Names provides static utility methods used by other rmic classes
* for dealing with identifiers.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public class Names {
/**
* Return stub class name for impl class name.
*/
static final public Identifier stubFor(Identifier name) {
return Identifier.lookup(name + "_Stub");
}
/**
* Return skeleton class name for impl class name.
*/
static final public Identifier skeletonFor(Identifier name) {
return Identifier.lookup(name + "_Skel");
}
/**
* If necessary, convert a class name to its mangled form, i.e. the
* non-inner class name used in the binary representation of
* inner classes. This is necessary to be able to name inner
* classes in the generated source code in places where the language
* does not permit it, such as when synthetically defining an inner
* class outside of its outer class, and for generating file names
* corresponding to inner classes.
*
* Currently this mangling involves modifying the internal names of
* inner classes by converting occurrences of ". " into "$".
*
* This code is taken from the "mangleInnerType" method of
* the "sun.tools.java.Type" class; this method cannot be accessed
* itself because it is package protected.
*/
static final public Identifier mangleClass(Identifier className) {
if (!className.isInner())
return className;
/*
* Get '.' qualified inner class name (with outer class
* qualification and no package qualification) and replace
* each '.' with '$'.
*/
Identifier mangled = Identifier.lookup(
className.getFlatName().toString()
.replace('.', sun.tools.java.Constants.SIGC_INNERCLASS));
if (mangled.isInner())
throw new Error("failed to mangle inner class name");
// prepend package qualifier back for returned identifier
return Identifier.lookup(className.getQualifier(), mangled);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic;
import sun.tools.java.Identifier;
/**
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public interface RMIConstants extends sun.rmi.rmic.Constants {
/*
* identifiers for RMI classes referenced by rmic
*/
public static final Identifier idRemoteObject =
Identifier.lookup("java.rmi.server.RemoteObject");
public static final Identifier idRemoteStub =
Identifier.lookup("java.rmi.server.RemoteStub");
public static final Identifier idRemoteRef =
Identifier.lookup("java.rmi.server.RemoteRef");
public static final Identifier idOperation =
Identifier.lookup("java.rmi.server.Operation");
public static final Identifier idSkeleton =
Identifier.lookup("java.rmi.server.Skeleton");
public static final Identifier idSkeletonMismatchException =
Identifier.lookup("java.rmi.server.SkeletonMismatchException");
public static final Identifier idRemoteCall =
Identifier.lookup("java.rmi.server.RemoteCall");
public static final Identifier idMarshalException =
Identifier.lookup("java.rmi.MarshalException");
public static final Identifier idUnmarshalException =
Identifier.lookup("java.rmi.UnmarshalException");
public static final Identifier idUnexpectedException =
Identifier.lookup("java.rmi.UnexpectedException");
/*
* stub protocol versions
*/
public static final int STUB_VERSION_1_1 = 1;
public static final int STUB_VERSION_FAT = 2;
public static final int STUB_VERSION_1_2 = 3;
/** serialVersionUID for all stubs that can use 1.2 protocol */
public static final long STUB_SERIAL_VERSION_UID = 2;
/** version number used to seed interface hash computation */
public static final int INTERFACE_HASH_STUB_VERSION = 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,876 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.MessageDigest;
import java.security.DigestOutputStream;
import java.security.NoSuchAlgorithmException;
import sun.tools.java.Type;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.MemberDefinition;
import sun.tools.java.Identifier;
import sun.tools.java.ClassNotFound;
/**
* A RemoteClass object encapsulates RMI-specific information about
* a remote implementation class, i.e. a class that implements
* one or more remote interfaces.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
*/
public class RemoteClass implements sun.rmi.rmic.RMIConstants {
/**
* Create a RemoteClass object representing the remote meta-information
* of the given class.
*
* Returns true if successful. If the class is not a properly formed
* remote implementation class or if some other error occurs, the
* return value will be null, and errors will have been reported to
* the supplied BatchEnvironment.
*/
public static RemoteClass forClass(BatchEnvironment env,
ClassDefinition implClassDef)
{
RemoteClass rc = new RemoteClass(env, implClassDef);
if (rc.initialize()) {
return rc;
} else {
return null;
}
}
/**
* Return the ClassDefinition for this class.
*/
public ClassDefinition getClassDefinition() {
return implClassDef;
}
/**
* Return the name of the class represented by this object.
*/
public Identifier getName() {
return implClassDef.getName();
}
/**
* Return an array of ClassDefinitions representing all of the remote
* interfaces implemented by this class.
*
* A remote interface is any interface that extends Remote,
* directly or indirectly. The remote interfaces of a class
* are the interfaces directly listed in either the class's
* "implements" clause, or the "implements" clause of any
* of its superclasses, that are remote interfaces.
*
* The order of the array returned is arbitrary, and some elements
* may be superfluous (i.e., superinterfaces of other interfaces
* in the array).
*/
public ClassDefinition[] getRemoteInterfaces() {
return remoteInterfaces.clone();
}
/**
* Return an array of RemoteClass.Method objects representing all of
* the remote methods implemented by this class, i.e. all of the
* methods in the class's remote interfaces.
*
* The methods in the array are ordered according to the comparision
* of the strings consisting of their method name followed by their
* type signature, so each method's index in the array corresponds
* to its "operation number" in the JDK 1.1 version of the
* stub/skeleton protocol.
*/
public Method[] getRemoteMethods() {
return remoteMethods.clone();
}
/**
* Return the "interface hash" used to match a stub/skeleton pair for
* this class in the JDK 1.1 version of the stub/skeleton protocol.
*/
public long getInterfaceHash() {
return interfaceHash;
}
/**
* Return string representation of this object, consisting of
* the string "remote class " followed by the class name.
*/
public String toString() {
return "remote class " + implClassDef.getName().toString();
}
/** rmic environment for this object */
private BatchEnvironment env;
/** the remote implementation class this object corresponds to */
private ClassDefinition implClassDef;
/** remote interfaces implemented by this class */
private ClassDefinition[] remoteInterfaces;
/** all the remote methods of this class */
private Method[] remoteMethods;
/** stub/skeleton "interface hash" for this class */
private long interfaceHash;
/** cached definition for certain classes used in this environment */
private ClassDefinition defRemote;
private ClassDefinition defException;
private ClassDefinition defRemoteException;
/**
* Create a RemoteClass instance for the given class. The resulting
* object is not yet initialized.
*/
private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {
this.env = env;
this.implClassDef = implClassDef;
}
/**
* Validate that the remote implementation class is properly formed
* and fill in the data structures required by the public interface.
*/
private boolean initialize() {
/*
* Verify that the "impl" is really a class, not an interface.
*/
if (implClassDef.isInterface()) {
env.error(0, "rmic.cant.make.stubs.for.interface",
implClassDef.getName());
return false;
}
/*
* Initialize cached definitions for the Remote interface and
* the RemoteException class.
*/
try {
defRemote =
env.getClassDeclaration(idRemote).getClassDefinition(env);
defException =
env.getClassDeclaration(idJavaLangException).
getClassDefinition(env);
defRemoteException =
env.getClassDeclaration(idRemoteException).
getClassDefinition(env);
} catch (ClassNotFound e) {
env.error(0, "rmic.class.not.found", e.name);
return false;
}
/*
* Here we find all of the remote interfaces of our remote
* implementation class. For each class up the superclass
* chain, add each directly-implemented interface that
* somehow extends Remote to a list.
*/
Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found
new Vector<ClassDefinition>();
for (ClassDefinition classDef = implClassDef;
classDef != null;)
{
try {
ClassDeclaration[] interfaces = classDef.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
ClassDefinition interfaceDef =
interfaces[i].getClassDefinition(env);
/*
* Add interface to the list if it extends Remote and
* it is not already there.
*/
if (!remotesImplemented.contains(interfaceDef) &&
defRemote.implementedBy(env, interfaces[i]))
{
remotesImplemented.addElement(interfaceDef);
/***** <DEBUG> */
if (env.verbose()) {
System.out.println("[found remote interface: " +
interfaceDef.getName() + "]");
/***** </DEBUG> */
}
}
}
/*
* Verify that the candidate remote implementation class
* implements at least one remote interface directly.
*/
if (classDef == implClassDef && remotesImplemented.isEmpty()) {
if (defRemote.implementedBy(env,
implClassDef.getClassDeclaration()))
{
/*
* This error message is used if the class does
* implement a remote interface through one of
* its superclasses, but not directly.
*/
env.error(0, "rmic.must.implement.remote.directly",
implClassDef.getName());
} else {
/*
* This error message is used if the class never
* implements a remote interface.
*/
env.error(0, "rmic.must.implement.remote",
implClassDef.getName());
}
return false;
}
/*
* Get definition for next superclass.
*/
classDef = (classDef.getSuperClass() != null ?
classDef.getSuperClass().getClassDefinition(env) :
null);
} catch (ClassNotFound e) {
env.error(0, "class.not.found", e.name, classDef.getName());
return false;
}
}
/*
* The "remotesImplemented" vector now contains all of the remote
* interfaces directly implemented by the remote class or by any
* of its superclasses.
*
* At this point, we could optimize the list by removing superfluous
* entries, i.e. any interfaces that are implemented by some other
* interface in the list anyway.
*
* This should be correct; would it be worthwhile?
*
* for (int i = 0; i < remotesImplemented.size();) {
* ClassDefinition interfaceDef =
* (ClassDefinition) remotesImplemented.elementAt(i);
* boolean isOtherwiseImplemented = false;
* for (int j = 0; j < remotesImplemented.size; j++) {
* if (j != i &&
* interfaceDef.implementedBy(env, (ClassDefinition)
* remotesImplemented.elementAt(j).
* getClassDeclaration()))
* {
* isOtherwiseImplemented = true;
* break;
* }
* }
* if (isOtherwiseImplemented) {
* remotesImplemented.removeElementAt(i);
* } else {
* ++i;
* }
* }
*/
/*
* Now we collect the methods from all of the remote interfaces
* into a hashtable.
*/
Hashtable<String, Method> methods = new Hashtable<String, Method>();
boolean errors = false;
for (Enumeration<ClassDefinition> enumeration
= remotesImplemented.elements();
enumeration.hasMoreElements();)
{
ClassDefinition interfaceDef = enumeration.nextElement();
if (!collectRemoteMethods(interfaceDef, methods))
errors = true;
}
if (errors)
return false;
/*
* Convert vector of remote interfaces to an array
* (order is not important for this array).
*/
remoteInterfaces = new ClassDefinition[remotesImplemented.size()];
remotesImplemented.copyInto(remoteInterfaces);
/*
* Sort table of remote methods into an array. The elements are
* sorted in ascending order of the string of the method's name
* and type signature, so that each elements index is equal to
* its operation number of the JDK 1.1 version of the stub/skeleton
* protocol.
*/
String[] orderedKeys = new String[methods.size()];
int count = 0;
for (Enumeration<Method> enumeration = methods.elements();
enumeration.hasMoreElements();)
{
Method m = enumeration.nextElement();
String key = m.getNameAndDescriptor();
int i;
for (i = count; i > 0; --i) {
if (key.compareTo(orderedKeys[i - 1]) >= 0) {
break;
}
orderedKeys[i] = orderedKeys[i - 1];
}
orderedKeys[i] = key;
++count;
}
remoteMethods = new Method[methods.size()];
for (int i = 0; i < remoteMethods.length; i++) {
remoteMethods[i] = methods.get(orderedKeys[i]);
/***** <DEBUG> */
if (env.verbose()) {
System.out.print("[found remote method <" + i + ">: " +
remoteMethods[i].getOperationString());
ClassDeclaration[] exceptions =
remoteMethods[i].getExceptions();
if (exceptions.length > 0)
System.out.print(" throws ");
for (int j = 0; j < exceptions.length; j++) {
if (j > 0)
System.out.print(", ");
System.out.print(exceptions[j].getName());
}
System.out.println("]");
}
/***** </DEBUG> */
}
/**
* Finally, pre-compute the interface hash to be used by
* stubs/skeletons for this remote class.
*/
interfaceHash = computeInterfaceHash();
return true;
}
/**
* Collect and validate all methods from given interface and all of
* its superinterfaces as remote methods. Remote methods are added
* to the supplied hashtable. Returns true if successful,
* or false if an error occurred.
*/
private boolean collectRemoteMethods(ClassDefinition interfaceDef,
Hashtable<String, Method> table)
{
if (!interfaceDef.isInterface()) {
throw new Error(
"expected interface, not class: " + interfaceDef.getName());
}
/*
* rmic used to enforce that a remote interface could not extend
* a non-remote interface, i.e. an interface that did not itself
* extend from Remote. The current version of rmic does not have
* this restriction, so the following code is now commented out.
*
* Verify that this interface extends Remote, since all interfaces
* extended by a remote interface must implement Remote.
*
* try {
* if (!defRemote.implementedBy(env,
* interfaceDef.getClassDeclaration()))
* {
* env.error(0, "rmic.can.mix.remote.nonremote",
* interfaceDef.getName());
* return false;
* }
* } catch (ClassNotFound e) {
* env.error(0, "class.not.found", e.name,
* interfaceDef.getName());
* return false;
* }
*/
boolean errors = false;
/*
* Search interface's members for methods.
*/
nextMember:
for (MemberDefinition member = interfaceDef.getFirstMember();
member != null;
member = member.getNextMember())
{
if (member.isMethod() &&
!member.isConstructor() && !member.isInitializer())
{
/*
* Verify that each method throws RemoteException.
*/
ClassDeclaration[] exceptions = member.getExceptions(env);
boolean hasRemoteException = false;
for (int i = 0; i < exceptions.length; i++) {
/*
* rmic used to enforce that a remote method had to
* explicitly list RemoteException in its "throws"
* clause; i.e., just throwing Exception was not
* acceptable. The current version of rmic does not
* have this restriction, so the following code is
* now commented out. Instead, the method is
* considered valid if RemoteException is a subclass
* of any of the methods declared exceptions.
*
* if (exceptions[i].getName().equals(
* idRemoteException))
* {
* hasRemoteException = true;
* break;
* }
*/
try {
if (defRemoteException.subClassOf(
env, exceptions[i]))
{
hasRemoteException = true;
break;
}
} catch (ClassNotFound e) {
env.error(0, "class.not.found", e.name,
interfaceDef.getName());
continue nextMember;
}
}
/*
* If this method did not throw RemoteException as required,
* generate the error but continue, so that multiple such
* errors can be reported.
*/
if (!hasRemoteException) {
env.error(0, "rmic.must.throw.remoteexception",
interfaceDef.getName(), member.toString());
errors = true;
continue nextMember;
}
/*
* Verify that the implementation of this method throws only
* java.lang.Exception or its subclasses (fix bugid 4092486).
* JRMP does not support remote methods throwing
* java.lang.Throwable or other subclasses.
*/
try {
MemberDefinition implMethod = implClassDef.findMethod(
env, member.getName(), member.getType());
if (implMethod != null) { // should not be null
exceptions = implMethod.getExceptions(env);
for (int i = 0; i < exceptions.length; i++) {
if (!defException.superClassOf(
env, exceptions[i]))
{
env.error(0, "rmic.must.only.throw.exception",
implMethod.toString(),
exceptions[i].getName());
errors = true;
continue nextMember;
}
}
}
} catch (ClassNotFound e) {
env.error(0, "class.not.found", e.name,
implClassDef.getName());
continue nextMember;
}
/*
* Create RemoteClass.Method object to represent this method
* found in a remote interface.
*/
Method newMethod = new Method(member);
/*
* Store remote method's representation in the table of
* remote methods found, keyed by its name and parameter
* signature.
*
* If the table already contains an entry with the same
* method name and parameter signature, then we must
* replace the old entry with a Method object that
* represents a legal combination of the old and the new
* methods; specifically, the combined method must have
* a throws list that contains (only) all of the checked
* exceptions that can be thrown by both the old or
* the new method (see bugid 4070653).
*/
String key = newMethod.getNameAndDescriptor();
Method oldMethod = table.get(key);
if (oldMethod != null) {
newMethod = newMethod.mergeWith(oldMethod);
if (newMethod == null) {
errors = true;
continue nextMember;
}
}
table.put(key, newMethod);
}
}
/*
* Recursively collect methods for all superinterfaces.
*/
try {
ClassDeclaration[] superDefs = interfaceDef.getInterfaces();
for (int i = 0; i < superDefs.length; i++) {
ClassDefinition superDef =
superDefs[i].getClassDefinition(env);
if (!collectRemoteMethods(superDef, table))
errors = true;
}
} catch (ClassNotFound e) {
env.error(0, "class.not.found", e.name, interfaceDef.getName());
return false;
}
return !errors;
}
/**
* Compute the "interface hash" of the stub/skeleton pair for this
* remote implementation class. This is the 64-bit value used to
* enforce compatibility between a stub and a skeleton using the
* JDK 1.1 version of the stub/skeleton protocol.
*
* It is calculated using the first 64 bits of a SHA digest. The
* digest is from a stream consisting of the following data:
* (int) stub version number, always 1
* for each remote method, in order of operation number:
* (UTF) method name
* (UTF) method type signature
* for each declared exception, in alphabetical name order:
* (UTF) name of exception class
*
*/
private long computeInterfaceHash() {
long hash = 0;
ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DataOutputStream out = new DataOutputStream(
new DigestOutputStream(sink, md));
out.writeInt(INTERFACE_HASH_STUB_VERSION);
for (int i = 0; i < remoteMethods.length; i++) {
MemberDefinition m = remoteMethods[i].getMemberDefinition();
Identifier name = m.getName();
Type type = m.getType();
out.writeUTF(name.toString());
// type signatures already use mangled class names
out.writeUTF(type.getTypeSignature());
ClassDeclaration exceptions[] = m.getExceptions(env);
sortClassDeclarations(exceptions);
for (int j = 0; j < exceptions.length; j++) {
out.writeUTF(Names.mangleClass(
exceptions[j].getName()).toString());
}
}
out.flush();
// use only the first 64 bits of the digest for the hash
byte hashArray[] = md.digest();
for (int i = 0; i < Math.min(8, hashArray.length); i++) {
hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
}
} catch (IOException e) {
throw new Error(
"unexpected exception computing intetrface hash: " + e);
} catch (NoSuchAlgorithmException e) {
throw new Error(
"unexpected exception computing intetrface hash: " + e);
}
return hash;
}
/**
* Sort array of class declarations alphabetically by their mangled
* fully-qualified class name. This is used to feed a method's exceptions
* in a canonical order into the digest stream for the interface hash
* computation.
*/
private void sortClassDeclarations(ClassDeclaration[] decl) {
for (int i = 1; i < decl.length; i++) {
ClassDeclaration curr = decl[i];
String name = Names.mangleClass(curr.getName()).toString();
int j;
for (j = i; j > 0; j--) {
if (name.compareTo(
Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)
{
break;
}
decl[j] = decl[j - 1];
}
decl[j] = curr;
}
}
/**
* A RemoteClass.Method object encapsulates RMI-specific information
* about a particular remote method in the remote implementation class
* represented by the outer instance.
*/
public class Method implements Cloneable {
/**
* Return the definition of the actual class member corresponing
* to this method of a remote interface.
*
* REMIND: Can this method be removed?
*/
public MemberDefinition getMemberDefinition() {
return memberDef;
}
/**
* Return the name of this method.
*/
public Identifier getName() {
return memberDef.getName();
}
/**
* Return the type of this method.
*/
public Type getType() {
return memberDef.getType();
}
/**
* Return an array of the exception classes declared to be
* thrown by this remote method.
*
* For methods with the same name and type signature inherited
* from multiple remote interfaces, the array will contain
* the set of exceptions declared in all of the interfaces'
* methods that can be legally thrown in each of them.
*/
public ClassDeclaration[] getExceptions() {
return exceptions.clone();
}
/**
* Return the "method hash" used to identify this remote method
* in the JDK 1.2 version of the stub protocol.
*/
public long getMethodHash() {
return methodHash;
}
/**
* Return the string representation of this method.
*/
public String toString() {
return memberDef.toString();
}
/**
* Return the string representation of this method appropriate
* for the construction of a java.rmi.server.Operation object.
*/
public String getOperationString() {
return memberDef.toString();
}
/**
* Return a string consisting of this method's name followed by
* its method descriptor, using the Java VM's notation for
* method descriptors (see section 4.3.3 of The Java Virtual
* Machine Specification).
*/
public String getNameAndDescriptor() {
return memberDef.getName().toString() +
memberDef.getType().getTypeSignature();
}
/**
* Member definition for this method, from one of the remote
* interfaces that this method was found in.
*
* Note that this member definition may be only one of several
* member defintions that correspond to this remote method object,
* if several of this class's remote interfaces contain methods
* with the same name and type signature. Therefore, this member
* definition may declare more exceptions thrown that this remote
* method does.
*/
private MemberDefinition memberDef;
/** stub "method hash" to identify this method */
private long methodHash;
/**
* Exceptions declared to be thrown by this remote method.
*
* This list can include superfluous entries, such as
* unchecked exceptions and subclasses of other entries.
*/
private ClassDeclaration[] exceptions;
/**
* Create a new Method object corresponding to the given
* method definition.
*/
/*
* Temporarily comment out the private modifier until
* the VM allows outer class to access inner class's
* private constructor
*/
/* private */ Method(MemberDefinition memberDef) {
this.memberDef = memberDef;
exceptions = memberDef.getExceptions(env);
methodHash = computeMethodHash();
}
/**
* Cloning is supported by returning a shallow copy of this object.
*/
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new Error("clone failed");
}
}
/**
* Return a new Method object that is a legal combination of
* this method object and another one.
*
* This requires determining the exceptions declared by the
* combined method, which must be (only) all of the exceptions
* declared in both old Methods that may thrown in either of
* them.
*/
private Method mergeWith(Method other) {
if (!getName().equals(other.getName()) ||
!getType().equals(other.getType()))
{
throw new Error("attempt to merge method \"" +
other.getNameAndDescriptor() + "\" with \"" +
getNameAndDescriptor());
}
Vector<ClassDeclaration> legalExceptions
= new Vector<ClassDeclaration>();
try {
collectCompatibleExceptions(
other.exceptions, exceptions, legalExceptions);
collectCompatibleExceptions(
exceptions, other.exceptions, legalExceptions);
} catch (ClassNotFound e) {
env.error(0, "class.not.found", e.name,
getClassDefinition().getName());
return null;
}
Method merged = (Method) clone();
merged.exceptions = new ClassDeclaration[legalExceptions.size()];
legalExceptions.copyInto(merged.exceptions);
return merged;
}
/**
* Add to the supplied list all exceptions in the "from" array
* that are subclasses of an exception in the "with" array.
*/
private void collectCompatibleExceptions(ClassDeclaration[] from,
ClassDeclaration[] with,
Vector<ClassDeclaration> list)
throws ClassNotFound
{
for (int i = 0; i < from.length; i++) {
ClassDefinition exceptionDef = from[i].getClassDefinition(env);
if (!list.contains(from[i])) {
for (int j = 0; j < with.length; j++) {
if (exceptionDef.subClassOf(env, with[j])) {
list.addElement(from[i]);
break;
}
}
}
}
}
/**
* Compute the "method hash" of this remote method. The method
* hash is a long containing the first 64 bits of the SHA digest
* from the UTF encoded string of the method name and descriptor.
*
* REMIND: Should this method share implementation code with
* the outer class's computeInterfaceHash() method?
*/
private long computeMethodHash() {
long hash = 0;
ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DataOutputStream out = new DataOutputStream(
new DigestOutputStream(sink, md));
String methodString = getNameAndDescriptor();
/***** <DEBUG> */
if (env.verbose()) {
System.out.println("[string used for method hash: \"" +
methodString + "\"]");
}
/***** </DEBUG> */
out.writeUTF(methodString);
// use only the first 64 bits of the digest for the hash
out.flush();
byte hashArray[] = md.digest();
for (int i = 0; i < Math.min(8, hashArray.length); i++) {
hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
}
} catch (IOException e) {
throw new Error(
"unexpected exception computing intetrface hash: " + e);
} catch (NoSuchAlgorithmException e) {
throw new Error(
"unexpected exception computing intetrface hash: " + e);
}
return hash;
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic;
import java.io.File;
import sun.tools.java.Identifier;
/**
* Util provides static utility methods used by other rmic classes.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Bryan Atsatt
*/
public class Util implements sun.rmi.rmic.Constants {
/**
* Return the directory that should be used for output for a given
* class.
* @param theClass The fully qualified name of the class.
* @param rootDir The directory to use as the root of the
* package hierarchy. May be null, in which case the current
* working directory is used as the root.
*/
public static File getOutputDirectoryFor(Identifier theClass,
File rootDir,
BatchEnvironment env) {
File outputDir = null;
String className = theClass.getFlatName().toString().replace('.', SIGC_INNERCLASS);
String qualifiedClassName = className;
String packagePath = null;
String packageName = theClass.getQualifier().toString();
if (packageName.length() > 0) {
qualifiedClassName = packageName + "." + className;
packagePath = packageName.replace('.', File.separatorChar);
}
// Do we have a root directory?
if (rootDir != null) {
// Yes, do we have a package name?
if (packagePath != null) {
// Yes, so use it as the root. Open the directory...
outputDir = new File(rootDir, packagePath);
// Make sure the directory exists...
ensureDirectory(outputDir,env);
} else {
// Default package, so use root as output dir...
outputDir = rootDir;
}
} else {
// No root directory. Get the current working directory...
String workingDirPath = System.getProperty("user.dir");
File workingDir = new File(workingDirPath);
// Do we have a package name?
if (packagePath == null) {
// No, so use working directory...
outputDir = workingDir;
} else {
// Yes, so use working directory as the root...
outputDir = new File(workingDir, packagePath);
// Make sure the directory exists...
ensureDirectory(outputDir,env);
}
}
// Finally, return the directory...
return outputDir;
}
private static void ensureDirectory (File dir, BatchEnvironment env) {
if (!dir.exists()) {
dir.mkdirs();
if (!dir.exists()) {
env.error(0,"rmic.cannot.create.dir",dir.getAbsolutePath());
throw new InternalError();
}
}
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
/**
* AbstractType represents any non-special interface which does not
* inherit from java.rmi.Remote, for which all methods throw RemoteException.
* <p>
* The static forAbstract(...) method must be used to obtain an instance, and will
* return null if the ClassDefinition is non-conforming.
* @author Bryan Atsatt
*/
public class AbstractType extends RemoteType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an AbstractType for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static AbstractType forAbstract(ClassDefinition classDef,
ContextStack stack,
boolean quiet)
{
boolean doPop = false;
AbstractType result = null;
try {
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof AbstractType)) return null; // False hit.
// Yep, so return it...
return (AbstractType) existing;
}
// Could this be an abstract?
if (couldBeAbstract(stack,classDef,quiet)) {
// Yes, so try it...
AbstractType it = new AbstractType(stack, classDef);
putType(theType,it,stack);
stack.push(it);
doPop = true;
if (it.initialize(quiet,stack)) {
stack.pop(true);
result = it;
} else {
removeType(theType,stack);
stack.pop(false);
}
}
} catch (CompilerError e) {
if (doPop) stack.pop(false);
}
return result;
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Abstract interface";
}
//_____________________________________________________________________
// Internal/Subclass Interfaces
//_____________________________________________________________________
/**
* Create a AbstractType instance for the given class. The resulting
* object is not yet completely initialized.
*/
private AbstractType(ContextStack stack, ClassDefinition classDef) {
super(stack,classDef,TYPE_ABSTRACT | TM_INTERFACE | TM_COMPOUND);
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
private static boolean couldBeAbstract(ContextStack stack, ClassDefinition classDef,
boolean quiet) {
// Return true if interface and not remote...
boolean result = false;
if (classDef.isInterface()) {
BatchEnvironment env = stack.getEnv();
try {
result = ! env.defRemote.implementedBy(env, classDef.getClassDeclaration());
if (!result) failedConstraint(15,quiet,stack,classDef.getName());
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
} else {
failedConstraint(14,quiet,stack,classDef.getName());
}
return result;
}
/**
* Initialize this instance.
*/
private boolean initialize (boolean quiet,ContextStack stack) {
boolean result = false;
ClassDefinition self = getClassDefinition();
try {
// Get methods...
Vector directMethods = new Vector();
if (addAllMethods(self,directMethods,true,quiet,stack) != null) {
// Do we have any methods?
boolean validMethods = true;
if (directMethods.size() > 0) {
// Yes. Walk 'em, ensuring each is a valid remote method...
for (int i = 0; i < directMethods.size(); i++) {
if (! isConformingRemoteMethod((Method) directMethods.elementAt(i),true)) {
validMethods = false;
}
}
}
if (validMethods) {
// We're ok, so pass 'em up...
result = initialize(null,directMethods,null,stack,quiet);
}
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return result;
}
}

View File

@@ -0,0 +1,270 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import java.util.HashSet;
import sun.tools.java.CompilerError;
import sun.tools.java.Identifier;
import sun.tools.java.ClassDefinition;
import java.lang.reflect.Array;
/**
* ArrayType is a wrapper for any of the other types. The getElementType()
* method can be used to get the array element type. The getArrayDimension()
* method can be used to get the array dimension.
*
* @author Bryan Atsatt
*/
public class ArrayType extends Type {
private Type type;
private int arrayDimension;
private String brackets;
private String bracketsSig;
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an ArrayType object for the given type.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static ArrayType forArray( sun.tools.java.Type theType,
ContextStack stack) {
ArrayType result = null;
sun.tools.java.Type arrayType = theType;
if (arrayType.getTypeCode() == TC_ARRAY) {
// Find real type...
while (arrayType.getTypeCode() == TC_ARRAY) {
arrayType = arrayType.getElementType();
}
// Do we already have it?
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof ArrayType)) return null; // False hit.
// Yep, so return it...
return (ArrayType) existing;
}
// Now try to make a Type from it...
Type temp = CompoundType.makeType(arrayType,null,stack);
if (temp != null) {
// Got a valid one. Make an array type...
result = new ArrayType(stack,temp,theType.getArrayDimension());
// Add it...
putType(theType,result,stack);
// Do the stack thing in case tracing on...
stack.push(result);
stack.pop(true);
}
}
return result;
}
/**
* Return signature for this type (e.g. com.acme.Dynamite
* would return "com.acme.Dynamite", byte = "B")
*/
public String getSignature() {
return bracketsSig + type.getSignature();
}
/**
* Get element type. Returns null if not an array.
*/
public Type getElementType () {
return type;
}
/**
* Get array dimension. Returns zero if not an array.
*/
public int getArrayDimension () {
return arrayDimension;
}
/**
* Get brackets string. Returns "" if not an array.
*/
public String getArrayBrackets () {
return brackets;
}
/**
* Return a string representation of this type.
*/
public String toString () {
return getQualifiedName() + brackets;
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Array of " + type.getTypeDescription();
}
/**
* Return the name of this type. For arrays, will include "[]" if useIDLNames == false.
* @param useQualifiedNames If true, print qualified names; otherwise, print unqualified names.
* @param useIDLNames If true, print IDL names; otherwise, print java names.
* @param globalIDLNames If true and useIDLNames true, prepends "::".
*/
public String getTypeName ( boolean useQualifiedNames,
boolean useIDLNames,
boolean globalIDLNames) {
if (useIDLNames) {
return super.getTypeName(useQualifiedNames,useIDLNames,globalIDLNames);
} else {
return super.getTypeName(useQualifiedNames,useIDLNames,globalIDLNames) + brackets;
}
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/**
* Convert all invalid types to valid ones.
*/
protected void swapInvalidTypes () {
if (type.getStatus() != STATUS_VALID) {
type = getValidType(type);
}
}
/*
* Add matching types to list. Return true if this type has not
* been previously checked, false otherwise.
*/
protected boolean addTypes (int typeCodeFilter,
HashSet checked,
Vector matching) {
// Check self.
boolean result = super.addTypes(typeCodeFilter,checked,matching);
// Have we been checked before?
if (result) {
// No, so add element type...
getElementType().addTypes(typeCodeFilter,checked,matching);
}
return result;
}
/**
* Create an ArrayType instance for the given type. The resulting
* object is not yet completely initialized.
*/
private ArrayType(ContextStack stack, Type type, int arrayDimension) {
super(stack,TYPE_ARRAY);
this.type = type;
this.arrayDimension = arrayDimension;
// Create our brackets string...
brackets = "";
bracketsSig = "";
for (int i = 0; i < arrayDimension; i ++) {
brackets += "[]";
bracketsSig += "[";
}
// Now set our names...
String idlName = IDLNames.getArrayName(type,arrayDimension);
String[] module = IDLNames.getArrayModuleNames(type);
setNames(type.getIdentifier(),module,idlName);
// Set our repositoryID...
setRepositoryID();
}
/*
* Load a Class instance. Return null if fail.
*/
protected Class loadClass() {
Class result = null;
Class elementClass = type.getClassInstance();
if (elementClass != null) {
result = Array.newInstance(elementClass, new int[arrayDimension]).getClass();
}
return result;
}
/**
* Release all resources
*/
protected void destroy () {
super.destroy();
if (type != null) {
type.destroy();
type = null;
}
brackets = null;
bracketsSig = null;
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.rmi.rmic.Main;
import sun.tools.java.ClassPath;
import java.io.OutputStream;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.Identifier;
import sun.tools.java.ClassNotFound;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Iterator;
/**
* BatchEnvironment for iiop extends rmic's version to add
* parse state.
*/
public class BatchEnvironment extends sun.rmi.rmic.BatchEnvironment implements Constants {
/*
* If the following flag is true, then the IDL generator can map
* the methods and constants of non-conforming types. However,
* this is very expensive, so the default should be false.
*/
private boolean parseNonConforming = false;
/**
* This flag indicates that the stubs and ties need to be generated without
* the package prefix (org.omg.stub).
*/
private boolean standardPackage;
/* Common objects used within package */
HashSet alreadyChecked = new HashSet();
Hashtable allTypes = new Hashtable(3001, 0.5f);
Hashtable invalidTypes = new Hashtable(256, 0.5f);
DirectoryLoader loader = null;
ClassPathLoader classPathLoader = null;
Hashtable nameContexts = null;
Hashtable namesCache = new Hashtable();
NameContext modulesContext = new NameContext(false);
ClassDefinition defRemote = null;
ClassDefinition defError = null;
ClassDefinition defException = null;
ClassDefinition defRemoteException = null;
ClassDefinition defCorbaObject = null;
ClassDefinition defSerializable = null;
ClassDefinition defExternalizable = null;
ClassDefinition defThrowable = null;
ClassDefinition defRuntimeException = null;
ClassDefinition defIDLEntity = null;
ClassDefinition defValueBase = null;
sun.tools.java.Type typeRemoteException = null;
sun.tools.java.Type typeIOException = null;
sun.tools.java.Type typeException = null;
sun.tools.java.Type typeThrowable = null;
ContextStack contextStack = null;
/**
* Create a BatchEnvironment for rmic with the given class path,
* stream for messages and Main.
*/
public BatchEnvironment(OutputStream out, ClassPath path, Main main) {
super(out,path,main);
// Make sure we have our definitions...
try {
defRemote =
getClassDeclaration(idRemote).getClassDefinition(this);
defError =
getClassDeclaration(idJavaLangError).getClassDefinition(this);
defException =
getClassDeclaration(idJavaLangException).getClassDefinition(this);
defRemoteException =
getClassDeclaration(idRemoteException).getClassDefinition(this);
defCorbaObject =
getClassDeclaration(idCorbaObject).getClassDefinition(this);
defSerializable =
getClassDeclaration(idJavaIoSerializable).getClassDefinition(this);
defRuntimeException =
getClassDeclaration(idJavaLangRuntimeException).getClassDefinition(this);
defExternalizable =
getClassDeclaration(idJavaIoExternalizable).getClassDefinition(this);
defThrowable=
getClassDeclaration(idJavaLangThrowable).getClassDefinition(this);
defIDLEntity=
getClassDeclaration(idIDLEntity).getClassDefinition(this);
defValueBase=
getClassDeclaration(idValueBase).getClassDefinition(this);
typeRemoteException = defRemoteException.getClassDeclaration().getType();
typeException = defException.getClassDeclaration().getType();
typeIOException = getClassDeclaration(idJavaIoIOException).getType();
typeThrowable = getClassDeclaration(idJavaLangThrowable).getType();
classPathLoader = new ClassPathLoader(path);
} catch (ClassNotFound e) {
error(0, "rmic.class.not.found", e.name);
throw new Error();
}
}
/**
* Return whether or not to parse non-conforming types.
*/
public boolean getParseNonConforming () {
return parseNonConforming;
}
/**
* Set whether or not to parse non-conforming types.
*/
public void setParseNonConforming (boolean parseEm) {
// If we are transitioning from not parsing to
// parsing, we need to throw out any previously
// parsed types...
if (parseEm && !parseNonConforming) {
reset();
}
parseNonConforming = parseEm;
}
void setStandardPackage(boolean standardPackage) {
this.standardPackage = standardPackage;
}
boolean getStandardPackage() {
return standardPackage;
}
/**
* Clear out any data from previous executions.
*/
public void reset () {
// First, find all Type instances and call destroy()
// on them...
for (Enumeration e = allTypes.elements() ; e.hasMoreElements() ;) {
Type type = (Type) e.nextElement();
type.destroy();
}
for (Enumeration e = invalidTypes.keys() ; e.hasMoreElements() ;) {
Type type = (Type) e.nextElement();
type.destroy();
}
for (Iterator e = alreadyChecked.iterator() ; e.hasNext() ;) {
Type type = (Type) e.next();
type.destroy();
}
if (contextStack != null) contextStack.clear();
// Remove and clear all NameContexts in the
// nameContexts cache...
if (nameContexts != null) {
for (Enumeration e = nameContexts.elements() ; e.hasMoreElements() ;) {
NameContext context = (NameContext) e.nextElement();
context.clear();
}
nameContexts.clear();
}
// Now remove all table entries...
allTypes.clear();
invalidTypes.clear();
alreadyChecked.clear();
namesCache.clear();
modulesContext.clear();
// Clean up remaining...
loader = null;
parseNonConforming = false;
// REVISIT - can't clean up classPathLoader here
}
/**
* Release resources, if any.
*/
public void shutdown() {
if (alreadyChecked != null) {
//System.out.println();
//System.out.println("allTypes.size() = "+ allTypes.size());
//System.out.println(" InstanceCount before reset = "+Type.instanceCount);
reset();
//System.out.println(" InstanceCount AFTER reset = "+Type.instanceCount);
alreadyChecked = null;
allTypes = null;
invalidTypes = null;
nameContexts = null;
namesCache = null;
modulesContext = null;
defRemote = null;
defError = null;
defException = null;
defRemoteException = null;
defCorbaObject = null;
defSerializable = null;
defExternalizable = null;
defThrowable = null;
defRuntimeException = null;
defIDLEntity = null;
defValueBase = null;
typeRemoteException = null;
typeIOException = null;
typeException = null;
typeThrowable = null;
super.shutdown();
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.iiop;
import java.io.*;
import sun.tools.java.ClassPath ;
import sun.tools.java.ClassFile ;
/**
* A ClassLoader that will ultimately use a given sun.tools.java.ClassPath to
* find the desired file. This works for any JAR files specified in the given
* ClassPath as well -- reusing all of that wonderful sun.tools.java code.
*
*@author Everett Anderson
*/
public class ClassPathLoader extends ClassLoader
{
private ClassPath classPath;
public ClassPathLoader(ClassPath classPath) {
this.classPath = classPath;
}
// Called by the super class
protected Class findClass(String name) throws ClassNotFoundException
{
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
/**
* Load the class with the given fully qualified name from the ClassPath.
*/
private byte[] loadClassData(String className)
throws ClassNotFoundException
{
// Build the file name and subdirectory from the
// class name
String filename = className.replace('.', File.separatorChar)
+ ".class";
// Have ClassPath find the file for us, and wrap it in a
// ClassFile. Note: This is where it looks inside jar files that
// are specified in the path.
ClassFile classFile = classPath.getFile(filename);
if (classFile != null) {
// Provide the most specific reason for failure in addition
// to ClassNotFound
Exception reportedError = null;
byte data[] = null;
try {
// ClassFile is beautiful because it shields us from
// knowing if it's a separate file or an entry in a
// jar file.
DataInputStream input
= new DataInputStream(classFile.getInputStream());
// Can't rely on input available() since it will be
// something unusual if it's a jar file! May need
// to worry about a possible problem if someone
// makes a jar file entry with a size greater than
// max int.
data = new byte[(int)classFile.length()];
try {
input.readFully(data);
} catch (IOException ex) {
// Something actually went wrong reading the file. This
// is a real error so save it to report it.
data = null;
reportedError = ex;
} finally {
// Just don't care if there's an exception on close!
// I hate that close can throw an IOException!
try { input.close(); } catch (IOException ex) {}
}
} catch (IOException ex) {
// Couldn't get the input stream for the file. This is
// probably also a real error.
reportedError = ex;
}
if (data == null)
throw new ClassNotFoundException(className, reportedError);
return data;
}
// Couldn't find the file in the class path.
throw new ClassNotFoundException(className);
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.ClassDefinition;
import sun.rmi.rmic.IndentingWriter;
import java.io.IOException;
/**
* ClassType is an abstract base representing any non-special class
* type.
*
* @author Bryan Atsatt
*/
public abstract class ClassType extends CompoundType {
private ClassType parent;
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Return the parent class of this type. Returns null if this
* type is an interface or if there is no parent.
*/
public ClassType getSuperclass() {
return parent;
}
/**
* Print this type.
* @param writer The stream to print to.
* @param useQualifiedNames If true, print qualified names; otherwise, print unqualified names.
* @param useIDLNames If true, print IDL names; otherwise, print java names.
* @param globalIDLNames If true and useIDLNames true, prepends "::".
*/
public void print ( IndentingWriter writer,
boolean useQualifiedNames,
boolean useIDLNames,
boolean globalIDLNames) throws IOException {
if (isInner()) {
writer.p("// " + getTypeDescription() + " (INNER)");
} else {
writer.p("// " + getTypeDescription());
}
writer.pln(" (" + getRepositoryID() + ")\n");
printPackageOpen(writer,useIDLNames);
if (!useIDLNames) {
writer.p("public ");
}
String prefix = "";
writer.p("class " + getTypeName(false,useIDLNames,false));
if (printExtends(writer,useQualifiedNames,useIDLNames,globalIDLNames)) {
prefix = ",";
}
printImplements(writer,prefix,useQualifiedNames,useIDLNames,globalIDLNames);
writer.plnI(" {");
printMembers(writer,useQualifiedNames,useIDLNames,globalIDLNames);
writer.pln();
printMethods(writer,useQualifiedNames,useIDLNames,globalIDLNames);
if (useIDLNames) {
writer.pOln("};");
} else {
writer.pOln("}");
}
printPackageClose(writer,useIDLNames);
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
protected void destroy () {
if (!destroyed) {
super.destroy();
if (parent != null) {
parent.destroy();
parent = null;
}
}
}
/**
* Create a ClassType instance for the given class. NOTE: This constructor
* is ONLY for SpecialClassType.
*/
protected ClassType(ContextStack stack, int typeCode, ClassDefinition classDef) {
super(stack,typeCode,classDef); // Call special parent constructor.
if ((typeCode & TM_CLASS) == 0 && classDef.isInterface()) {
throw new CompilerError("Not a class");
}
parent = null;
}
/**
* Create a ClassType instance for the given class. NOTE: This constructor
* is ONLY for ImplementationType. It does not walk the parent chain.
*/
protected ClassType(int typeCode, ClassDefinition classDef,ContextStack stack) {
super(stack,classDef,typeCode);
if ((typeCode & TM_CLASS) == 0 && classDef.isInterface()) {
throw new CompilerError("Not a class");
}
parent = null;
}
/**
* Create an ClassType instance for the given class. The resulting
* object is not yet completely initialized. Subclasses must call
* initialize(directInterfaces,directInterfaces,directConstants);
*/
protected ClassType(ContextStack stack,
ClassDefinition classDef,
int typeCode) {
super(stack,classDef,typeCode);
if ((typeCode & TM_CLASS) == 0 && classDef.isInterface()) {
throw new CompilerError("Not a class");
}
parent = null;
}
/**
* Convert all invalid types to valid ones.
*/
protected void swapInvalidTypes () {
super.swapInvalidTypes();
if (parent != null && parent.getStatus() != STATUS_VALID) {
parent = (ClassType) getValidType(parent);
}
}
/**
* Modify the type description with exception info.
*/
public String addExceptionDescription (String typeDesc) {
if (isException) {
if (isCheckedException) {
typeDesc = typeDesc + " - Checked Exception";
} else {
typeDesc = typeDesc + " - Unchecked Exception";
}
}
return typeDesc;
}
protected boolean initParents(ContextStack stack) {
stack.setNewContextCode(ContextStack.EXTENDS);
BatchEnvironment env = stack.getEnv();
// Init parent...
boolean result = true;
try {
ClassDeclaration parentDecl = getClassDefinition().getSuperClass(env);
if (parentDecl != null) {
ClassDefinition parentDef = parentDecl.getClassDefinition(env);
parent = (ClassType) makeType(parentDef.getType(),parentDef,stack);
if (parent == null) {
result = false;
}
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
throw new CompilerError("ClassType constructor");
}
return result;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,297 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.Identifier;
public interface Constants extends sun.rmi.rmic.Constants {
// Identifiers for referenced classes:
public static final Identifier idReplyHandler =
Identifier.lookup("org.omg.CORBA.portable.ResponseHandler");
public static final Identifier idStubBase =
Identifier.lookup("javax.rmi.CORBA.Stub");
public static final Identifier idTieBase =
Identifier.lookup("org.omg.CORBA.portable.ObjectImpl");
public static final Identifier idTieInterface =
Identifier.lookup("javax.rmi.CORBA.Tie");
public static final Identifier idPOAServantType =
Identifier.lookup( "org.omg.PortableServer.Servant" ) ;
public static final Identifier idDelegate =
Identifier.lookup("org.omg.CORBA.portable.Delegate");
public static final Identifier idOutputStream =
Identifier.lookup("org.omg.CORBA.portable.OutputStream");
public static final Identifier idExtOutputStream =
Identifier.lookup("org.omg.CORBA_2_3.portable.OutputStream");
public static final Identifier idInputStream =
Identifier.lookup("org.omg.CORBA.portable.InputStream");
public static final Identifier idExtInputStream =
Identifier.lookup("org.omg.CORBA_2_3.portable.InputStream");
public static final Identifier idSystemException =
Identifier.lookup("org.omg.CORBA.SystemException");
public static final Identifier idBadMethodException =
Identifier.lookup("org.omg.CORBA.BAD_OPERATION");
public static final Identifier idPortableUnknownException =
Identifier.lookup("org.omg.CORBA.portable.UnknownException");
public static final Identifier idApplicationException =
Identifier.lookup("org.omg.CORBA.portable.ApplicationException");
public static final Identifier idRemarshalException =
Identifier.lookup("org.omg.CORBA.portable.RemarshalException");
public static final Identifier idJavaIoExternalizable =
Identifier.lookup("java.io.Externalizable");
public static final Identifier idCorbaObject =
Identifier.lookup("org.omg.CORBA.Object");
public static final Identifier idCorbaORB =
Identifier.lookup("org.omg.CORBA.ORB");
public static final Identifier idClassDesc =
Identifier.lookup("javax.rmi.CORBA.ClassDesc");
public static final Identifier idJavaIoIOException =
Identifier.lookup("java.io.IOException");
public static final Identifier idIDLEntity =
Identifier.lookup("org.omg.CORBA.portable.IDLEntity");
public static final Identifier idValueBase =
Identifier.lookup("org.omg.CORBA.portable.ValueBase");
public static final Identifier idBoxedRMI =
Identifier.lookup("org.omg.boxedRMI");
public static final Identifier idBoxedIDL =
Identifier.lookup("org.omg.boxedIDL");
public static final Identifier idCorbaUserException =
Identifier.lookup("org.omg.CORBA.UserException");
// Identifiers for primitive types:
public static final Identifier idBoolean =
Identifier.lookup("boolean");
public static final Identifier idByte =
Identifier.lookup("byte");
public static final Identifier idChar =
Identifier.lookup("char");
public static final Identifier idShort =
Identifier.lookup("short");
public static final Identifier idInt =
Identifier.lookup("int");
public static final Identifier idLong =
Identifier.lookup("long");
public static final Identifier idFloat =
Identifier.lookup("float");
public static final Identifier idDouble =
Identifier.lookup("double");
public static final Identifier idVoid =
Identifier.lookup("void");
// IndentingWriter constructor args:
public static final int INDENT_STEP = 4;
public static final int TAB_SIZE = Integer.MAX_VALUE; // No tabs.
// Type status codes:
public static final int STATUS_PENDING = 0;
public static final int STATUS_VALID = 1;
public static final int STATUS_INVALID = 2;
// Java Names:
public static final String NAME_SEPARATOR = ".";
public static final String SERIAL_VERSION_UID = "serialVersionUID";
// IDL Names:
public static final String[] IDL_KEYWORDS = {
"abstract",
"any",
"attribute",
"boolean",
"case",
"char",
"const",
"context",
"custom",
"default",
"double",
"enum",
"exception",
"factory",
"FALSE",
"fixed",
"float",
"in",
"inout",
"interface",
"long",
"module",
"native",
"Object",
"octet",
"oneway",
"out",
"private",
"public",
"raises",
"readonly",
"sequence",
"short",
"string",
"struct",
"supports",
"switch",
"TRUE",
"truncatable",
"typedef",
"unsigned",
"union",
"ValueBase",
"valuetype",
"void",
"wchar",
"wstring",
};
public static final String EXCEPTION_SUFFIX = "Exception";
public static final String ERROR_SUFFIX = "Error";
public static final String EX_SUFFIX = "Ex";
public static final String IDL_REPOSITORY_ID_PREFIX = "IDL:";
public static final String IDL_REPOSITORY_ID_VERSION = ":1.0";
public static final String[] IDL_CORBA_MODULE = {"CORBA"};
public static final String[] IDL_SEQUENCE_MODULE = {"org","omg","boxedRMI"};
public static final String[] IDL_BOXEDIDL_MODULE = {"org","omg","boxedIDL"};
public static final String IDL_CLASS = "ClassDesc";
public static final String[] IDL_CLASS_MODULE = {"javax","rmi","CORBA"};
public static final String IDL_IDLENTITY = "IDLEntity";
public static final String IDL_SERIALIZABLE = "Serializable";
public static final String IDL_EXTERNALIZABLE = "Externalizable";
public static final String[] IDL_JAVA_IO_MODULE = {"java","io"};
public static final String[] IDL_ORG_OMG_CORBA_MODULE = {"org","omg","CORBA"};
public static final String[] IDL_ORG_OMG_CORBA_PORTABLE_MODULE = {"org","omg","CORBA","portable"};
public static final String IDL_JAVA_LANG_OBJECT = "_Object";
public static final String[] IDL_JAVA_LANG_MODULE = {"java","lang"};
public static final String IDL_JAVA_RMI_REMOTE = "Remote";
public static final String[] IDL_JAVA_RMI_MODULE = {"java","rmi"};
public static final String IDL_SEQUENCE = "seq";
public static final String IDL_CONSTRUCTOR = "create";
public static final String IDL_NAME_SEPARATOR = "::";
public static final String IDL_BOOLEAN = "boolean";
public static final String IDL_BYTE = "octet";
public static final String IDL_CHAR = "wchar";
public static final String IDL_SHORT = "short";
public static final String IDL_INT = "long";
public static final String IDL_LONG = "long long";
public static final String IDL_FLOAT = "float";
public static final String IDL_DOUBLE = "double";
public static final String IDL_VOID = "void";
public static final String IDL_STRING = "WStringValue";
public static final String IDL_CONSTANT_STRING = "wstring";
public static final String IDL_CORBA_OBJECT = "Object";
public static final String IDL_ANY = "any";
// File names:
public static final String SOURCE_FILE_EXTENSION = ".java";
public static final String IDL_FILE_EXTENSION = ".idl";
// Type Codes:
public static final int TYPE_VOID = 0x00000001; // In PrimitiveType
public static final int TYPE_BOOLEAN = 0x00000002; // In PrimitiveType
public static final int TYPE_BYTE = 0x00000004; // In PrimitiveType
public static final int TYPE_CHAR = 0x00000008; // In PrimitiveType
public static final int TYPE_SHORT = 0x00000010; // In PrimitiveType
public static final int TYPE_INT = 0x00000020; // In PrimitiveType
public static final int TYPE_LONG = 0x00000040; // In PrimitiveType
public static final int TYPE_FLOAT = 0x00000080; // In PrimitiveType
public static final int TYPE_DOUBLE = 0x00000100; // In PrimitiveType
public static final int TYPE_STRING = 0x00000200; // In SpecialClassType (String)
public static final int TYPE_ANY = 0x00000400; // In SpecialInterfaceType (Serializable,Externalizable)
public static final int TYPE_CORBA_OBJECT = 0x00000800; // In SpecialInterfaceType (CORBA.Object,Remote)
public static final int TYPE_REMOTE = 0x00001000; // In RemoteType
public static final int TYPE_ABSTRACT = 0x00002000; // In AbstractType
public static final int TYPE_NC_INTERFACE = 0x00004000; // In NCInterfaceType
public static final int TYPE_VALUE = 0x00008000; // In ValueType
public static final int TYPE_IMPLEMENTATION = 0x00010000; // In ImplementationType
public static final int TYPE_NC_CLASS = 0x00020000; // In NCClassType
public static final int TYPE_ARRAY = 0x00040000; // In ArrayType
public static final int TYPE_JAVA_RMI_REMOTE = 0x00080000; // In SpecialInterfaceType
// Type code masks:
public static final int TYPE_NONE = 0x00000000;
public static final int TYPE_ALL = 0xFFFFFFFF;
public static final int TYPE_MASK = 0x00FFFFFF;
public static final int TM_MASK = 0xFF000000;
// Type code modifiers:
public static final int TM_PRIMITIVE = 0x01000000;
public static final int TM_COMPOUND = 0x02000000;
public static final int TM_CLASS = 0x04000000;
public static final int TM_INTERFACE = 0x08000000;
public static final int TM_SPECIAL_CLASS = 0x10000000;
public static final int TM_SPECIAL_INTERFACE= 0x20000000;
public static final int TM_NON_CONFORMING = 0x40000000;
public static final int TM_INNER = 0x80000000;
// Attribute kinds...
public static final int ATTRIBUTE_NONE = 0; // Not an attribute.
public static final int ATTRIBUTE_IS = 1; // read-only, had "is" prefix.
public static final int ATTRIBUTE_GET = 2; // read-only, had "get" prefix.
public static final int ATTRIBUTE_IS_RW = 3; // read-write, had "is" prefix.
public static final int ATTRIBUTE_GET_RW = 4; // read-write, had "get" prefix.
public static final int ATTRIBUTE_SET = 5; // had "set" prefix.
public static final String[] ATTRIBUTE_WIRE_PREFIX = {
"",
"_get_",
"_get_",
"_get_",
"_get_",
"_set_",
};
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
/**
* ContextElement provides a common interface for elements of a ContextStack.
* @author Bryan Atsatt
*/
public interface ContextElement {
public String getElementName();
}

View File

@@ -0,0 +1,447 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.CompilerError;
/**
* ContextStack provides a mechanism to record parsing state.
*
* @author Bryan Atsatt
*/
public class ContextStack {
// Context codes.
public static final int TOP = 1;
public static final int METHOD = 2;
public static final int METHOD_RETURN = 3;
public static final int METHOD_ARGUMENT = 4;
public static final int METHOD_EXCEPTION = 5;
public static final int MEMBER = 6;
public static final int MEMBER_CONSTANT = 7;
public static final int MEMBER_STATIC = 8;
public static final int MEMBER_TRANSIENT = 9;
public static final int IMPLEMENTS = 10;
public static final int EXTENDS = 11;
// String versions of context codes.
private static final String[] CODE_NAMES = {
"UNKNOWN ",
"Top level type ",
"Method ",
"Return parameter ",
"Parameter ",
"Exception ",
"Member ",
"Constant member ",
"Static member ",
"Transient member ",
"Implements ",
"Extends ",
};
// Member data.
private int currentIndex = -1;
private int maxIndex = 100;
private TypeContext[] stack = new TypeContext[maxIndex];
private int newCode = TOP;
private BatchEnvironment env = null;
private boolean trace = false;
private TypeContext tempContext = new TypeContext();
private static final String TRACE_INDENT = " ";
/**
* Constructor.
*/
public ContextStack (BatchEnvironment env) {
this.env = env;
env.contextStack = this;
}
/**
* Return true if env.nerrors > 0.
*/
public boolean anyErrors () {
return env.nerrors > 0;
}
/**
* Enable/disable tracing.
*/
public void setTrace(boolean trace) {
this.trace = trace;
}
/**
* Check trace flag.
*/
public boolean isTraceOn() {
return trace;
}
/**
* Get the environment.
*/
public BatchEnvironment getEnv() {
return env;
}
/**
* Set the new context.
*/
public void setNewContextCode(int code) {
newCode = code;
}
/**
* Get the current context code.
*/
public int getCurrentContextCode() {
return newCode;
}
/**
* If tracing on, write the current call stack (not the context stack) to
* System.out.
*/
final void traceCallStack () {
if (trace) dumpCallStack();
}
public final static void dumpCallStack() {
new Error().printStackTrace(System.out);
}
/**
* Print a line indented by stack depth.
*/
final private void tracePrint (String text, boolean line) {
int length = text.length() + (currentIndex * TRACE_INDENT.length());
StringBuffer buffer = new StringBuffer(length);
for (int i = 0; i < currentIndex; i++) {
buffer.append(TRACE_INDENT);
}
buffer.append(text);
if (line) {
buffer.append("\n");
}
System.out.print(buffer.toString());
}
/**
* If tracing on, print a line.
*/
final void trace (String text) {
if (trace) {
tracePrint(text,false);
}
}
/**
* If tracing on, print a line followed by a '\n'.
*/
final void traceln (String text) {
if (trace) {
tracePrint(text,true);
}
}
/**
* If tracing on, print a pre-mapped ContextElement.
*/
final void traceExistingType (Type type) {
if (trace) {
tempContext.set(newCode,type);
traceln(toResultString(tempContext,true,true));
}
}
/**
* Push a new element on the stack.
* @return the new element.
*/
public TypeContext push (ContextElement element) {
currentIndex++;
// Grow array if need to...
if (currentIndex == maxIndex) {
int newMax = maxIndex * 2;
TypeContext[] newStack = new TypeContext[newMax];
System.arraycopy(stack,0,newStack,0,maxIndex);
maxIndex = newMax;
stack = newStack;
}
// Make sure we have a context object to use at this position...
TypeContext it = stack[currentIndex];
if (it == null) {
it = new TypeContext();
stack[currentIndex] = it;
}
// Set the context object...
it.set(newCode,element);
// Trace...
traceln(toTrialString(it));
// Return...
return it;
}
/**
* Pop an element from the stack.
* @return the new current element or null if top.
*/
public TypeContext pop (boolean wasValid) {
if (currentIndex < 0) {
throw new CompilerError("Nothing on stack!");
}
newCode = stack[currentIndex].getCode();
traceln(toResultString(stack[currentIndex],wasValid,false));
Type last = stack[currentIndex].getCandidateType();
if (last != null) {
// Set status...
if (wasValid) {
last.setStatus(Constants.STATUS_VALID);
} else {
last.setStatus(Constants.STATUS_INVALID);
}
}
currentIndex--;
if (currentIndex < 0) {
// Done parsing, so update the invalid types
// if this type was valid...
if (wasValid) {
Type.updateAllInvalidTypes(this);
}
return null;
} else {
return stack[currentIndex];
}
}
/**
* Get the current size.
*/
public int size () {
return currentIndex + 1;
}
/**
* Get a specific context.
*/
public TypeContext getContext (int index) {
if (currentIndex < index) {
throw new Error("Index out of range");
}
return stack[index];
}
/**
* Get the current top context.
*/
public TypeContext getContext () {
if (currentIndex < 0) {
throw new Error("Nothing on stack!");
}
return stack[currentIndex];
}
/**
* Is parent context a value type?
*/
public boolean isParentAValue () {
if (currentIndex > 0) {
return stack[currentIndex - 1].isValue();
} else {
return false;
}
}
/**
* Get parent context. Null if none.
*/
public TypeContext getParentContext () {
if (currentIndex > 0) {
return stack[currentIndex - 1];
} else {
return null;
}
}
/**
* Get a string for the context name...
*/
public String getContextCodeString () {
if (currentIndex >= 0) {
return CODE_NAMES[newCode];
} else {
return CODE_NAMES[0];
}
}
/**
* Get a string for the given context code...
*/
public static String getContextCodeString (int contextCode) {
return CODE_NAMES[contextCode];
}
private String toTrialString(TypeContext it) {
int code = it.getCode();
if (code != METHOD && code != MEMBER) {
return it.toString() + " (trying " + it.getTypeDescription() + ")";
} else {
return it.toString();
}
}
private String toResultString (TypeContext it, boolean result, boolean preExisting) {
int code = it.getCode();
if (code != METHOD && code != MEMBER) {
if (result) {
String str = it.toString() + " --> " + it.getTypeDescription();
if (preExisting) {
return str + " [Previously mapped]";
} else {
return str;
}
}
} else {
if (result) {
return it.toString() + " --> [Mapped]";
}
}
return it.toString() + " [Did not map]";
}
public void clear () {
for (int i = 0; i < stack.length; i++) {
if (stack[i] != null) stack[i].destroy();
}
}
}
class TypeContext {
public void set(int code, ContextElement element) {
this.code = code;
this.element = element;
if (element instanceof ValueType) {
isValue = true;
} else {
isValue = false;
}
}
public int getCode() {
return code;
}
public String getName() {
return element.getElementName();
}
public Type getCandidateType() {
if (element instanceof Type) {
return (Type) element;
} else {
return null;
}
}
public String getTypeDescription() {
if (element instanceof Type) {
return ((Type) element).getTypeDescription();
} else {
return "[unknown type]";
}
}
public String toString () {
if (element != null) {
return ContextStack.getContextCodeString(code) + element.getElementName();
} else {
return ContextStack.getContextCodeString(code) + "null";
}
}
public boolean isValue () {
return isValue;
}
public boolean isConstant () {
return code == ContextStack.MEMBER_CONSTANT;
}
public void destroy() {
if (element instanceof Type) {
((Type)element).destroy();
}
element = null;
}
private int code = 0;
private ContextElement element = null;
private boolean isValue = false;
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Hashtable;
import java.io.File;
import java.io.FileInputStream;
/**
* DirectoryLoader is a simple ClassLoader which loads from a specified
* file system directory.
* @author Bryan Atsatt
*/
public class DirectoryLoader extends ClassLoader {
private Hashtable cache;
private File root;
/**
* Constructor.
*/
public DirectoryLoader (File rootDir) {
cache = new Hashtable();
if (rootDir == null || !rootDir.isDirectory()) {
throw new IllegalArgumentException();
}
root = rootDir;
}
private DirectoryLoader () {}
/**
* Convenience version of loadClass which sets 'resolve' == true.
*/
public Class loadClass(String className) throws ClassNotFoundException {
return loadClass(className, true);
}
/**
* This is the required version of loadClass which is called
* both from loadClass above and from the internal function
* FindClassFromClass.
*/
public synchronized Class loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class result;
byte classData[];
// Do we already have it in the cache?
result = (Class) cache.get(className);
if (result == null) {
// Nope, can we get if from the system class loader?
try {
result = super.findSystemClass(className);
} catch (ClassNotFoundException e) {
// No, so try loading it...
classData = getClassFileData(className);
if (classData == null) {
throw new ClassNotFoundException();
}
// Parse the class file data...
result = defineClass(classData, 0, classData.length);
if (result == null) {
throw new ClassFormatError();
}
// Resolve it...
if (resolve) resolveClass(result);
// Add to cache...
cache.put(className, result);
}
}
return result;
}
/**
* Reurn a byte array containing the contents of the class file. Returns null
* if an exception occurs.
*/
private byte[] getClassFileData (String className) {
byte result[] = null;
FileInputStream stream = null;
// Get the file...
File classFile = new File(root,className.replace('.',File.separatorChar) + ".class");
// Now get the bits...
try {
stream = new FileInputStream(classFile);
result = new byte[stream.available()];
stream.read(result);
} catch(ThreadDeath death) {
throw death;
} catch (Throwable e) {
}
finally {
if (stream != null) {
try {
stream.close();
} catch(ThreadDeath death) {
throw death;
} catch (Throwable e) {
}
}
}
return result;
}
}

View File

@@ -0,0 +1,436 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import sun.tools.java.Identifier;
import sun.tools.java.ClassPath;
import sun.tools.java.ClassFile;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassDeclaration;
import sun.rmi.rmic.IndentingWriter;
import sun.rmi.rmic.Main;
import sun.rmi.rmic.iiop.Util;
import java.util.HashSet;
/**
* Generator provides a small framework from which IIOP-specific
* generators can inherit. Common logic is implemented here which uses
* both abstract methods as well as concrete methods which subclasses may
* want to override. The following methods must be present in any subclass:
* <pre>
* Default constructor
* CompoundType getTopType(BatchEnvironment env, ClassDefinition cdef);
* int parseArgs(String argv[], int currentIndex);
* boolean requireNewInstance();
* OutputType[] getOutputTypesFor(CompoundType topType,
* HashSet alreadyChecked);
* String getFileNameExtensionFor(OutputType outputType);
* void writeOutputFor ( OutputType outputType,
* HashSet alreadyChecked,
* IndentingWriter writer) throws IOException;
* </pre>
* @author Bryan Atsatt
*/
public abstract class Generator implements sun.rmi.rmic.Generator,
sun.rmi.rmic.iiop.Constants {
protected boolean alwaysGenerate = false;
protected BatchEnvironment env = null;
protected ContextStack contextStack = null;
private boolean trace = false;
protected boolean idl = false;
/**
* Examine and consume command line arguments.
* @param argv The command line arguments. Ignore null
* and unknown arguments. Set each consumed argument to null.
* @param error Report any errors using the main.error() methods.
* @return true if no errors, false otherwise.
*/
public boolean parseArgs(String argv[], Main main) {
for (int i = 0; i < argv.length; i++) {
if (argv[i] != null) {
if (argv[i].equalsIgnoreCase("-always") ||
argv[i].equalsIgnoreCase("-alwaysGenerate")) {
alwaysGenerate = true;
argv[i] = null;
} else if (argv[i].equalsIgnoreCase("-xtrace")) {
trace = true;
argv[i] = null;
}
}
}
return true;
}
/**
* Return true if non-conforming types should be parsed.
* @param stack The context stack.
*/
protected abstract boolean parseNonConforming(ContextStack stack);
/**
* Create and return a top-level type.
* @param cdef The top-level class definition.
* @param stack The context stack.
* @return The compound type or null if is non-conforming.
*/
protected abstract CompoundType getTopType(ClassDefinition cdef, ContextStack stack);
/**
* Return an array containing all the file names and types that need to be
* generated for the given top-level type. The file names must NOT have an
* extension (e.g. ".java").
* @param topType The type returned by getTopType().
* @param alreadyChecked A set of Types which have already been checked.
* Intended to be passed to Type.collectMatching(filter,alreadyChecked).
*/
protected abstract OutputType[] getOutputTypesFor(CompoundType topType,
HashSet alreadyChecked);
/**
* Return the file name extension for the given file name (e.g. ".java").
* All files generated with the ".java" extension will be compiled. To
* change this behavior for ".java" files, override the compileJavaSourceFile
* method to return false.
* @param outputType One of the items returned by getOutputTypesFor(...)
*/
protected abstract String getFileNameExtensionFor(OutputType outputType);
/**
* Write the output for the given OutputFileName into the output stream.
* @param name One of the items returned by getOutputTypesFor(...)
* @param alreadyChecked A set of Types which have already been checked.
* Intended to be passed to Type.collectMatching(filter,alreadyChecked).
* @param writer The output stream.
*/
protected abstract void writeOutputFor(OutputType outputType,
HashSet alreadyChecked,
IndentingWriter writer) throws IOException;
/**
* Return true if a new instance should be created for each
* class on the command line. Subclasses which return true
* should override newInstance() to return an appropriately
* constructed instance.
*/
protected abstract boolean requireNewInstance();
/**
* Return true if the specified file needs generation.
*/
public boolean requiresGeneration (File target, Type theType) {
boolean result = alwaysGenerate;
if (!result) {
// Get a ClassFile instance for base source or class
// file. We use ClassFile so that if the base is in
// a zip file, we can still get at it's mod time...
ClassFile baseFile;
ClassPath path = env.getClassPath();
String className = theType.getQualifiedName().replace('.',File.separatorChar);
// First try the source file...
baseFile = path.getFile(className + ".source");
if (baseFile == null) {
// Then try class file...
baseFile = path.getFile(className + ".class");
}
// Do we have a baseFile?
if (baseFile != null) {
// Yes, grab baseFile's mod time...
long baseFileMod = baseFile.lastModified();
// Get a File instance for the target. If it is a source
// file, create a class file instead since the source file
// will frequently be deleted...
String targetName = IDLNames.replace(target.getName(),".java",".class");
String parentPath = target.getParent();
File targetFile = new File(parentPath,targetName);
// Does the target file exist?
if (targetFile.exists()) {
// Yes, so grab it's mod time...
long targetFileMod = targetFile.lastModified();
// Set result...
result = targetFileMod < baseFileMod;
} else {
// No, so we must generate...
result = true;
}
} else {
// No, so we must generate...
result = true;
}
}
return result;
}
/**
* Create and return a new instance of self. Subclasses
* which need to do something other than default construction
* must override this method.
*/
protected Generator newInstance() {
Generator result = null;
try {
result = (Generator) getClass().newInstance();
}
catch (Exception e){} // Should ALWAYS work!
return result;
}
/**
* Default constructor for subclasses to use.
*/
protected Generator() {
}
/**
* Generate output. Any source files created which need compilation should
* be added to the compiler environment using the addGeneratedFile(File)
* method.
*
* @param env The compiler environment
* @param cdef The definition for the implementation class or interface from
* which to generate output
* @param destDir The directory for the root of the package hierarchy
* for generated files. May be null.
*/
public void generate(sun.rmi.rmic.BatchEnvironment env, ClassDefinition cdef, File destDir) {
this.env = (BatchEnvironment) env;
contextStack = new ContextStack(this.env);
contextStack.setTrace(trace);
// Make sure the environment knows whether or not to parse
// non-conforming types. This will clear out any previously
// parsed types if necessary...
this.env.setParseNonConforming(parseNonConforming(contextStack));
// Get our top level type...
CompoundType topType = getTopType(cdef,contextStack);
if (topType != null) {
Generator generator = this;
// Do we need to make a new instance?
if (requireNewInstance()) {
// Yes, so make one. 'this' instance is the one instantiated by Main
// and which knows any needed command line args...
generator = newInstance();
}
// Now generate all output files...
generator.generateOutputFiles(topType, this.env, destDir);
}
}
/**
* Create and return a new instance of self. Subclasses
* which need to do something other than default construction
* must override this method.
*/
protected void generateOutputFiles (CompoundType topType,
BatchEnvironment env,
File destDir) {
// Grab the 'alreadyChecked' HashSet from the environment...
HashSet alreadyChecked = env.alreadyChecked;
// Ask subclass for a list of output types...
OutputType[] types = getOutputTypesFor(topType,alreadyChecked);
// Process each file...
for (int i = 0; i < types.length; i++) {
OutputType current = types[i];
String className = current.getName();
File file = getFileFor(current,destDir);
boolean sourceFile = false;
// Do we need to generate this file?
if (requiresGeneration(file,current.getType())) {
// Yes. If java source file, add to environment so will be compiled...
if (file.getName().endsWith(".java")) {
sourceFile = compileJavaSourceFile(current);
// Are we supposeded to compile this one?
if (sourceFile) {
env.addGeneratedFile(file);
}
}
// Now create an output stream and ask subclass to fill it up...
try {
IndentingWriter out = new IndentingWriter(
new OutputStreamWriter(new FileOutputStream(file)),INDENT_STEP,TAB_SIZE);
long startTime = 0;
if (env.verbose()) {
startTime = System.currentTimeMillis();
}
writeOutputFor(types[i],alreadyChecked,out);
out.close();
if (env.verbose()) {
long duration = System.currentTimeMillis() - startTime;
env.output(Main.getText("rmic.generated", file.getPath(), Long.toString(duration)));
}
if (sourceFile) {
env.parseFile(new ClassFile(file));
}
} catch (IOException e) {
env.error(0, "cant.write", file.toString());
return;
}
} else {
// No, say so if we need to...
if (env.verbose()) {
env.output(Main.getText("rmic.previously.generated", file.getPath()));
}
}
}
}
/**
* Return the File object that should be used as the output file
* for the given OutputType.
* @param outputType The type to create a file for.
* @param destinationDir The directory to use as the root of the
* package heirarchy. May be null, in which case the current
* classpath is searched to find the directory in which to create
* the output file. If that search fails (most likely because the
* package directory lives in a zip or jar file rather than the
* file system), the current user directory is used.
*/
protected File getFileFor(OutputType outputType, File destinationDir) {
// Calling this method does some crucial initialization
// in a subclass implementation. Don't skip it.
Identifier id = getOutputId(outputType);
File packageDir = null;
if(idl){
packageDir = Util.getOutputDirectoryForIDL(id,destinationDir,env);
} else {
packageDir = Util.getOutputDirectoryForStub(id,destinationDir,env);
}
String classFileName = outputType.getName() + getFileNameExtensionFor(outputType);
return new File(packageDir, classFileName);
}
/**
* Return an identifier to use for output.
* @param outputType the type for which output is to be generated.
* @return the new identifier. This implementation returns the input parameter.
*/
protected Identifier getOutputId (OutputType outputType) {
return outputType.getType().getIdentifier();
}
/**
* Return true if the given file should be compiled.
* @param outputType One of the items returned by getOutputTypesFor(...) for
* which getFileNameExtensionFor(OutputType) returned ".java".
*/
protected boolean compileJavaSourceFile (OutputType outputType) {
return true;
}
//_____________________________________________________________________
// OutputType is a simple wrapper for a name and a Type
//_____________________________________________________________________
public class OutputType {
private String name;
private Type type;
public OutputType (String name, Type type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
import sun.tools.java.MemberDefinition;
/**
* ImplementationType represents any non-special class which implements
* one or more interfaces which inherit from java.rmi.Remote.
* <p>
* The static forImplementation(...) method must be used to obtain an instance,
* and will return null if the ClassDefinition is non-conforming.
*
* @author Bryan Atsatt
*/
public class ImplementationType extends ClassType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an ImplementationType for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static ImplementationType forImplementation(ClassDefinition classDef,
ContextStack stack,
boolean quiet) {
if (stack.anyErrors()) return null;
boolean doPop = false;
ImplementationType result = null;
try {
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof ImplementationType)) return null; // False hit.
// Yep, so return it...
return (ImplementationType) existing;
}
// Could this be an implementation?
if (couldBeImplementation(quiet,stack,classDef)) {
// Yes, so check it...
ImplementationType it = new ImplementationType(stack, classDef);
putType(theType,it,stack);
stack.push(it);
doPop = true;
if (it.initialize(stack,quiet)) {
stack.pop(true);
result = it;
} else {
removeType(theType,stack);
stack.pop(false);
}
}
} catch (CompilerError e) {
if (doPop) stack.pop(false);
}
return result;
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Implementation";
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
/**
* Create a ImplementationType instance for the given class. The resulting
* object is not yet completely initialized.
*/
private ImplementationType(ContextStack stack, ClassDefinition classDef) {
super(TYPE_IMPLEMENTATION | TM_CLASS | TM_COMPOUND,classDef,stack); // Use special constructor.
}
private static boolean couldBeImplementation(boolean quiet, ContextStack stack,
ClassDefinition classDef) {
boolean result = false;
BatchEnvironment env = stack.getEnv();
try {
if (!classDef.isClass()) {
failedConstraint(17,quiet,stack,classDef.getName());
} else {
result = env.defRemote.implementedBy(env, classDef.getClassDeclaration());
if (!result) failedConstraint(8,quiet,stack,classDef.getName());
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return result;
}
/**
* Initialize this instance.
*/
private boolean initialize (ContextStack stack, boolean quiet) {
boolean result = false;
ClassDefinition theClass = getClassDefinition();
if (initParents(stack)) {
// Make up our collections...
Vector directInterfaces = new Vector();
Vector directMethods = new Vector();
// Check interfaces...
try {
if (addRemoteInterfaces(directInterfaces,true,stack) != null) {
boolean haveRemote = false;
// Get methods from all interfaces...
for (int i = 0; i < directInterfaces.size(); i++) {
InterfaceType theInt = (InterfaceType) directInterfaces.elementAt(i);
if (theInt.isType(TYPE_REMOTE) ||
theInt.isType(TYPE_JAVA_RMI_REMOTE)) {
haveRemote = true;
}
copyRemoteMethods(theInt,directMethods);
}
// Make sure we have at least one remote interface...
if (!haveRemote) {
failedConstraint(8,quiet,stack,getQualifiedName());
return false;
}
// Now check the methods to ensure we have the
// correct throws clauses...
if (checkMethods(theClass,directMethods,stack,quiet)) {
// We're ok, so pass 'em up...
result = initialize(directInterfaces,directMethods,null,stack,quiet);
}
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
}
return result;
}
private static void copyRemoteMethods(InterfaceType type, Vector list) {
if (type.isType(TYPE_REMOTE)) {
// Copy all the unique methods from type...
Method[] allMethods = type.getMethods();
for (int i = 0; i < allMethods.length; i++) {
Method theMethod = allMethods[i];
if (!list.contains(theMethod)) {
list.addElement(theMethod);
}
}
// Now recurse thru all inherited interfaces...
InterfaceType[] allInterfaces = type.getInterfaces();
for (int i = 0; i < allInterfaces.length; i++) {
copyRemoteMethods(allInterfaces[i],list);
}
}
}
// Walk all methods of the class, and for each that is already in
// the list, call setImplExceptions()...
private boolean checkMethods(ClassDefinition theClass, Vector list,
ContextStack stack, boolean quiet) {
// Convert vector to array...
Method[] methods = new Method[list.size()];
list.copyInto(methods);
for (MemberDefinition member = theClass.getFirstMember();
member != null;
member = member.getNextMember()) {
if (member.isMethod() && !member.isConstructor()
&& !member.isInitializer()) {
// It's a method...
if (!updateExceptions(member,methods,stack,quiet)) {
return false;
}
}
}
return true;
}
private boolean updateExceptions (MemberDefinition implMethod, Method[] list,
ContextStack stack, boolean quiet) {
int length = list.length;
String implMethodSig = implMethod.toString();
for (int i = 0; i < length; i++) {
Method existingMethod = list[i];
MemberDefinition existing = existingMethod.getMemberDefinition();
// Do we have a matching method?
if (implMethodSig.equals(existing.toString())) {
// Yes, so create exception list...
try {
ValueType[] implExcept = getMethodExceptions(implMethod,quiet,stack);
existingMethod.setImplExceptions(implExcept);
} catch (Exception e) {
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.io.IOException;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassDefinition;
import sun.rmi.rmic.IndentingWriter;
/**
* InterfaceType is an abstract base representing any non-special
* interface type.
*
* @author Bryan Atsatt
*/
public abstract class InterfaceType extends CompoundType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Print this type.
* @param writer The stream to print to.
* @param useQualifiedNames If true, print qualified names; otherwise, print unqualified names.
* @param useIDLNames If true, print IDL names; otherwise, print java names.
* @param globalIDLNames If true and useIDLNames true, prepends "::".
*/
public void print ( IndentingWriter writer,
boolean useQualifiedNames,
boolean useIDLNames,
boolean globalIDLNames) throws IOException {
if (isInner()) {
writer.p("// " + getTypeDescription() + " (INNER)");
} else {
writer.p("// " + getTypeDescription() + "");
}
writer.pln(" (" + getRepositoryID() + ")\n");
printPackageOpen(writer,useIDLNames);
if (!useIDLNames) {
writer.p("public ");
}
writer.p("interface " + getTypeName(false,useIDLNames,false));
printImplements(writer,"",useQualifiedNames,useIDLNames,globalIDLNames);
writer.plnI(" {");
printMembers(writer,useQualifiedNames,useIDLNames,globalIDLNames);
writer.pln();
printMethods(writer,useQualifiedNames,useIDLNames,globalIDLNames);
writer.pln();
if (useIDLNames) {
writer.pOln("};");
} else {
writer.pOln("}");
}
printPackageClose(writer,useIDLNames);
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/**
* Create a InterfaceType instance for the given class. NOTE: This constructor
* is ONLY for SpecialInterfaceType.
*/
protected InterfaceType(ContextStack stack, int typeCode, ClassDefinition classDef) {
super(stack,typeCode,classDef); // Call special parent constructor.
if ((typeCode & TM_INTERFACE) == 0 || ! classDef.isInterface()) {
throw new CompilerError("Not an interface");
}
}
/**
* Create a InterfaceType instance for the given class. The resulting
* object is not yet completely initialized. Subclasses must call
* initialize(directInterfaces,directInterfaces,directConstants);
*/
protected InterfaceType(ContextStack stack,
ClassDefinition classDef,
int typeCode) {
super(stack,classDef,typeCode);
if ((typeCode & TM_INTERFACE) == 0 || ! classDef.isInterface()) {
throw new CompilerError("Not an interface");
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
/**
* NCClassType represents any non-special class which does not
* extends one or more interfaces which inherit from java.rmi.Remote.
* <p>
* The static forImplementation(...) method must be used to obtain an instance,
* and will return null if the ClassDefinition is non-conforming.
*
* @author Bryan Atsatt
*/
public class NCClassType extends ClassType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an NCClassType for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static NCClassType forNCClass(ClassDefinition classDef,
ContextStack stack) {
if (stack.anyErrors()) return null;
boolean doPop = false;
try {
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof NCClassType)) return null; // False hit.
// Yep, so return it...
return (NCClassType) existing;
}
NCClassType it = new NCClassType(stack, classDef);
putType(theType,it,stack);
stack.push(it);
doPop = true;
if (it.initialize(stack)) {
stack.pop(true);
return it;
} else {
removeType(theType,stack);
stack.pop(false);
return null;
}
} catch (CompilerError e) {
if (doPop) stack.pop(false);
return null;
}
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return addExceptionDescription("Non-conforming class");
}
//_____________________________________________________________________
// Internal/Subclass Interfaces
//_____________________________________________________________________
/**
* Create a NCClassType instance for the given class. The resulting
* object is not yet completely initialized.
*/
private NCClassType(ContextStack stack, ClassDefinition classDef) {
super(stack,classDef,TYPE_NC_CLASS | TM_CLASS | TM_COMPOUND);
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
/**
* Initialize this instance.
*/
private boolean initialize (ContextStack stack) {
if (!initParents(stack)) {
return false;
}
if (stack.getEnv().getParseNonConforming()) {
Vector directInterfaces = new Vector();
Vector directMethods = new Vector();
Vector directMembers = new Vector();
try {
// Get methods...
if (addAllMethods(getClassDefinition(),directMethods,false,false,stack) != null) {
// Update parent class methods...
if (updateParentClassMethods(getClassDefinition(),directMethods,false,stack) != null) {
// Get conforming constants...
if (addConformingConstants(directMembers,false,stack)) {
// We're ok, so pass 'em up...
if (!initialize(directInterfaces,directMethods,directMembers,stack,false)) {
return false;
}
}
}
}
return true;
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return false;
} else {
return initialize(null,null,null,stack,false);
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
/**
* NCInterfaceType represents any non-special, non-conforming interface.
* <p>
* The static forNCInterface(...) method must be used to obtain an instance.
* @author Bryan Atsatt
*/
public class NCInterfaceType extends InterfaceType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an NCInterfaceType for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static NCInterfaceType forNCInterface( ClassDefinition classDef,
ContextStack stack) {
if (stack.anyErrors()) return null;
boolean doPop = false;
try {
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof NCInterfaceType)) return null; // False hit.
// Yep, so return it...
return (NCInterfaceType) existing;
}
NCInterfaceType it = new NCInterfaceType(stack, classDef);
putType(theType,it,stack);
stack.push(it);
doPop = true;
if (it.initialize(stack)) {
stack.pop(true);
return it;
} else {
removeType(theType,stack);
stack.pop(false);
return null;
}
} catch (CompilerError e) {
if (doPop) stack.pop(false);
return null;
}
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Non-conforming interface";
}
//_____________________________________________________________________
// Internal/Subclass Interfaces
//_____________________________________________________________________
/**
* Create a NCInterfaceType instance for the given class. The resulting
* object is not yet completely initialized.
*/
private NCInterfaceType(ContextStack stack, ClassDefinition classDef) {
super(stack,classDef,TYPE_NC_INTERFACE | TM_INTERFACE | TM_COMPOUND);
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
/**
* Initialize this instance.
*/
private boolean initialize (ContextStack stack) {
if (stack.getEnv().getParseNonConforming()) {
Vector directInterfaces = new Vector();
Vector directMethods = new Vector();
Vector directMembers = new Vector();
try {
// need to include parent interfaces in IDL generation...
addNonRemoteInterfaces( directInterfaces,stack );
// Get methods...
if (addAllMethods(getClassDefinition(),directMethods,false,false,stack) != null) {
// Get conforming constants...
if (addConformingConstants(directMembers,false,stack)) {
// We're ok, so pass 'em up...
if (!initialize(directInterfaces,directMethods,directMembers,stack,false)) {
return false;
}
}
}
return true;
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return false;
} else {
return initialize(null,null,null,stack,false);
}
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Hashtable;
/**
* A NameContext enables detection of strings which differ only
* in case.
*
* @author Bryan Atsatt
*/
class NameContext {
private Hashtable table;
private boolean allowCollisions;
/**
* Get a context for the given name. Name may be null, in
* which case this method will return the default context.
*/
public static synchronized NameContext forName (String name,
boolean allowCollisions,
BatchEnvironment env) {
NameContext result = null;
// Do we need to use the default context?
if (name == null) {
// Yes.
name = "null";
}
// Have we initialized our hashtable?
if (env.nameContexts == null) {
// Nope, so do it...
env.nameContexts = new Hashtable();
} else {
// Yes, see if we already have the requested
// context...
result = (NameContext) env.nameContexts.get(name);
}
// Do we have the requested context?
if (result == null) {
// Nope, so create and add it...
result = new NameContext(allowCollisions);
env.nameContexts.put(name,result);
}
return result;
}
/**
* Construct a context.
* @param allowCollisions true if case-sensitive name collisions
* are allowed, false if not.
*/
public NameContext (boolean allowCollisions) {
this.allowCollisions = allowCollisions;
table = new Hashtable();
}
/**
* Add a name to this context. If constructed with allowCollisions
* false and a collision occurs, this method will throw an exception
* in which the message contains the string: "name" and "collision".
*/
public void assertPut (String name) throws Exception {
String message = add(name);
if (message != null) {
throw new Exception(message);
}
}
/**
* Add a name to this context..
*/
public void put (String name) {
if (allowCollisions == false) {
throw new Error("Must use assertPut(name)");
}
add(name);
}
/**
* Add a name to this context. If constructed with allowCollisions
* false and a collision occurs, this method will return a message
* string, otherwise returns null.
*/
private String add (String name) {
// First, create a key by converting name to lowercase...
String key = name.toLowerCase();
// Does this key exist in the context?
Name value = (Name) table.get(key);
if (value != null) {
// Yes, so they match if we ignore case. Do they match if
// we don't ignore case?
if (!name.equals(value.name)) {
// No, so this is a case-sensitive match. Are we
// supposed to allow this?
if (allowCollisions) {
// Yes, make sure it knows that it collides...
value.collisions = true;
} else {
// No, so return a message string...
return new String("\"" + name + "\" and \"" + value.name + "\"");
}
}
} else {
// No, so add it...
table.put(key,new Name(name,false));
}
return null;
}
/**
* Get a name from the context. If it has collisions, the name
* will be converted as specified in section 5.2.7.
*/
public String get (String name) {
Name it = (Name) table.get(name.toLowerCase());
String result = name;
// Do we need to mangle it?
if (it.collisions) {
// Yep, so do it...
int length = name.length();
boolean allLower = true;
for (int i = 0; i < length; i++) {
if (Character.isUpperCase(name.charAt(i))) {
result += "_";
result += i;
allLower = false;
}
}
if (allLower) {
result += "_";
}
}
return result;
}
/**
* Remove all entries.
*/
public void clear () {
table.clear();
}
public class Name {
public String name;
public boolean collisions;
public Name (String name, boolean collisions) {
this.name = name;
this.collisions = collisions;
}
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.CompilerError;
import sun.tools.java.Identifier;
import sun.tools.java.ClassDefinition;
/**
* PrimitiveType wraps primitive types and void.
* <p>
* The static forPrimitive(...) method must be used to obtain an instance, and
* will return null if the type is non-conforming.
*
* @author Bryan Atsatt
*/
public class PrimitiveType extends Type {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create a PrimitiveType object for the given type.
*
* If the type is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static PrimitiveType forPrimitive(sun.tools.java.Type type,
ContextStack stack) {
if (stack.anyErrors()) return null;
// Do we already have it?
Type existing = getType(type,stack);
if (existing != null) {
if (!(existing instanceof PrimitiveType)) return null; // False hit.
// Yep, so return it...
return (PrimitiveType) existing;
}
int typeCode;
switch (type.getTypeCode()) {
case TC_VOID: typeCode = TYPE_VOID; break;
case TC_BOOLEAN: typeCode = TYPE_BOOLEAN; break;
case TC_BYTE: typeCode = TYPE_BYTE; break;
case TC_CHAR: typeCode = TYPE_CHAR; break;
case TC_SHORT: typeCode = TYPE_SHORT; break;
case TC_INT: typeCode = TYPE_INT; break;
case TC_LONG: typeCode = TYPE_LONG; break;
case TC_FLOAT: typeCode = TYPE_FLOAT; break;
case TC_DOUBLE: typeCode = TYPE_DOUBLE; break;
default: return null;
}
PrimitiveType it = new PrimitiveType(stack,typeCode);
// Add it...
putType(type,it,stack);
// Do the stack thing in case tracing on...
stack.push(it);
stack.pop(true);
return it;
}
/**
* Return signature for this type (e.g. com.acme.Dynamite
* would return "com.acme.Dynamite", byte = "B")
*/
public String getSignature() {
switch (getTypeCode()) {
case TYPE_VOID: return SIG_VOID;
case TYPE_BOOLEAN: return SIG_BOOLEAN;
case TYPE_BYTE: return SIG_BYTE;
case TYPE_CHAR: return SIG_CHAR;
case TYPE_SHORT: return SIG_SHORT;
case TYPE_INT: return SIG_INT;
case TYPE_LONG: return SIG_LONG;
case TYPE_FLOAT: return SIG_FLOAT;
case TYPE_DOUBLE: return SIG_DOUBLE;
default: return null;
}
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Primitive";
}
/**
* IDL_Naming
* Return the fully qualified IDL name for this type (e.g. com.acme.Dynamite would
* return "com::acme::Dynamite").
* @param global If true, prepends "::".
*/
public String getQualifiedIDLName(boolean global) {
return super.getQualifiedIDLName(false);
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/*
* Load a Class instance. Return null if fail.
*/
protected Class loadClass() {
switch (getTypeCode()) {
case TYPE_VOID: return Null.class;
case TYPE_BOOLEAN: return boolean.class;
case TYPE_BYTE: return byte.class;
case TYPE_CHAR: return char.class;
case TYPE_SHORT: return short.class;
case TYPE_INT: return int.class;
case TYPE_LONG: return long.class;
case TYPE_FLOAT: return float.class;
case TYPE_DOUBLE: return double.class;
default: throw new CompilerError("Not a primitive type");
}
}
/**
* IDL_Naming
* Create an PrimitiveType instance for the given class.
*/
private PrimitiveType(ContextStack stack, int typeCode) {
super(stack,typeCode | TM_PRIMITIVE);
// Validate type and set names...
String idlName = IDLNames.getTypeName(typeCode,false);
Identifier id = null;
switch (typeCode) {
case TYPE_VOID: id = idVoid; break;
case TYPE_BOOLEAN: id = idBoolean; break;
case TYPE_BYTE: id = idByte; break;
case TYPE_CHAR: id = idChar; break;
case TYPE_SHORT: id = idShort; break;
case TYPE_INT: id = idInt; break;
case TYPE_LONG: id = idLong; break;
case TYPE_FLOAT: id = idFloat; break;
case TYPE_DOUBLE: id = idDouble; break;
default: throw new CompilerError("Not a primitive type");
}
setNames(id,null,idlName);
setRepositoryID();
}
}
class Null {}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassDefinition;
import sun.rmi.rmic.IndentingWriter;
import sun.rmi.rmic.Main;
/**
* An IDL generator for rmic.
*
* @author Bryan Atsatt
*/
public class PrintGenerator implements sun.rmi.rmic.Generator,
sun.rmi.rmic.iiop.Constants {
private static final int JAVA = 0;
private static final int IDL = 1;
private static final int BOTH = 2;
private int whatToPrint; // Initialized in parseArgs.
private boolean global = false;
private boolean qualified = false;
private boolean trace = false;
private boolean valueMethods = false;
private IndentingWriter out;
/**
* Default constructor for Main to use.
*/
public PrintGenerator() {
OutputStreamWriter writer = new OutputStreamWriter(System.out);
out = new IndentingWriter (writer);
}
/**
* Examine and consume command line arguments.
* @param argv The command line arguments. Ignore null
* @param error Report any errors using the main.error() methods.
* @return true if no errors, false otherwise.
*/
public boolean parseArgs(String argv[], Main main) {
for (int i = 0; i < argv.length; i++) {
if (argv[i] != null) {
String arg = argv[i].toLowerCase();
if (arg.equals("-xprint")) {
whatToPrint = JAVA;
argv[i] = null;
if (i+1 < argv.length) {
if (argv[i+1].equalsIgnoreCase("idl")) {
argv[++i] = null;
whatToPrint = IDL;
} else if (argv[i+1].equalsIgnoreCase("both")) {
argv[++i] = null;
whatToPrint = BOTH;
}
}
} else if (arg.equals("-xglobal")) {
global = true;
argv[i] = null;
} else if (arg.equals("-xqualified")) {
qualified = true;
argv[i] = null;
} else if (arg.equals("-xtrace")) {
trace = true;
argv[i] = null;
} else if (arg.equals("-xvaluemethods")) {
valueMethods = true;
argv[i] = null;
}
}
}
return true;
}
/**
* Generate output. Any source files created which need compilation should
* be added to the compiler environment using the addGeneratedFile(File)
* method.
*
* @param env The compiler environment
* @param cdef The definition for the implementation class or interface from
* which to generate output
* @param destDir The directory for the root of the package hierarchy
* for generated files. May be null.
*/
public void generate(sun.rmi.rmic.BatchEnvironment env, ClassDefinition cdef, File destDir) {
BatchEnvironment ourEnv = (BatchEnvironment) env;
ContextStack stack = new ContextStack(ourEnv);
stack.setTrace(trace);
if (valueMethods) {
ourEnv.setParseNonConforming(true);
}
// Get our top level type...
CompoundType topType = CompoundType.forCompound(cdef,stack);
if (topType != null) {
try {
// Collect up all the compound types...
Type[] theTypes = topType.collectMatching(TM_COMPOUND);
for (int i = 0; i < theTypes.length; i++) {
out.pln("\n-----------------------------------------------------------\n");
Type theType = theTypes[i];
switch (whatToPrint) {
case JAVA: theType.println(out,qualified,false,false);
break;
case IDL: theType.println(out,qualified,true,global);
break;
case BOTH: theType.println(out,qualified,false,false);
theType.println(out,qualified,true,global);
break;
default: throw new CompilerError("Unknown type!");
}
}
out.flush();
} catch (IOException e) {
throw new CompilerError("PrintGenerator caught " + e);
}
}
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.CompilerError;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassNotFound;
/**
* RemoteType represents any non-special interface which inherits
* from java.rmi.Remote.
* <p>
* The static forRemote(...) method must be used to obtain an instance, and will
* return null if the ClassDefinition is non-conforming.
* @author Bryan Atsatt
*/
public class RemoteType extends InterfaceType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an RemoteType for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static RemoteType forRemote(ClassDefinition classDef,
ContextStack stack,
boolean quiet) {
if (stack.anyErrors()) return null;
boolean doPop = false;
RemoteType result = null;
try {
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
Type existing = getType(theType,stack);
if (existing != null) {
if (!(existing instanceof RemoteType)) return null; // False hit.
// Yep, so return it...
return (RemoteType) existing;
}
// Could this be a remote type?
if (couldBeRemote(quiet,stack,classDef)) {
// Yes, so check it...
RemoteType it = new RemoteType(stack,classDef);
putType(theType,it,stack);
stack.push(it);
doPop = true;
if (it.initialize(quiet,stack)) {
stack.pop(true);
result = it;
} else {
removeType(theType,stack);
stack.pop(false);
}
}
} catch (CompilerError e) {
if (doPop) stack.pop(false);
}
return result;
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Remote interface";
}
//_____________________________________________________________________
// Internal/Subclass Interfaces
//_____________________________________________________________________
/**
* Create a RemoteType instance for the given class. The resulting
* object is not yet completely initialized.
*/
protected RemoteType(ContextStack stack, ClassDefinition classDef) {
super(stack,classDef,TYPE_REMOTE | TM_INTERFACE | TM_COMPOUND);
}
/**
* Create a RemoteType instance for the given class. The resulting
* object is not yet completely initialized.
*/
protected RemoteType(ContextStack stack, ClassDefinition classDef, int typeCode) {
super(stack,classDef,typeCode);
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
private static boolean couldBeRemote (boolean quiet, ContextStack stack,
ClassDefinition classDef) {
boolean result = false;
BatchEnvironment env = stack.getEnv();
try {
if (!classDef.isInterface()) {
failedConstraint(16,quiet,stack,classDef.getName());
} else {
result = env.defRemote.implementedBy(env,classDef.getClassDeclaration());
if (!result) failedConstraint(1,quiet,stack,classDef.getName());
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return result;
}
/**
* Initialize this instance.
*/
private boolean initialize (boolean quiet,ContextStack stack) {
boolean result = false;
// Go check it out and gather up the info we need...
Vector directInterfaces = new Vector();
Vector directMethods = new Vector();
Vector directConstants = new Vector();
if (isConformingRemoteInterface(directInterfaces,
directMethods,
directConstants,
quiet,
stack)){
// We're ok, so pass 'em up...
result = initialize(directInterfaces,directMethods,directConstants,stack,quiet);
}
return result;
}
/**
* Check to ensure that the interface and all it's methods and arguments
* conforms to the RMI/IDL java subset for remote interfaces as defined
* by the "Java to IDL Mapping" specification, section 4.
* @param directInterfaces All directly implmented interfaces will be
* added to this list.
* @param directMethods All directly implemented methods (other than
* constructors and initializers) will be added to this list.
* @param directConstants All constants defined by theInterface will be
* added to this list.
* @param quiet True if should not report constraint failures.
* @return true if constraints satisfied, false otherwise.
*/
private boolean isConformingRemoteInterface ( Vector directInterfaces,
Vector directMethods,
Vector directConstants,
boolean quiet,
ContextStack stack) {
ClassDefinition theInterface = getClassDefinition();
try {
// Get all remote interfaces...
if (addRemoteInterfaces(directInterfaces,false,stack) == null ) {
return false;
}
// Make sure all constants are conforming...
if (!addAllMembers(directConstants,true,quiet,stack)) {
return false;
}
// Now, collect up all methods...
if (addAllMethods(theInterface,directMethods,true,quiet,stack) == null) {
// Failed a constraint check...
return false;
}
// Now walk 'em, ensuring each is a valid remote method...
boolean methodsConform = true;
for (int i = 0; i < directMethods.size(); i++) {
if (! isConformingRemoteMethod((Method) directMethods.elementAt(i),quiet)) {
methodsConform = false;
}
}
if (!methodsConform) {
return false;
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.ClassNotFound;
import sun.tools.java.CompilerError;
import sun.tools.java.Identifier;
import sun.tools.java.ClassDefinition;
/**
* SpecialClassType represents any one of the following types:
* <pre>
* java.lang.Object
* java.lang.String
* </pre>
* all of which are treated as special cases.
* <p>
* The static forSpecial(...) method must be used to obtain an instance, and
* will return null if the type is non-conforming.
*
* @author Bryan Atsatt
*/
public class SpecialClassType extends ClassType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create a SpecialClassType object for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static SpecialClassType forSpecial (ClassDefinition theClass,
ContextStack stack) {
if (stack.anyErrors()) return null;
sun.tools.java.Type type = theClass.getType();
// Do we already have it?
String typeKey = type.toString() + stack.getContextCodeString();
Type existing = getType(typeKey,stack);
if (existing != null) {
if (!(existing instanceof SpecialClassType)) return null; // False hit.
// Yep, so return it...
return (SpecialClassType) existing;
}
// Is it a special type?
int typeCode = getTypeCode(type,theClass,stack);
if (typeCode != TYPE_NONE) {
// Yes...
SpecialClassType result = new SpecialClassType(stack,typeCode,theClass);
putType(typeKey,result,stack);
stack.push(result);
stack.pop(true);
return result;
} else {
return null;
}
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Special class";
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/**
* Create an SpecialClassType instance for the given class.
*/
private SpecialClassType(ContextStack stack, int typeCode,
ClassDefinition theClass) {
super(stack,typeCode | TM_SPECIAL_CLASS | TM_CLASS | TM_COMPOUND, theClass);
Identifier id = theClass.getName();
String idlName = null;
String[] idlModuleName = null;
boolean constant = stack.size() > 0 && stack.getContext().isConstant();
// Set names...
switch (typeCode) {
case TYPE_STRING: {
idlName = IDLNames.getTypeName(typeCode,constant);
if (!constant) {
idlModuleName = IDL_CORBA_MODULE;
}
break;
}
case TYPE_ANY: {
idlName = IDL_JAVA_LANG_OBJECT;
idlModuleName = IDL_JAVA_LANG_MODULE;
break;
}
}
setNames(id,idlModuleName,idlName);
// Init parents...
if (!initParents(stack)) {
// Should not be possible!
throw new CompilerError("SpecialClassType found invalid parent.");
}
// Initialize CompoundType...
initialize(null,null,null,stack,false);
}
private static int getTypeCode(sun.tools.java.Type type, ClassDefinition theClass, ContextStack stack) {
if (type.isType(TC_CLASS)) {
Identifier id = type.getClassName();
if (id == idJavaLangString) return TYPE_STRING;
if (id == idJavaLangObject) return TYPE_ANY;
}
return TYPE_NONE;
}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import sun.tools.java.ClassNotFound;
import sun.tools.java.CompilerError;
import sun.tools.java.Identifier;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.ClassDefinition;
/**
* SpecialInterfaceType represents any one of the following types:
* <pre>
* java.rmi.Remote
* java.io.Serializable
* java.io.Externalizable
* org.omg.CORBA.Object
* org.omg.CORBA.portable.IDLEntity
* </pre>
* all of which are treated as special cases. For all but CORBA.Object,
* the type must match exactly. For CORBA.Object, the type must either be
* CORBA.Object or inherit from it.
* <p>
* The static forSpecial(...) method must be used to obtain an instance, and
* will return null if the type is non-conforming.
*
* @author Bryan Atsatt
*/
public class SpecialInterfaceType extends InterfaceType {
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create a SpecialInterfaceType object for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static SpecialInterfaceType forSpecial ( ClassDefinition theClass,
ContextStack stack) {
if (stack.anyErrors()) return null;
// Do we already have it?
sun.tools.java.Type type = theClass.getType();
Type existing = getType(type,stack);
if (existing != null) {
if (!(existing instanceof SpecialInterfaceType)) return null; // False hit.
// Yep, so return it...
return (SpecialInterfaceType) existing;
}
// Is it special?
if (isSpecial(type,theClass,stack)) {
// Yes...
SpecialInterfaceType result = new SpecialInterfaceType(stack,0,theClass);
putType(type,result,stack);
stack.push(result);
if (result.initialize(type,stack)) {
stack.pop(true);
return result;
} else {
removeType(type,stack);
stack.pop(false);
return null;
}
}
return null;
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
return "Special interface";
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/**
* Create an SpecialInterfaceType instance for the given class.
*/
private SpecialInterfaceType(ContextStack stack, int typeCode,
ClassDefinition theClass) {
super(stack,typeCode | TM_SPECIAL_INTERFACE | TM_INTERFACE | TM_COMPOUND, theClass);
setNames(theClass.getName(),null,null); // Fixed in initialize.
}
private static boolean isSpecial(sun.tools.java.Type type,
ClassDefinition theClass,
ContextStack stack) {
if (type.isType(TC_CLASS)) {
Identifier id = type.getClassName();
if (id.equals(idRemote)) return true;
if (id == idJavaIoSerializable) return true;
if (id == idJavaIoExternalizable) return true;
if (id == idCorbaObject) return true;
if (id == idIDLEntity) return true;
BatchEnvironment env = stack.getEnv();
try {
if (env.defCorbaObject.implementedBy(env,theClass.getClassDeclaration())) return true;
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
}
return false;
}
private boolean initialize(sun.tools.java.Type type, ContextStack stack) {
int typeCode = TYPE_NONE;
Identifier id = null;
String idlName = null;
String[] idlModuleName = null;
boolean constant = stack.size() > 0 && stack.getContext().isConstant();
if (type.isType(TC_CLASS)) {
id = type.getClassName();
if (id.equals(idRemote)) {
typeCode = TYPE_JAVA_RMI_REMOTE;
idlName = IDL_JAVA_RMI_REMOTE;
idlModuleName = IDL_JAVA_RMI_MODULE;
} else if (id == idJavaIoSerializable) {
typeCode = TYPE_ANY;
idlName = IDL_SERIALIZABLE;
idlModuleName = IDL_JAVA_IO_MODULE;
} else if (id == idJavaIoExternalizable) {
typeCode = TYPE_ANY;
idlName = IDL_EXTERNALIZABLE;
idlModuleName = IDL_JAVA_IO_MODULE;
} else if (id == idIDLEntity) {
typeCode = TYPE_ANY;
idlName = IDL_IDLENTITY;
idlModuleName = IDL_ORG_OMG_CORBA_PORTABLE_MODULE;
} else {
typeCode = TYPE_CORBA_OBJECT;
// Is it exactly org.omg.CORBA.Object?
if (id == idCorbaObject) {
// Yes, so special case...
idlName = IDLNames.getTypeName(typeCode,constant);
idlModuleName = null;
} else {
// No, so get the correct names...
try {
// These can fail if we get case-sensitive name matches...
idlName = IDLNames.getClassOrInterfaceName(id,env);
idlModuleName = IDLNames.getModuleNames(id,isBoxed(),env);
} catch (Exception e) {
failedConstraint(7,false,stack,id.toString(),e.getMessage());
throw new CompilerError("");
}
}
}
}
if (typeCode == TYPE_NONE) {
return false;
}
// Reset type code...
setTypeCode(typeCode | TM_SPECIAL_INTERFACE | TM_INTERFACE | TM_COMPOUND);
// Set names
if (idlName == null) {
throw new CompilerError("Not a special type");
}
setNames(id,idlModuleName,idlName);
// Initialize CompoundType...
return initialize(null,null,null,stack,false);
}
}

View File

@@ -0,0 +1,372 @@
/*
* Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
/**
* StaticStringsHash takes an array of constant strings and
* uses several different hash methods to try to find the
* 'best' one for that set. The set of methods is currently
* fixed, but with a little work could be made extensible thru
* subclassing.
* <p>
* The current set of methods is:
* <ol>
* <li> length() - works well when all strings are different length.</li>
* <li> charAt(n) - works well when one offset into all strings is different.</li>
* <li> hashCode() - works well with larger arrays.</li>
* </ol>
* After constructing an instance over the set of strings, the
* <code>getKey(String)</code> method can be used to use the selected hash
* method to produce a key. The <code>method</code> string will contain
* "length()", "charAt(n)", or "hashCode()", and is intended for use by
* code generators.
* <p>
* The <code>keys</code> array will contain the full set of unique keys.
* <p>
* The <code>buckets</code> array will contain a set of arrays, one for
* each key in the <code>keys</code>, where <code>buckets[x][y]</code>
* is an index into the <code>strings</code> array.
* @author Bryan Atsatt
*/
public class StaticStringsHash {
/** The set of strings upon which the hash info is created */
public String[] strings = null;
/** Unique hash keys */
public int[] keys = null;
/** Buckets for each key, where buckets[x][y] is an index
* into the strings[] array. */
public int[][] buckets = null;
/** The method to invoke on String to produce the hash key */
public String method = null;
/** Get a key for the given string using the
* selected hash method.
* @param str the string to return a key for.
* @return the key.
*/
public int getKey(String str) {
switch (keyKind) {
case LENGTH: return str.length();
case CHAR_AT: return str.charAt(charAt);
case HASH_CODE: return str.hashCode();
}
throw new Error("Bad keyKind");
}
/** Constructor
* @param strings the set of strings upon which to
* find an optimal hash method. Must not contain
* duplicates.
*/
public StaticStringsHash(String[] strings) {
this.strings = strings;
length = strings.length;
tempKeys = new int[length];
bucketSizes = new int[length];
setMinStringLength();
// Decide on the best algorithm based on
// which one has the smallest maximum
// bucket depth. First, try length()...
int currentMaxDepth = getKeys(LENGTH);
int useCharAt = -1;
boolean useHashCode = false;
if (currentMaxDepth > 1) {
// At least one bucket had more than one
// entry, so try charAt(i). If there
// are a lot of strings in the array,
// and minStringLength is large, limit
// the search to a smaller number of
// characters to avoid spending a lot
// of time here that is most likely to
// be pointless...
int minLength = minStringLength;
if (length > CHAR_AT_MAX_LINES &&
length * minLength > CHAR_AT_MAX_CHARS) {
minLength = length/CHAR_AT_MAX_CHARS;
}
charAt = 0;
for (int i = 0; i < minLength; i++) {
int charAtDepth = getKeys(CHAR_AT);
if (charAtDepth < currentMaxDepth) {
currentMaxDepth = charAtDepth;
useCharAt = i;
if (currentMaxDepth == 1) {
break;
}
}
charAt++;
}
charAt = useCharAt;
if (currentMaxDepth > 1) {
// At least one bucket had more than one
// entry, try hashCode().
//
// Since the cost of computing a full hashCode
// (for the runtime target string) is much higher
// than the previous methods, use it only if it is
// substantially better. The definition of 'substantial'
// here is not very well founded, and could be improved
// with some further analysis ;^)
int hashCodeDepth = getKeys(HASH_CODE);
if (hashCodeDepth < currentMaxDepth-3) {
// Using the full hashCode results in at least
// 3 fewer entries in the worst bucket, so will
// therefore avoid at least 3 calls to equals()
// in the worst case.
//
// Note that using a number smaller than 3 could
// result in using a hashCode when there are only
// 2 strings in the array, and that would surely
// be a poor performance choice.
useHashCode = true;
}
}
// Reset keys if needed...
if (!useHashCode) {
if (useCharAt >= 0) {
// Use the charAt(i) method...
getKeys(CHAR_AT);
} else {
// Use length method...
getKeys(LENGTH);
}
}
}
// Now allocate and fill our real hashKeys array...
keys = new int[bucketCount];
System.arraycopy(tempKeys,0,keys,0,bucketCount);
// Sort keys and bucketSizes arrays...
boolean didSwap;
do {
didSwap = false;
for (int i = 0; i < bucketCount - 1; i++) {
if (keys[i] > keys[i+1]) {
int temp = keys[i];
keys[i] = keys[i+1];
keys[i+1] = temp;
temp = bucketSizes[i];
bucketSizes[i] = bucketSizes[i+1];
bucketSizes[i+1] = temp;
didSwap = true;
}
}
}
while (didSwap == true);
// Allocate our buckets array. Fill the string
// index slot with an unused key so we can
// determine which are free...
int unused = findUnusedKey();
buckets = new int[bucketCount][];
for (int i = 0; i < bucketCount; i++) {
buckets[i] = new int[bucketSizes[i]];
for (int j = 0; j < bucketSizes[i]; j++) {
buckets[i][j] = unused;
}
}
// And fill it in...
for(int i = 0; i < strings.length; i++) {
int key = getKey(strings[i]);
for (int j = 0; j < bucketCount; j++) {
if (keys[j] == key) {
int k = 0;
while (buckets[j][k] != unused) {
k++;
}
buckets[j][k] = i;
break;
}
}
}
}
/** Print an optimized 'contains' method for the
* argument strings
*/
public static void main (String[] args) {
StaticStringsHash hash = new StaticStringsHash(args);
System.out.println();
System.out.println(" public boolean contains(String key) {");
System.out.println(" switch (key."+hash.method+") {");
for (int i = 0; i < hash.buckets.length; i++) {
System.out.println(" case "+hash.keys[i]+": ");
for (int j = 0; j < hash.buckets[i].length; j++) {
if (j > 0) {
System.out.print(" } else ");
} else {
System.out.print(" ");
}
System.out.println("if (key.equals(\""+ hash.strings[hash.buckets[i][j]] +"\")) {");
System.out.println(" return true;");
}
System.out.println(" }");
}
System.out.println(" }");
System.out.println(" return false;");
System.out.println(" }");
}
private int length;
private int[] tempKeys;
private int[] bucketSizes;
private int bucketCount;
private int maxDepth;
private int minStringLength = Integer.MAX_VALUE;
private int keyKind;
private int charAt;
private static final int LENGTH = 0;
private static final int CHAR_AT = 1;
private static final int HASH_CODE = 2;
/* Determines the maximum number of charAt(i)
* tests that will be done. The search is
* limited because if the number of characters
* is large enough, the likelyhood of finding
* a good hash key based on this method is
* low. The CHAR_AT_MAX_CHARS limit only
* applies f there are more strings than
* CHAR_AT_MAX_LINES.
*/
private static final int CHAR_AT_MAX_LINES = 50;
private static final int CHAR_AT_MAX_CHARS = 1000;
private void resetKeys(int keyKind) {
this.keyKind = keyKind;
switch (keyKind) {
case LENGTH: method = "length()"; break;
case CHAR_AT: method = "charAt("+charAt+")"; break;
case HASH_CODE: method = "hashCode()"; break;
}
maxDepth = 1;
bucketCount = 0;
for (int i = 0; i < length; i++) {
tempKeys[i] = 0;
bucketSizes[i] = 0;
}
}
private void setMinStringLength() {
for (int i = 0; i < length; i++) {
if (strings[i].length() < minStringLength) {
minStringLength = strings[i].length();
}
}
}
private int findUnusedKey() {
int unused = 0;
int keysLength = keys.length;
// Note that we just assume that resource
// exhaustion will occur rather than an
// infinite loop here if the set of keys
// is very large.
while (true) {
boolean match = false;
for (int i = 0; i < keysLength; i++) {
if (keys[i] == unused) {
match = true;
break;
}
}
if (match) {
unused--;
} else {
break;
}
}
return unused;
}
private int getKeys(int methodKind) {
resetKeys(methodKind);
for(int i = 0; i < strings.length; i++) {
addKey(getKey(strings[i]));
}
return maxDepth;
}
private void addKey(int key) {
// Have we seen this one before?
boolean addIt = true;
for (int j = 0; j < bucketCount; j++) {
if (tempKeys[j] == key) {
addIt = false;
bucketSizes[j]++;
if (bucketSizes[j] > maxDepth) {
maxDepth = bucketSizes[j];
}
break;
}
}
if (addIt) {
tempKeys[bucketCount] = key;
bucketSizes[bucketCount] = 1;
bucketCount++;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
/*
* Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.io.File;
import sun.tools.java.Identifier;
import com.sun.corba.se.impl.util.PackagePrefixChecker;
/**
* Util provides static utility methods used by other rmic classes.
* @author Bryan Atsatt
*/
public final class Util implements sun.rmi.rmic.Constants {
public static String packagePrefix(){ return PackagePrefixChecker.packagePrefix();}
/**
* Return the directory that should be used for output for a given
* class.
* @param theClass The fully qualified name of the class.
* @param rootDir The directory to use as the root of the
* package heirarchy. May be null, in which case the current
* working directory is used as the root.
*/
private static File getOutputDirectoryFor(Identifier theClass,
File rootDir,
BatchEnvironment env,
boolean idl ) {
File outputDir = null;
String className = theClass.getFlatName().toString().replace('.', SIGC_INNERCLASS);
String qualifiedClassName = className;
String packagePath = null;
String packageName = theClass.getQualifier().toString();
//Shift package names for stubs generated for interfaces.
/*if(type.isInterface())*/
packageName = correctPackageName(packageName, idl, env.getStandardPackage());
//Done.
if (packageName.length() > 0) {
qualifiedClassName = packageName + "." + className;
packagePath = packageName.replace('.', File.separatorChar);
}
// Do we have a root directory?
if (rootDir != null) {
// Yes, do we have a package name?
if (packagePath != null) {
// Yes, so use it as the root. Open the directory...
outputDir = new File(rootDir, packagePath);
// Make sure the directory exists...
ensureDirectory(outputDir,env);
} else {
// Default package, so use root as output dir...
outputDir = rootDir;
}
} else {
// No root directory. Get the current working directory...
String workingDirPath = System.getProperty("user.dir");
File workingDir = new File(workingDirPath);
// Do we have a package name?
if (packagePath == null) {
// No, so use working directory...
outputDir = workingDir;
} else {
// Yes, so use working directory as the root...
outputDir = new File(workingDir, packagePath);
// Make sure the directory exists...
ensureDirectory(outputDir,env);
}
}
// Finally, return the directory...
return outputDir;
}
public static File getOutputDirectoryForIDL(Identifier theClass,
File rootDir,
BatchEnvironment env) {
return getOutputDirectoryFor(theClass, rootDir, env, true);
}
public static File getOutputDirectoryForStub(Identifier theClass,
File rootDir,
BatchEnvironment env) {
return getOutputDirectoryFor(theClass, rootDir, env, false);
}
private static void ensureDirectory (File dir, BatchEnvironment env) {
if (!dir.exists()) {
dir.mkdirs();
if (!dir.exists()) {
env.error(0,"rmic.cannot.create.dir",dir.getAbsolutePath());
throw new InternalError();
}
}
}
public static String correctPackageName(
String p, boolean idl, boolean standardPackage){
if (idl){
return p;
} else {
if (standardPackage) {
return p;
} else {
return PackagePrefixChecker.correctPackageName(p);
}
}
}
public static boolean isOffendingPackage(String p){
return PackagePrefixChecker.isOffendingPackage(p);
}
public static boolean hasOffendingPrefix(String p){
return PackagePrefixChecker.hasOffendingPrefix(p);
}
}

View File

@@ -0,0 +1,488 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.util.Vector;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDeclaration;
import sun.tools.java.ClassDefinition;
import sun.tools.java.MemberDefinition;
import java.util.Hashtable;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
/**
* ValueType represents any non-special class which does inherit from
* java.io.Serializable and does not inherit from java.rmi.Remote.
* <p>
* The static forValue(...) method must be used to obtain an instance, and
* will return null if the ClassDefinition is non-conforming.
*
* @author Bryan Atsatt
*/
public class ValueType extends ClassType {
private boolean isCustom;
//_____________________________________________________________________
// Public Interfaces
//_____________________________________________________________________
/**
* Create an ValueType object for the given class.
*
* If the class is not a properly formed or if some other error occurs, the
* return value will be null, and errors will have been reported to the
* supplied BatchEnvironment.
*/
public static ValueType forValue(ClassDefinition classDef,
ContextStack stack,
boolean quiet) {
if (stack.anyErrors()) return null;
// Do we already have it?
sun.tools.java.Type theType = classDef.getType();
String typeKey = theType.toString();
Type existing = getType(typeKey,stack);
if (existing != null) {
if (!(existing instanceof ValueType)) return null; // False hit.
// Yep, so return it...
return (ValueType) existing;
}
// Is this java.lang.Class?
boolean javaLangClass = false;
if (classDef.getClassDeclaration().getName() == idJavaLangClass) {
// Yes, so replace classDef with one for
// javax.rmi.CORBA.ClassDesc...
javaLangClass = true;
BatchEnvironment env = stack.getEnv();
ClassDeclaration decl = env.getClassDeclaration(idClassDesc);
ClassDefinition def = null;
try {
def = decl.getClassDefinition(env);
} catch (ClassNotFound ex) {
classNotFound(stack,ex);
return null;
}
classDef = def;
}
// Could this be a value?
if (couldBeValue(stack,classDef)) {
// Yes, so check it...
ValueType it = new ValueType(classDef,stack,javaLangClass);
putType(typeKey,it,stack);
stack.push(it);
if (it.initialize(stack,quiet)) {
stack.pop(true);
return it;
} else {
removeType(typeKey,stack);
stack.pop(false);
return null;
}
} else {
return null;
}
}
/**
* Return a string describing this type.
*/
public String getTypeDescription () {
String result = addExceptionDescription("Value");
if (isCustom) {
result = "Custom " + result;
}
if (isIDLEntity) {
result = result + " [IDLEntity]";
}
return result;
}
/**
* Return true if this type is a "custom" type (i.e.
* it implements java.io.Externalizable or has a
* method with the following signature:
*
* private void writeObject(java.io.ObjectOutputStream out);
*
*/
public boolean isCustom () {
return isCustom;
}
//_____________________________________________________________________
// Subclass/Internal Interfaces
//_____________________________________________________________________
/**
* Create a ValueType instance for the given class. The resulting
* object is not yet completely initialized.
*/
private ValueType(ClassDefinition classDef,
ContextStack stack,
boolean isMappedJavaLangClass) {
super(stack,classDef,TYPE_VALUE | TM_CLASS | TM_COMPOUND);
isCustom = false;
// If this is the mapped version of java.lang.Class,
// set the non-IDL names back to java.lang.Class...
if (isMappedJavaLangClass) {
setNames(idJavaLangClass,IDL_CLASS_MODULE,IDL_CLASS);
}
}
//_____________________________________________________________________
// Internal Interfaces
//_____________________________________________________________________
/**
* Initialize this instance.
*/
private static boolean couldBeValue(ContextStack stack, ClassDefinition classDef) {
boolean result = false;
ClassDeclaration classDecl = classDef.getClassDeclaration();
BatchEnvironment env = stack.getEnv();
try {
// Make sure it's not remote...
if (env.defRemote.implementedBy(env, classDecl)) {
failedConstraint(10,false,stack,classDef.getName());
} else {
// Make sure it's Serializable...
if (!env.defSerializable.implementedBy(env, classDecl)) {
failedConstraint(11,false,stack,classDef.getName());
} else {
result = true;
}
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return result;
}
/**
* Initialize this instance.
*/
private boolean initialize (ContextStack stack, boolean quiet) {
ClassDefinition ourDef = getClassDefinition();
ClassDeclaration ourDecl = getClassDeclaration();
try {
// Make sure our parentage is ok...
if (!initParents(stack)) {
failedConstraint(12,quiet,stack,getQualifiedName());
return false;
}
// We're ok, so make up our collections...
Vector directInterfaces = new Vector();
Vector directMethods = new Vector();
Vector directMembers = new Vector();
// Get interfaces...
if (addNonRemoteInterfaces(directInterfaces,stack) != null) {
// Get methods...
if (addAllMethods(ourDef,directMethods,false,false,stack) != null) {
// Update parent class methods
if (updateParentClassMethods(ourDef,directMethods,false,stack) != null) {
// Get constants and members...
if (addAllMembers(directMembers,false,false,stack)) {
// We're ok, so pass 'em up...
if (!initialize(directInterfaces,directMethods,directMembers,stack,quiet)) {
return false;
}
// Is this class Externalizable?
boolean externalizable = false;
if (!env.defExternalizable.implementedBy(env, ourDecl)) {
// No, so check to see if we have a serialPersistentField
// that will modify the members.
if (!checkPersistentFields(getClassInstance(),quiet)) {
return false;
}
} else {
// Yes.
externalizable = true;
}
// Should this class be considered "custom"? It is if
// it is Externalizable OR if it has a method with the
// following signature:
//
// private void writeObject(java.io.ObjectOutputStream out);
//
if (externalizable) {
isCustom = true;
} else {
for (MemberDefinition member = ourDef.getFirstMember();
member != null;
member = member.getNextMember()) {
if (member.isMethod() &&
!member.isInitializer() &&
member.isPrivate() &&
member.getName().toString().equals("writeObject")) {
// Check return type, arguments and exceptions...
sun.tools.java.Type methodType = member.getType();
sun.tools.java.Type rtnType = methodType.getReturnType();
if (rtnType == sun.tools.java.Type.tVoid) {
// Return type is correct. How about arguments?
sun.tools.java.Type[] args = methodType.getArgumentTypes();
if (args.length == 1 &&
args[0].getTypeSignature().equals("Ljava/io/ObjectOutputStream;")) {
// Arguments are correct, so it is a custom
// value type...
isCustom = true;
}
}
}
}
}
}
return true;
}
}
}
} catch (ClassNotFound e) {
classNotFound(stack,e);
}
return false;
}
private boolean checkPersistentFields (Class clz, boolean quiet) {
// Do we have a writeObject method?
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals("writeObject") &&
methods[i].getArguments().length == 1) {
Type returnType = methods[i].getReturnType();
Type arg = methods[i].getArguments()[0];
String id = arg.getQualifiedName();
if (returnType.isType(TYPE_VOID) &&
id.equals("java.io.ObjectOutputStream")) {
// Got one, so there's nothing to do...
return true;
}
}
}
// Do we have a valid serialPersistentField array?
MemberDefinition spfDef = null;
for (int i = 0; i < members.length; i++) {
if (members[i].getName().equals("serialPersistentFields")) {
Member member = members[i];
Type type = member.getType();
Type elementType = type.getElementType();
// We have a member with the correct name. Make sure
// we have the correct signature...
if (elementType != null &&
elementType.getQualifiedName().equals(
"java.io.ObjectStreamField")
) {
if (member.isStatic() &&
member.isFinal() &&
member.isPrivate()) {
// We have the correct signature
spfDef = member.getMemberDefinition();
} else {
// Bad signature...
failedConstraint(4,quiet,stack,getQualifiedName());
return false;
}
}
}
}
// If we do not have a serialPersistentField,
// there's nothing to do, so return with no error...
if (spfDef == null) {
return true;
}
// Ok, now we must examine the contents of the array -
// then validate them...
Hashtable fields = getPersistentFields(clz);
boolean result = true;
for (int i = 0; i < members.length; i++) {
String fieldName = members[i].getName();
String fieldType = members[i].getType().getSignature();
// Is this field present in the array?
String type = (String) fields.get(fieldName);
if (type == null) {
// No, so mark it transient...
members[i].setTransient();
} else {
// Yes, does the type match?
if (type.equals(fieldType)) {
// Yes, so remove it from the fields table...
fields.remove(fieldName);
} else {
// No, so error...
result = false;
failedConstraint(2,quiet,stack,fieldName,getQualifiedName());
}
}
}
// Ok, we've checked all of our fields. Are there any left in the "array"?
// If so, it's an error...
if (result && fields.size() > 0) {
result = false;
failedConstraint(9,quiet,stack,getQualifiedName());
}
// Return result...
return result;
}
/**
* Get the names and types of all the persistent fields of a Class.
*/
private Hashtable getPersistentFields (Class clz) {
Hashtable result = new Hashtable();
ObjectStreamClass osc = ObjectStreamClass.lookup(clz);
if (osc != null) {
ObjectStreamField[] fields = osc.getFields();
for (int i = 0; i < fields.length; i++) {
String typeSig;
String typePrefix = String.valueOf(fields[i].getTypeCode());
if (fields[i].isPrimitive()) {
typeSig = typePrefix;
} else {
if (fields[i].getTypeCode() == '[') {
typePrefix = "";
}
typeSig = typePrefix + fields[i].getType().getName().replace('.','/');
if (typeSig.endsWith(";")) {
typeSig = typeSig.substring(0,typeSig.length()-1);
}
}
result.put(fields[i].getName(),typeSig);
}
}
return result;
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static sun.rmi.rmic.newrmic.Constants.*;
/**
* The environment for an rmic compilation batch.
*
* A BatchEnvironment contains a RootDoc, which is the entry point
* into the doclet environment for the associated rmic compilation
* batch. A BatchEnvironment collects the source files generated
* during the batch's execution, for eventual source code compilation
* and, possibly, deletion. Errors that occur during generation
* activity should be reported through the BatchEnvironment's "error"
* method.
*
* A protocol-specific generator class may require the use of a
* particular BatchEnvironment subclass for enhanced environment
* functionality. A BatchEnvironment subclass must declare a
* public constructor with one parameter of type RootDoc.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public class BatchEnvironment {
private final RootDoc rootDoc;
/** cached ClassDoc for certain types used by rmic */
private final ClassDoc docRemote;
private final ClassDoc docException;
private final ClassDoc docRemoteException;
private final ClassDoc docRuntimeException;
private boolean verbose = false;
private final List<File> generatedFiles = new ArrayList<File>();
/**
* Creates a new BatchEnvironment with the specified RootDoc.
**/
public BatchEnvironment(RootDoc rootDoc) {
this.rootDoc = rootDoc;
/*
* Initialize cached ClassDoc for types used by rmic. Note
* that any of these could be null if the boot class path is
* incorrect, which could cause a NullPointerException later.
*/
docRemote = rootDoc().classNamed(REMOTE);
docException = rootDoc().classNamed(EXCEPTION);
docRemoteException = rootDoc().classNamed(REMOTE_EXCEPTION);
docRuntimeException = rootDoc().classNamed(RUNTIME_EXCEPTION);
}
/**
* Returns the RootDoc for this environment.
**/
public RootDoc rootDoc() {
return rootDoc;
}
public ClassDoc docRemote() { return docRemote; }
public ClassDoc docException() { return docException; }
public ClassDoc docRemoteException() { return docRemoteException; }
public ClassDoc docRuntimeException() { return docRuntimeException; }
/**
* Sets this environment's verbosity status.
**/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Returns this environment's verbosity status.
**/
public boolean verbose() {
return verbose;
}
/**
* Adds the specified file to the list of source files generated
* during this batch.
**/
public void addGeneratedFile(File file) {
generatedFiles.add(file);
}
/**
* Returns the list of files generated during this batch.
**/
public List<File> generatedFiles() {
return Collections.unmodifiableList(generatedFiles);
}
/**
* Outputs the specified (non-error) message.
**/
public void output(String msg) {
rootDoc.printNotice(msg);
}
/**
* Reports an error using the specified resource key and text
* formatting arguments.
**/
public void error(String key, String... args) {
rootDoc.printError(Resources.getText(key, args));
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
/**
* Constants potentially useful to all rmic generators.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public final class Constants {
private Constants() { throw new AssertionError(); }
/*
* fully-qualified names of types used by rmic
*/
public static final String REMOTE = "java.rmi.Remote";
public static final String EXCEPTION = "java.lang.Exception";
public static final String REMOTE_EXCEPTION = "java.rmi.RemoteException";
public static final String RUNTIME_EXCEPTION = "java.lang.RuntimeException";
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
import com.sun.javadoc.ClassDoc;
import java.io.File;
import java.util.Set;
/**
* The interface to rmic back end implementations. Classes that
* implement this interface correspond to the various generation modes
* of rmic (JRMP, IIOP, IDL, etc.).
*
* A Generator instance corresponds to a particular rmic compilation
* batch, and its instance state represents the generator-specific
* command line options for that batch. Main will instantiate a
* generator class when the command line arguments indicate selection
* of the corresponding generation mode. Main will then invoke the
* "parseArgs" method to allow the generator to process any
* generator-specific command line options and set its instance state
* accordingly.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public interface Generator {
/**
* Processes the command line options specific to this generator.
* Processed options are set to null in the specified array.
* Returns true if successful or false if an error occurs. Errors
* are output to the specific Main instance.
**/
public boolean parseArgs(String[] args, Main main);
/**
* Returns the most specific environment class required by this
* generator.
**/
public Class<? extends BatchEnvironment> envClass();
/**
* Returns the names of the classes that must be available through
* the doclet API in order for this generator to function.
**/
public Set<String> bootstrapClassNames();
/**
* Generates the protocol-specific rmic output files for the
* specified remote class. This method is invoked once for each
* class or interface specified on the command line for the rmic
* compilation batch associated with this instance.
*
* Any generated source files (to be compiled with javac) are
* passed to the addGeneratedFile method of the specified
* BatchEnvironment.
**/
public void generate(BatchEnvironment env,
ClassDoc inputClass,
File destDir);
}

View File

@@ -0,0 +1,291 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
import java.io.Writer;
import java.io.BufferedWriter;
import java.io.IOException;
/**
* A BufferedWriter that supports automatic indentation of lines of
* text written to the underlying Writer.
*
* Methods are provided for compact/convenient indenting in and out,
* writing text, and writing lines of text in various combinations.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public class IndentingWriter extends BufferedWriter {
/** number of spaces to change indent when indenting in or out */
private final int indentStep;
/** number of spaces to convert into tabs (use MAX_VALUE to disable) */
private final int tabSize;
/** true if the next character written is the first on a line */
private boolean beginningOfLine = true;
/** current number of spaces to prepend to lines */
private int currentIndent = 0;
/**
* Creates a new IndentingWriter that writes indented text to the
* given Writer. Use the default indent step of four spaces.
**/
public IndentingWriter(Writer out) {
this(out, 4);
}
/**
* Creates a new IndentingWriter that writes indented text to the
* given Writer and uses the supplied indent step.
**/
public IndentingWriter(Writer out, int indentStep) {
this(out, indentStep, 8);
}
/**
* Creates a new IndentingWriter that writes indented text to the
* given Writer and uses the supplied indent step and tab size.
**/
public IndentingWriter(Writer out, int indentStep, int tabSize) {
super(out);
if (indentStep < 0) {
throw new IllegalArgumentException("negative indent step");
}
if (tabSize < 0) {
throw new IllegalArgumentException("negative tab size");
}
this.indentStep = indentStep;
this.tabSize = tabSize;
}
/**
* Writes a single character.
**/
public void write(int c) throws IOException {
checkWrite();
super.write(c);
}
/**
* Writes a portion of an array of characters.
**/
public void write(char[] cbuf, int off, int len) throws IOException {
if (len > 0) {
checkWrite();
}
super.write(cbuf, off, len);
}
/**
* Writes a portion of a String.
**/
public void write(String s, int off, int len) throws IOException {
if (len > 0) {
checkWrite();
}
super.write(s, off, len);
}
/**
* Writes a line separator. The next character written will be
* preceded by an indent.
**/
public void newLine() throws IOException {
super.newLine();
beginningOfLine = true;
}
/**
* Checks if an indent needs to be written before writing the next
* character.
*
* The indent generation is optimized (and made consistent with
* certain coding conventions) by condensing groups of eight
* spaces into tab characters.
**/
protected void checkWrite() throws IOException {
if (beginningOfLine) {
beginningOfLine = false;
int i = currentIndent;
while (i >= tabSize) {
super.write('\t');
i -= tabSize;
}
while (i > 0) {
super.write(' ');
i--;
}
}
}
/**
* Increases the current indent by the indent step.
**/
protected void indentIn() {
currentIndent += indentStep;
}
/**
* Decreases the current indent by the indent step.
**/
protected void indentOut() {
currentIndent -= indentStep;
if (currentIndent < 0)
currentIndent = 0;
}
/**
* Indents in.
**/
public void pI() {
indentIn();
}
/**
* Indents out.
**/
public void pO() {
indentOut();
}
/**
* Writes string.
**/
public void p(String s) throws IOException {
write(s);
}
/**
* Ends current line.
**/
public void pln() throws IOException {
newLine();
}
/**
* Writes string; ends current line.
**/
public void pln(String s) throws IOException {
p(s);
pln();
}
/**
* Writes string; ends current line; indents in.
**/
public void plnI(String s) throws IOException {
p(s);
pln();
pI();
}
/**
* Indents out; writes string.
**/
public void pO(String s) throws IOException {
pO();
p(s);
}
/**
* Indents out; writes string; ends current line.
**/
public void pOln(String s) throws IOException {
pO(s);
pln();
}
/**
* Indents out; writes string; ends current line; indents in.
*
* This method is useful for generating lines of code that both
* end and begin nested blocks, like "} else {".
**/
public void pOlnI(String s) throws IOException {
pO(s);
pln();
pI();
}
/**
* Writes object.
**/
public void p(Object o) throws IOException {
write(o.toString());
}
/**
* Writes object; ends current line.
**/
public void pln(Object o) throws IOException {
p(o.toString());
pln();
}
/**
* Writes object; ends current line; indents in.
**/
public void plnI(Object o) throws IOException {
p(o.toString());
pln();
pI();
}
/**
* Indents out; writes object.
**/
public void pO(Object o) throws IOException {
pO();
p(o.toString());
}
/**
* Indents out; writes object; ends current line.
**/
public void pOln(Object o) throws IOException {
pO(o.toString());
pln();
}
/**
* Indents out; writes object; ends current line; indents in.
*
* This method is useful for generating lines of code that both
* end and begin nested blocks, like "} else {".
**/
public void pOlnI(Object o) throws IOException {
pO(o.toString());
pln();
pI();
}
}

View File

@@ -0,0 +1,689 @@
/*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import sun.rmi.rmic.newrmic.jrmp.JrmpGenerator;
import sun.tools.util.CommandLine;
/**
* The rmic front end. This class contains the "main" method for rmic
* command line invocation.
*
* A Main instance contains the stream to output error messages and
* other diagnostics to.
*
* An rmic compilation batch (for example, one rmic command line
* invocation) is executed by invoking the "compile" method of a Main
* instance.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* NOTE: If and when there is a J2SE API for invoking SDK tools, this
* class should be updated to support that API.
*
* NOTE: This class is the front end for a "new" rmic implementation,
* which uses javadoc and the doclet API for reading class files and
* javac for compiling generated source files. This implementation is
* incomplete: it lacks any CORBA-based back end implementations, and
* thus the command line options "-idl", "-iiop", and their related
* options are not yet supported. The front end for the "old",
* oldjavac-based rmic implementation is sun.rmi.rmic.Main.
*
* @author Peter Jones
**/
public class Main {
/*
* Implementation note:
*
* In order to use the doclet API to read class files, much of
* this implementation of rmic executes as a doclet within an
* invocation of javadoc. This class is used as the doclet class
* for such javadoc invocations, via its static "start" and
* "optionLength" methods. There is one javadoc invocation per
* rmic compilation batch.
*
* The only guaranteed way to pass data to a doclet through a
* javadoc invocation is through doclet-specific options on the
* javadoc "command line". Rather than passing numerous pieces of
* individual data in string form as javadoc options, we use a
* single doclet-specific option ("-batchID") to pass a numeric
* identifier that uniquely identifies the rmic compilation batch
* that the javadoc invocation is for, and that identifier can
* then be used as a key in a global table to retrieve an object
* containing all of batch-specific data (rmic command line
* arguments, etc.).
*/
/** guards "batchCount" */
private static final Object batchCountLock = new Object();
/** number of batches run; used to generated batch IDs */
private static long batchCount = 0;
/** maps batch ID to batch data */
private static final Map<Long,Batch> batchTable =
Collections.synchronizedMap(new HashMap<Long,Batch>());
/** stream to output error messages and other diagnostics to */
private final PrintStream out;
/** name of this program, to use in error messages */
private final String program;
/**
* Command line entry point.
**/
public static void main(String[] args) {
Main rmic = new Main(System.err, "rmic");
System.exit(rmic.compile(args) ? 0 : 1);
}
/**
* Creates a Main instance that writes output to the specified
* stream. The specified program name is used in error messages.
**/
public Main(OutputStream out, String program) {
this.out = out instanceof PrintStream ?
(PrintStream) out : new PrintStream(out);
this.program = program;
}
/**
* Compiles a batch of input classes, as given by the specified
* command line arguments. Protocol-specific generators are
* determined by the choice options on the command line. Returns
* true if successful, or false if an error occurred.
*
* NOTE: This method is retained for transitional consistency with
* previous implementations.
**/
public boolean compile(String[] args) {
long startTime = System.currentTimeMillis();
long batchID;
synchronized (batchCountLock) {
batchID = batchCount++; // assign batch ID
}
// process command line
Batch batch = parseArgs(args);
if (batch == null) {
return false; // terminate if error occurred
}
/*
* With the batch data retrievable in the global table, run
* javadoc to continue the rest of the batch's compliation as
* a doclet.
*/
boolean status;
try {
batchTable.put(batchID, batch);
status = invokeJavadoc(batch, batchID);
} finally {
batchTable.remove(batchID);
}
if (batch.verbose) {
long deltaTime = System.currentTimeMillis() - startTime;
output(Resources.getText("rmic.done_in",
Long.toString(deltaTime)));
}
return status;
}
/**
* Prints the specified string to the output stream of this Main
* instance.
**/
public void output(String msg) {
out.println(msg);
}
/**
* Prints an error message to the output stream of this Main
* instance. The first argument is used as a key in rmic's
* resource bundle, and the rest of the arguments are used as
* arguments in the formatting of the resource string.
**/
public void error(String msg, String... args) {
output(Resources.getText(msg, args));
}
/**
* Prints rmic's usage message to the output stream of this Main
* instance.
*
* This method is public so that it can be used by the "parseArgs"
* methods of Generator implementations.
**/
public void usage() {
error("rmic.usage", program);
}
/**
* Processes rmic command line arguments. Returns a Batch object
* representing the command line arguments if successful, or null
* if an error occurred. Processed elements of the args array are
* set to null.
**/
private Batch parseArgs(String[] args) {
Batch batch = new Batch();
/*
* Pre-process command line for @file arguments.
*/
try {
args = CommandLine.parse(args);
} catch (FileNotFoundException e) {
error("rmic.cant.read", e.getMessage());
return null;
} catch (IOException e) {
e.printStackTrace(out);
return null;
}
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
// already processed by a generator
continue;
} else if (args[i].equals("-Xnew")) {
// we're already using the "new" implementation
args[i] = null;
} else if (args[i].equals("-show")) {
// obselete: fail
error("rmic.option.unsupported", args[i]);
usage();
return null;
} else if (args[i].equals("-O")) {
// obselete: warn but tolerate
error("rmic.option.unsupported", args[i]);
args[i] = null;
} else if (args[i].equals("-debug")) {
// obselete: warn but tolerate
error("rmic.option.unsupported", args[i]);
args[i] = null;
} else if (args[i].equals("-depend")) {
// obselete: warn but tolerate
// REMIND: should this fail instead?
error("rmic.option.unsupported", args[i]);
args[i] = null;
} else if (args[i].equals("-keep") ||
args[i].equals("-keepgenerated"))
{
batch.keepGenerated = true;
args[i] = null;
} else if (args[i].equals("-g")) {
batch.debug = true;
args[i] = null;
} else if (args[i].equals("-nowarn")) {
batch.noWarn = true;
args[i] = null;
} else if (args[i].equals("-nowrite")) {
batch.noWrite = true;
args[i] = null;
} else if (args[i].equals("-verbose")) {
batch.verbose = true;
args[i] = null;
} else if (args[i].equals("-Xnocompile")) {
batch.noCompile = true;
batch.keepGenerated = true;
args[i] = null;
} else if (args[i].equals("-bootclasspath")) {
if ((i + 1) >= args.length) {
error("rmic.option.requires.argument", args[i]);
usage();
return null;
}
if (batch.bootClassPath != null) {
error("rmic.option.already.seen", args[i]);
usage();
return null;
}
args[i] = null;
batch.bootClassPath = args[++i];
assert batch.bootClassPath != null;
args[i] = null;
} else if (args[i].equals("-extdirs")) {
if ((i + 1) >= args.length) {
error("rmic.option.requires.argument", args[i]);
usage();
return null;
}
if (batch.extDirs != null) {
error("rmic.option.already.seen", args[i]);
usage();
return null;
}
args[i] = null;
batch.extDirs = args[++i];
assert batch.extDirs != null;
args[i] = null;
} else if (args[i].equals("-classpath")) {
if ((i + 1) >= args.length) {
error("rmic.option.requires.argument", args[i]);
usage();
return null;
}
if (batch.classPath != null) {
error("rmic.option.already.seen", args[i]);
usage();
return null;
}
args[i] = null;
batch.classPath = args[++i];
assert batch.classPath != null;
args[i] = null;
} else if (args[i].equals("-d")) {
if ((i + 1) >= args.length) {
error("rmic.option.requires.argument", args[i]);
usage();
return null;
}
if (batch.destDir != null) {
error("rmic.option.already.seen", args[i]);
usage();
return null;
}
args[i] = null;
batch.destDir = new File(args[++i]);
assert batch.destDir != null;
args[i] = null;
if (!batch.destDir.exists()) {
error("rmic.no.such.directory", batch.destDir.getPath());
usage();
return null;
}
} else if (args[i].equals("-v1.1") ||
args[i].equals("-vcompat") ||
args[i].equals("-v1.2"))
{
Generator gen = new JrmpGenerator();
batch.generators.add(gen);
// JrmpGenerator only requires base BatchEnvironment class
if (!gen.parseArgs(args, this)) {
return null;
}
} else if (args[i].equalsIgnoreCase("-iiop")) {
error("rmic.option.unimplemented", args[i]);
return null;
// Generator gen = new IiopGenerator();
// batch.generators.add(gen);
// if (!batch.envClass.isAssignableFrom(gen.envClass())) {
// error("rmic.cannot.use.both",
// batch.envClass.getName(), gen.envClass().getName());
// return null;
// }
// batch.envClass = gen.envClass();
// if (!gen.parseArgs(args, this)) {
// return null;
// }
} else if (args[i].equalsIgnoreCase("-idl")) {
error("rmic.option.unimplemented", args[i]);
return null;
// see implementation sketch above
} else if (args[i].equalsIgnoreCase("-xprint")) {
error("rmic.option.unimplemented", args[i]);
return null;
// see implementation sketch above
}
}
/*
* At this point, all that remains non-null in the args
* array are input class names or illegal options.
*/
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
if (args[i].startsWith("-")) {
error("rmic.no.such.option", args[i]);
usage();
return null;
} else {
batch.classes.add(args[i]);
}
}
}
if (batch.classes.isEmpty()) {
usage();
return null;
}
/*
* If options did not specify at least one protocol-specific
* generator, then JRMP is the default.
*/
if (batch.generators.isEmpty()) {
batch.generators.add(new JrmpGenerator());
}
return batch;
}
/**
* Doclet class entry point.
**/
public static boolean start(RootDoc rootDoc) {
/*
* Find batch ID among javadoc options, and retrieve
* corresponding batch data from global table.
*/
long batchID = -1;
for (String[] option : rootDoc.options()) {
if (option[0].equals("-batchID")) {
try {
batchID = Long.parseLong(option[1]);
} catch (NumberFormatException e) {
throw new AssertionError(e);
}
}
}
Batch batch = batchTable.get(batchID);
assert batch != null;
/*
* Construct batch environment using class agreed upon by
* generator implementations.
*/
BatchEnvironment env;
try {
Constructor<? extends BatchEnvironment> cons =
batch.envClass.getConstructor(new Class<?>[] { RootDoc.class });
env = cons.newInstance(rootDoc);
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InstantiationException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
throw new AssertionError(e);
}
env.setVerbose(batch.verbose);
/*
* Determine the destination directory (the top of the package
* hierarchy) for the output of this batch; if no destination
* directory was specified on the command line, then the
* default is the current working directory.
*/
File destDir = batch.destDir;
if (destDir == null) {
destDir = new File(System.getProperty("user.dir"));
}
/*
* Run each input class through each generator.
*/
for (String inputClassName : batch.classes) {
ClassDoc inputClass = rootDoc.classNamed(inputClassName);
try {
for (Generator gen : batch.generators) {
gen.generate(env, inputClass, destDir);
}
} catch (NullPointerException e) {
/*
* We assume that this means that some class that was
* needed (perhaps even a bootstrap class) was not
* found, and that javadoc has already reported this
* as an error. There is nothing for us to do here
* but try to continue with the next input class.
*
* REMIND: More explicit error checking throughout
* would be preferable, however.
*/
}
}
/*
* Compile any generated source files, if configured to do so.
*/
boolean status = true;
List<File> generatedFiles = env.generatedFiles();
if (!batch.noCompile && !batch.noWrite && !generatedFiles.isEmpty()) {
status = batch.enclosingMain().invokeJavac(batch, generatedFiles);
}
/*
* Delete any generated source files, if configured to do so.
*/
if (!batch.keepGenerated) {
for (File file : generatedFiles) {
file.delete();
}
}
return status;
}
/**
* Doclet class method that indicates that this doclet class
* recognizes (only) the "-batchID" option on the javadoc command
* line, and that the "-batchID" option comprises two arguments on
* the javadoc command line.
**/
public static int optionLength(String option) {
if (option.equals("-batchID")) {
return 2;
} else {
return 0;
}
}
/**
* Runs the javadoc tool to invoke this class as a doclet, passing
* command line options derived from the specified batch data and
* indicating the specified batch ID.
*
* NOTE: This method currently uses a J2SE-internal API to run
* javadoc. If and when there is a J2SE API for invoking SDK
* tools, this method should be updated to use that API instead.
**/
private boolean invokeJavadoc(Batch batch, long batchID) {
List<String> javadocArgs = new ArrayList<String>();
// include all types, regardless of language-level access
javadocArgs.add("-private");
// inputs are class names, not source files
javadocArgs.add("-Xclasses");
// reproduce relevant options from rmic invocation
if (batch.verbose) {
javadocArgs.add("-verbose");
}
if (batch.bootClassPath != null) {
javadocArgs.add("-bootclasspath");
javadocArgs.add(batch.bootClassPath);
}
if (batch.extDirs != null) {
javadocArgs.add("-extdirs");
javadocArgs.add(batch.extDirs);
}
if (batch.classPath != null) {
javadocArgs.add("-classpath");
javadocArgs.add(batch.classPath);
}
// specify batch ID
javadocArgs.add("-batchID");
javadocArgs.add(Long.toString(batchID));
/*
* Run javadoc on union of rmic input classes and all
* generators' bootstrap classes, so that they will all be
* available to the doclet code.
*/
Set<String> classNames = new HashSet<String>();
for (Generator gen : batch.generators) {
classNames.addAll(gen.bootstrapClassNames());
}
classNames.addAll(batch.classes);
for (String s : classNames) {
javadocArgs.add(s);
}
// run javadoc with our program name and output stream
int status = com.sun.tools.javadoc.Main.execute(
program,
new PrintWriter(out, true),
new PrintWriter(out, true),
new PrintWriter(out, true),
this.getClass().getName(), // doclet class is this class
javadocArgs.toArray(new String[javadocArgs.size()]));
return status == 0;
}
/**
* Runs the javac tool to compile the specified source files,
* passing command line options derived from the specified batch
* data.
*
* NOTE: This method currently uses a J2SE-internal API to run
* javac. If and when there is a J2SE API for invoking SDK tools,
* this method should be updated to use that API instead.
**/
private boolean invokeJavac(Batch batch, List<File> files) {
List<String> javacArgs = new ArrayList<String>();
// rmic never wants to display javac warnings
javacArgs.add("-nowarn");
// reproduce relevant options from rmic invocation
if (batch.debug) {
javacArgs.add("-g");
}
if (batch.verbose) {
javacArgs.add("-verbose");
}
if (batch.bootClassPath != null) {
javacArgs.add("-bootclasspath");
javacArgs.add(batch.bootClassPath);
}
if (batch.extDirs != null) {
javacArgs.add("-extdirs");
javacArgs.add(batch.extDirs);
}
if (batch.classPath != null) {
javacArgs.add("-classpath");
javacArgs.add(batch.classPath);
}
/*
* For now, rmic still always produces class files that have a
* class file format version compatible with JDK 1.1.
*/
javacArgs.add("-source");
javacArgs.add("1.3");
javacArgs.add("-target");
javacArgs.add("1.1");
// add source files to compile
for (File file : files) {
javacArgs.add(file.getPath());
}
// run javac with our output stream
int status = com.sun.tools.javac.Main.compile(
javacArgs.toArray(new String[javacArgs.size()]),
new PrintWriter(out, true));
return status == 0;
}
/**
* The data for an rmic compliation batch: the processed command
* line arguments.
**/
private class Batch {
boolean keepGenerated = false; // -keep or -keepgenerated
boolean debug = false; // -g
boolean noWarn = false; // -nowarn
boolean noWrite = false; // -nowrite
boolean verbose = false; // -verbose
boolean noCompile = false; // -Xnocompile
String bootClassPath = null; // -bootclasspath
String extDirs = null; // -extdirs
String classPath = null; // -classpath
File destDir = null; // -d
List<Generator> generators = new ArrayList<Generator>();
Class<? extends BatchEnvironment> envClass = BatchEnvironment.class;
List<String> classes = new ArrayList<String>();
Batch() { }
/**
* Returns the Main instance for this batch.
**/
Main enclosingMain() {
return Main.this;
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic;
import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* Provides resource support for rmic.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public final class Resources {
private static ResourceBundle resources = null;
private static ResourceBundle resourcesExt = null;
static {
try {
resources =
ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic");
} catch (MissingResourceException e) {
// gracefully handle this later
}
try {
resourcesExt =
ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext");
} catch (MissingResourceException e) {
// OK if this isn't found
}
}
private Resources() { throw new AssertionError(); }
/**
* Returns the text of the rmic resource for the specified key
* formatted with the specified arguments.
**/
public static String getText(String key, String... args) {
String format = getString(key);
if (format == null) {
format = "missing resource key: key = \"" + key + "\", " +
"arguments = \"{0}\", \"{1}\", \"{2}\"";
}
return MessageFormat.format(format, (Object[]) args);
}
/**
* Returns the rmic resource string for the specified key.
**/
private static String getString(String key) {
if (resourcesExt != null) {
try {
return resourcesExt.getString(key);
} catch (MissingResourceException e) {
}
}
if (resources != null) {
try {
return resources.getString(key);
} catch (MissingResourceException e) {
return null;
}
}
return "missing resource bundle: key = \"" + key + "\", " +
"arguments = \"{0}\", \"{1}\", \"{2}\"";
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic.jrmp;
/**
* Constants specific to the JRMP rmic generator.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
final class Constants {
private Constants() { throw new AssertionError(); }
/*
* fully-qualified names of types used by rmic
*/
static final String REMOTE_OBJECT = "java.rmi.server.RemoteObject";
static final String REMOTE_STUB = "java.rmi.server.RemoteStub";
static final String REMOTE_REF = "java.rmi.server.RemoteRef";
static final String OPERATION = "java.rmi.server.Operation";
static final String SKELETON = "java.rmi.server.Skeleton";
static final String SKELETON_MISMATCH_EXCEPTION =
"java.rmi.server.SkeletonMismatchException";
static final String REMOTE_CALL = "java.rmi.server.RemoteCall";
static final String MARSHAL_EXCEPTION = "java.rmi.MarshalException";
static final String UNMARSHAL_EXCEPTION = "java.rmi.UnmarshalException";
static final String UNEXPECTED_EXCEPTION = "java.rmi.UnexpectedException";
/*
* stub protocol versions
*/
enum StubVersion { V1_1, VCOMPAT, V1_2 };
/*
* serialVersionUID for all stubs that can use 1.2 protocol
*/
static final long STUB_SERIAL_VERSION_UID = 2;
/*
* version number used to seed interface hash computation
*/
static final int INTERFACE_HASH_STUB_VERSION = 1;
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic.jrmp;
import com.sun.javadoc.ClassDoc;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import sun.rmi.rmic.newrmic.BatchEnvironment;
import sun.rmi.rmic.newrmic.Generator;
import sun.rmi.rmic.newrmic.IndentingWriter;
import sun.rmi.rmic.newrmic.Main;
import sun.rmi.rmic.newrmic.Resources;
import static sun.rmi.rmic.newrmic.jrmp.Constants.*;
/**
* JRMP rmic back end; generates source code for JRMP stub and
* skeleton classes.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
public class JrmpGenerator implements Generator {
private static final Map<String,StubVersion> versionOptions =
new HashMap<String,StubVersion>();
static {
versionOptions.put("-v1.1", StubVersion.V1_1);
versionOptions.put("-vcompat", StubVersion.VCOMPAT);
versionOptions.put("-v1.2", StubVersion.V1_2);
}
private static final Set<String> bootstrapClassNames =
new HashSet<String>();
static {
bootstrapClassNames.add("java.lang.Exception");
bootstrapClassNames.add("java.rmi.Remote");
bootstrapClassNames.add("java.rmi.RemoteException");
bootstrapClassNames.add("java.lang.RuntimeException");
};
/** version of the JRMP stub protocol to generate code for */
private StubVersion version = StubVersion.V1_2; // default is -v1.2
/**
* Creates a new JrmpGenerator.
**/
public JrmpGenerator() { }
/**
* The JRMP generator recognizes command line options for
* selecting the JRMP stub protocol version to generate classes
* for. Only one such option is allowed.
**/
public boolean parseArgs(String[] args, Main main) {
String explicitVersion = null;
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (versionOptions.containsKey(arg)) {
if (explicitVersion != null && !explicitVersion.equals(arg)) {
main.error("rmic.cannot.use.both", explicitVersion, arg);
return false;
}
explicitVersion = arg;
version = versionOptions.get(arg);
args[i] = null;
}
}
return true;
}
/**
* The JRMP generator does not require an environment class more
* specific than BatchEnvironment.
**/
public Class<? extends BatchEnvironment> envClass() {
return BatchEnvironment.class;
}
public Set<String> bootstrapClassNames() {
return Collections.unmodifiableSet(bootstrapClassNames);
}
/**
* Generates the source file(s) for the JRMP stub class and
* (optionally) skeleton class for the specified remote
* implementation class.
**/
public void generate(BatchEnvironment env,
ClassDoc inputClass,
File destDir)
{
RemoteClass remoteClass = RemoteClass.forClass(env, inputClass);
if (remoteClass == null) {
return; // an error must have occurred
}
StubSkeletonWriter writer =
new StubSkeletonWriter(env, remoteClass, version);
File stubFile = sourceFileForClass(writer.stubClassName(), destDir);
try {
IndentingWriter out = new IndentingWriter(
new OutputStreamWriter(new FileOutputStream(stubFile)));
writer.writeStub(out);
out.close();
if (env.verbose()) {
env.output(Resources.getText("rmic.wrote",
stubFile.getPath()));
}
env.addGeneratedFile(stubFile);
} catch (IOException e) {
env.error("rmic.cant.write", stubFile.toString());
return;
}
File skeletonFile =
sourceFileForClass(writer.skeletonClassName(), destDir);
if (version == StubVersion.V1_1 ||
version == StubVersion.VCOMPAT)
{
try {
IndentingWriter out = new IndentingWriter(
new OutputStreamWriter(
new FileOutputStream(skeletonFile)));
writer.writeSkeleton(out);
out.close();
if (env.verbose()) {
env.output(Resources.getText("rmic.wrote",
skeletonFile.getPath()));
}
env.addGeneratedFile(skeletonFile);
} catch (IOException e) {
env.error("rmic.cant.write", skeletonFile.toString());
return;
}
} else {
/*
* If skeleton files are not being generated for this run,
* delete old skeleton source or class files for this
* remote implementation class that were (presumably) left
* over from previous runs, to avoid user confusion from
* extraneous or inconsistent generated files.
*/
File skeletonClassFile =
classFileForClass(writer.skeletonClassName(), destDir);
skeletonFile.delete(); // ignore failures (no big deal)
skeletonClassFile.delete();
}
}
/**
* Returns the File object to be used as the source file for a
* class with the specified binary name, with the specified
* destination directory as the top of the package hierarchy.
**/
private File sourceFileForClass(String binaryName, File destDir) {
return fileForClass(binaryName, destDir, ".java");
}
/**
* Returns the File object to be used as the class file for a
* class with the specified binary name, with the supplied
* destination directory as the top of the package hierarchy.
**/
private File classFileForClass(String binaryName, File destDir) {
return fileForClass(binaryName, destDir, ".class");
}
private File fileForClass(String binaryName, File destDir, String ext) {
int i = binaryName.lastIndexOf('.');
String classFileName = binaryName.substring(i + 1) + ext;
if (i != -1) {
String packageName = binaryName.substring(0, i);
String packagePath = packageName.replace('.', File.separatorChar);
File packageDir = new File(destDir, packagePath);
/*
* Make sure that the directory for this package exists.
* We assume that the caller has verified that the top-
* level destination directory exists, so we need not
* worry about creating it unintentionally.
*/
if (!packageDir.exists()) {
packageDir.mkdirs();
}
return new File(packageDir, classFileName);
} else {
return new File(destDir, classFileName);
}
}
}

View File

@@ -0,0 +1,710 @@
/*
* Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic.jrmp;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.Type;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.MessageDigest;
import java.security.DigestOutputStream;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import sun.rmi.rmic.newrmic.BatchEnvironment;
import static sun.rmi.rmic.newrmic.Constants.*;
import static sun.rmi.rmic.newrmic.jrmp.Constants.*;
/**
* Encapsulates RMI-specific information about a remote implementation
* class (a class that implements one or more remote interfaces).
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
final class RemoteClass {
/** rmic environment for this object */
private final BatchEnvironment env;
/** the remote implementation class this object represents */
private final ClassDoc implClass;
/** remote interfaces implemented by this class */
private ClassDoc[] remoteInterfaces;
/** the remote methods of this class */
private Method[] remoteMethods;
/** stub/skeleton "interface hash" for this class */
private long interfaceHash;
/**
* Creates a RemoteClass instance that represents the RMI-specific
* information about the specified remote implementation class.
*
* If the class is not a valid remote implementation class or if
* some other error occurs, the return value will be null, and
* errors will have been reported to the supplied
* BatchEnvironment.
**/
static RemoteClass forClass(BatchEnvironment env, ClassDoc implClass) {
RemoteClass remoteClass = new RemoteClass(env, implClass);
if (remoteClass.init()) {
return remoteClass;
} else {
return null;
}
}
/**
* Creates a RemoteClass instance for the specified class. The
* resulting object is not yet initialized.
**/
private RemoteClass(BatchEnvironment env, ClassDoc implClass) {
this.env = env;
this.implClass = implClass;
}
/**
* Returns the ClassDoc for this remote implementation class.
**/
ClassDoc classDoc() {
return implClass;
}
/**
* Returns the remote interfaces implemented by this remote
* implementation class.
*
* A remote interface is an interface that is a subinterface of
* java.rmi.Remote. The remote interfaces of a class are the
* direct superinterfaces of the class and all of its superclasses
* that are remote interfaces.
*
* The order of the array returned is arbitrary, and some elements
* may be superfluous (i.e., superinterfaces of other interfaces
* in the array).
**/
ClassDoc[] remoteInterfaces() {
return remoteInterfaces.clone();
}
/**
* Returns an array of RemoteClass.Method objects representing all
* of the remote methods of this remote implementation class (all
* of the member methods of the class's remote interfaces).
*
* The methods in the array are ordered according to a comparison
* of strings consisting of their name followed by their
* descriptor, so each method's index in the array corresponds to
* its "operation number" in the JDK 1.1 version of the JRMP
* stub/skeleton protocol.
**/
Method[] remoteMethods() {
return remoteMethods.clone();
}
/**
* Returns the "interface hash" used to match a stub/skeleton pair
* for this remote implementation class in the JDK 1.1 version of
* the JRMP stub/skeleton protocol.
**/
long interfaceHash() {
return interfaceHash;
}
/**
* Validates this remote implementation class and computes the
* RMI-specific information. Returns true if successful, or false
* if an error occurred.
**/
private boolean init() {
/*
* Verify that it is really a class, not an interface.
*/
if (implClass.isInterface()) {
env.error("rmic.cant.make.stubs.for.interface",
implClass.qualifiedName());
return false;
}
/*
* Find all of the remote interfaces of our remote
* implementation class-- for each class up the superclass
* chain, add each directly-implemented interface that somehow
* extends Remote to a list.
*/
List<ClassDoc> remotesImplemented = new ArrayList<ClassDoc>();
for (ClassDoc cl = implClass; cl != null; cl = cl.superclass()) {
for (ClassDoc intf : cl.interfaces()) {
/*
* Add interface to the list if it extends Remote and
* it is not already there.
*/
if (!remotesImplemented.contains(intf) &&
intf.subclassOf(env.docRemote()))
{
remotesImplemented.add(intf);
if (env.verbose()) {
env.output("[found remote interface: " +
intf.qualifiedName() + "]");
}
}
}
/*
* Verify that the candidate remote implementation class
* implements at least one remote interface directly.
*/
if (cl == implClass && remotesImplemented.isEmpty()) {
if (implClass.subclassOf(env.docRemote())) {
/*
* This error message is used if the class does
* implement a remote interface through one of its
* superclasses, but not directly.
*/
env.error("rmic.must.implement.remote.directly",
implClass.qualifiedName());
} else {
/*
* This error message is used if the class does
* not implement a remote interface at all.
*/
env.error("rmic.must.implement.remote",
implClass.qualifiedName());
}
return false;
}
}
/*
* Convert list of remote interfaces to an array
* (order is not important for this array).
*/
remoteInterfaces =
remotesImplemented.toArray(
new ClassDoc[remotesImplemented.size()]);
/*
* Collect the methods from all of the remote interfaces into
* a table, which maps from method name-and-descriptor string
* to Method object.
*/
Map<String,Method> methods = new HashMap<String,Method>();
boolean errors = false;
for (ClassDoc intf : remotesImplemented) {
if (!collectRemoteMethods(intf, methods)) {
/*
* Continue iterating despite errors in order to
* generate more complete error report.
*/
errors = true;
}
}
if (errors) {
return false;
}
/*
* Sort table of remote methods into an array. The elements
* are sorted in ascending order of the string of the method's
* name and descriptor, so that each elements index is equal
* to its operation number in the JDK 1.1 version of the JRMP
* stub/skeleton protocol.
*/
String[] orderedKeys =
methods.keySet().toArray(new String[methods.size()]);
Arrays.sort(orderedKeys);
remoteMethods = new Method[methods.size()];
for (int i = 0; i < remoteMethods.length; i++) {
remoteMethods[i] = methods.get(orderedKeys[i]);
if (env.verbose()) {
String msg = "[found remote method <" + i + ">: " +
remoteMethods[i].operationString();
ClassDoc[] exceptions = remoteMethods[i].exceptionTypes();
if (exceptions.length > 0) {
msg += " throws ";
for (int j = 0; j < exceptions.length; j++) {
if (j > 0) {
msg += ", ";
}
msg += exceptions[j].qualifiedName();
}
}
msg += "\n\tname and descriptor = \"" +
remoteMethods[i].nameAndDescriptor();
msg += "\n\tmethod hash = " +
remoteMethods[i].methodHash() + "]";
env.output(msg);
}
}
/*
* Finally, pre-compute the interface hash to be used by
* stubs/skeletons for this remote class in the JDK 1.1
* version of the JRMP stub/skeleton protocol.
*/
interfaceHash = computeInterfaceHash();
return true;
}
/**
* Collects and validates all methods from the specified interface
* and all of its superinterfaces as remote methods. Remote
* methods are added to the supplied table. Returns true if
* successful, or false if an error occurred.
**/
private boolean collectRemoteMethods(ClassDoc intf,
Map<String,Method> table)
{
if (!intf.isInterface()) {
throw new AssertionError(
intf.qualifiedName() + " not an interface");
}
boolean errors = false;
/*
* Search interface's declared methods.
*/
nextMethod:
for (MethodDoc method : intf.methods()) {
/*
* Verify that each method throws RemoteException (or a
* superclass of RemoteException).
*/
boolean hasRemoteException = false;
for (ClassDoc ex : method.thrownExceptions()) {
if (env.docRemoteException().subclassOf(ex)) {
hasRemoteException = true;
break;
}
}
/*
* If this method did not throw RemoteException as required,
* generate the error but continue, so that multiple such
* errors can be reported.
*/
if (!hasRemoteException) {
env.error("rmic.must.throw.remoteexception",
intf.qualifiedName(),
method.name() + method.signature());
errors = true;
continue nextMethod;
}
/*
* Verify that the implementation of this method throws only
* java.lang.Exception or its subclasses (fix bugid 4092486).
* JRMP does not support remote methods throwing
* java.lang.Throwable or other subclasses.
*/
MethodDoc implMethod = findImplMethod(method);
if (implMethod != null) { // should not be null
for (ClassDoc ex : implMethod.thrownExceptions()) {
if (!ex.subclassOf(env.docException())) {
env.error("rmic.must.only.throw.exception",
implMethod.name() + implMethod.signature(),
ex.qualifiedName());
errors = true;
continue nextMethod;
}
}
}
/*
* Create RemoteClass.Method object to represent this method
* found in a remote interface.
*/
Method newMethod = new Method(method);
/*
* Store remote method's representation in the table of
* remote methods found, keyed by its name and descriptor.
*
* If the table already contains an entry with the same
* method name and descriptor, then we must replace the
* old entry with a Method object that represents a legal
* combination of the old and the new methods;
* specifically, the combined method must have a throws
* clause that contains (only) all of the checked
* exceptions that can be thrown by both the old and the
* new method (see bugid 4070653).
*/
String key = newMethod.nameAndDescriptor();
Method oldMethod = table.get(key);
if (oldMethod != null) {
newMethod = newMethod.mergeWith(oldMethod);
}
table.put(key, newMethod);
}
/*
* Recursively collect methods for all superinterfaces.
*/
for (ClassDoc superintf : intf.interfaces()) {
if (!collectRemoteMethods(superintf, table)) {
errors = true;
}
}
return !errors;
}
/**
* Returns the MethodDoc for the method of this remote
* implementation class that implements the specified remote
* method of a remote interface. Returns null if no matching
* method was found in this remote implementation class.
**/
private MethodDoc findImplMethod(MethodDoc interfaceMethod) {
String name = interfaceMethod.name();
String desc = Util.methodDescriptorOf(interfaceMethod);
for (MethodDoc implMethod : implClass.methods()) {
if (name.equals(implMethod.name()) &&
desc.equals(Util.methodDescriptorOf(implMethod)))
{
return implMethod;
}
}
return null;
}
/**
* Computes the "interface hash" of the stub/skeleton pair for
* this remote implementation class. This is the 64-bit value
* used to enforce compatibility between a stub class and a
* skeleton class in the JDK 1.1 version of the JRMP stub/skeleton
* protocol.
*
* It is calculated using the first 64 bits of an SHA digest. The
* digest is of a stream consisting of the following data:
* (int) stub version number, always 1
* for each remote method, in order of operation number:
* (UTF-8) method name
* (UTF-8) method descriptor
* for each declared exception, in alphabetical name order:
* (UTF-8) name of exception class
* (where "UTF-8" includes a 16-bit length prefix as written by
* java.io.DataOutput.writeUTF).
**/
private long computeInterfaceHash() {
long hash = 0;
ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DataOutputStream out = new DataOutputStream(
new DigestOutputStream(sink, md));
out.writeInt(INTERFACE_HASH_STUB_VERSION);
for (Method method : remoteMethods) {
MethodDoc methodDoc = method.methodDoc();
out.writeUTF(methodDoc.name());
out.writeUTF(Util.methodDescriptorOf(methodDoc));
// descriptors already use binary names
ClassDoc exceptions[] = methodDoc.thrownExceptions();
Arrays.sort(exceptions, new ClassDocComparator());
for (ClassDoc ex : exceptions) {
out.writeUTF(Util.binaryNameOf(ex));
}
}
out.flush();
// use only the first 64 bits of the digest for the hash
byte hashArray[] = md.digest();
for (int i = 0; i < Math.min(8, hashArray.length); i++) {
hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
}
} catch (IOException e) {
throw new AssertionError(e);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
return hash;
}
/**
* Compares ClassDoc instances according to the lexicographic
* order of their binary names.
**/
private static class ClassDocComparator implements Comparator<ClassDoc> {
public int compare(ClassDoc o1, ClassDoc o2) {
return Util.binaryNameOf(o1).compareTo(Util.binaryNameOf(o2));
}
}
/**
* Encapsulates RMI-specific information about a particular remote
* method in the remote implementation class represented by the
* enclosing RemoteClass.
**/
final class Method implements Cloneable {
/**
* MethodDoc for this remove method, from one of the remote
* interfaces that this method was found in.
*
* Note that this MethodDoc may be only one of multiple that
* correspond to this remote method object, if multiple of
* this class's remote interfaces contain methods with the
* same name and descriptor. Therefore, this MethodDoc may
* declare more exceptions thrown that this remote method
* does.
**/
private final MethodDoc methodDoc;
/** java.rmi.server.Operation string for this remote method */
private final String operationString;
/** name and descriptor of this remote method */
private final String nameAndDescriptor;
/** JRMP "method hash" for this remote method */
private final long methodHash;
/**
* Exceptions declared to be thrown by this remote method.
*
* This list may include superfluous entries, such as
* unchecked exceptions and subclasses of other entries.
**/
private ClassDoc[] exceptionTypes;
/**
* Creates a new Method instance for the specified method.
**/
Method(MethodDoc methodDoc) {
this.methodDoc = methodDoc;
exceptionTypes = methodDoc.thrownExceptions();
/*
* Sort exception types to improve consistency with
* previous implementations.
*/
Arrays.sort(exceptionTypes, new ClassDocComparator());
operationString = computeOperationString();
nameAndDescriptor =
methodDoc.name() + Util.methodDescriptorOf(methodDoc);
methodHash = computeMethodHash();
}
/**
* Returns the MethodDoc object corresponding to this method
* of a remote interface.
**/
MethodDoc methodDoc() {
return methodDoc;
}
/**
* Returns the parameter types declared by this method.
**/
Type[] parameterTypes() {
Parameter[] parameters = methodDoc.parameters();
Type[] paramTypes = new Type[parameters.length];
for (int i = 0; i < paramTypes.length; i++) {
paramTypes[i] = parameters[i].type();
}
return paramTypes;
}
/**
* Returns the exception types declared to be thrown by this
* remote method.
*
* For methods with the same name and descriptor inherited
* from multiple remote interfaces, the array will contain the
* set of exceptions declared in all of the interfaces'
* methods that can be legally thrown by all of them.
**/
ClassDoc[] exceptionTypes() {
return exceptionTypes.clone();
}
/**
* Returns the JRMP "method hash" used to identify this remote
* method in the JDK 1.2 version of the stub protocol.
**/
long methodHash() {
return methodHash;
}
/**
* Returns the string representation of this method
* appropriate for the construction of a
* java.rmi.server.Operation object.
**/
String operationString() {
return operationString;
}
/**
* Returns a string consisting of this method's name followed
* by its descriptor.
**/
String nameAndDescriptor() {
return nameAndDescriptor;
}
/**
* Returns a new Method object that is a legal combination of
* this Method object and another one.
*
* Doing this requires determining the exceptions declared by
* the combined method, which must be (only) all of the
* exceptions declared in both old Methods that may thrown in
* either of them.
**/
Method mergeWith(Method other) {
if (!nameAndDescriptor().equals(other.nameAndDescriptor())) {
throw new AssertionError(
"attempt to merge method \"" +
other.nameAndDescriptor() + "\" with \"" +
nameAndDescriptor());
}
List<ClassDoc> legalExceptions = new ArrayList<ClassDoc>();
collectCompatibleExceptions(
other.exceptionTypes, exceptionTypes, legalExceptions);
collectCompatibleExceptions(
exceptionTypes, other.exceptionTypes, legalExceptions);
Method merged = clone();
merged.exceptionTypes =
legalExceptions.toArray(new ClassDoc[legalExceptions.size()]);
return merged;
}
/**
* Cloning is supported by returning a shallow copy of this
* object.
**/
protected Method clone() {
try {
return (Method) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/**
* Adds to the supplied list all exceptions in the "froms"
* array that are subclasses of an exception in the "withs"
* array.
**/
private void collectCompatibleExceptions(ClassDoc[] froms,
ClassDoc[] withs,
List<ClassDoc> list)
{
for (ClassDoc from : froms) {
if (!list.contains(from)) {
for (ClassDoc with : withs) {
if (from.subclassOf(with)) {
list.add(from);
break;
}
}
}
}
}
/**
* Computes the JRMP "method hash" of this remote method. The
* method hash is a long containing the first 64 bits of the
* SHA digest from the UTF-8 encoded string of the method name
* and descriptor.
**/
private long computeMethodHash() {
long hash = 0;
ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DataOutputStream out = new DataOutputStream(
new DigestOutputStream(sink, md));
String methodString = nameAndDescriptor();
out.writeUTF(methodString);
// use only the first 64 bits of the digest for the hash
out.flush();
byte hashArray[] = md.digest();
for (int i = 0; i < Math.min(8, hashArray.length); i++) {
hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
}
} catch (IOException e) {
throw new AssertionError(e);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
return hash;
}
/**
* Computes the string representation of this method
* appropriate for the construction of a
* java.rmi.server.Operation object.
**/
private String computeOperationString() {
/*
* To be consistent with previous implementations, we use
* the deprecated style of placing the "[]" for the return
* type (if any) after the parameter list.
*/
Type returnType = methodDoc.returnType();
String op = returnType.qualifiedTypeName() + " " +
methodDoc.name() + "(";
Parameter[] parameters = methodDoc.parameters();
for (int i = 0; i < parameters.length; i++) {
if (i > 0) {
op += ", ";
}
op += parameters[i].type().toString();
}
op += ")" + returnType.dimension();
return op;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic.jrmp;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.Type;
/**
* Provides static utility methods.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
final class Util {
private Util() { throw new AssertionError(); }
/**
* Returns the binary name of the class or interface represented
* by the specified ClassDoc.
**/
static String binaryNameOf(ClassDoc cl) {
String flat = cl.name().replace('.', '$');
String packageName = cl.containingPackage().name();
return packageName.equals("") ? flat : packageName + "." + flat;
}
/**
* Returns the method descriptor for the specified method.
*
* See section 4.3.3 of The Java Virtual Machine Specification
* Second Edition for the definition of a "method descriptor".
**/
static String methodDescriptorOf(MethodDoc method) {
String desc = "(";
Parameter[] parameters = method.parameters();
for (int i = 0; i < parameters.length; i++) {
desc += typeDescriptorOf(parameters[i].type());
}
desc += ")" + typeDescriptorOf(method.returnType());
return desc;
}
/**
* Returns the descriptor for the specified type, as appropriate
* for either a parameter or return type in a method descriptor.
**/
private static String typeDescriptorOf(Type type) {
String desc;
ClassDoc classDoc = type.asClassDoc();
if (classDoc == null) {
/*
* Handle primitive types.
*/
String name = type.typeName();
if (name.equals("boolean")) {
desc = "Z";
} else if (name.equals("byte")) {
desc = "B";
} else if (name.equals("char")) {
desc = "C";
} else if (name.equals("short")) {
desc = "S";
} else if (name.equals("int")) {
desc = "I";
} else if (name.equals("long")) {
desc = "J";
} else if (name.equals("float")) {
desc = "F";
} else if (name.equals("double")) {
desc = "D";
} else if (name.equals("void")) {
desc = "V";
} else {
throw new AssertionError(
"unrecognized primitive type: " + name);
}
} else {
/*
* Handle non-array reference types.
*/
desc = "L" + binaryNameOf(classDoc).replace('.', '/') + ";";
}
/*
* Handle array types.
*/
int dimensions = type.dimension().length() / 2;
for (int i = 0; i < dimensions; i++) {
desc = "[" + desc;
}
return desc;
}
/**
* Returns a reader-friendly string representation of the
* specified method's signature. Names of reference types are not
* package-qualified.
**/
static String getFriendlyUnqualifiedSignature(MethodDoc method) {
String sig = method.name() + "(";
Parameter[] parameters = method.parameters();
for (int i = 0; i < parameters.length; i++) {
if (i > 0) {
sig += ", ";
}
Type paramType = parameters[i].type();
sig += paramType.typeName() + paramType.dimension();
}
sig += ")";
return sig;
}
/**
* Returns true if the specified type is void.
**/
static boolean isVoid(Type type) {
return type.asClassDoc() == null && type.typeName().equals("void");
}
}

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.runtime;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.OutputStream;
import java.rmi.server.LogStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Formatter;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;
import java.util.Map;
import java.util.HashMap;
/**
* Utility which provides an abstract "logger" like RMI internal API
* which can be directed to use one of two types of logging
* infrastructure: the java.util.logging API or the
* java.rmi.server.LogStream API. The default behavior is to use the
* java.util.logging API. The LogStream API may be used instead by
* setting the system property sun.rmi.log.useOld to true.
*
* For backwards compatibility, supports the RMI system logging
* properties which pre-1.4 comprised the only way to configure RMI
* logging. If the java.util.logging API is used and RMI system log
* properties are set, the system properties override initial RMI
* logger values as appropriate. If the java.util.logging API is
* turned off, pre-1.4 logging behavior is used.
*
* @author Laird Dornin
* @since 1.4
*/
@SuppressWarnings("deprecation")
public abstract class Log {
/** Logger re-definition of old RMI log values */
public static final Level BRIEF = Level.FINE;
public static final Level VERBOSE = Level.FINER;
/* selects log implementation */
private static final LogFactory logFactory;
static {
boolean useOld =
Boolean.valueOf(java.security.AccessController.
doPrivileged(new sun.security.action.GetPropertyAction(
"sun.rmi.log.useOld"))).booleanValue();
/* set factory to select the logging facility to use */
logFactory = (useOld ? (LogFactory) new LogStreamLogFactory() :
(LogFactory) new LoggerLogFactory());
}
/** "logger like" API to be used by RMI implementation */
public abstract boolean isLoggable(Level level);
public abstract void log(Level level, String message);
public abstract void log(Level level, String message, Throwable thrown);
/** get and set the RMI server call output stream */
public abstract void setOutputStream(OutputStream stream);
public abstract PrintStream getPrintStream();
/** factory interface enables Logger and LogStream implementations */
private static interface LogFactory {
Log createLog(String loggerName, String oldLogName, Level level);
}
/* access log objects */
/**
* Access log for a tri-state system property.
*
* Need to first convert override value to a log level, taking
* care to interpret a range of values between BRIEF, VERBOSE and
* SILENT.
*
* An override < 0 is interpreted to mean that the logging
* configuration should not be overridden. The level passed to the
* factories createLog method will be null in this case.
*
* Note that if oldLogName is null and old logging is on, the
* returned LogStreamLog will ignore the override parameter - the
* log will never log messages. This permits new logs that only
* write to Loggers to do nothing when old logging is active.
*
* Do not call getLog multiple times on the same logger name.
* Since this is an internal API, no checks are made to ensure
* that multiple logs do not exist for the same logger.
*/
public static Log getLog(String loggerName, String oldLogName,
int override)
{
Level level;
if (override < 0) {
level = null;
} else if (override == LogStream.SILENT) {
level = Level.OFF;
} else if ((override > LogStream.SILENT) &&
(override <= LogStream.BRIEF)) {
level = BRIEF;
} else if ((override > LogStream.BRIEF) &&
(override <= LogStream.VERBOSE))
{
level = VERBOSE;
} else {
level = Level.FINEST;
}
return logFactory.createLog(loggerName, oldLogName, level);
}
/**
* Access logs associated with boolean properties
*
* Do not call getLog multiple times on the same logger name.
* Since this is an internal API, no checks are made to ensure
* that multiple logs do not exist for the same logger.
*/
public static Log getLog(String loggerName, String oldLogName,
boolean override)
{
Level level = (override ? VERBOSE : null);
return logFactory.createLog(loggerName, oldLogName, level);
}
/**
* Factory to create Log objects which deliver log messages to the
* java.util.logging API.
*/
private static class LoggerLogFactory implements LogFactory {
LoggerLogFactory() {}
/*
* Accessor to obtain an arbitrary RMI logger with name
* loggerName. If the level of the logger is greater than the
* level for the system property with name, the logger level
* will be set to the value of system property.
*/
public Log createLog(final String loggerName, String oldLogName,
final Level level)
{
Logger logger = Logger.getLogger(loggerName);
return new LoggerLog(logger, level);
}
}
/**
* Class specialized to log messages to the java.util.logging API
*/
private static class LoggerLog extends Log {
/* alternate console handler for RMI loggers */
private static final Handler alternateConsole =
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Handler>() {
public Handler run() {
InternalStreamHandler alternate =
new InternalStreamHandler(System.err);
alternate.setLevel(Level.ALL);
return alternate;
}
});
/** handler to which messages are copied */
private InternalStreamHandler copyHandler = null;
/* logger to which log messages are written */
private final Logger logger;
/* used as return value of RemoteServer.getLog */
private LoggerPrintStream loggerSandwich;
/** creates a Log which will delegate to the given logger */
private LoggerLog(final Logger logger, final Level level) {
this.logger = logger;
if (level != null){
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
if (!logger.isLoggable(level)) {
logger.setLevel(level);
}
logger.addHandler(alternateConsole);
return null;
}
}
);
}
}
public boolean isLoggable(Level level) {
return logger.isLoggable(level);
}
public void log(Level level, String message) {
if (isLoggable(level)) {
String[] source = getSource();
logger.logp(level, source[0], source[1],
Thread.currentThread().getName() + ": " + message);
}
}
public void log(Level level, String message, Throwable thrown) {
if (isLoggable(level)) {
String[] source = getSource();
logger.logp(level, source[0], source[1],
Thread.currentThread().getName() + ": " +
message, thrown);
}
}
/**
* Set the output stream associated with the RMI server call
* logger.
*
* Calling code needs LoggingPermission "control".
*/
public synchronized void setOutputStream(OutputStream out) {
if (out != null) {
if (!logger.isLoggable(VERBOSE)) {
logger.setLevel(VERBOSE);
}
copyHandler = new InternalStreamHandler(out);
copyHandler.setLevel(Log.VERBOSE);
logger.addHandler(copyHandler);
} else {
/* ensure that messages are not logged */
if (copyHandler != null) {
logger.removeHandler(copyHandler);
}
copyHandler = null;
}
}
public synchronized PrintStream getPrintStream() {
if (loggerSandwich == null) {
loggerSandwich = new LoggerPrintStream(logger);
}
return loggerSandwich;
}
}
/**
* Subclass of StreamHandler for redirecting log output. flush
* must be called in the publish and close methods.
*/
private static class InternalStreamHandler extends StreamHandler {
InternalStreamHandler(OutputStream out) {
super(out, new SimpleFormatter());
}
public void publish(LogRecord record) {
super.publish(record);
flush();
}
public void close() {
flush();
}
}
/**
* PrintStream which forwards log messages to the logger. Class
* is needed to maintain backwards compatibility with
* RemoteServer.{set|get}Log().
*/
private static class LoggerPrintStream extends PrintStream {
/** logger where output of this log is sent */
private final Logger logger;
/** record the last character written to this stream */
private int last = -1;
/** stream used for buffering lines */
private final ByteArrayOutputStream bufOut;
private LoggerPrintStream(Logger logger)
{
super(new ByteArrayOutputStream());
bufOut = (ByteArrayOutputStream) super.out;
this.logger = logger;
}
public void write(int b) {
if ((last == '\r') && (b == '\n')) {
last = -1;
return;
} else if ((b == '\n') || (b == '\r')) {
try {
/* write the converted bytes of the log message */
String message =
Thread.currentThread().getName() + ": " +
bufOut.toString();
logger.logp(Level.INFO, "LogStream", "print", message);
} finally {
bufOut.reset();
}
} else {
super.write(b);
}
last = b;
}
public void write(byte b[], int off, int len) {
if (len < 0) {
throw new ArrayIndexOutOfBoundsException(len);
}
for (int i = 0; i < len; i++) {
write(b[off + i]);
}
}
public String toString() {
return "RMI";
}
}
/**
* Factory to create Log objects which deliver log messages to the
* java.rmi.server.LogStream API
*/
private static class LogStreamLogFactory implements LogFactory {
LogStreamLogFactory() {}
/* create a new LogStreamLog for the specified log */
public Log createLog(String loggerName, String oldLogName,
Level level)
{
LogStream stream = null;
if (oldLogName != null) {
stream = LogStream.log(oldLogName);
}
return new LogStreamLog(stream, level);
}
}
/**
* Class specialized to log messages to the
* java.rmi.server.LogStream API
*/
private static class LogStreamLog extends Log {
/** Log stream to which log messages are written */
private final LogStream stream;
/** the level of the log as set by associated property */
private int levelValue = Level.OFF.intValue();
private LogStreamLog(LogStream stream, Level level) {
if ((stream != null) && (level != null)) {
/* if the stream or level is null, don't log any
* messages
*/
levelValue = level.intValue();
}
this.stream = stream;
}
public synchronized boolean isLoggable(Level level) {
return (level.intValue() >= levelValue);
}
public void log(Level messageLevel, String message) {
if (isLoggable(messageLevel)) {
String[] source = getSource();
stream.println(unqualifiedName(source[0]) +
"." + source[1] + ": " + message);
}
}
public void log(Level level, String message, Throwable thrown) {
if (isLoggable(level)) {
/*
* keep output contiguous and maintain the contract of
* RemoteServer.getLog
*/
synchronized (stream) {
String[] source = getSource();
stream.println(unqualifiedName(source[0]) + "." +
source[1] + ": " + message);
thrown.printStackTrace(stream);
}
}
}
public PrintStream getPrintStream() {
return stream;
}
public synchronized void setOutputStream(OutputStream out) {
if (out != null) {
if (VERBOSE.intValue() < levelValue) {
levelValue = VERBOSE.intValue();
}
stream.setOutputStream(out);
} else {
/* ensure that messages are not logged */
levelValue = Level.OFF.intValue();
}
}
/*
* Mimic old log messages that only contain unqualified names.
*/
private static String unqualifiedName(String name) {
int lastDot = name.lastIndexOf(".");
if (lastDot >= 0) {
name = name.substring(lastDot + 1);
}
name = name.replace('$', '.');
return name;
}
}
/**
* Obtain class and method names of code calling a log method.
*/
private static String[] getSource() {
StackTraceElement[] trace = (new Exception()).getStackTrace();
return new String[] {
trace[3].getClassName(),
trace[3].getMethodName()
};
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.runtime;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.security.util.SecurityConstants;
/**
* A PrivilegedAction for creating a new thread conveniently with an
* AccessController.doPrivileged construct.
*
* All constructors allow the choice of the Runnable for the new
* thread to execute, the name of the new thread (which will be
* prefixed with "RMI "), and whether or not it will be a daemon
* thread.
*
* The new thread may be created in the system thread group (the root
* of the thread group tree) or an internally created non-system
* thread group, as specified at construction of this class.
*
* The new thread will have the system class loader as its initial
* context class loader (that is, its context class loader will NOT be
* inherited from the current thread).
*
* @author Peter Jones
**/
public final class NewThreadAction implements PrivilegedAction<Thread> {
/** cached reference to the system (root) thread group */
static final ThreadGroup systemThreadGroup =
AccessController.doPrivileged(new PrivilegedAction<ThreadGroup>() {
public ThreadGroup run() {
ThreadGroup group = Thread.currentThread().getThreadGroup();
ThreadGroup parent;
while ((parent = group.getParent()) != null) {
group = parent;
}
return group;
}
});
/**
* special child of the system thread group for running tasks that
* may execute user code, so that the security policy for threads in
* the system thread group will not apply
*/
static final ThreadGroup userThreadGroup =
AccessController.doPrivileged(new PrivilegedAction<ThreadGroup>() {
public ThreadGroup run() {
return new ThreadGroup(systemThreadGroup, "RMI Runtime");
}
});
private final ThreadGroup group;
private final Runnable runnable;
private final String name;
private final boolean daemon;
NewThreadAction(ThreadGroup group, Runnable runnable,
String name, boolean daemon)
{
this.group = group;
this.runnable = runnable;
this.name = name;
this.daemon = daemon;
}
/**
* Creates an action that will create a new thread in the
* system thread group.
*
* @param runnable the Runnable for the new thread to execute
*
* @param name the name of the new thread
*
* @param daemon if true, new thread will be a daemon thread;
* if false, new thread will not be a daemon thread
*/
public NewThreadAction(Runnable runnable, String name, boolean daemon) {
this(systemThreadGroup, runnable, name, daemon);
}
/**
* Creates an action that will create a new thread.
*
* @param runnable the Runnable for the new thread to execute
*
* @param name the name of the new thread
*
* @param daemon if true, new thread will be a daemon thread;
* if false, new thread will not be a daemon thread
*
* @param user if true, thread will be created in a non-system
* thread group; if false, thread will be created in the system
* thread group
*/
public NewThreadAction(Runnable runnable, String name, boolean daemon,
boolean user)
{
this(user ? userThreadGroup : systemThreadGroup,
runnable, name, daemon);
}
public Thread run() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
Thread t = new Thread(group, runnable, "RMI " + name);
t.setContextClassLoader(ClassLoader.getSystemClassLoader());
t.setDaemon(daemon);
return t;
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.runtime;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import sun.security.action.GetIntegerAction;
/**
* RMI runtime implementation utilities.
*
* There is a single instance of this class, which can be obtained
* with a GetInstanceAction. Getting the instance requires
* RuntimePermission("sun.rmi.runtime.RuntimeUtil.getInstance")
* because the public methods of this class expose security-sensitive
* capabilities.
*
* @author Peter Jones
**/
public final class RuntimeUtil {
/** runtime package log */
private static final Log runtimeLog =
Log.getLog("sun.rmi.runtime", null, false);
/** number of scheduler threads */
private static final int schedulerThreads = // default 1
AccessController.doPrivileged(
new GetIntegerAction("sun.rmi.runtime.schedulerThreads", 1));
/** permission required to get instance */
private static final Permission GET_INSTANCE_PERMISSION =
new RuntimePermission("sun.rmi.runtime.RuntimeUtil.getInstance");
/** the singleton instance of this class */
private static final RuntimeUtil instance = new RuntimeUtil();
/** thread pool for scheduling delayed tasks */
private final ScheduledThreadPoolExecutor scheduler;
private RuntimeUtil() {
scheduler = new ScheduledThreadPoolExecutor(
schedulerThreads,
new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(0);
public Thread newThread(Runnable runnable) {
try {
return AccessController.doPrivileged(
new NewThreadAction(runnable,
"Scheduler(" + count.getAndIncrement() + ")",
true));
} catch (Throwable t) {
runtimeLog.log(Level.WARNING,
"scheduler thread factory throws", t);
return null;
}
}
});
/*
* We would like to allow the scheduler's threads to terminate
* if possible, but a bug in DelayQueue.poll can cause code
* like this to result in a busy loop:
*/
// stpe.setKeepAliveTime(10, TimeUnit.MINUTES);
// stpe.allowCoreThreadTimeOut(true);
}
/**
* A PrivilegedAction for getting the RuntimeUtil instance.
**/
public static class GetInstanceAction
implements PrivilegedAction<RuntimeUtil>
{
/**
* Creates an action that returns the RuntimeUtil instance.
**/
public GetInstanceAction() {
}
public RuntimeUtil run() {
return getInstance();
}
}
private static RuntimeUtil getInstance() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(GET_INSTANCE_PERMISSION);
}
return instance;
}
/**
* Returns the shared thread pool for scheduling delayed tasks.
*
* Note that the returned pool has limited concurrency, so
* submitted tasks should be short-lived and should not block.
**/
public ScheduledThreadPoolExecutor getScheduler() {
return scheduler;
}
}

View File

@@ -0,0 +1,412 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.*;
import java.rmi.activation.*;
import java.rmi.server.Operation;
import java.rmi.server.RMIClassLoader;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.rmi.server.RemoteRef;
import java.rmi.server.RemoteStub;
@SuppressWarnings("deprecation")
public class ActivatableRef implements RemoteRef {
private static final long serialVersionUID = 7579060052569229166L;
protected ActivationID id;
protected RemoteRef ref;
transient boolean force = false;
private static final int MAX_RETRIES = 3;
private static final String versionComplaint =
"activation requires 1.2 stubs";
/**
* Create a new (empty) ActivatableRef
*/
public ActivatableRef()
{}
/**
* Create a ActivatableRef with the specified id
*/
public ActivatableRef(ActivationID id, RemoteRef ref)
{
this.id = id;
this.ref = ref;
}
/**
* Returns the stub for the remote object whose class is
* specified in the activation descriptor. The ActivatableRef
* in the resulting stub has its activation id set to the
* activation id supplied as the second argument.
*/
public static Remote getStub(ActivationDesc desc, ActivationID id)
throws StubNotFoundException
{
String className = desc.getClassName();
try {
Class<?> cl =
RMIClassLoader.loadClass(desc.getLocation(), className);
RemoteRef clientRef = new ActivatableRef(id, null);
return Util.createProxy(cl, clientRef, false);
} catch (IllegalArgumentException e) {
throw new StubNotFoundException(
"class implements an illegal remote interface", e);
} catch (ClassNotFoundException e) {
throw new StubNotFoundException("unable to load class: " +
className, e);
} catch (MalformedURLException e) {
throw new StubNotFoundException("malformed URL", e);
}
}
/**
* Invoke method on remote object. This method delegates remote
* method invocation to the underlying ref type. If the
* underlying reference is not known (is null), then the object
* must be activated first. If an attempt at method invocation
* fails, the object should force reactivation. Method invocation
* must preserve "at most once" call semantics. In RMI, "at most
* once" applies to parameter deserialization at the remote site
* and the remote object's method execution. "At most once" does
* not apply to parameter serialization at the client so the
* parameters of a call don't need to be buffered in anticipation
* of call retry. Thus, a method call is only be retried if the
* initial method invocation does not execute at all at the server
* (including parameter deserialization).
*/
public Object invoke(Remote obj,
java.lang.reflect.Method method,
Object[] params,
long opnum)
throws Exception
{
boolean force = false;
RemoteRef localRef;
Exception exception = null;
/*
* Attempt object activation if active ref is unknown.
* Throws a RemoteException if object can't be activated.
*/
synchronized (this) {
if (ref == null) {
localRef = activate(force);
force = true;
} else {
localRef = ref;
}
}
for (int retries = MAX_RETRIES; retries > 0; retries--) {
try {
return localRef.invoke(obj, method, params, opnum);
} catch (NoSuchObjectException e) {
/*
* Object is not active in VM; retry call
*/
exception = e;
} catch (ConnectException e) {
/*
* Failure during connection setup; retry call
*/
exception = e;
} catch (UnknownHostException e) {
/*
* Failure during connection setup; retry call.
*/
exception = e;
} catch (ConnectIOException e) {
/*
* Failure setting up multiplexed connection or reusing
* cached connection; retry call
*/
exception = e;
} catch (MarshalException e) {
/*
* Failure during parameter serialization; call may
* have reached server, so call retry not possible.
*/
throw e;
} catch (ServerError e) {
/*
* Call reached server; propagate remote exception.
*/
throw e;
} catch (ServerException e) {
/*
* Call reached server; propagate remote exception
*/
throw e;
} catch (RemoteException e) {
/*
* This is a catch-all for other RemoteExceptions.
* UnmarshalException being the only one relevant.
*
* StubNotFoundException should never show up because
* it is generally thrown when attempting to locate
* a stub.
*
* UnexpectedException should never show up because
* it is only thrown by a stub and would be wrapped
* in a ServerException if it was propagated by a
* remote call.
*/
synchronized (this) {
if (localRef == ref) {
ref = null; // this may be overly conservative
}
}
throw e;
}
if (retries > 1) {
/*
* Activate object, since object could not be reached.
*/
synchronized (this) {
if (localRef.remoteEquals(ref) || ref == null) {
RemoteRef newRef = activate(force);
if (newRef.remoteEquals(localRef) &&
exception instanceof NoSuchObjectException &&
force == false) {
/*
* If last exception was NoSuchObjectException,
* then old value of ref is definitely wrong,
* so make sure that it is different.
*/
newRef = activate(true);
}
localRef = newRef;
force = true;
} else {
localRef = ref;
force = false;
}
}
}
}
/*
* Retries unsuccessful, so throw last exception
*/
throw exception;
}
/**
* private method to obtain the ref for a call.
*/
private synchronized RemoteRef getRef()
throws RemoteException
{
if (ref == null) {
ref = activate(false);
}
return ref;
}
/**
* private method to activate the remote object.
*
* NOTE: the caller must be synchronized on "this" before
* calling this method.
*/
private RemoteRef activate(boolean force)
throws RemoteException
{
assert Thread.holdsLock(this);
ref = null;
try {
/*
* Activate the object and retrieve the remote reference
* from inside the stub returned as the result. Then
* set this activatable ref's internal ref to be the
* ref inside the ref of the stub. In more clear terms,
* the stub returned from the activate call contains an
* ActivatableRef. We need to set the ref in *this*
* ActivatableRef to the ref inside the ActivatableRef
* retrieved from the stub. The ref type embedded in the
* ActivatableRef is typically a UnicastRef.
*/
Remote proxy = id.activate(force);
ActivatableRef newRef = null;
if (proxy instanceof RemoteStub) {
newRef = (ActivatableRef) ((RemoteStub) proxy).getRef();
} else {
/*
* Assume that proxy is an instance of a dynamic proxy
* class. If that assumption is not correct, or either of
* the casts below fails, the resulting exception will be
* wrapped in an ActivateFailedException below.
*/
RemoteObjectInvocationHandler handler =
(RemoteObjectInvocationHandler)
Proxy.getInvocationHandler(proxy);
newRef = (ActivatableRef) handler.getRef();
}
ref = newRef.ref;
return ref;
} catch (ConnectException e) {
throw new ConnectException("activation failed", e);
} catch (RemoteException e) {
throw new ConnectIOException("activation failed", e);
} catch (UnknownObjectException e) {
throw new NoSuchObjectException("object not registered");
} catch (ActivationException e) {
throw new ActivateFailedException("activation failed", e);
}
}
/**
* This call is used by the old 1.1 stub protocol and is
* unsupported since activation requires 1.2 stubs.
*/
public synchronized RemoteCall newCall(RemoteObject obj,
Operation[] ops,
int opnum,
long hash)
throws RemoteException
{
throw new UnsupportedOperationException(versionComplaint);
}
/**
* This call is used by the old 1.1 stub protocol and is
* unsupported since activation requires 1.2 stubs.
*/
public void invoke(RemoteCall call) throws Exception
{
throw new UnsupportedOperationException(versionComplaint);
}
/**
* This call is used by the old 1.1 stub protocol and is
* unsupported since activation requires 1.2 stubs.
*/
public void done(RemoteCall call) throws RemoteException {
throw new UnsupportedOperationException(versionComplaint);
}
/**
* Returns the class of the ref type to be serialized
*/
public String getRefClass(ObjectOutput out)
{
return "ActivatableRef";
}
/**
* Write out external representation for remote ref.
*/
public void writeExternal(ObjectOutput out) throws IOException
{
RemoteRef localRef = ref;
out.writeObject(id);
if (localRef == null) {
out.writeUTF("");
} else {
out.writeUTF(localRef.getRefClass(out));
localRef.writeExternal(out);
}
}
/**
* Read in external representation for remote ref.
* @exception ClassNotFoundException If the class for an object
* being restored cannot be found.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
id = (ActivationID)in.readObject();
ref = null;
String className = in.readUTF();
if (className.equals("")) return;
try {
Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." +
className);
ref = (RemoteRef)refClass.newInstance();
ref.readExternal(in);
} catch (InstantiationException e) {
throw new UnmarshalException("Unable to create remote reference",
e);
} catch (IllegalAccessException e) {
throw new UnmarshalException("Illegal access creating remote reference");
}
}
//----------------------------------------------------------------------;
/**
* Method from object, forward from RemoteObject
*/
public String remoteToString() {
return Util.getUnqualifiedName(getClass()) +
" [remoteRef: " + ref + "]";
}
/**
* default implementation of hashCode for remote objects
*/
public int remoteHashCode() {
return id.hashCode();
}
/** default implementation of equals for remote objects
*/
public boolean remoteEquals(RemoteRef ref) {
if (ref instanceof ActivatableRef)
return id.equals(((ActivatableRef)ref).id);
return false;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutput;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.activation.ActivationID;
import sun.rmi.transport.LiveRef;
/**
* Server-side ref for a persistent remote impl.
*
* @author Ann Wollrath
*/
public class ActivatableServerRef extends UnicastServerRef2 {
private static final long serialVersionUID = 2002967993223003793L;
private ActivationID id;
/**
* Construct a Unicast server remote reference to be exported
* on the specified port.
*/
public ActivatableServerRef(ActivationID id, int port)
{
this(id, port, null, null);
}
/**
* Construct a Unicast server remote reference to be exported
* on the specified port.
*/
public ActivatableServerRef(ActivationID id, int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
{
super(new LiveRef(port, csf, ssf));
this.id = id;
}
/**
* Returns the class of the ref type to be serialized
*/
public String getRefClass(ObjectOutput out)
{
return "ActivatableServerRef";
}
/**
* Return the client remote reference for this remoteRef.
* In the case of a client RemoteRef "this" is the answer.
* For a server remote reference, a client side one will have to
* found or created.
*/
protected RemoteRef getClientRef() {
return new ActivatableRef(id, new UnicastRef2(ref));
}
/**
* Prevents serialization (because deserializaion is impossible).
*/
public void writeExternal(ObjectOutput out) throws IOException {
throw new NotSerializableException(
"ActivatableServerRef not serializable");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.rmi.MarshalledObject;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.activation.Activatable;
import java.rmi.activation.ActivationDesc;
import java.rmi.activation.ActivationException;
import java.rmi.activation.ActivationGroup;
import java.rmi.activation.ActivationGroupID;
import java.rmi.activation.ActivationID;
import java.rmi.activation.UnknownObjectException;
import java.rmi.server.RMIClassLoader;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.RMISocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import sun.rmi.registry.RegistryImpl;
/**
* The default activation group implementation.
*
* @author Ann Wollrath
* @since 1.2
* @see java.rmi.activation.ActivationGroup
*/
public class ActivationGroupImpl extends ActivationGroup {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 5758693559430427303L;
/** maps persistent IDs to activated remote objects */
private final Hashtable<ActivationID,ActiveEntry> active =
new Hashtable<>();
private boolean groupInactive = false;
private final ActivationGroupID groupID;
private final List<ActivationID> lockedIDs = new ArrayList<>();
/**
* Creates a default activation group implementation.
*
* @param id the group's identifier
* @param data ignored
*/
public ActivationGroupImpl(ActivationGroupID id, MarshalledObject<?> data)
throws RemoteException
{
super(id);
groupID = id;
/*
* Unexport activation group impl and attempt to export it on
* an unshared anonymous port. See 4692286.
*/
unexportObject(this, true);
RMIServerSocketFactory ssf = new ServerSocketFactoryImpl();
UnicastRemoteObject.exportObject(this, 0, null, ssf);
if (System.getSecurityManager() == null) {
try {
// Provide a default security manager.
System.setSecurityManager(new SecurityManager());
} catch (Exception e) {
throw new RemoteException("unable to set security manager", e);
}
}
}
/**
* Trivial server socket factory used to export the activation group
* impl on an unshared port.
*/
private static class ServerSocketFactoryImpl
implements RMIServerSocketFactory
{
public ServerSocket createServerSocket(int port) throws IOException
{
RMISocketFactory sf = RMISocketFactory.getSocketFactory();
if (sf == null) {
sf = RMISocketFactory.getDefaultSocketFactory();
}
return sf.createServerSocket(port);
}
}
/*
* Obtains a lock on the ActivationID id before returning. Allows only one
* thread at a time to hold a lock on a particular id. If the lock for id
* is in use, all requests for an equivalent (in the Object.equals sense)
* id will wait for the id to be notified and use the supplied id as the
* next lock. The caller of "acquireLock" must execute the "releaseLock"
* method" to release the lock and "notifyAll" waiters for the id lock
* obtained from this method. The typical usage pattern is as follows:
*
* try {
* acquireLock(id);
* // do stuff pertaining to id...
* } finally {
* releaseLock(id);
* checkInactiveGroup();
* }
*/
private void acquireLock(ActivationID id) {
ActivationID waitForID;
for (;;) {
synchronized (lockedIDs) {
int index = lockedIDs.indexOf(id);
if (index < 0) {
lockedIDs.add(id);
return;
} else {
waitForID = lockedIDs.get(index);
}
}
synchronized (waitForID) {
synchronized (lockedIDs) {
int index = lockedIDs.indexOf(waitForID);
if (index < 0) continue;
ActivationID actualID = lockedIDs.get(index);
if (actualID != waitForID)
/*
* don't wait on an id that won't be notified.
*/
continue;
}
try {
waitForID.wait();
} catch (InterruptedException ignore) {
}
}
}
}
/*
* Releases the id lock obtained via the "acquireLock" method and then
* notifies all threads waiting on the lock.
*/
private void releaseLock(ActivationID id) {
synchronized (lockedIDs) {
id = lockedIDs.remove(lockedIDs.indexOf(id));
}
synchronized (id) {
id.notifyAll();
}
}
/**
* Creates a new instance of an activatable remote object. The
* <code>Activator</code> calls this method to create an activatable
* object in this group. This method should be idempotent; a call to
* activate an already active object should return the previously
* activated object.
*
* Note: this method assumes that the Activator will only invoke
* newInstance for the same object in a serial fashion (i.e.,
* the activator will not allow the group to see concurrent requests
* to activate the same object.
*
* @param id the object's activation identifier
* @param desc the object's activation descriptor
* @return a marshalled object containing the activated object's stub
*/
public MarshalledObject<? extends Remote>
newInstance(final ActivationID id,
final ActivationDesc desc)
throws ActivationException, RemoteException
{
RegistryImpl.checkAccess("ActivationInstantiator.newInstance");
if (!groupID.equals(desc.getGroupID()))
throw new ActivationException("newInstance in wrong group");
try {
acquireLock(id);
synchronized (this) {
if (groupInactive == true)
throw new InactiveGroupException("group is inactive");
}
ActiveEntry entry = active.get(id);
if (entry != null)
return entry.mobj;
String className = desc.getClassName();
final Class<? extends Remote> cl =
RMIClassLoader.loadClass(desc.getLocation(), className)
.asSubclass(Remote.class);
Remote impl = null;
final Thread t = Thread.currentThread();
final ClassLoader savedCcl = t.getContextClassLoader();
ClassLoader objcl = cl.getClassLoader();
final ClassLoader ccl = covers(objcl, savedCcl) ? objcl : savedCcl;
/*
* Fix for 4164971: allow non-public activatable class
* and/or constructor, create the activatable object in a
* privileged block
*/
try {
/*
* The code below is in a doPrivileged block to
* protect against user code which code might have set
* a global socket factory (in which case application
* code would be on the stack).
*/
impl = AccessController.doPrivileged(
new PrivilegedExceptionAction<Remote>() {
public Remote run() throws InstantiationException,
NoSuchMethodException, IllegalAccessException,
InvocationTargetException
{
Constructor<? extends Remote> constructor =
cl.getDeclaredConstructor(
ActivationID.class, MarshalledObject.class);
constructor.setAccessible(true);
try {
/*
* Fix for 4289544: make sure to set the
* context class loader to be the class
* loader of the impl class before
* constructing that class.
*/
t.setContextClassLoader(ccl);
return constructor.newInstance(id,
desc.getData());
} finally {
t.setContextClassLoader(savedCcl);
}
}
});
} catch (PrivilegedActionException pae) {
Throwable e = pae.getException();
// narrow the exception's type and rethrow it
if (e instanceof InstantiationException) {
throw (InstantiationException) e;
} else if (e instanceof NoSuchMethodException) {
throw (NoSuchMethodException) e;
} else if (e instanceof IllegalAccessException) {
throw (IllegalAccessException) e;
} else if (e instanceof InvocationTargetException) {
throw (InvocationTargetException) e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else if (e instanceof Error) {
throw (Error) e;
}
}
entry = new ActiveEntry(impl);
active.put(id, entry);
return entry.mobj;
} catch (NoSuchMethodException | NoSuchMethodError e) {
/* user forgot to provide activatable constructor?
* or code recompiled and user forgot to provide
* activatable constructor?
*/
throw new ActivationException
("Activatable object must provide an activation"+
" constructor", e );
} catch (InvocationTargetException e) {
throw new ActivationException("exception in object constructor",
e.getTargetException());
} catch (Exception e) {
throw new ActivationException("unable to activate object", e);
} finally {
releaseLock(id);
checkInactiveGroup();
}
}
/**
* The group's <code>inactiveObject</code> method is called
* indirectly via a call to the <code>Activatable.inactive</code>
* method. A remote object implementation must call
* <code>Activatable</code>'s <code>inactive</code> method when
* that object deactivates (the object deems that it is no longer
* active). If the object does not call
* <code>Activatable.inactive</code> when it deactivates, the
* object will never be garbage collected since the group keeps
* strong references to the objects it creates. <p>
*
* The group's <code>inactiveObject</code> method
* unexports the remote object from the RMI runtime so that the
* object can no longer receive incoming RMI calls. This call will
* only succeed if the object has no pending/executing calls. If
* the object does have pending/executing RMI calls, then false
* will be returned.
*
* If the object has no pending/executing calls, the object is
* removed from the RMI runtime and the group informs its
* <code>ActivationMonitor</code> (via the monitor's
* <code>inactiveObject</code> method) that the remote object is
* not currently active so that the remote object will be
* re-activated by the activator upon a subsequent activation
* request.
*
* @param id the object's activation identifier
* @returns true if the operation succeeds (the operation will
* succeed if the object in currently known to be active and is
* either already unexported or is currently exported and has no
* pending/executing calls); false is returned if the object has
* pending/executing calls in which case it cannot be deactivated
* @exception UnknownObjectException if object is unknown (may already
* be inactive)
* @exception RemoteException if call informing monitor fails
*/
public boolean inactiveObject(ActivationID id)
throws ActivationException, UnknownObjectException, RemoteException
{
try {
acquireLock(id);
synchronized (this) {
if (groupInactive == true)
throw new ActivationException("group is inactive");
}
ActiveEntry entry = active.get(id);
if (entry == null) {
// REMIND: should this be silent?
throw new UnknownObjectException("object not active");
}
try {
if (Activatable.unexportObject(entry.impl, false) == false)
return false;
} catch (NoSuchObjectException allowUnexportedObjects) {
}
try {
super.inactiveObject(id);
} catch (UnknownObjectException allowUnregisteredObjects) {
}
active.remove(id);
} finally {
releaseLock(id);
checkInactiveGroup();
}
return true;
}
/*
* Determines if the group has become inactive and
* marks it as such.
*/
private void checkInactiveGroup() {
boolean groupMarkedInactive = false;
synchronized (this) {
if (active.size() == 0 && lockedIDs.size() == 0 &&
groupInactive == false)
{
groupInactive = true;
groupMarkedInactive = true;
}
}
if (groupMarkedInactive) {
try {
super.inactiveGroup();
} catch (Exception ignoreDeactivateFailure) {
}
try {
UnicastRemoteObject.unexportObject(this, true);
} catch (NoSuchObjectException allowUnexportedGroup) {
}
}
}
/**
* The group's <code>activeObject</code> method is called when an
* object is exported (either by <code>Activatable</code> object
* construction or an explicit call to
* <code>Activatable.exportObject</code>. The group must inform its
* <code>ActivationMonitor</code> that the object is active (via
* the monitor's <code>activeObject</code> method) if the group
* hasn't already done so.
*
* @param id the object's identifier
* @param obj the remote object implementation
* @exception UnknownObjectException if object is not registered
* @exception RemoteException if call informing monitor fails
*/
public void activeObject(ActivationID id, Remote impl)
throws ActivationException, UnknownObjectException, RemoteException
{
try {
acquireLock(id);
synchronized (this) {
if (groupInactive == true)
throw new ActivationException("group is inactive");
}
if (!active.contains(id)) {
ActiveEntry entry = new ActiveEntry(impl);
active.put(id, entry);
// created new entry, so inform monitor of active object
try {
super.activeObject(id, entry.mobj);
} catch (RemoteException e) {
// daemon can still find it by calling newInstance
}
}
} finally {
releaseLock(id);
checkInactiveGroup();
}
}
/**
* Entry in table for active object.
*/
private static class ActiveEntry {
Remote impl;
MarshalledObject<Remote> mobj;
ActiveEntry(Remote impl) throws ActivationException {
this.impl = impl;
try {
this.mobj = new MarshalledObject<Remote>(impl);
} catch (IOException e) {
throw new
ActivationException("failed to marshal remote object", e);
}
}
}
/**
* Returns true if the first argument is either equal to, or is a
* descendant of, the second argument. Null is treated as the root of
* the tree.
*/
private static boolean covers(ClassLoader sub, ClassLoader sup) {
if (sup == null) {
return true;
} else if (sub == null) {
return false;
}
do {
if (sub == sup) {
return true;
}
sub = sub.getParent();
} while (sub != null);
return false;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.rmi.activation.ActivationGroupDesc;
import java.rmi.activation.ActivationGroupID;
import java.rmi.activation.ActivationGroup;
/**
* This is the bootstrap code to start a VM executing an activation
* group.
*
* The activator spawns (as a child process) an activation group as needed
* and directs activation requests to the appropriate activation
* group. After spawning the VM, the activator passes some
* information to the bootstrap code via its stdin: <p>
* <ul>
* <li> the activation group's id,
* <li> the activation group's descriptor (an instance of the class
* java.rmi.activation.ActivationGroupDesc) for the group, adn
* <li> the group's incarnation number.
* </ul><p>
*
* When the bootstrap VM starts executing, it reads group id and
* descriptor from its stdin so that it can create the activation
* group for the VM.
*
* @author Ann Wollrath
*/
public abstract class ActivationGroupInit
{
/**
* Main program to start a VM for an activation group.
*/
public static void main(String args[])
{
try {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
// read group id, descriptor, and incarnation number from stdin
MarshalInputStream in = new MarshalInputStream(System.in);
ActivationGroupID id = (ActivationGroupID)in.readObject();
ActivationGroupDesc desc = (ActivationGroupDesc)in.readObject();
long incarnation = in.readLong();
// create and set group for the VM
ActivationGroup.createGroup(id, desc, incarnation);
} catch (Exception e) {
System.err.println("Exception in starting ActivationGroupInit:");
e.printStackTrace();
} finally {
try {
System.in.close();
// note: system out/err shouldn't be closed
// since the parent may want to read them.
} catch (Exception ex) {
// ignore exceptions
}
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.ObjectStreamClass;
import java.lang.reflect.Method;
/**
* Implementing this interface to have a deserialization control when RMI
* dispatches a remote request. If an exported object implements this interface,
* RMI dispatching mechanism will call the method {@code check} every time
* deserialising a remote object for invoking a method of the exported object.
*
* @author sjiang
*/
public interface DeserializationChecker {
/**
* Will be called to check a descriptor.
* This method may be called 2 times, the first time is when a descriptor is read
* from the stream, the second is just before creating an object described
* by this descriptor.
*
* @param method the method invoked from a remote request.
* @param descriptor The descriptor of the class of any object deserialised
* while deserialising the parameter. The first descriptor will be that of
* the top level object (the concrete class of the parameter itself);
* Subsequent calls with the same {@code method}, {@code paramIndex} and
* {@code callID} will correspond to objects contained in the parameter.
* @param paramIndex an index indicates the position of a parameter in the
* method. This index will be reused for deserialising all
* objects contained in the parameter object. For example, the parameter
* being deserialised is a {@code List}, all deserialisation calls for its
* elements will have same index.
* @param callID a unique ID identifying one
* time method invocation, the same ID is used for deserialization call of
* all parameters within the method.
*/
public void check(Method method,
ObjectStreamClass descriptor,
int paramIndex,
int callID);
/**
* Will be called to validate a Proxy interfaces from a remote user before loading it.
* @param method the method invoked from a remote request.
* @param ifaces a string table of all interfaces implemented by the proxy to be checked.
* @param paramIndex an index indicates the position of a parameter in the
* method. This index will be reused for deserialising all
* objects contained in the parameter object. For example, the parameter
* being deserialised is a {@code List}, all deserialisation calls for its
* elements will have same index.
* @param callID a unique ID identifying one
* time method invocation, the same ID is used for deserialization call of
* all parameters within the method.
*/
public void checkProxyClass(Method method,
String[] ifaces,
int paramIndex,
int callID);
/**
* Inform of the completion of parameter deserialisation for a method invocation.
* This is useful if the last parameter is a complex object, like a {@code List}
* which elements are complex object too.
*
* The default implementation does nothing.
* @param callID the ID identifying a method invocation.
*/
public default void end(int callID) {}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.rmi.Remote;
import java.rmi.server.RemoteCall;
/**
* The Dispatcher interface allows the transport to make
* the upcall to the server side remote reference.
*/
@SuppressWarnings("deprecation")
public interface Dispatcher {
/**
* Call to dispatch to the remote object (on the server side).
* The up-call to the server and the marshaling of return result
* (or exception) should be handled before returning from this
* method.
* @param obj the target remote object for the call
* @param call the "remote call" from which operation and
* method arguments can be obtained.
* @exception RemoteException unable to marshal
* return result
*/
void dispatch(Remote obj, RemoteCall call)
throws java.io.IOException;
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.rmi.activation.ActivationException;
/**
* Thrown if a local or remote call is made on a group implementation
* instance that is inactive.
*
* @author Sun Microsystems, Inc.
*
* @since 1.6
*/
public class InactiveGroupException extends ActivationException {
private static final long serialVersionUID = -7491041778450214975L;
/**
* Constructs an instance with the specified detail message.
*
* @param s the detail message
*/
public InactiveGroupException(String s) {
super(s);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,357 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.io.StreamCorruptedException;
import java.util.*;
import java.security.AccessControlException;
import java.security.Permission;
import java.rmi.server.RMIClassLoader;
import sun.misc.ObjectStreamClassValidator;
import sun.misc.SharedSecrets;
/**
* MarshalInputStream is an extension of ObjectInputStream. When resolving
* a class, it reads an object from the stream written by a corresponding
* MarshalOutputStream. If the class to be resolved is not available
* locally, from the first class loader on the execution stack, or from the
* context class loader of the current thread, it will attempt to load the
* class from the location annotated by the sending MarshalOutputStream.
* This location object must be a string representing a path of URLs.
*
* A new MarshalInputStream should be created to deserialize remote objects or
* graphs containing remote objects. Objects are created from the stream
* using the ObjectInputStream.readObject method.
*
* @author Peter Jones
*/
public class MarshalInputStream extends ObjectInputStream {
interface StreamChecker extends ObjectStreamClassValidator {
void checkProxyInterfaceNames(String[] ifaces);
}
private volatile StreamChecker streamChecker = null;
/**
* Value of "java.rmi.server.useCodebaseOnly" property,
* as cached at class initialization time.
*
* The default value is true. That is, the value is true
* if the property is absent or is not equal to "false".
* The value is only false when the property is present
* and is equal to "false".
*/
private static final boolean useCodebaseOnlyProperty =
! java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java.rmi.server.useCodebaseOnly", "true"))
.equalsIgnoreCase("false");
/** table to hold sun classes to which access is explicitly permitted */
protected static Map<String, Class<?>> permittedSunClasses
= new HashMap<>(3);
/** if true, don't try superclass first in resolveClass() */
private boolean skipDefaultResolveClass = false;
/** callbacks to make when done() called: maps Object to Runnable */
private final Map<Object, Runnable> doneCallbacks
= new HashMap<>(3);
/**
* if true, load classes (if not available locally) only from the
* URL specified by the "java.rmi.server.codebase" property.
*/
private boolean useCodebaseOnly = useCodebaseOnlyProperty;
/*
* Fix for 4179055: The remote object services inside the
* activation daemon use stubs that are in the package
* sun.rmi.server. Classes for these stubs should be loaded from
* the classpath by RMI system code and not by the normal
* unmarshalling process as applications should not need to have
* permission to access the sun implementation classes.
*
* Note: this fix should be redone when API changes may be
* integrated
*
* During parameter unmarshalling RMI needs to explicitly permit
* access to three sun.* stub classes
*/
static {
try {
String system =
"sun.rmi.server.Activation$ActivationSystemImpl_Stub";
String registry = "sun.rmi.registry.RegistryImpl_Stub";
permittedSunClasses.put(system, Class.forName(system));
permittedSunClasses.put(registry, Class.forName(registry));
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError("Missing system class: " +
e.getMessage());
}
}
/**
* Create a new MarshalInputStream object.
*/
public MarshalInputStream(InputStream in)
throws IOException, StreamCorruptedException
{
super(in);
}
/**
* Returns a callback previously registered via the setDoneCallback
* method with given key, or null if no callback has yet been registered
* with that key.
*/
public Runnable getDoneCallback(Object key) {
return doneCallbacks.get(key); // not thread-safe
}
/**
* Registers a callback to make when this stream's done() method is
* invoked, along with a key for retrieving the same callback instance
* subsequently from the getDoneCallback method.
*/
public void setDoneCallback(Object key, Runnable callback) {
//assert(!doneCallbacks.contains(key));
doneCallbacks.put(key, callback); // not thread-safe
}
/**
* Indicates that the user of this MarshalInputStream is done reading
* objects from it, so all callbacks registered with the setDoneCallback
* method should now be (synchronously) executed. When this method
* returns, there are no more callbacks registered.
*
* This method is implicitly invoked by close() before it delegates to
* the superclass's close method.
*/
public void done() {
Iterator<Runnable> iter = doneCallbacks.values().iterator();
while (iter.hasNext()) { // not thread-safe
Runnable callback = iter.next();
callback.run();
}
doneCallbacks.clear();
}
/**
* Closes this stream, implicitly invoking done() first.
*/
public void close() throws IOException {
done();
super.close();
}
/**
* resolveClass is extended to acquire (if present) the location
* from which to load the specified class.
* It will find, load, and return the class.
*/
protected Class<?> resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException
{
/*
* Always read annotation written by MarshalOutputStream
* describing where to load class from.
*/
Object annotation = readLocation();
String className = classDesc.getName();
/*
* Unless we were told to skip this consideration, choose the
* "default loader" to simulate the default ObjectInputStream
* resolveClass mechanism (that is, choose the first non-null
* loader on the execution stack) to maximize the likelihood of
* type compatibility with calling code. (This consideration
* is skipped during server parameter unmarshalling using the 1.2
* stub protocol, because there would never be a non-null class
* loader on the stack in that situation anyway.)
*/
ClassLoader defaultLoader =
skipDefaultResolveClass ? null : latestUserDefinedLoader();
/*
* If the "java.rmi.server.useCodebaseOnly" property was true or
* useCodebaseOnly() was called or the annotation is not a String,
* load from the local loader using the "java.rmi.server.codebase"
* URL. Otherwise, load from a loader using the codebase URL in
* the annotation.
*/
String codebase = null;
if (!useCodebaseOnly && annotation instanceof String) {
codebase = (String) annotation;
}
try {
return RMIClassLoader.loadClass(codebase, className,
defaultLoader);
} catch (AccessControlException e) {
return checkSunClass(className, e);
} catch (ClassNotFoundException e) {
/*
* Fix for 4442373: delegate to ObjectInputStream.resolveClass()
* to resolve primitive classes.
*/
try {
if (Character.isLowerCase(className.charAt(0)) &&
className.indexOf('.') == -1)
{
return super.resolveClass(classDesc);
}
} catch (ClassNotFoundException e2) {
}
throw e;
}
}
/**
* resolveProxyClass is extended to acquire (if present) the location
* to determine the class loader to define the proxy class in.
*/
protected Class<?> resolveProxyClass(String[] interfaces)
throws IOException, ClassNotFoundException
{
StreamChecker checker = streamChecker;
if (checker != null) {
checker.checkProxyInterfaceNames(interfaces);
}
/*
* Always read annotation written by MarshalOutputStream.
*/
Object annotation = readLocation();
ClassLoader defaultLoader =
skipDefaultResolveClass ? null : latestUserDefinedLoader();
String codebase = null;
if (!useCodebaseOnly && annotation instanceof String) {
codebase = (String) annotation;
}
return RMIClassLoader.loadProxyClass(codebase, interfaces,
defaultLoader);
}
/*
* Returns first non-privileged class loader on the stack (excluding
* reflection generated frames) or the extension class loader if only
* class loaded by the boot class loader and extension class loader are
* found on the stack.
*/
private static ClassLoader latestUserDefinedLoader() {
return sun.misc.VM.latestUserDefinedLoader();
}
/**
* Fix for 4179055: Need to assist resolving sun stubs; resolve
* class locally if it is a "permitted" sun class
*/
private Class<?> checkSunClass(String className, AccessControlException e)
throws AccessControlException
{
// ensure that we are giving out a stub for the correct reason
Permission perm = e.getPermission();
String name = null;
if (perm != null) {
name = perm.getName();
}
Class<?> resolvedClass = permittedSunClasses.get(className);
// if class not permitted, throw the SecurityException
if ((name == null) ||
(resolvedClass == null) ||
((!name.equals("accessClassInPackage.sun.rmi.server")) &&
(!name.equals("accessClassInPackage.sun.rmi.registry"))))
{
throw e;
}
return resolvedClass;
}
/**
* Return the location for the class in the stream. This method can
* be overridden by subclasses that store this annotation somewhere
* else than as the next object in the stream, as is done by this class.
*/
protected Object readLocation()
throws IOException, ClassNotFoundException
{
return readObject();
}
/**
* Set a flag to indicate that the superclass's default resolveClass()
* implementation should not be invoked by our resolveClass().
*/
void skipDefaultResolveClass() {
skipDefaultResolveClass = true;
}
/**
* Disable code downloading except from the URL specified by the
* "java.rmi.server.codebase" property.
*/
void useCodebaseOnly() {
useCodebaseOnly = true;
}
synchronized void setStreamChecker(StreamChecker checker) {
streamChecker = checker;
SharedSecrets.getJavaObjectInputStreamAccess().setValidator(this, checker);
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException,
ClassNotFoundException {
ObjectStreamClass descriptor = super.readClassDescriptor();
validateDesc(descriptor);
return descriptor;
}
private void validateDesc(ObjectStreamClass descriptor) {
StreamChecker checker;
synchronized (this) {
checker = streamChecker;
}
if (checker != null) {
checker.validateDescriptor(descriptor);
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.*;
import java.rmi.Remote;
import java.rmi.server.RemoteStub;
import sun.rmi.transport.ObjectTable;
import sun.rmi.transport.Target;
/**
* A MarshalOutputStream extends ObjectOutputStream to add functions
* specific to marshaling of remote object references. If it is
* necessary to serialize remote objects or objects that contain
* references to remote objects a MarshalOutputStream must be used
* instead of ObjectOutputStream. <p>
*
* A new MarshalOutputStream is constructed to serialize remote
* objects or graphs containing remote objects. Objects are written to
* the stream using the ObjectOutputStream.writeObject method. <p>
*
* MarshalOutputStream maps remote objects to the corresponding remote
* stub and embeds the location from which to load the stub
* classes. The location may be ignored by the client but is supplied.
*/
public class MarshalOutputStream extends ObjectOutputStream
{
/**
* Creates a marshal output stream with protocol version 1.
*/
public MarshalOutputStream(OutputStream out) throws IOException {
this(out, ObjectStreamConstants.PROTOCOL_VERSION_1);
}
/**
* Creates a marshal output stream with the given protocol version.
*/
public MarshalOutputStream(OutputStream out, int protocolVersion)
throws IOException
{
super(out);
this.useProtocolVersion(protocolVersion);
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
enableReplaceObject(true);
return null;
}
});
}
/**
* Checks for objects that are instances of java.rmi.Remote
* that need to be serialized as proxy objects.
*/
protected final Object replaceObject(Object obj) throws IOException {
if ((obj instanceof Remote) && !(obj instanceof RemoteStub)) {
Target target = ObjectTable.getTarget((Remote) obj);
if (target != null) {
return target.getStub();
}
}
return obj;
}
/**
* Serializes a location from which to load the the specified class.
*/
protected void annotateClass(Class<?> cl) throws IOException {
writeLocation(java.rmi.server.RMIClassLoader.getClassAnnotation(cl));
}
/**
* Serializes a location from which to load the specified class.
*/
protected void annotateProxyClass(Class<?> cl) throws IOException {
annotateClass(cl);
}
/**
* Writes the location for the class into the stream. This method can
* be overridden by subclasses that store this annotation somewhere
* else than as the next object in the stream, as is done by this class.
*/
protected void writeLocation(String location) throws IOException {
writeObject(location);
}
}

View File

@@ -0,0 +1,519 @@
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.rmi.MarshalException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.UnmarshalException;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteRef;
import java.security.AccessController;
import sun.misc.SharedSecrets;
import sun.rmi.runtime.Log;
import sun.rmi.transport.Connection;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.StreamRemoteCall;
import sun.security.action.GetBooleanAction;
/**
* NOTE: There is a JDK-internal dependency on the existence of this
* class's getLiveRef method (as it is inherited by UnicastRef2) in
* the implementation of javax.management.remote.rmi.RMIConnector.
*/
@SuppressWarnings("deprecation")
public class UnicastRef implements RemoteRef {
/**
* Client-side transport log.
*/
public static final Log clientRefLog =
Log.getLog("sun.rmi.client.ref", "transport", Util.logLevel);
/**
* Client-side call log.
*/
public static final Log clientCallLog =
Log.getLog("sun.rmi.client.call", "RMI",
AccessController.doPrivileged(
new GetBooleanAction("sun.rmi.client.logCalls")));
private static final long serialVersionUID = 8258372400816541186L;
protected LiveRef ref;
/**
* Create a new (empty) Unicast remote reference.
*/
public UnicastRef() {
}
/**
* Create a new Unicast RemoteRef.
*/
public UnicastRef(LiveRef liveRef) {
ref = liveRef;
}
/**
* Returns the current value of this UnicastRef's underlying
* LiveRef.
*
* NOTE: There is a JDK-internal dependency on the existence of
* this method (as it is inherited by UnicastRef) in the
* implementation of javax.management.remote.rmi.RMIConnector.
**/
public LiveRef getLiveRef() {
return ref;
}
/**
* Invoke a method. This form of delegating method invocation
* to the reference allows the reference to take care of
* setting up the connection to the remote host, marshalling
* some representation for the method and parameters, then
* communicating the method invocation to the remote host.
* This method either returns the result of a method invocation
* on the remote object which resides on the remote host or
* throws a RemoteException if the call failed or an
* application-level exception if the remote invocation throws
* an exception.
*
* @param obj the proxy for the remote object
* @param method the method to be invoked
* @param params the parameter list
* @param opnum a hash that may be used to represent the method
* @since 1.2
*/
public Object invoke(Remote obj,
Method method,
Object[] params,
long opnum)
throws Exception
{
if (clientRefLog.isLoggable(Log.VERBOSE)) {
clientRefLog.log(Log.VERBOSE, "method: " + method);
}
if (clientCallLog.isLoggable(Log.VERBOSE)) {
logClientCall(obj, method);
}
Connection conn = ref.getChannel().newConnection();
RemoteCall call = null;
boolean reuse = true;
/* If the call connection is "reused" early, remember not to
* reuse again.
*/
boolean alreadyFreed = false;
try {
if (clientRefLog.isLoggable(Log.VERBOSE)) {
clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);
}
// create call context
call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
// marshal parameters
try {
ObjectOutput out = call.getOutputStream();
marshalCustomCallData(out);
Class<?>[] types = method.getParameterTypes();
for (int i = 0; i < types.length; i++) {
marshalValue(types[i], params[i], out);
}
} catch (IOException e) {
clientRefLog.log(Log.BRIEF,
"IOException marshalling arguments: ", e);
throw new MarshalException("error marshalling arguments", e);
}
// unmarshal return
call.executeCall();
try {
Class<?> rtype = method.getReturnType();
if (rtype == void.class)
return null;
ObjectInput in = call.getInputStream();
/* StreamRemoteCall.done() does not actually make use
* of conn, therefore it is safe to reuse this
* connection before the dirty call is sent for
* registered refs.
*/
Object returnValue = unmarshalValue(rtype, in);
/* we are freeing the connection now, do not free
* again or reuse.
*/
alreadyFreed = true;
/* if we got to this point, reuse must have been true. */
clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
/* Free the call's connection early. */
ref.getChannel().free(conn, true);
return returnValue;
} catch (IOException | ClassNotFoundException e) {
// disable saving any refs in the inputStream for GC
((StreamRemoteCall)call).discardPendingRefs();
clientRefLog.log(Log.BRIEF,
e.getClass().getName() + " unmarshalling return: ", e);
throw new UnmarshalException("error unmarshalling return", e);
} finally {
try {
call.done();
} catch (IOException e) {
/* WARNING: If the conn has been reused early,
* then it is too late to recover from thrown
* IOExceptions caught here. This code is relying
* on StreamRemoteCall.done() not actually
* throwing IOExceptions.
*/
reuse = false;
}
}
} catch (RuntimeException e) {
/*
* Need to distinguish between client (generated by the
* invoke method itself) and server RuntimeExceptions.
* Client side RuntimeExceptions are likely to have
* corrupted the call connection and those from the server
* are not likely to have done so. If the exception came
* from the server the call connection should be reused.
*/
if ((call == null) ||
(((StreamRemoteCall) call).getServerException() != e))
{
reuse = false;
}
throw e;
} catch (RemoteException e) {
/*
* Some failure during call; assume connection cannot
* be reused. Must assume failure even if ServerException
* or ServerError occurs since these failures can happen
* during parameter deserialization which would leave
* the connection in a corrupted state.
*/
reuse = false;
throw e;
} catch (Error e) {
/* If errors occurred, the connection is most likely not
* reusable.
*/
reuse = false;
throw e;
} finally {
/* alreadyFreed ensures that we do not log a reuse that
* may have already happened.
*/
if (!alreadyFreed) {
if (clientRefLog.isLoggable(Log.BRIEF)) {
clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
reuse + ")");
}
ref.getChannel().free(conn, reuse);
}
}
}
protected void marshalCustomCallData(ObjectOutput out) throws IOException
{}
/**
* Marshal value to an ObjectOutput sink using RMI's serialization
* format for parameters or return values.
*/
protected static void marshalValue(Class<?> type, Object value,
ObjectOutput out)
throws IOException
{
if (type.isPrimitive()) {
if (type == int.class) {
out.writeInt(((Integer) value).intValue());
} else if (type == boolean.class) {
out.writeBoolean(((Boolean) value).booleanValue());
} else if (type == byte.class) {
out.writeByte(((Byte) value).byteValue());
} else if (type == char.class) {
out.writeChar(((Character) value).charValue());
} else if (type == short.class) {
out.writeShort(((Short) value).shortValue());
} else if (type == long.class) {
out.writeLong(((Long) value).longValue());
} else if (type == float.class) {
out.writeFloat(((Float) value).floatValue());
} else if (type == double.class) {
out.writeDouble(((Double) value).doubleValue());
} else {
throw new Error("Unrecognized primitive type: " + type);
}
} else {
out.writeObject(value);
}
}
/**
* Unmarshal value from an ObjectInput source using RMI's serialization
* format for parameters or return values.
*/
protected static Object unmarshalValue(Class<?> type, ObjectInput in)
throws IOException, ClassNotFoundException
{
if (type.isPrimitive()) {
if (type == int.class) {
return Integer.valueOf(in.readInt());
} else if (type == boolean.class) {
return Boolean.valueOf(in.readBoolean());
} else if (type == byte.class) {
return Byte.valueOf(in.readByte());
} else if (type == char.class) {
return Character.valueOf(in.readChar());
} else if (type == short.class) {
return Short.valueOf(in.readShort());
} else if (type == long.class) {
return Long.valueOf(in.readLong());
} else if (type == float.class) {
return Float.valueOf(in.readFloat());
} else if (type == double.class) {
return Double.valueOf(in.readDouble());
} else {
throw new Error("Unrecognized primitive type: " + type);
}
} else if (type == String.class && in instanceof ObjectInputStream) {
return SharedSecrets.getJavaObjectInputStreamReadString().readString((ObjectInputStream)in);
} else {
return in.readObject();
}
}
/**
* Create an appropriate call object for a new call on this object.
* Passing operation array and index, allows the stubs generator to
* assign the operation indexes and interpret them. The RemoteRef
* may need the operation to encode in for the call.
*/
public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum,
long hash)
throws RemoteException
{
clientRefLog.log(Log.BRIEF, "get connection");
Connection conn = ref.getChannel().newConnection();
try {
clientRefLog.log(Log.VERBOSE, "create call context");
/* log information about the outgoing call */
if (clientCallLog.isLoggable(Log.VERBOSE)) {
logClientCall(obj, ops[opnum]);
}
RemoteCall call =
new StreamRemoteCall(conn, ref.getObjID(), opnum, hash);
try {
marshalCustomCallData(call.getOutputStream());
} catch (IOException e) {
throw new MarshalException("error marshaling " +
"custom call data");
}
return call;
} catch (RemoteException e) {
ref.getChannel().free(conn, false);
throw e;
}
}
/**
* Invoke makes the remote call present in the RemoteCall object.
*
* Invoke will raise any "user" exceptions which
* should pass through and not be caught by the stub. If any
* exception is raised during the remote invocation, invoke should
* take care of cleaning up the connection before raising the
* "user" or remote exception.
*/
public void invoke(RemoteCall call) throws Exception {
try {
clientRefLog.log(Log.VERBOSE, "execute call");
call.executeCall();
} catch (RemoteException e) {
/*
* Call did not complete; connection can't be reused.
*/
clientRefLog.log(Log.BRIEF, "exception: ", e);
free(call, false);
throw e;
} catch (Error e) {
/* If errors occurred, the connection is most likely not
* reusable.
*/
clientRefLog.log(Log.BRIEF, "error: ", e);
free(call, false);
throw e;
} catch (RuntimeException e) {
/*
* REMIND: Since runtime exceptions are no longer wrapped,
* we can't assue that the connection was left in
* a reusable state. Is this okay?
*/
clientRefLog.log(Log.BRIEF, "exception: ", e);
free(call, false);
throw e;
} catch (Exception e) {
/*
* Assume that these other exceptions are user exceptions
* and leave the connection in a reusable state.
*/
clientRefLog.log(Log.BRIEF, "exception: ", e);
free(call, true);
/* reraise user (and unknown) exceptions. */
throw e;
}
/*
* Don't free the connection if an exception did not
* occur because the stub needs to unmarshal the
* return value. The connection will be freed
* by a call to the "done" method.
*/
}
/**
* Private method to free a connection.
*/
private void free(RemoteCall call, boolean reuse) throws RemoteException {
Connection conn = ((StreamRemoteCall)call).getConnection();
ref.getChannel().free(conn, reuse);
}
/**
* Done should only be called if the invoke returns successfully
* (non-exceptionally) to the stub. It allows the remote reference to
* clean up (or reuse) the connection.
*/
public void done(RemoteCall call) throws RemoteException {
/* Done only uses the connection inside the call to obtain the
* channel the connection uses. Once all information is read
* from the connection, the connection may be freed.
*/
clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
/* Free the call connection early. */
free(call, true);
try {
call.done();
} catch (IOException e) {
/* WARNING: If the conn has been reused early, then it is
* too late to recover from thrown IOExceptions caught
* here. This code is relying on StreamRemoteCall.done()
* not actually throwing IOExceptions.
*/
}
}
/**
* Log the details of an outgoing call. The method parameter is either of
* type java.lang.reflect.Method or java.rmi.server.Operation.
*/
void logClientCall(Object obj, Object method) {
clientCallLog.log(Log.VERBOSE, "outbound call: " +
ref + " : " + obj.getClass().getName() +
ref.getObjID().toString() + ": " + method);
}
/**
* Returns the class of the ref type to be serialized
*/
public String getRefClass(ObjectOutput out) {
return "UnicastRef";
}
/**
* Write out external representation for remote ref.
*/
public void writeExternal(ObjectOutput out) throws IOException {
ref.write(out, false);
}
/**
* Read in external representation for remote ref.
* @exception ClassNotFoundException If the class for an object
* being restored cannot be found.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
ref = LiveRef.read(in, false);
}
//----------------------------------------------------------------------;
/**
* Method from object, forward from RemoteObject
*/
public String remoteToString() {
return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]";
}
/**
* default implementation of hashCode for remote objects
*/
public int remoteHashCode() {
return ref.hashCode();
}
/** default implementation of equals for remote objects
*/
public boolean remoteEquals(RemoteRef sub) {
if (sub instanceof UnicastRef)
return ref.remoteEquals(((UnicastRef)sub).ref);
return false;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import sun.rmi.transport.LiveRef;
/**
* NOTE: There is a JDK-internal dependency on the existence of this
* class and its getLiveRef method (inherited from UnicastRef) in the
* implementation of javax.management.remote.rmi.RMIConnector.
**/
public class UnicastRef2 extends UnicastRef {
private static final long serialVersionUID = 1829537514995881838L;
/**
* Create a new (empty) Unicast remote reference.
*/
public UnicastRef2()
{}
/**
* Create a new Unicast RemoteRef.
*/
public UnicastRef2(LiveRef liveRef) {
super(liveRef);
}
/**
* Returns the class of the ref type to be serialized
*/
public String getRefClass(ObjectOutput out)
{
return "UnicastRef2";
}
/**
* Write out external representation for remote ref.
*/
public void writeExternal(ObjectOutput out) throws IOException
{
ref.write(out, true);
}
/**
* Read in external representation for remote ref.
* @exception ClassNotFoundException If the class for an object
* being restored cannot be found.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
ref = LiveRef.read(in, true);
}
}

View File

@@ -0,0 +1,689 @@
/*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectStreamClass;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.AccessException;
import java.rmi.MarshalException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.ServerError;
import java.rmi.ServerException;
import java.rmi.UnmarshalException;
import java.rmi.server.ExportException;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteRef;
import java.rmi.server.RemoteStub;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.ServerRef;
import java.rmi.server.Skeleton;
import java.rmi.server.SkeletonNotFoundException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import sun.misc.ObjectInputFilter;
import sun.rmi.runtime.Log;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.StreamRemoteCall;
import sun.rmi.transport.Target;
import sun.rmi.transport.tcp.TCPTransport;
import sun.security.action.GetBooleanAction;
/**
* UnicastServerRef implements the remote reference layer server-side
* behavior for remote objects exported with the "UnicastRef" reference
* type.
* If an {@link ObjectInputFilter ObjectInputFilter} is supplied it is
* invoked during deserialization to filter the arguments,
* otherwise the default filter of {@link ObjectInputStream ObjectInputStream}
* applies.
*
* @author Ann Wollrath
* @author Roger Riggs
* @author Peter Jones
*/
@SuppressWarnings("deprecation")
public class UnicastServerRef extends UnicastRef
implements ServerRef, Dispatcher
{
/** value of server call log property */
public static final boolean logCalls = AccessController.doPrivileged(
new GetBooleanAction("java.rmi.server.logCalls"));
/** server call log */
public static final Log callLog =
Log.getLog("sun.rmi.server.call", "RMI", logCalls);
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = -7384275867073752268L;
/** flag to enable writing exceptions to System.err */
private static final boolean wantExceptionLog =
AccessController.doPrivileged(
new GetBooleanAction("sun.rmi.server.exceptionTrace"));
private boolean forceStubUse = false;
/**
* flag to remove server-side stack traces before marshalling
* exceptions thrown by remote invocations to this VM
*/
private static final boolean suppressStackTraces =
AccessController.doPrivileged(
new GetBooleanAction(
"sun.rmi.server.suppressStackTraces"));
/**
* skeleton to dispatch remote calls through, for 1.1 stub protocol
* (may be null if stub class only uses 1.2 stub protocol)
*/
private transient Skeleton skel;
// The ObjectInputFilter for checking the invocation arguments
private final transient ObjectInputFilter filter;
/** maps method hash to Method object for each remote method */
private transient Map<Long,Method> hashToMethod_Map = null;
/**
* A weak hash map, mapping classes to hash maps that map method
* hashes to method objects.
**/
private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps =
new HashToMethod_Maps();
/** cache of impl classes that have no corresponding skeleton class */
private static final Map<Class<?>,?> withoutSkeletons =
Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>());
private final AtomicInteger methodCallIDCount = new AtomicInteger(0);
/**
* Create a new (empty) Unicast server remote reference.
* The filter is null to defer to the default ObjectInputStream filter, if any.
*/
public UnicastServerRef() {
this.filter = null;
}
/**
* Construct a Unicast server remote reference for a specified
* liveRef.
* The filter is null to defer to the default ObjectInputStream filter, if any.
*/
public UnicastServerRef(LiveRef ref) {
super(ref);
this.filter = null;
}
/**
* Construct a Unicast server remote reference for a specified
* liveRef and filter.
*/
public UnicastServerRef(LiveRef ref, ObjectInputFilter filter) {
super(ref);
this.filter = filter;
}
/**
* Construct a Unicast server remote reference to be exported
* on the specified port.
*/
public UnicastServerRef(int port) {
super(new LiveRef(port));
this.filter = null;
}
/**
* Constructs a UnicastServerRef to be exported on an
* anonymous port (i.e., 0) and that uses a pregenerated stub class
* (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'.
*
* This constructor is only called by the method
* UnicastRemoteObject.exportObject(Remote) passing 'true' for
* 'forceStubUse'. The UnicastRemoteObject.exportObject(Remote) method
* returns RemoteStub, so it must ensure that the stub for the
* exported object is an instance of a pregenerated stub class that
* extends RemoteStub (instead of an instance of a dynamic proxy class
* which is not an instance of RemoteStub).
**/
public UnicastServerRef(boolean forceStubUse) {
this(0);
this.forceStubUse = forceStubUse;
}
/**
* With the addition of support for dynamic proxies as stubs, this
* method is obsolete because it returns RemoteStub instead of the more
* general Remote. It should not be called. It sets the
* 'forceStubUse' flag to true so that the stub for the exported object
* is forced to be an instance of the pregenerated stub class, which
* extends RemoteStub.
*
* Export this object, create the skeleton and stubs for this
* dispatcher. Create a stub based on the type of the impl,
* initialize it with the appropriate remote reference. Create the
* target defined by the impl, dispatcher (this) and stub.
* Export that target via the Ref.
**/
public RemoteStub exportObject(Remote impl, Object data)
throws RemoteException
{
forceStubUse = true;
return (RemoteStub) exportObject(impl, data, false);
}
/**
* Export this object, create the skeleton and stubs for this
* dispatcher. Create a stub based on the type of the impl,
* initialize it with the appropriate remote reference. Create the
* target defined by the impl, dispatcher (this) and stub.
* Export that target via the Ref.
*/
public Remote exportObject(Remote impl, Object data,
boolean permanent)
throws RemoteException
{
Class<?> implClass = impl.getClass();
Remote stub;
try {
stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
} catch (IllegalArgumentException e) {
throw new ExportException(
"remote object implements illegal remote interface", e);
}
if (stub instanceof RemoteStub) {
setSkeleton(impl);
}
Target target =
new Target(impl, this, stub, ref.getObjID(), permanent);
ref.exportObject(target);
hashToMethod_Map = hashToMethod_Maps.get(implClass);
return stub;
}
/**
* Return the hostname of the current client. When called from a
* thread actively handling a remote method invocation the
* hostname of the client is returned.
* @exception ServerNotActiveException If called outside of servicing
* a remote method invocation.
*/
public String getClientHost() throws ServerNotActiveException {
return TCPTransport.getClientHost();
}
/**
* Discovers and sets the appropriate skeleton for the impl.
*/
public void setSkeleton(Remote impl) throws RemoteException {
if (!withoutSkeletons.containsKey(impl.getClass())) {
try {
skel = Util.createSkeleton(impl);
} catch (SkeletonNotFoundException e) {
/*
* Ignore exception for skeleton class not found, because a
* skeleton class is not necessary with the 1.2 stub protocol.
* Remember that this impl's class does not have a skeleton
* class so we don't waste time searching for it again.
*/
withoutSkeletons.put(impl.getClass(), null);
}
}
}
/**
* Call to dispatch to the remote object (on the server side).
* The up-call to the server and the marshalling of return result
* (or exception) should be handled before returning from this
* method.
* @param obj the target remote object for the call
* @param call the "remote call" from which operation and
* method arguments can be obtained.
* @exception IOException If unable to marshal return result or
* release input or output streams
*/
public void dispatch(Remote obj, RemoteCall call) throws IOException {
// positive operation number in 1.1 stubs;
// negative version number in 1.2 stubs and beyond...
int num;
long op;
try {
// read remote call header
ObjectInput in;
try {
in = call.getInputStream();
num = in.readInt();
} catch (Exception readEx) {
throw new UnmarshalException("error unmarshalling call header",
readEx);
}
if (skel != null) {
// If there is a skeleton, use it
oldDispatch(obj, call, num);
return;
} else if (num >= 0){
throw new UnmarshalException(
"skeleton class not found but required for client version");
}
try {
op = in.readLong();
} catch (Exception readEx) {
throw new UnmarshalException("error unmarshalling call header",
readEx);
}
/*
* Since only system classes (with null class loaders) will be on
* the execution stack during parameter unmarshalling for the 1.2
* stub protocol, tell the MarshalInputStream not to bother trying
* to resolve classes using its superclasses's default method of
* consulting the first non-null class loader on the stack.
*/
MarshalInputStream marshalStream = (MarshalInputStream) in;
marshalStream.skipDefaultResolveClass();
Method method = hashToMethod_Map.get(op);
if (method == null) {
throw new UnmarshalException("unrecognized method hash: " +
"method not supported by remote object");
}
// if calls are being logged, write out object id and operation
logCall(obj, method);
// unmarshal parameters
Object[] params = null;
try {
unmarshalCustomCallData(in);
params = unmarshalParameters(obj, method, marshalStream);
} catch (AccessException aex) {
// For compatibility, AccessException is not wrapped in UnmarshalException
// disable saving any refs in the inputStream for GC
((StreamRemoteCall) call).discardPendingRefs();
throw aex;
} catch (java.io.IOException | ClassNotFoundException e) {
// disable saving any refs in the inputStream for GC
((StreamRemoteCall) call).discardPendingRefs();
throw new UnmarshalException(
"error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
// make upcall on remote object
Object result;
try {
result = method.invoke(obj, params);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
// marshal return value
try {
ObjectOutput out = call.getResultStream(true);
Class<?> rtype = method.getReturnType();
if (rtype != void.class) {
marshalValue(rtype, result, out);
}
} catch (IOException ex) {
throw new MarshalException("error marshalling return", ex);
/*
* This throw is problematic because when it is caught below,
* we attempt to marshal it back to the client, but at this
* point, a "normal return" has already been indicated,
* so marshalling an exception will corrupt the stream.
* This was the case with skeletons as well; there is no
* immediately obvious solution without a protocol change.
*/
}
} catch (Throwable e) {
Throwable origEx = e;
logCallException(e);
ObjectOutput out = call.getResultStream(false);
if (e instanceof Error) {
e = new ServerError(
"Error occurred in server thread", (Error) e);
} else if (e instanceof RemoteException) {
e = new ServerException(
"RemoteException occurred in server thread",
(Exception) e);
}
if (suppressStackTraces) {
clearStackTraces(e);
}
out.writeObject(e);
// AccessExceptions should cause Transport.serviceCall
// to flag the connection as unusable.
if (origEx instanceof AccessException) {
throw new IOException("Connection is not reusable", origEx);
}
} finally {
call.releaseInputStream(); // in case skeleton doesn't
call.releaseOutputStream();
}
}
/**
* Sets a filter for invocation arguments, if a filter has been set.
* Called by dispatch before the arguments are read.
*/
protected void unmarshalCustomCallData(ObjectInput in)
throws IOException, ClassNotFoundException {
if (filter != null &&
in instanceof ObjectInputStream) {
// Set the filter on the stream
ObjectInputStream ois = (ObjectInputStream) in;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
ObjectInputFilter.Config.setObjectInputFilter(ois, filter);
return null;
}
});
}
}
/**
* Handle server-side dispatch using the RMI 1.1 stub/skeleton
* protocol, given a non-negative operation number or negative method hash
* that has already been read from the call stream.
* Exceptions are handled by the caller to be sent to the remote client.
*
* @param obj the target remote object for the call
* @param call the "remote call" from which operation and
* method arguments can be obtained.
* @param op the operation number
* @throws Exception if unable to marshal return result or
* release input or output streams
*/
private void oldDispatch(Remote obj, RemoteCall call, int op)
throws Exception
{
long hash; // hash for matching stub with skeleton
// read remote call header
ObjectInput in;
in = call.getInputStream();
try {
Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
if (clazz.isAssignableFrom(skel.getClass())) {
((MarshalInputStream)in).useCodebaseOnly();
}
} catch (ClassNotFoundException ignore) { }
try {
hash = in.readLong();
} catch (Exception ioe) {
throw new UnmarshalException("error unmarshalling call header", ioe);
}
// if calls are being logged, write out object id and operation
Operation[] operations = skel.getOperations();
logCall(obj, op >= 0 && op < operations.length ? operations[op] : "op: " + op);
unmarshalCustomCallData(in);
// dispatch to skeleton for remote object
skel.dispatch(obj, call, op, hash);
}
/**
* Clear the stack trace of the given Throwable by replacing it with
* an empty StackTraceElement array, and do the same for all of its
* chained causative exceptions.
*/
public static void clearStackTraces(Throwable t) {
StackTraceElement[] empty = new StackTraceElement[0];
while (t != null) {
t.setStackTrace(empty);
t = t.getCause();
}
}
/**
* Log the details of an incoming call. The method parameter is either of
* type java.lang.reflect.Method or java.rmi.server.Operation.
*/
private void logCall(Remote obj, Object method) {
if (callLog.isLoggable(Log.VERBOSE)) {
String clientHost;
try {
clientHost = getClientHost();
} catch (ServerNotActiveException snae) {
clientHost = "(local)"; // shouldn't happen
}
callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
obj.getClass().getName() +
ref.getObjID().toString() + ": " +
method + "]");
}
}
/**
* Log the exception detail of an incoming call.
*/
private void logCallException(Throwable e) {
// if calls are being logged, log them
if (callLog.isLoggable(Log.BRIEF)) {
String clientHost = "";
try {
clientHost = "[" + getClientHost() + "] ";
} catch (ServerNotActiveException snae) {
}
callLog.log(Log.BRIEF, clientHost + "exception: ", e);
}
// write exceptions (only) to System.err if desired
if (wantExceptionLog) {
java.io.PrintStream log = System.err;
synchronized (log) {
log.println();
log.println("Exception dispatching call to " +
ref.getObjID() + " in thread \"" +
Thread.currentThread().getName() +
"\" at " + (new Date()) + ":");
e.printStackTrace(log);
}
}
}
/**
* Returns the class of the ref type to be serialized.
*/
public String getRefClass(ObjectOutput out) {
return "UnicastServerRef";
}
/**
* Return the client remote reference for this remoteRef.
* In the case of a client RemoteRef "this" is the answer.
* For a server remote reference, a client side one will have to
* found or created.
*/
protected RemoteRef getClientRef() {
return new UnicastRef(ref);
}
/**
* Write out external representation for remote ref.
*/
public void writeExternal(ObjectOutput out) throws IOException {
}
/**
* Read in external representation for remote ref.
* @exception ClassNotFoundException If the class for an object
* being restored cannot be found.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
// object is re-exported elsewhere (e.g., by UnicastRemoteObject)
ref = null;
skel = null;
}
/**
* A weak hash map, mapping classes to hash maps that map method
* hashes to method objects.
**/
private static class HashToMethod_Maps
extends WeakClassHashMap<Map<Long,Method>>
{
HashToMethod_Maps() {}
protected Map<Long,Method> computeValue(Class<?> remoteClass) {
Map<Long,Method> map = new HashMap<>();
for (Class<?> cl = remoteClass;
cl != null;
cl = cl.getSuperclass())
{
for (Class<?> intf : cl.getInterfaces()) {
if (Remote.class.isAssignableFrom(intf)) {
for (Method method : intf.getMethods()) {
final Method m = method;
/*
* Set this Method object to override language
* access checks so that the dispatcher can invoke
* methods from non-public remote interfaces.
*/
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run() {
m.setAccessible(true);
return null;
}
});
map.put(Util.computeMethodHash(m), m);
}
}
}
}
return map;
}
}
/**
* Unmarshal parameters for the given method of the given instance over
* the given marshalinputstream. Perform any necessary checks.
*/
private Object[] unmarshalParameters(Object obj, Method method, MarshalInputStream in)
throws IOException, ClassNotFoundException {
return (obj instanceof DeserializationChecker) ?
unmarshalParametersChecked((DeserializationChecker)obj, method, in) :
unmarshalParametersUnchecked(method, in);
}
/**
* Unmarshal parameters for the given method of the given instance over
* the given marshalinputstream. Do not perform any additional checks.
*/
private Object[] unmarshalParametersUnchecked(Method method, ObjectInput in)
throws IOException, ClassNotFoundException {
Class<?>[] types = method.getParameterTypes();
Object[] params = new Object[types.length];
for (int i = 0; i < types.length; i++) {
params[i] = unmarshalValue(types[i], in);
}
return params;
}
/**
* Unmarshal parameters for the given method of the given instance over
* the given marshalinputstream. Do perform all additional checks.
*/
private Object[] unmarshalParametersChecked(
DeserializationChecker checker,
Method method, MarshalInputStream in)
throws IOException, ClassNotFoundException {
int callID = methodCallIDCount.getAndIncrement();
MyChecker myChecker = new MyChecker(checker, method, callID);
in.setStreamChecker(myChecker);
try {
Class<?>[] types = method.getParameterTypes();
Object[] values = new Object[types.length];
for (int i = 0; i < types.length; i++) {
myChecker.setIndex(i);
values[i] = unmarshalValue(types[i], in);
}
myChecker.end(callID);
return values;
} finally {
in.setStreamChecker(null);
}
}
private static class MyChecker implements MarshalInputStream.StreamChecker {
private final DeserializationChecker descriptorCheck;
private final Method method;
private final int callID;
private int parameterIndex;
MyChecker(DeserializationChecker descriptorCheck, Method method, int callID) {
this.descriptorCheck = descriptorCheck;
this.method = method;
this.callID = callID;
}
@Override
public void validateDescriptor(ObjectStreamClass descriptor) {
descriptorCheck.check(method, descriptor, parameterIndex, callID);
}
@Override
public void checkProxyInterfaceNames(String[] ifaces) {
descriptorCheck.checkProxyClass(method, ifaces, parameterIndex, callID);
}
void setIndex(int parameterIndex) {
this.parameterIndex = parameterIndex;
}
void end(int callId) {
descriptorCheck.end(callId);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.ObjectOutput;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.RemoteRef;
import sun.misc.ObjectInputFilter;
import sun.rmi.transport.LiveRef;
/**
* Server-side ref for a remote impl that uses a custom socket factory.
*
* @author Ann Wollrath
* @author Roger Riggs
*/
public class UnicastServerRef2 extends UnicastServerRef
{
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = -2289703812660767614L;
/**
* Create a new (empty) Unicast server remote reference.
*/
public UnicastServerRef2()
{}
/**
* Construct a Unicast server remote reference for a specified
* liveRef.
*/
public UnicastServerRef2(LiveRef ref)
{
super(ref);
}
/**
* Construct a Unicast server remote reference for a specified
* liveRef and filter.
*/
public UnicastServerRef2(LiveRef ref,
ObjectInputFilter filter)
{
super(ref, filter);
}
/**
* Construct a Unicast server remote reference to be exported
* on the specified port.
*/
public UnicastServerRef2(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
{
super(new LiveRef(port, csf, ssf));
}
/**
* Construct a Unicast server remote reference to be exported
* on the specified port.
*/
public UnicastServerRef2(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf,
ObjectInputFilter filter)
{
super(new LiveRef(port, csf, ssf), filter);
}
/**
* Returns the class of the ref type to be serialized
*/
public String getRefClass(ObjectOutput out)
{
return "UnicastServerRef2";
}
/**
* Return the client remote reference for this remoteRef.
* In the case of a client RemoteRef "this" is the answer.
* For a server remote reference, a client side one will have to
* found or created.
*/
protected RemoteRef getClientRef() {
return new UnicastRef2(ref);
}
}

View File

@@ -0,0 +1,466 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.DataOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.StubNotFoundException;
import java.rmi.registry.Registry;
import java.rmi.server.LogStream;
import java.rmi.server.ObjID;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.rmi.server.RemoteRef;
import java.rmi.server.RemoteStub;
import java.rmi.server.Skeleton;
import java.rmi.server.SkeletonNotFoundException;
import java.security.AccessController;
import java.security.MessageDigest;
import java.security.DigestOutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import sun.rmi.registry.RegistryImpl;
import sun.rmi.runtime.Log;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
/**
* A utility class with static methods for creating stubs/proxies and
* skeletons for remote objects.
*/
@SuppressWarnings("deprecation")
public final class Util {
/** "server" package log level */
static final int logLevel = LogStream.parseLevel(
AccessController.doPrivileged(
new GetPropertyAction("sun.rmi.server.logLevel")));
/** server reference log */
public static final Log serverRefLog =
Log.getLog("sun.rmi.server.ref", "transport", Util.logLevel);
/** cached value of property java.rmi.server.ignoreStubClasses */
private static final boolean ignoreStubClasses =
AccessController.doPrivileged(
new GetBooleanAction("java.rmi.server.ignoreStubClasses")).
booleanValue();
/** cache of impl classes that have no corresponding stub class */
private static final Map<Class<?>, Void> withoutStubs =
Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>(11));
/** parameter types for stub constructor */
private static final Class<?>[] stubConsParamTypes = { RemoteRef.class };
private Util() {
}
/**
* Returns a proxy for the specified implClass.
*
* If both of the following criteria is satisfied, a dynamic proxy for
* the specified implClass is returned (otherwise a RemoteStub instance
* for the specified implClass is returned):
*
* a) either the property java.rmi.server.ignoreStubClasses is true or
* a pregenerated stub class does not exist for the impl class, and
* b) forceStubUse is false.
*
* If the above criteria are satisfied, this method constructs a
* dynamic proxy instance (that implements the remote interfaces of
* implClass) constructed with a RemoteObjectInvocationHandler instance
* constructed with the clientRef.
*
* Otherwise, this method loads the pregenerated stub class (which
* extends RemoteStub and implements the remote interfaces of
* implClass) and constructs an instance of the pregenerated stub
* class with the clientRef.
*
* @param implClass the class to obtain remote interfaces from
* @param clientRef the remote ref to use in the invocation handler
* @param forceStubUse if true, forces creation of a RemoteStub
* @throws IllegalArgumentException if implClass implements illegal
* remote interfaces
* @throws StubNotFoundException if problem locating/creating stub or
* creating the dynamic proxy instance
**/
public static Remote createProxy(Class<?> implClass,
RemoteRef clientRef,
boolean forceStubUse)
throws StubNotFoundException
{
Class<?> remoteClass;
try {
remoteClass = getRemoteClass(implClass);
} catch (ClassNotFoundException ex ) {
throw new StubNotFoundException(
"object does not implement a remote interface: " +
implClass.getName());
}
if (forceStubUse ||
!(ignoreStubClasses || !stubClassExists(remoteClass)))
{
return createStub(remoteClass, clientRef);
}
final ClassLoader loader = implClass.getClassLoader();
final Class<?>[] interfaces = getRemoteInterfaces(implClass);
final InvocationHandler handler =
new RemoteObjectInvocationHandler(clientRef);
/* REMIND: private remote interfaces? */
try {
return AccessController.doPrivileged(new PrivilegedAction<Remote>() {
public Remote run() {
return (Remote) Proxy.newProxyInstance(loader,
interfaces,
handler);
}});
} catch (IllegalArgumentException e) {
throw new StubNotFoundException("unable to create proxy", e);
}
}
/**
* Returns true if a stub class for the given impl class can be loaded,
* otherwise returns false.
*
* @param remoteClass the class to obtain remote interfaces from
*/
private static boolean stubClassExists(Class<?> remoteClass) {
if (!withoutStubs.containsKey(remoteClass)) {
try {
Class.forName(remoteClass.getName() + "_Stub",
false,
remoteClass.getClassLoader());
return true;
} catch (ClassNotFoundException cnfe) {
withoutStubs.put(remoteClass, null);
}
}
return false;
}
/*
* Returns the class/superclass that implements the remote interface.
* @throws ClassNotFoundException if no class is found to have a
* remote interface
*/
private static Class<?> getRemoteClass(Class<?> cl)
throws ClassNotFoundException
{
while (cl != null) {
Class<?>[] interfaces = cl.getInterfaces();
for (int i = interfaces.length -1; i >= 0; i--) {
if (Remote.class.isAssignableFrom(interfaces[i]))
return cl; // this class implements remote object
}
cl = cl.getSuperclass();
}
throw new ClassNotFoundException(
"class does not implement java.rmi.Remote");
}
/**
* Returns an array containing the remote interfaces implemented
* by the given class.
*
* @param remoteClass the class to obtain remote interfaces from
* @throws IllegalArgumentException if remoteClass implements
* any illegal remote interfaces
* @throws NullPointerException if remoteClass is null
*/
private static Class<?>[] getRemoteInterfaces(Class<?> remoteClass) {
ArrayList<Class<?>> list = new ArrayList<>();
getRemoteInterfaces(list, remoteClass);
return list.toArray(new Class<?>[list.size()]);
}
/**
* Fills the given array list with the remote interfaces implemented
* by the given class.
*
* @throws IllegalArgumentException if the specified class implements
* any illegal remote interfaces
* @throws NullPointerException if the specified class or list is null
*/
private static void getRemoteInterfaces(ArrayList<Class<?>> list, Class<?> cl) {
Class<?> superclass = cl.getSuperclass();
if (superclass != null) {
getRemoteInterfaces(list, superclass);
}
Class<?>[] interfaces = cl.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class<?> intf = interfaces[i];
/*
* If it is a remote interface (if it extends from
* java.rmi.Remote) and is not already in the list,
* then add the interface to the list.
*/
if (Remote.class.isAssignableFrom(intf)) {
if (!(list.contains(intf))) {
Method[] methods = intf.getMethods();
for (int j = 0; j < methods.length; j++) {
checkMethod(methods[j]);
}
list.add(intf);
}
}
}
}
/**
* Verifies that the supplied method has at least one declared exception
* type that is RemoteException or one of its superclasses. If not,
* then this method throws IllegalArgumentException.
*
* @throws IllegalArgumentException if m is an illegal remote method
*/
private static void checkMethod(Method m) {
Class<?>[] ex = m.getExceptionTypes();
for (int i = 0; i < ex.length; i++) {
if (ex[i].isAssignableFrom(RemoteException.class))
return;
}
throw new IllegalArgumentException(
"illegal remote method encountered: " + m);
}
/**
* Creates a RemoteStub instance for the specified class, constructed
* with the specified RemoteRef. The supplied class must be the most
* derived class in the remote object's superclass chain that
* implements a remote interface. The stub class name is the name of
* the specified remoteClass with the suffix "_Stub". The loading of
* the stub class is initiated from class loader of the specified class
* (which may be the bootstrap class loader).
**/
private static RemoteStub createStub(Class<?> remoteClass, RemoteRef ref)
throws StubNotFoundException
{
String stubname = remoteClass.getName() + "_Stub";
/* Make sure to use the local stub loader for the stub classes.
* When loaded by the local loader the load path can be
* propagated to remote clients, by the MarshalOutputStream/InStream
* pickle methods
*/
try {
Class<?> stubcl =
Class.forName(stubname, false, remoteClass.getClassLoader());
Constructor<?> cons = stubcl.getConstructor(stubConsParamTypes);
return (RemoteStub) cons.newInstance(new Object[] { ref });
} catch (ClassNotFoundException e) {
throw new StubNotFoundException(
"Stub class not found: " + stubname, e);
} catch (NoSuchMethodException e) {
throw new StubNotFoundException(
"Stub class missing constructor: " + stubname, e);
} catch (InstantiationException e) {
throw new StubNotFoundException(
"Can't create instance of stub class: " + stubname, e);
} catch (IllegalAccessException e) {
throw new StubNotFoundException(
"Stub class constructor not public: " + stubname, e);
} catch (InvocationTargetException e) {
throw new StubNotFoundException(
"Exception creating instance of stub class: " + stubname, e);
} catch (ClassCastException e) {
throw new StubNotFoundException(
"Stub class not instance of RemoteStub: " + stubname, e);
}
}
/**
* Locate and return the Skeleton for the specified remote object
*/
static Skeleton createSkeleton(Remote object)
throws SkeletonNotFoundException
{
Class<?> cl;
try {
cl = getRemoteClass(object.getClass());
} catch (ClassNotFoundException ex ) {
throw new SkeletonNotFoundException(
"object does not implement a remote interface: " +
object.getClass().getName());
}
// now try to load the skeleton based ont he name of the class
String skelname = cl.getName() + "_Skel";
try {
Class<?> skelcl = Class.forName(skelname, false, cl.getClassLoader());
return (Skeleton)skelcl.newInstance();
} catch (ClassNotFoundException ex) {
throw new SkeletonNotFoundException("Skeleton class not found: " +
skelname, ex);
} catch (InstantiationException ex) {
throw new SkeletonNotFoundException("Can't create skeleton: " +
skelname, ex);
} catch (IllegalAccessException ex) {
throw new SkeletonNotFoundException("No public constructor: " +
skelname, ex);
} catch (ClassCastException ex) {
throw new SkeletonNotFoundException(
"Skeleton not of correct class: " + skelname, ex);
}
}
/**
* Compute the "method hash" of a remote method. The method hash
* is a long containing the first 64 bits of the SHA digest from
* the UTF encoded string of the method name and descriptor.
*/
public static long computeMethodHash(Method m) {
long hash = 0;
ByteArrayOutputStream sink = new ByteArrayOutputStream(127);
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DataOutputStream out = new DataOutputStream(
new DigestOutputStream(sink, md));
String s = getMethodNameAndDescriptor(m);
if (serverRefLog.isLoggable(Log.VERBOSE)) {
serverRefLog.log(Log.VERBOSE,
"string used for method hash: \"" + s + "\"");
}
out.writeUTF(s);
// use only the first 64 bits of the digest for the hash
out.flush();
byte hasharray[] = md.digest();
for (int i = 0; i < Math.min(8, hasharray.length); i++) {
hash += ((long) (hasharray[i] & 0xFF)) << (i * 8);
}
} catch (IOException ignore) {
/* can't happen, but be deterministic anyway. */
hash = -1;
} catch (NoSuchAlgorithmException complain) {
throw new SecurityException(complain.getMessage());
}
return hash;
}
/**
* Return a string consisting of the given method's name followed by
* its "method descriptor", as appropriate for use in the computation
* of the "method hash".
*
* See section 4.3.3 of The Java Virtual Machine Specification for
* the definition of a "method descriptor".
*/
private static String getMethodNameAndDescriptor(Method m) {
StringBuffer desc = new StringBuffer(m.getName());
desc.append('(');
Class<?>[] paramTypes = m.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
desc.append(getTypeDescriptor(paramTypes[i]));
}
desc.append(')');
Class<?> returnType = m.getReturnType();
if (returnType == void.class) { // optimization: handle void here
desc.append('V');
} else {
desc.append(getTypeDescriptor(returnType));
}
return desc.toString();
}
/**
* Get the descriptor of a particular type, as appropriate for either
* a parameter or return type in a method descriptor.
*/
private static String getTypeDescriptor(Class<?> type) {
if (type.isPrimitive()) {
if (type == int.class) {
return "I";
} else if (type == boolean.class) {
return "Z";
} else if (type == byte.class) {
return "B";
} else if (type == char.class) {
return "C";
} else if (type == short.class) {
return "S";
} else if (type == long.class) {
return "J";
} else if (type == float.class) {
return "F";
} else if (type == double.class) {
return "D";
} else if (type == void.class) {
return "V";
} else {
throw new Error("unrecognized primitive type: " + type);
}
} else if (type.isArray()) {
/*
* According to JLS 20.3.2, the getName() method on Class does
* return the VM type descriptor format for array classes (only);
* using that should be quicker than the otherwise obvious code:
*
* return "[" + getTypeDescriptor(type.getComponentType());
*/
return type.getName().replace('.', '/');
} else {
return "L" + type.getName().replace('.', '/') + ";";
}
}
/**
* Returns the binary name of the given type without package
* qualification. Nested types are treated no differently from
* top-level types, so for a nested type, the returned name will
* still be qualified with the simple name of its enclosing
* top-level type (and perhaps other enclosing types), the
* separator will be '$', etc.
**/
public static String getUnqualifiedName(Class<?> c) {
String binaryName = c.getName();
return binaryName.substring(binaryName.lastIndexOf('.') + 1);
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.server;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Abstract class that maps Class objects to lazily-computed values of
* type V. A concrete subclass must implement the computeValue method
* to determine how the values are computed.
*
* The keys are only weakly reachable through this map, so this map
* does not prevent a class (along with its class loader, etc.) from
* being garbage collected if it is not otherwise strongly reachable.
* The values are only softly reachable through this map, so that the
* computed values generally persist while not otherwise strongly
* reachable, but their storage may be reclaimed if necessary. Also,
* note that if a key is strongly reachable from a value, then the key
* is effectively softly reachable through this map, which may delay
* garbage collection of classes (see 4429536).
**/
public abstract class WeakClassHashMap<V> {
private Map<Class<?>,ValueCell<V>> internalMap = new WeakHashMap<>();
protected WeakClassHashMap() { }
public V get(Class<?> remoteClass) {
/*
* Use a mutable cell (a one-element list) to hold the soft
* reference to a value, to allow the lazy value computation
* to be synchronized with entry-level granularity instead of
* by locking the whole table.
*/
ValueCell<V> valueCell;
synchronized (internalMap) {
valueCell = internalMap.get(remoteClass);
if (valueCell == null) {
valueCell = new ValueCell<V>();
internalMap.put(remoteClass, valueCell);
}
}
synchronized (valueCell) {
V value = null;
if (valueCell.ref != null) {
value = valueCell.ref.get();
}
if (value == null) {
value = computeValue(remoteClass);
valueCell.ref = new SoftReference<V>(value);
}
return value;
}
}
protected abstract V computeValue(Class<?> remoteClass);
private static class ValueCell<T> {
Reference<T> ref = null;
ValueCell() { }
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 1996, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.rmi.RemoteException;
public interface Channel {
/**
* Generates a new connection to the endpoint of the address space
* for which this is a channel.
*/
public Connection newConnection() throws RemoteException;
/**
* Returns the endpoint of the address space for which this is a
* channel.
*/
public Endpoint getEndpoint();
/**
* Free the connection generated by this channel.
* @param c The connection
* @param reuse If true, the connection is in a state in which it
* can be reused for another method call.
*/
public void free(Connection conn, boolean reuse) throws RemoteException;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.*;
public interface Connection {
/**
* Gets the input stream for this connection.
*/
public InputStream getInputStream() throws IOException;
/*
* Release the input stream for this connection.
*/
public void releaseInputStream() throws IOException;
/**
* Gets the output stream for this connection
*/
public OutputStream getOutputStream() throws IOException;
/*
* Release the output stream for this connection.
*/
public void releaseOutputStream() throws IOException;
/**
* Return true if channel can be used for multiple operations.
*/
public boolean isReusable();
/**
* Close connection.
*/
public void close() throws IOException;
/**
* Returns the channel for this connection.
*/
public Channel getChannel();
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.*;
import java.util.*;
import java.rmi.RemoteException;
import java.rmi.server.UID;
import sun.rmi.server.MarshalInputStream;
import sun.rmi.runtime.Log;
/**
* Special stream to keep track of refs being unmarshaled so that
* refs can be ref-counted locally.
*
* @author Ann Wollrath
*/
class ConnectionInputStream extends MarshalInputStream {
/** indicates whether ack is required for DGC */
private boolean dgcAckNeeded = false;
/** Hashtable mapping Endpoints to lists of LiveRefs to register */
private Map<Endpoint, List<LiveRef>> incomingRefTable = new HashMap<>(5);
/** identifier for gc ack*/
private UID ackID;
/**
* Constructs a marshal input stream using the underlying
* stream "in".
*/
ConnectionInputStream(InputStream in) throws IOException {
super(in);
}
void readID() throws IOException {
ackID = UID.read((DataInput) this);
}
/**
* Save reference in order to send "dirty" call after all args/returns
* have been unmarshaled. Save in hashtable incomingRefTable. This
* table is keyed on endpoints, and holds objects of type
* IncomingRefTableEntry.
*/
void saveRef(LiveRef ref) {
Endpoint ep = ref.getEndpoint();
// check whether endpoint is already in the hashtable
List<LiveRef> refList = incomingRefTable.get(ep);
if (refList == null) {
refList = new ArrayList<LiveRef>();
incomingRefTable.put(ep, refList);
}
// add ref to list of refs for endpoint ep
refList.add(ref);
}
/**
* Discard the saved incoming refs so there is nothing to register
* when {@code registerRefs} is called.
*/
void discardRefs() {
incomingRefTable.clear();
}
/**
* Add references to DGC table (and possibly send dirty call).
* RegisterRefs now calls DGCClient.referenced on all
* refs with the same endpoint at once to achieve batching of
* calls to the DGC
*/
void registerRefs() throws IOException {
if (!incomingRefTable.isEmpty()) {
for (Map.Entry<Endpoint, List<LiveRef>> entry :
incomingRefTable.entrySet()) {
DGCClient.registerRefs(entry.getKey(), entry.getValue());
}
}
}
/**
* Indicate that an ack is required to the distributed
* collector.
*/
void setAckNeeded() {
dgcAckNeeded = true;
}
/**
* Done with input stream for remote call. Send DGC ack if necessary.
* Allow sending of ack to fail without flagging an error.
*/
void done(Connection c) {
/*
* WARNING: The connection c may have already been freed. It
* is only be safe to use c to obtain c's channel.
*/
if (dgcAckNeeded) {
Connection conn = null;
Channel ch = null;
boolean reuse = true;
DGCImpl.dgcLog.log(Log.VERBOSE, "send ack");
try {
ch = c.getChannel();
conn = ch.newConnection();
DataOutputStream out =
new DataOutputStream(conn.getOutputStream());
out.writeByte(TransportConstants.DGCAck);
if (ackID == null) {
ackID = new UID();
}
ackID.write((DataOutput) out);
conn.releaseOutputStream();
/*
* Fix for 4221173: if this connection is on top of an
* HttpSendSocket, the DGCAck won't actually get sent until a
* read operation is attempted on the socket. Calling
* available() is the most innocuous way of triggering the
* write.
*/
conn.getInputStream().available();
conn.releaseInputStream();
} catch (RemoteException e) {
reuse = false;
} catch (IOException e) {
reuse = false;
}
try {
if (conn != null)
ch.free(conn, reuse);
} catch (RemoteException e){
// eat exception
}
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.IOException;
import java.rmi.server.UID;
import sun.rmi.server.MarshalOutputStream;
/**
* Special stream to keep track of refs being marshaled as return
* results to determine whether a special ack needs to be sent
* to the distributed collector.
*
* @author Ann Wollrath
*/
class ConnectionOutputStream extends MarshalOutputStream {
/** connection associated with ConnectionOutputStream */
private final Connection conn;
/** indicates whether output stream is used to marshal results */
private final boolean resultStream;
/** identifier for gc ack*/
private final UID ackID;
/** to store refs to returned remote object until DGC ack is received */
private DGCAckHandler dgcAckHandler = null;
/**
* Constructs an marshal output stream using the underlying
* stream associated with the connection, the parameter c.
* @param c is the Connection object associated with the
* ConnectionOutputStream object being constructed
* @param resultStream indicates whether this stream is used
* to marshal return results
*/
ConnectionOutputStream(Connection conn, boolean resultStream)
throws IOException
{
super(conn.getOutputStream());
this.conn = conn;
this.resultStream = resultStream;
ackID = resultStream ? new UID() : null;
}
void writeID() throws IOException {
assert resultStream;
ackID.write(this);
}
/**
* Returns true if this output stream is used to marshal return
* results; otherwise returns false.
*/
boolean isResultStream() {
return resultStream;
}
/**
* Saves a reference to the specified object in this stream's
* DGCAckHandler.
**/
void saveObject(Object obj) {
// should always be accessed from same thread
if (dgcAckHandler == null) {
dgcAckHandler = new DGCAckHandler(ackID);
}
dgcAckHandler.add(obj);
}
/**
* Returns this stream's DGCAckHandler, or null if it doesn't have
* one (saveObject was not invoked). This method should only be
* invoked after all objects have been written to the stream,
* because future objects written may yet cause a DGCAckHandler to
* be created (by invoking saveObject).
**/
DGCAckHandler getDGCAckHandler() {
return dgcAckHandler;
}
void done() {
if (dgcAckHandler != null) {
dgcAckHandler.startTimer();
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.rmi.server.UID;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import sun.rmi.runtime.RuntimeUtil;
import sun.security.action.GetLongAction;
/**
* Holds strong references to a set of remote objects, or live remote
* references to remote objects, after they have been marshalled (as
* remote references) as parts of the arguments or the result of a
* remote invocation. The purpose is to prevent remote objects or
* live remote references that might otherwise be determined to be
* unreachable in this VM from being locally garbage collected before
* the receiver has had an opportunity to register the unmarshalled
* remote references for DGC.
*
* The references are held strongly until an acknowledgment has been
* received that the receiver has had an opportunity to process the
* remote references or until a timeout has expired. For remote
* references sent as parts of the arguments of a remote invocation,
* the acknowledgment is the beginning of the response indicating
* completion of the remote invocation. For remote references sent as
* parts of the result of a remote invocation, a UID is included as
* part of the result, and the acknowledgment is a transport-level
* "DGCAck" message containing that UID.
*
* @author Ann Wollrath
* @author Peter Jones
**/
public class DGCAckHandler {
/** timeout for holding references without receiving an acknowledgment */
private static final long dgcAckTimeout = // default 5 minutes
AccessController.doPrivileged(
new GetLongAction("sun.rmi.dgc.ackTimeout", 300000));
/** thread pool for scheduling delayed tasks */
private static final ScheduledExecutorService scheduler =
AccessController.doPrivileged(
new RuntimeUtil.GetInstanceAction()).getScheduler();
/** table mapping ack ID to handler */
private static final Map<UID,DGCAckHandler> idTable =
Collections.synchronizedMap(new HashMap<UID,DGCAckHandler>());
private final UID id;
private List<Object> objList = new ArrayList<>(); // null if released
private Future<?> task = null;
/**
* Creates a new DGCAckHandler, associated with the specified UID
* if the argument is not null.
*
* References added to this DGCAckHandler will be held strongly
* until its "release" method is invoked or (after the
* "startTimer" method has been invoked) the timeout has expired.
* If the argument is not null, then invoking the static
* "received" method with the specified UID is equivalent to
* invoking this instance's "release" method.
**/
DGCAckHandler(UID id) {
this.id = id;
if (id != null) {
assert !idTable.containsKey(id);
idTable.put(id, this);
}
}
/**
* Adds the specified reference to this DGCAckHandler.
**/
synchronized void add(Object obj) {
if (objList != null) {
objList.add(obj);
}
}
/**
* Starts the timer for this DGCAckHandler. After the timeout has
* expired, the references are released even if the acknowledgment
* has not been received.
**/
synchronized void startTimer() {
if (objList != null && task == null) {
task = scheduler.schedule(new Runnable() {
public void run() {
if (id != null) {
idTable.remove(id);
}
release();
}
}, dgcAckTimeout, TimeUnit.MILLISECONDS);
}
}
/**
* Releases the references held by this DGCAckHandler.
**/
synchronized void release() {
if (task != null) {
task.cancel(false);
task = null;
}
objList = null;
}
/**
* Causes the DGCAckHandler associated with the specified UID to
* release its references.
**/
public static void received(UID id) {
DGCAckHandler h = idTable.remove(id);
if (h != null) {
h.release();
}
}
}

View File

@@ -0,0 +1,841 @@
/*
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.InvalidClassException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.net.SocketPermission;
import java.rmi.UnmarshalException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.rmi.ConnectException;
import java.rmi.RemoteException;
import java.rmi.dgc.DGC;
import java.rmi.dgc.Lease;
import java.rmi.dgc.VMID;
import java.rmi.server.ObjID;
import sun.misc.GC;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.NewThreadAction;
import sun.rmi.server.UnicastRef;
import sun.rmi.server.Util;
import sun.security.action.GetLongAction;
import java.security.AccessControlContext;
import java.security.Permissions;
import java.security.ProtectionDomain;
/**
* DGCClient implements the client-side of the RMI distributed garbage
* collection system.
*
* The external interface to DGCClient is the "registerRefs" method.
* When a LiveRef to a remote object enters the VM, it needs to be
* registered with the DGCClient to participate in distributed garbage
* collection.
*
* When the first LiveRef to a particular remote object is registered,
* a "dirty" call is made to the server-side distributed garbage
* collector for the remote object, which returns a lease guaranteeing
* that the server-side DGC will not collect the remote object for a
* certain period of time. While LiveRef instances to remote objects
* on a particular server exist, the DGCClient periodically sends more
* "dirty" calls to renew its lease.
*
* The DGCClient tracks the local reachability of registered LiveRef
* instances (using phantom references). When the LiveRef instance
* for a particular remote object becomes garbage collected locally,
* a "clean" call is made to the server-side distributed garbage
* collector, indicating that the server no longer needs to keep the
* remote object alive for this client.
*
* @see java.rmi.dgc.DGC, sun.rmi.transport.DGCImpl
*
* @author Ann Wollrath
* @author Peter Jones
*/
final class DGCClient {
/** next sequence number for DGC calls (access synchronized on class) */
private static long nextSequenceNum = Long.MIN_VALUE;
/** unique identifier for this VM as a client of DGC */
private static VMID vmid = new VMID();
/** lease duration to request (usually ignored by server) */
private static final long leaseValue = // default 10 minutes
AccessController.doPrivileged(
new GetLongAction("java.rmi.dgc.leaseValue",
600000)).longValue();
/** maximum interval between retries of failed clean calls */
private static final long cleanInterval = // default 3 minutes
AccessController.doPrivileged(
new GetLongAction("sun.rmi.dgc.cleanInterval",
180000)).longValue();
/** maximum interval between complete garbage collections of local heap */
private static final long gcInterval = // default 1 hour
AccessController.doPrivileged(
new GetLongAction("sun.rmi.dgc.client.gcInterval",
3600000)).longValue();
/** minimum retry count for dirty calls that fail */
private static final int dirtyFailureRetries = 5;
/** retry count for clean calls that fail with ConnectException */
private static final int cleanFailureRetries = 5;
/** constant empty ObjID array for lease renewal optimization */
private static final ObjID[] emptyObjIDArray = new ObjID[0];
/** ObjID for server-side DGC object */
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
/**
* An AccessControlContext with only socket permissions,
* suitable for an RMIClientSocketFactory.
*/
private static final AccessControlContext SOCKET_ACC;
static {
Permissions perms = new Permissions();
perms.add(new SocketPermission("*", "connect,resolve"));
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
SOCKET_ACC = new AccessControlContext(pd);
}
/*
* Disallow anyone from creating one of these.
*/
private DGCClient() {}
/**
* Register the LiveRef instances in the supplied list to participate
* in distributed garbage collection.
*
* All of the LiveRefs in the list must be for remote objects at the
* given endpoint.
*/
static void registerRefs(Endpoint ep, List<LiveRef> refs) {
/*
* Look up the given endpoint and register the refs with it.
* The retrieved entry may get removed from the global endpoint
* table before EndpointEntry.registerRefs() is able to acquire
* its lock; in this event, it returns false, and we loop and
* try again.
*/
EndpointEntry epEntry;
do {
epEntry = EndpointEntry.lookup(ep);
} while (!epEntry.registerRefs(refs));
}
/**
* Get the next sequence number to be used for a dirty or clean
* operation from this VM. This method should only be called while
* synchronized on the EndpointEntry whose data structures the
* operation affects.
*/
private static synchronized long getNextSequenceNum() {
return nextSequenceNum++;
}
/**
* Given the length of a lease and the time that it was granted,
* compute the absolute time at which it should be renewed, giving
* room for reasonable computational and communication delays.
*/
private static long computeRenewTime(long grantTime, long duration) {
/*
* REMIND: This algorithm should be more sophisticated, waiting
* a longer fraction of the lease duration for longer leases.
*/
return grantTime + (duration / 2);
}
/**
* EndpointEntry encapsulates the client-side DGC information specific
* to a particular Endpoint. Of most significance is the table that
* maps LiveRef value to RefEntry objects and the renew/clean thread
* that handles asynchronous client-side DGC operations.
*/
private static class EndpointEntry {
/** the endpoint that this entry is for */
private Endpoint endpoint;
/** synthesized reference to the remote server-side DGC */
private DGC dgc;
/** table of refs held for endpoint: maps LiveRef to RefEntry */
private Map<LiveRef, RefEntry> refTable = new HashMap<>(5);
/** set of RefEntry instances from last (failed) dirty call */
private Set<RefEntry> invalidRefs = new HashSet<>(5);
/** true if this entry has been removed from the global table */
private boolean removed = false;
/** absolute time to renew current lease to this endpoint */
private long renewTime = Long.MAX_VALUE;
/** absolute time current lease to this endpoint will expire */
private long expirationTime = Long.MIN_VALUE;
/** count of recent dirty calls that have failed */
private int dirtyFailures = 0;
/** absolute time of first recent failed dirty call */
private long dirtyFailureStartTime;
/** (average) elapsed time for recent failed dirty calls */
private long dirtyFailureDuration;
/** renew/clean thread for handling lease renewals and clean calls */
private Thread renewCleanThread;
/** true if renew/clean thread may be interrupted */
private boolean interruptible = false;
/** reference queue for phantom references */
private ReferenceQueue<LiveRef> refQueue = new ReferenceQueue<>();
/** set of clean calls that need to be made */
private Set<CleanRequest> pendingCleans = new HashSet<>(5);
/** global endpoint table: maps Endpoint to EndpointEntry */
private static Map<Endpoint,EndpointEntry> endpointTable = new HashMap<>(5);
/** handle for GC latency request (for future cancellation) */
private static GC.LatencyRequest gcLatencyRequest = null;
/**
* Look up the EndpointEntry for the given Endpoint. An entry is
* created if one does not already exist.
*/
public static EndpointEntry lookup(Endpoint ep) {
synchronized (endpointTable) {
EndpointEntry entry = endpointTable.get(ep);
if (entry == null) {
entry = new EndpointEntry(ep);
endpointTable.put(ep, entry);
/*
* While we are tracking live remote references registered
* in this VM, request a maximum latency for inspecting the
* entire heap from the local garbage collector, to place
* an upper bound on the time to discover remote references
* that have become unreachable (see bugid 4171278).
*/
if (gcLatencyRequest == null) {
gcLatencyRequest = GC.requestLatency(gcInterval);
}
}
return entry;
}
}
private EndpointEntry(final Endpoint endpoint) {
this.endpoint = endpoint;
try {
LiveRef dgcRef = new LiveRef(dgcID, endpoint, false);
dgc = (DGC) Util.createProxy(DGCImpl.class,
new UnicastRef(dgcRef), true);
} catch (RemoteException e) {
throw new Error("internal error creating DGC stub");
}
renewCleanThread = AccessController.doPrivileged(
new NewThreadAction(new RenewCleanThread(),
"RenewClean-" + endpoint, true));
renewCleanThread.start();
}
/**
* Register the LiveRef instances in the supplied list to participate
* in distributed garbage collection.
*
* This method returns false if this entry was removed from the
* global endpoint table (because it was empty) before these refs
* could be registered. In that case, a new EndpointEntry needs
* to be looked up.
*
* This method must NOT be called while synchronized on this entry.
*/
public boolean registerRefs(List<LiveRef> refs) {
assert !Thread.holdsLock(this);
Set<RefEntry> refsToDirty = null; // entries for refs needing dirty
long sequenceNum; // sequence number for dirty call
synchronized (this) {
if (removed) {
return false;
}
Iterator<LiveRef> iter = refs.iterator();
while (iter.hasNext()) {
LiveRef ref = iter.next();
assert ref.getEndpoint().equals(endpoint);
RefEntry refEntry = refTable.get(ref);
if (refEntry == null) {
LiveRef refClone = (LiveRef) ref.clone();
refEntry = new RefEntry(refClone);
refTable.put(refClone, refEntry);
if (refsToDirty == null) {
refsToDirty = new HashSet<>(5);
}
refsToDirty.add(refEntry);
}
refEntry.addInstanceToRefSet(ref);
}
if (refsToDirty == null) {
return true;
}
refsToDirty.addAll(invalidRefs);
invalidRefs.clear();
sequenceNum = getNextSequenceNum();
}
makeDirtyCall(refsToDirty, sequenceNum);
return true;
}
/**
* Remove the given RefEntry from the ref table. If that makes
* the ref table empty, remove this entry from the global endpoint
* table.
*
* This method must ONLY be called while synchronized on this entry.
*/
private void removeRefEntry(RefEntry refEntry) {
assert Thread.holdsLock(this);
assert !removed;
assert refTable.containsKey(refEntry.getRef());
refTable.remove(refEntry.getRef());
invalidRefs.remove(refEntry);
if (refTable.isEmpty()) {
synchronized (endpointTable) {
endpointTable.remove(endpoint);
Transport transport = endpoint.getOutboundTransport();
transport.free(endpoint);
/*
* If there are no longer any live remote references
* registered, we are no longer concerned with the
* latency of local garbage collection here.
*/
if (endpointTable.isEmpty()) {
assert gcLatencyRequest != null;
gcLatencyRequest.cancel();
gcLatencyRequest = null;
}
removed = true;
}
}
}
/**
* Make a DGC dirty call to this entry's endpoint, for the ObjIDs
* corresponding to the given set of refs and with the given
* sequence number.
*
* This method must NOT be called while synchronized on this entry.
*/
private void makeDirtyCall(Set<RefEntry> refEntries, long sequenceNum) {
assert !Thread.holdsLock(this);
ObjID[] ids;
if (refEntries != null) {
ids = createObjIDArray(refEntries);
} else {
ids = emptyObjIDArray;
}
long startTime = System.currentTimeMillis();
try {
Lease lease =
dgc.dirty(ids, sequenceNum, new Lease(vmid, leaseValue));
long duration = lease.getValue();
long newRenewTime = computeRenewTime(startTime, duration);
long newExpirationTime = startTime + duration;
synchronized (this) {
dirtyFailures = 0;
setRenewTime(newRenewTime);
expirationTime = newExpirationTime;
}
} catch (Exception e) {
long endTime = System.currentTimeMillis();
synchronized (this) {
dirtyFailures++;
if (e instanceof UnmarshalException
&& e.getCause() instanceof InvalidClassException) {
DGCImpl.dgcLog.log(Log.BRIEF, "InvalidClassException exception in DGC dirty call", e);
return; // protocol error, do not register these refs
}
if (dirtyFailures == 1) {
/*
* If this was the first recent failed dirty call,
* reschedule another one immediately, in case there
* was just a transient network problem, and remember
* the start time and duration of this attempt for
* future calculations of the delays between retries.
*/
dirtyFailureStartTime = startTime;
dirtyFailureDuration = endTime - startTime;
setRenewTime(endTime);
} else {
/*
* For each successive failed dirty call, wait for a
* (binary) exponentially increasing delay before
* retrying, to avoid network congestion.
*/
int n = dirtyFailures - 2;
if (n == 0) {
/*
* Calculate the initial retry delay from the
* average time elapsed for each of the first
* two failed dirty calls. The result must be
* at least 1000ms, to prevent a tight loop.
*/
dirtyFailureDuration =
Math.max((dirtyFailureDuration +
(endTime - startTime)) >> 1, 1000);
}
long newRenewTime =
endTime + (dirtyFailureDuration << n);
/*
* Continue if the last known held lease has not
* expired, or else at least a fixed number of times,
* or at least until we've tried for a fixed amount
* of time (the default lease value we request).
*/
if (newRenewTime < expirationTime ||
dirtyFailures < dirtyFailureRetries ||
newRenewTime < dirtyFailureStartTime + leaseValue)
{
setRenewTime(newRenewTime);
} else {
/*
* Give up: postpone lease renewals until next
* ref is registered for this endpoint.
*/
setRenewTime(Long.MAX_VALUE);
}
}
if (refEntries != null) {
/*
* Add all of these refs to the set of refs for this
* endpoint that may be invalid (this VM may not be in
* the server's referenced set), so that we will
* attempt to explicitly dirty them again in the
* future.
*/
invalidRefs.addAll(refEntries);
/*
* Record that a dirty call has failed for all of these
* refs, so that clean calls for them in the future
* will be strong.
*/
Iterator<RefEntry> iter = refEntries.iterator();
while (iter.hasNext()) {
RefEntry refEntry = iter.next();
refEntry.markDirtyFailed();
}
}
/*
* If the last known held lease will have expired before
* the next renewal, all refs might be invalid.
*/
if (renewTime >= expirationTime) {
invalidRefs.addAll(refTable.values());
}
}
}
}
/**
* Set the absolute time at which the lease for this entry should
* be renewed.
*
* This method must ONLY be called while synchronized on this entry.
*/
private void setRenewTime(long newRenewTime) {
assert Thread.holdsLock(this);
if (newRenewTime < renewTime) {
renewTime = newRenewTime;
if (interruptible) {
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run() {
renewCleanThread.interrupt();
return null;
}
});
}
} else {
renewTime = newRenewTime;
}
}
/**
* RenewCleanThread handles the asynchronous client-side DGC activity
* for this entry: renewing the leases and making clean calls.
*/
private class RenewCleanThread implements Runnable {
public void run() {
do {
long timeToWait;
RefEntry.PhantomLiveRef phantom = null;
boolean needRenewal = false;
Set<RefEntry> refsToDirty = null;
long sequenceNum = Long.MIN_VALUE;
synchronized (EndpointEntry.this) {
/*
* Calculate time to block (waiting for phantom
* reference notifications). It is the time until the
* lease renewal should be done, bounded on the low
* end by 1 ms so that the reference queue will always
* get processed, and if there are pending clean
* requests (remaining because some clean calls
* failed), bounded on the high end by the maximum
* clean call retry interval.
*/
long timeUntilRenew =
renewTime - System.currentTimeMillis();
timeToWait = Math.max(timeUntilRenew, 1);
if (!pendingCleans.isEmpty()) {
timeToWait = Math.min(timeToWait, cleanInterval);
}
/*
* Set flag indicating that it is OK to interrupt this
* thread now, such as if a earlier lease renewal time
* is set, because we are only going to be blocking
* and can deal with interrupts.
*/
interruptible = true;
}
try {
/*
* Wait for the duration calculated above for any of
* our phantom references to be enqueued.
*/
phantom = (RefEntry.PhantomLiveRef)
refQueue.remove(timeToWait);
} catch (InterruptedException e) {
}
synchronized (EndpointEntry.this) {
/*
* Set flag indicating that it is NOT OK to interrupt
* this thread now, because we may be undertaking I/O
* operations that should not be interrupted (and we
* will not be blocking arbitrarily).
*/
interruptible = false;
Thread.interrupted(); // clear interrupted state
/*
* If there was a phantom reference enqueued, process
* it and all the rest on the queue, generating
* clean requests as necessary.
*/
if (phantom != null) {
processPhantomRefs(phantom);
}
/*
* Check if it is time to renew this entry's lease.
*/
long currentTime = System.currentTimeMillis();
if (currentTime > renewTime) {
needRenewal = true;
if (!invalidRefs.isEmpty()) {
refsToDirty = invalidRefs;
invalidRefs = new HashSet<>(5);
}
sequenceNum = getNextSequenceNum();
}
}
boolean needRenewal_ = needRenewal;
Set<RefEntry> refsToDirty_ = refsToDirty;
long sequenceNum_ = sequenceNum;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (needRenewal_) {
makeDirtyCall(refsToDirty_, sequenceNum_);
}
if (!pendingCleans.isEmpty()) {
makeCleanCalls();
}
return null;
}}, SOCKET_ACC);
} while (!removed || !pendingCleans.isEmpty());
}
}
/**
* Process the notification of the given phantom reference and any
* others that are on this entry's reference queue. Each phantom
* reference is removed from its RefEntry's ref set. All ref
* entries that have no more registered instances are collected
* into up to two batched clean call requests: one for refs
* requiring a "strong" clean call, and one for the rest.
*
* This method must ONLY be called while synchronized on this entry.
*/
private void processPhantomRefs(RefEntry.PhantomLiveRef phantom) {
assert Thread.holdsLock(this);
Set<RefEntry> strongCleans = null;
Set<RefEntry> normalCleans = null;
do {
RefEntry refEntry = phantom.getRefEntry();
refEntry.removeInstanceFromRefSet(phantom);
if (refEntry.isRefSetEmpty()) {
if (refEntry.hasDirtyFailed()) {
if (strongCleans == null) {
strongCleans = new HashSet<>(5);
}
strongCleans.add(refEntry);
} else {
if (normalCleans == null) {
normalCleans = new HashSet<>(5);
}
normalCleans.add(refEntry);
}
removeRefEntry(refEntry);
}
} while ((phantom =
(RefEntry.PhantomLiveRef) refQueue.poll()) != null);
if (strongCleans != null) {
pendingCleans.add(
new CleanRequest(createObjIDArray(strongCleans),
getNextSequenceNum(), true));
}
if (normalCleans != null) {
pendingCleans.add(
new CleanRequest(createObjIDArray(normalCleans),
getNextSequenceNum(), false));
}
}
/**
* CleanRequest holds the data for the parameters of a clean call
* that needs to be made.
*/
private static class CleanRequest {
final ObjID[] objIDs;
final long sequenceNum;
final boolean strong;
/** how many times this request has failed */
int failures = 0;
CleanRequest(ObjID[] objIDs, long sequenceNum, boolean strong) {
this.objIDs = objIDs;
this.sequenceNum = sequenceNum;
this.strong = strong;
}
}
/**
* Make all of the clean calls described by the clean requests in
* this entry's set of "pending cleans". Clean requests for clean
* calls that succeed are removed from the "pending cleans" set.
*
* This method must NOT be called while synchronized on this entry.
*/
private void makeCleanCalls() {
assert !Thread.holdsLock(this);
Iterator<CleanRequest> iter = pendingCleans.iterator();
while (iter.hasNext()) {
CleanRequest request = iter.next();
try {
dgc.clean(request.objIDs, request.sequenceNum, vmid,
request.strong);
iter.remove();
} catch (Exception e) {
/*
* Many types of exceptions here could have been
* caused by a transient failure, so try again a
* few times, but not forever.
*/
if (++request.failures >= cleanFailureRetries) {
iter.remove();
}
}
}
}
/**
* Create an array of ObjIDs (needed for the DGC remote calls)
* from the ids in the given set of refs.
*/
private static ObjID[] createObjIDArray(Set<RefEntry> refEntries) {
ObjID[] ids = new ObjID[refEntries.size()];
Iterator<RefEntry> iter = refEntries.iterator();
for (int i = 0; i < ids.length; i++) {
ids[i] = iter.next().getRef().getObjID();
}
return ids;
}
/**
* RefEntry encapsulates the client-side DGC information specific
* to a particular LiveRef value. In particular, it contains a
* set of phantom references to all of the instances of the LiveRef
* value registered in the system (but not garbage collected
* locally).
*/
private class RefEntry {
/** LiveRef value for this entry (not a registered instance) */
private LiveRef ref;
/** set of phantom references to registered instances */
private Set<PhantomLiveRef> refSet = new HashSet<>(5);
/** true if a dirty call containing this ref has failed */
private boolean dirtyFailed = false;
public RefEntry(LiveRef ref) {
this.ref = ref;
}
/**
* Return the LiveRef value for this entry (not a registered
* instance).
*/
public LiveRef getRef() {
return ref;
}
/**
* Add a LiveRef to the set of registered instances for this entry.
*
* This method must ONLY be invoked while synchronized on this
* RefEntry's EndpointEntry.
*/
public void addInstanceToRefSet(LiveRef ref) {
assert Thread.holdsLock(EndpointEntry.this);
assert ref.equals(this.ref);
/*
* Only keep a phantom reference to the registered instance,
* so that it can be garbage collected normally (and we can be
* notified when that happens).
*/
refSet.add(new PhantomLiveRef(ref));
}
/**
* Remove a PhantomLiveRef from the set of registered instances.
*
* This method must ONLY be invoked while synchronized on this
* RefEntry's EndpointEntry.
*/
public void removeInstanceFromRefSet(PhantomLiveRef phantom) {
assert Thread.holdsLock(EndpointEntry.this);
assert refSet.contains(phantom);
refSet.remove(phantom);
}
/**
* Return true if there are no registered LiveRef instances for
* this entry still reachable in this VM.
*
* This method must ONLY be invoked while synchronized on this
* RefEntry's EndpointEntry.
*/
public boolean isRefSetEmpty() {
assert Thread.holdsLock(EndpointEntry.this);
return refSet.size() == 0;
}
/**
* Record that a dirty call that explicitly contained this
* entry's ref has failed.
*
* This method must ONLY be invoked while synchronized on this
* RefEntry's EndpointEntry.
*/
public void markDirtyFailed() {
assert Thread.holdsLock(EndpointEntry.this);
dirtyFailed = true;
}
/**
* Return true if a dirty call that explicitly contained this
* entry's ref has failed (and therefore a clean call for this
* ref needs to be marked "strong").
*
* This method must ONLY be invoked while synchronized on this
* RefEntry's EndpointEntry.
*/
public boolean hasDirtyFailed() {
assert Thread.holdsLock(EndpointEntry.this);
return dirtyFailed;
}
/**
* PhantomLiveRef is a PhantomReference to a LiveRef instance,
* used to detect when the LiveRef becomes permanently
* unreachable in this VM.
*/
private class PhantomLiveRef extends PhantomReference<LiveRef> {
public PhantomLiveRef(LiveRef ref) {
super(ref, EndpointEntry.this.refQueue);
}
public RefEntry getRefEntry() {
return RefEntry.this;
}
}
}
}
}

View File

@@ -0,0 +1,448 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.net.SocketPermission;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.dgc.DGC;
import java.rmi.dgc.Lease;
import java.rmi.dgc.VMID;
import java.rmi.server.LogStream;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UID;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import sun.misc.ObjectInputFilter;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.RuntimeUtil;
import sun.rmi.server.UnicastRef;
import sun.rmi.server.UnicastServerRef;
import sun.rmi.server.Util;
import sun.security.action.GetLongAction;
import sun.security.action.GetPropertyAction;
/**
* This class implements the guts of the server-side distributed GC
* algorithm
*
* @author Ann Wollrath
*/
@SuppressWarnings("deprecation")
final class DGCImpl implements DGC {
/* dgc system log */
static final Log dgcLog = Log.getLog("sun.rmi.dgc", "dgc",
LogStream.parseLevel(AccessController.doPrivileged(
new GetPropertyAction("sun.rmi.dgc.logLevel"))));
/** lease duration to grant to clients */
private static final long leaseValue = // default 10 minutes
AccessController.doPrivileged(
new GetLongAction("java.rmi.dgc.leaseValue", 600000));
/** lease check interval; default is half of lease grant duration */
private static final long leaseCheckInterval =
AccessController.doPrivileged(
new GetLongAction("sun.rmi.dgc.checkInterval", leaseValue / 2));
/** thread pool for scheduling delayed tasks */
private static final ScheduledExecutorService scheduler =
AccessController.doPrivileged(
new RuntimeUtil.GetInstanceAction()).getScheduler();
/** remote implementation of DGC interface for this VM */
private static DGCImpl dgc;
/** table that maps VMID to LeaseInfo */
private Map<VMID,LeaseInfo> leaseTable = new HashMap<>();
/** checks for lease expiration */
private Future<?> checker = null;
/**
* Return the remote implementation of the DGC interface for
* this VM.
*/
static DGCImpl getDGCImpl() {
return dgc;
}
/**
* Property name of the DGC serial filter to augment
* the built-in list of allowed types.
* Setting the property in the {@code lib/security/java.security} file
* will enable the augmented filter.
*/
private static final String DGC_FILTER_PROPNAME = "sun.rmi.transport.dgcFilter";
/** Registry max depth of remote invocations. **/
private static int DGC_MAX_DEPTH = 5;
/** Registry maximum array size in remote invocations. **/
private static int DGC_MAX_ARRAY_SIZE = 10000;
/**
* The dgcFilter created from the value of the {@code "sun.rmi.transport.dgcFilter"}
* property.
*/
private static final ObjectInputFilter dgcFilter =
AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)DGCImpl::initDgcFilter);
/**
* Initialize the dgcFilter from the security properties or system property; if any
* @return an ObjectInputFilter, or null
*/
private static ObjectInputFilter initDgcFilter() {
ObjectInputFilter filter = null;
String props = System.getProperty(DGC_FILTER_PROPNAME);
if (props == null) {
props = Security.getProperty(DGC_FILTER_PROPNAME);
}
if (props != null) {
filter = ObjectInputFilter.Config.createFilter(props);
if (dgcLog.isLoggable(Log.BRIEF)) {
dgcLog.log(Log.BRIEF, "dgcFilter = " + filter);
}
}
return filter;
}
/**
* Construct a new server-side remote object collector at
* a particular port. Disallow construction from outside.
*/
private DGCImpl() {}
/**
* The dirty call adds the VMID "vmid" to the set of clients
* that hold references to the object associated with the ObjID
* id. The long "sequenceNum" is used to detect late dirty calls. If
* the VMID "vmid" is null, a VMID will be generated on the
* server (for use by the client in subsequent calls) and
* returned.
*
* The client must call the "dirty" method to renew the lease
* before the "lease" time expires or all references to remote
* objects in this VM that the client holds are considered
* "unreferenced".
*/
public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) {
VMID vmid = lease.getVMID();
/*
* The server specifies the lease value; the client has
* no say in the matter.
*/
long duration = leaseValue;
if (dgcLog.isLoggable(Log.VERBOSE)) {
dgcLog.log(Log.VERBOSE, "vmid = " + vmid);
}
// create a VMID if one wasn't supplied
if (vmid == null) {
vmid = new VMID();
if (dgcLog.isLoggable(Log.BRIEF)) {
String clientHost;
try {
clientHost = RemoteServer.getClientHost();
} catch (ServerNotActiveException e) {
clientHost = "<unknown host>";
}
dgcLog.log(Log.BRIEF, " assigning vmid " + vmid +
" to client " + clientHost);
}
}
lease = new Lease(vmid, duration);
// record lease information
synchronized (leaseTable) {
LeaseInfo info = leaseTable.get(vmid);
if (info == null) {
leaseTable.put(vmid, new LeaseInfo(vmid, duration));
if (checker == null) {
checker = scheduler.scheduleWithFixedDelay(
new Runnable() {
public void run() {
checkLeases();
}
},
leaseCheckInterval,
leaseCheckInterval, TimeUnit.MILLISECONDS);
}
} else {
info.renew(duration);
}
}
for (ObjID id : ids) {
if (dgcLog.isLoggable(Log.VERBOSE)) {
dgcLog.log(Log.VERBOSE, "id = " + id +
", vmid = " + vmid + ", duration = " + duration);
}
ObjectTable.referenced(id, sequenceNum, vmid);
}
// return the VMID used
return lease;
}
/**
* The clean call removes the VMID from the set of clients
* that hold references to the object associated with the LiveRef
* ref. The sequence number is used to detect late clean calls. If the
* argument "strong" is true, then the clean call is a result of a
* failed "dirty" call, thus the sequence number for the VMID needs
* to be remembered until the client goes away.
*/
public void clean(ObjID[] ids, long sequenceNum, VMID vmid, boolean strong)
{
for (ObjID id : ids) {
if (dgcLog.isLoggable(Log.VERBOSE)) {
dgcLog.log(Log.VERBOSE, "id = " + id +
", vmid = " + vmid + ", strong = " + strong);
}
ObjectTable.unreferenced(id, sequenceNum, vmid, strong);
}
}
/**
* Register interest in receiving a callback when this VMID
* becomes inaccessible.
*/
void registerTarget(VMID vmid, Target target) {
synchronized (leaseTable) {
LeaseInfo info = leaseTable.get(vmid);
if (info == null) {
target.vmidDead(vmid);
} else {
info.notifySet.add(target);
}
}
}
/**
* Remove notification request.
*/
void unregisterTarget(VMID vmid, Target target) {
synchronized (leaseTable) {
LeaseInfo info = leaseTable.get(vmid);
if (info != null) {
info.notifySet.remove(target);
}
}
}
/**
* Check if leases have expired. If a lease has expired, remove
* it from the table and notify all interested parties that the
* VMID is essentially "dead".
*
* @return if true, there are leases outstanding; otherwise leases
* no longer need to be checked
*/
private void checkLeases() {
long time = System.currentTimeMillis();
/* List of vmids that need to be removed from the leaseTable */
List<LeaseInfo> toUnregister = new ArrayList<>();
/* Build a list of leaseInfo objects that need to have
* targets removed from their notifySet. Remove expired
* leases from leaseTable.
*/
synchronized (leaseTable) {
Iterator<LeaseInfo> iter = leaseTable.values().iterator();
while (iter.hasNext()) {
LeaseInfo info = iter.next();
if (info.expired(time)) {
toUnregister.add(info);
iter.remove();
}
}
if (leaseTable.isEmpty()) {
checker.cancel(false);
checker = null;
}
}
/* Notify and unegister targets without holding the lock on
* the leaseTable so we avoid deadlock.
*/
for (LeaseInfo info : toUnregister) {
for (Target target : info.notifySet) {
target.vmidDead(info.vmid);
}
}
}
static {
/*
* "Export" the singleton DGCImpl in a context isolated from
* the arbitrary current thread context.
*/
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ClassLoader savedCcl =
Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(
ClassLoader.getSystemClassLoader());
/*
* Put remote collector object in table by hand to prevent
* listen on port. (UnicastServerRef.exportObject would
* cause transport to listen.)
*/
try {
dgc = new DGCImpl();
ObjID dgcID = new ObjID(ObjID.DGC_ID);
LiveRef ref = new LiveRef(dgcID, 0);
UnicastServerRef disp = new UnicastServerRef(ref,
DGCImpl::checkInput);
Remote stub =
Util.createProxy(DGCImpl.class,
new UnicastRef(ref), true);
disp.setSkeleton(dgc);
Permissions perms = new Permissions();
perms.add(new SocketPermission("*", "accept,resolve"));
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
AccessControlContext acceptAcc = new AccessControlContext(pd);
Target target = AccessController.doPrivileged(
new PrivilegedAction<Target>() {
public Target run() {
return new Target(dgc, disp, stub, dgcID, true);
}
}, acceptAcc);
ObjectTable.putTarget(target);
} catch (RemoteException e) {
throw new Error(
"exception initializing server-side DGC", e);
}
} finally {
Thread.currentThread().setContextClassLoader(savedCcl);
}
return null;
}
});
}
/**
* ObjectInputFilter to filter DGC input objects.
* The list of acceptable classes is very short and explicit.
* The depth and array sizes are limited.
*
* @param filterInfo access to class, arrayLength, etc.
* @return {@link ObjectInputFilter.Status#ALLOWED} if allowed,
* {@link ObjectInputFilter.Status#REJECTED} if rejected,
* otherwise {@link ObjectInputFilter.Status#UNDECIDED}
*/
private static ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) {
if (dgcFilter != null) {
ObjectInputFilter.Status status = dgcFilter.checkInput(filterInfo);
if (status != ObjectInputFilter.Status.UNDECIDED) {
// The DGC filter can override the built-in white-list
return status;
}
}
if (filterInfo.depth() > DGC_MAX_DEPTH) {
return ObjectInputFilter.Status.REJECTED;
}
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
while (clazz.isArray()) {
if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > DGC_MAX_ARRAY_SIZE) {
return ObjectInputFilter.Status.REJECTED;
}
// Arrays are allowed depending on the component type
clazz = clazz.getComponentType();
}
if (clazz.isPrimitive()) {
// Arrays of primitives are allowed
return ObjectInputFilter.Status.ALLOWED;
}
return (clazz == ObjID.class ||
clazz == UID.class ||
clazz == VMID.class ||
clazz == Lease.class)
? ObjectInputFilter.Status.ALLOWED
: ObjectInputFilter.Status.REJECTED;
}
// Not a class, not size limited
return ObjectInputFilter.Status.UNDECIDED;
}
private static class LeaseInfo {
VMID vmid;
long expiration;
Set<Target> notifySet = new HashSet<>();
LeaseInfo(VMID vmid, long lease) {
this.vmid = vmid;
expiration = System.currentTimeMillis() + lease;
}
synchronized void renew(long lease) {
long newExpiration = System.currentTimeMillis() + lease;
if (newExpiration > expiration)
expiration = newExpiration;
}
boolean expired(long time) {
if (expiration < time) {
if (dgcLog.isLoggable(Log.BRIEF)) {
dgcLog.log(Log.BRIEF, vmid.toString());
}
return true;
} else {
return false;
}
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.IOException;
/**
* Skeleton to dispatch DGC methods.
* Originally generated by RMIC but frozen to match the stubs.
*/
@SuppressWarnings({"deprecation", "serial"})
public final class DGCImpl_Skel
implements java.rmi.server.Skeleton {
private static final java.rmi.server.Operation[] operations = {
new java.rmi.server.Operation("void clean(java.rmi.server.ObjID[], long, java.rmi.dgc.VMID, boolean)"),
new java.rmi.server.Operation("java.rmi.dgc.Lease dirty(java.rmi.server.ObjID[], long, java.rmi.dgc.Lease)")
};
private static final long interfaceHash = -669196253586618813L;
public java.rmi.server.Operation[] getOperations() {
return operations.clone();
}
public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall remoteCall, int opnum, long hash)
throws java.lang.Exception {
if (hash != interfaceHash)
throw new java.rmi.server.SkeletonMismatchException("interface hash mismatch");
sun.rmi.transport.DGCImpl server = (sun.rmi.transport.DGCImpl) obj;
StreamRemoteCall call = (StreamRemoteCall) remoteCall;
switch (opnum) {
case 0: // clean(ObjID[], long, VMID, boolean)
{
java.rmi.server.ObjID[] $param_arrayOf_ObjID_1;
long $param_long_2;
java.rmi.dgc.VMID $param_VMID_3;
boolean $param_boolean_4;
try {
java.io.ObjectInput in = call.getInputStream();
$param_arrayOf_ObjID_1 = (java.rmi.server.ObjID[]) in.readObject();
$param_long_2 = in.readLong();
$param_VMID_3 = (java.rmi.dgc.VMID) in.readObject();
$param_boolean_4 = in.readBoolean();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
server.clean($param_arrayOf_ObjID_1, $param_long_2, $param_VMID_3, $param_boolean_4);
try {
call.getResultStream(true);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
case 1: // dirty(ObjID[], long, Lease)
{
java.rmi.server.ObjID[] $param_arrayOf_ObjID_1;
long $param_long_2;
java.rmi.dgc.Lease $param_Lease_3;
try {
java.io.ObjectInput in = call.getInputStream();
$param_arrayOf_ObjID_1 = (java.rmi.server.ObjID[]) in.readObject();
$param_long_2 = in.readLong();
$param_Lease_3 = (java.rmi.dgc.Lease) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
java.rmi.dgc.Lease $result = server.dirty($param_arrayOf_ObjID_1, $param_long_2, $param_Lease_3);
try {
java.io.ObjectOutput out = call.getResultStream(true);
out.writeObject($result);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling return", e);
}
break;
}
default:
throw new java.rmi.UnmarshalException("invalid method number");
}
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import sun.rmi.transport.tcp.TCPConnection;
import java.io.IOException;
import java.rmi.dgc.Lease;
import java.rmi.dgc.VMID;
import java.rmi.server.UID;
import java.util.ArrayList;
import sun.misc.ObjectInputFilter;
/**
* Stubs to invoke DGC remote methods.
* Originally generated from RMIC but frozen to insert serialFilter.
*/
@SuppressWarnings({"deprecation", "serial"})
public final class DGCImpl_Stub
extends java.rmi.server.RemoteStub
implements java.rmi.dgc.DGC {
private static final java.rmi.server.Operation[] operations = {
new java.rmi.server.Operation("void clean(java.rmi.server.ObjID[], long, java.rmi.dgc.VMID, boolean)"),
new java.rmi.server.Operation("java.rmi.dgc.Lease dirty(java.rmi.server.ObjID[], long, java.rmi.dgc.Lease)")
};
private static final long interfaceHash = -669196253586618813L;
/** Registry max depth of remote invocations. **/
private static int DGCCLIENT_MAX_DEPTH = 6;
/** Registry maximum array size in remote invocations. **/
private static int DGCCLIENT_MAX_ARRAY_SIZE = 10000;
// constructors
public DGCImpl_Stub() {
super();
}
public DGCImpl_Stub(java.rmi.server.RemoteRef ref) {
super(ref);
}
// methods from remote interfaces
// implementation of clean(ObjID[], long, VMID, boolean)
public void clean(java.rmi.server.ObjID[] $param_arrayOf_ObjID_1, long $param_long_2, java.rmi.dgc.VMID $param_VMID_3, boolean $param_boolean_4)
throws java.rmi.RemoteException {
try {
StreamRemoteCall call = (StreamRemoteCall)ref.newCall((java.rmi.server.RemoteObject) this,
operations, 0, interfaceHash);
call.setObjectInputFilter(DGCImpl_Stub::leaseFilter);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_arrayOf_ObjID_1);
out.writeLong($param_long_2);
out.writeObject($param_VMID_3);
out.writeBoolean($param_boolean_4);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
ref.done(call);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
// implementation of dirty(ObjID[], long, Lease)
public java.rmi.dgc.Lease dirty(java.rmi.server.ObjID[] $param_arrayOf_ObjID_1, long $param_long_2, java.rmi.dgc.Lease $param_Lease_3)
throws java.rmi.RemoteException {
try {
StreamRemoteCall call =
(StreamRemoteCall)ref.newCall((java.rmi.server.RemoteObject) this,
operations, 1, interfaceHash);
call.setObjectInputFilter(DGCImpl_Stub::leaseFilter);
try {
java.io.ObjectOutput out = call.getOutputStream();
out.writeObject($param_arrayOf_ObjID_1);
out.writeLong($param_long_2);
out.writeObject($param_Lease_3);
} catch (java.io.IOException e) {
throw new java.rmi.MarshalException("error marshalling arguments", e);
}
ref.invoke(call);
java.rmi.dgc.Lease $result;
Connection connection = call.getConnection();
try {
java.io.ObjectInput in = call.getInputStream();
$result = (java.rmi.dgc.Lease) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
if (connection instanceof TCPConnection) {
// Modified to prevent re-use of the connection after an exception
((TCPConnection) connection).getChannel().free(connection, false);
}
call.discardPendingRefs();
throw new java.rmi.UnmarshalException("error unmarshalling return", e);
} finally {
ref.done(call);
}
return $result;
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.rmi.RemoteException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.rmi.UnexpectedException("undeclared checked exception", e);
}
}
/**
* ObjectInputFilter to filter DGCClient return value (a Lease).
* The list of acceptable classes is very short and explicit.
* The depth and array sizes are limited.
* <p>
* The filter must accept normal and exception returns.
* A DGC server may throw exceptions that may have a cause
* and suppressed exceptions.
*
* @param filterInfo access to class, arrayLength, etc.
* @return {@link ObjectInputFilter.Status#ALLOWED} if allowed,
* {@link ObjectInputFilter.Status#REJECTED} if rejected,
* otherwise {@link ObjectInputFilter.Status#UNDECIDED}
*/
private static ObjectInputFilter.Status leaseFilter(ObjectInputFilter.FilterInfo filterInfo) {
if (filterInfo.depth() > DGCCLIENT_MAX_DEPTH) {
return ObjectInputFilter.Status.REJECTED;
}
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
while (clazz.isArray()) {
if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > DGCCLIENT_MAX_ARRAY_SIZE) {
return ObjectInputFilter.Status.REJECTED;
}
// Arrays are allowed depending on the component type
clazz = clazz.getComponentType();
}
if (clazz.isPrimitive()) {
// Arrays of primitives are allowed
return ObjectInputFilter.Status.ALLOWED;
}
return (clazz == UID.class ||
clazz == VMID.class ||
clazz == Lease.class ||
(Throwable.class.isAssignableFrom(clazz) &&
clazz.getClassLoader() ==
Object.class.getClassLoader()) ||
clazz == StackTraceElement.class ||
clazz == ArrayList.class || // for suppressed exceptions, if any
clazz == Object.class ||
clazz.getName().equals("java.util.Collections$UnmodifiableList") ||
clazz.getName().equals("java.util.Collections$UnmodifiableCollection") ||
clazz.getName().equals("java.util.Collections$UnmodifiableRandomAccessList"))
? ObjectInputFilter.Status.ALLOWED
: ObjectInputFilter.Status.REJECTED;
}
// Not a class, not size limited
return ObjectInputFilter.Status.UNDECIDED;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.rmi.RemoteException;
import java.rmi.Remote;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteServer;
public interface Endpoint {
/**
* Return a channel that generates connections to the remote
* endpoint.
*/
Channel getChannel();
/**
* Export the object so that it can accept incoming calls at
* the endpoint.
*/
void exportObject(Target target)
throws RemoteException;
/**
* Returns the transport for incoming connections to this endpoint.
**/
Transport getInboundTransport();
/**
* Returns transport for making connections to remote endpoints.
**/
Transport getOutboundTransport();
}

View File

@@ -0,0 +1,317 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ObjID;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.Arrays;
import sun.rmi.transport.tcp.TCPEndpoint;
/**
* NOTE: There is a JDK-internal dependency on the existence of this
* class and its getClientSocketFactory method in the implementation
* of javax.management.remote.rmi.RMIConnector.
**/
public class LiveRef implements Cloneable {
/** wire representation for the object*/
private final Endpoint ep;
private final ObjID id;
/** cached connection service for the object */
private transient Channel ch;
/** flag to indicate whether this ref specifies a local server or
* is a ref for a remote object (surrogate)
*/
private final boolean isLocal;
/**
* Construct a "well-known" live reference to a remote object
* @param isLocalServer If true, indicates this ref specifies a local
* server in this address space; if false, the ref is for a remote
* object (hence a surrogate or proxy) in another address space.
*/
public LiveRef(ObjID objID, Endpoint endpoint, boolean isLocal) {
ep = endpoint;
id = objID;
this.isLocal = isLocal;
}
/**
* Construct a new live reference for a server object in the local
* address space.
*/
public LiveRef(int port) {
this((new ObjID()), port);
}
/**
* Construct a new live reference for a server object in the local
* address space, to use sockets of the specified type.
*/
public LiveRef(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
{
this((new ObjID()), port, csf, ssf);
}
/**
* Construct a new live reference for a "well-known" server object
* in the local address space.
*/
public LiveRef(ObjID objID, int port) {
this(objID, TCPEndpoint.getLocalEndpoint(port), true);
}
/**
* Construct a new live reference for a "well-known" server object
* in the local address space, to use sockets of the specified type.
*/
public LiveRef(ObjID objID, int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
{
this(objID, TCPEndpoint.getLocalEndpoint(port, csf, ssf), true);
}
/**
* Return a shallow copy of this ref.
*/
public Object clone() {
try {
LiveRef newRef = (LiveRef) super.clone();
return newRef;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString(), e);
}
}
/**
* Return the port number associated with this ref.
*/
public int getPort() {
return ((TCPEndpoint) ep).getPort();
}
/**
* Return the client socket factory associated with this ref.
*
* NOTE: There is a JDK-internal dependency on the existence of
* this method in the implementation of
* javax.management.remote.rmi.RMIConnector.
**/
public RMIClientSocketFactory getClientSocketFactory() {
return ((TCPEndpoint) ep).getClientSocketFactory();
}
/**
* Return the server socket factory associated with this ref.
*/
public RMIServerSocketFactory getServerSocketFactory() {
return ((TCPEndpoint) ep).getServerSocketFactory();
}
/**
* Export the object to accept incoming calls.
*/
public void exportObject(Target target) throws RemoteException {
ep.exportObject(target);
}
public Channel getChannel() throws RemoteException {
if (ch == null) {
ch = ep.getChannel();
}
return ch;
}
public ObjID getObjID() {
return id;
}
Endpoint getEndpoint() {
return ep;
}
public String toString() {
String type;
if (isLocal)
type = "local";
else
type = "remote";
return "[endpoint:" + ep + "(" + type + ")," +
"objID:" + id + "]";
}
public int hashCode() {
return id.hashCode();
}
public boolean equals(Object obj) {
if (obj != null && obj instanceof LiveRef) {
LiveRef ref = (LiveRef) obj;
return (ep.equals(ref.ep) && id.equals(ref.id) &&
isLocal == ref.isLocal);
} else {
return false;
}
}
public boolean remoteEquals(Object obj) {
if (obj != null && obj instanceof LiveRef) {
LiveRef ref = (LiveRef) obj;
TCPEndpoint thisEp = ((TCPEndpoint) ep);
TCPEndpoint refEp = ((TCPEndpoint) ref.ep);
RMIClientSocketFactory thisClientFactory =
thisEp.getClientSocketFactory();
RMIClientSocketFactory refClientFactory =
refEp.getClientSocketFactory();
/**
* Fix for 4254103: LiveRef.remoteEquals should not fail
* if one of the objects in the comparison has a null
* server socket. Comparison should only consider the
* following criteria:
*
* hosts, ports, client socket factories and object IDs.
*/
if (thisEp.getPort() != refEp.getPort() ||
!thisEp.getHost().equals(refEp.getHost()))
{
return false;
}
if ((thisClientFactory == null) ^ (refClientFactory == null)) {
return false;
}
if ((thisClientFactory != null) &&
!((thisClientFactory.getClass() ==
refClientFactory.getClass()) &&
(thisClientFactory.equals(refClientFactory))))
{
return false;
}
return (id.equals(ref.id));
} else {
return false;
}
}
public void write(ObjectOutput out, boolean useNewFormat)
throws IOException
{
boolean isResultStream = false;
if (out instanceof ConnectionOutputStream) {
ConnectionOutputStream stream = (ConnectionOutputStream) out;
isResultStream = stream.isResultStream();
/*
* Ensure that referential integrity is not broken while
* this LiveRef is in transit. If it is being marshalled
* as part of a result, it may not otherwise be strongly
* reachable after the remote call has completed; even if
* it is being marshalled as part of an argument, the VM
* may determine that the reference on the stack is no
* longer reachable after marshalling (see 6181943)--
* therefore, tell the stream to save a reference until a
* timeout expires or, for results, a DGCAck message has
* been received from the caller, or for arguments, the
* remote call has completed. For a "local" LiveRef, save
* a reference to the impl directly, because the impl is
* not reachable from the LiveRef (see 4114579);
* otherwise, save a reference to the LiveRef, for the
* client-side DGC to watch over. (Also see 4017232.)
*/
if (isLocal) {
ObjectEndpoint oe =
new ObjectEndpoint(id, ep.getInboundTransport());
Target target = ObjectTable.getTarget(oe);
if (target != null) {
Remote impl = target.getImpl();
if (impl != null) {
stream.saveObject(impl);
}
}
} else {
stream.saveObject(this);
}
}
// All together now write out the endpoint, id, and flag
// (need to choose whether or not to use old JDK1.1 endpoint format)
if (useNewFormat) {
((TCPEndpoint) ep).write(out);
} else {
((TCPEndpoint) ep).writeHostPortFormat(out);
}
id.write(out);
out.writeBoolean(isResultStream);
}
public static LiveRef read(ObjectInput in, boolean useNewFormat)
throws IOException, ClassNotFoundException
{
Endpoint ep;
ObjID id;
// Now read in the endpoint, id, and result flag
// (need to choose whether or not to read old JDK1.1 endpoint format)
if (useNewFormat) {
ep = TCPEndpoint.read(in);
} else {
ep = TCPEndpoint.readHostPortFormat(in);
}
id = ObjID.read(in);
boolean isResultStream = in.readBoolean();
LiveRef ref = new LiveRef(id, ep, false);
if (in instanceof ConnectionInputStream) {
ConnectionInputStream stream = (ConnectionInputStream)in;
// save ref to send "dirty" call after all args/returns
// have been unmarshaled.
stream.saveRef(ref);
if (isResultStream) {
// set flag in stream indicating that remote objects were
// unmarshaled. A DGC ack should be sent by the transport.
stream.setAckNeeded();
}
} else {
DGCClient.registerRefs(ep, Arrays.asList(new LiveRef[] { ref }));
}
return ref;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.rmi.server.ObjID;
/**
* An object used as a key to the object table that maps an
* instance of this class to a Target.
*
* @author Ann Wollrath
**/
class ObjectEndpoint {
private final ObjID id;
private final Transport transport;
/**
* Constructs a new ObjectEndpoint instance with the specified id and
* transport. The specified id must be non-null, and the specified
* transport must either be non-null or the specified id must be
* equivalent to an ObjID constructed with ObjID.DGC_ID.
*
* @param id the object identifier
* @param transport the transport
* @throws NullPointerException if id is null
**/
ObjectEndpoint(ObjID id, Transport transport) {
if (id == null) {
throw new NullPointerException();
}
assert transport != null || id.equals(new ObjID(ObjID.DGC_ID));
this.id = id;
this.transport = transport;
}
/**
* Compares the specified object with this object endpoint for
* equality.
*
* This method returns true if and only if the specified object is an
* ObjectEndpoint instance with the same object identifier and
* transport as this object.
**/
public boolean equals(Object obj) {
if (obj instanceof ObjectEndpoint) {
ObjectEndpoint oe = (ObjectEndpoint) obj;
return id.equals(oe.id) && transport == oe.transport;
} else {
return false;
}
}
/**
* Returns the hash code value for this object endpoint.
*/
public int hashCode() {
return id.hashCode() ^ (transport != null ? transport.hashCode() : 0);
}
/**
* Returns a string representation for this object endpoint.
*/
public String toString() {
return id.toString();
}
}

View File

@@ -0,0 +1,371 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.lang.ref.ReferenceQueue;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.dgc.VMID;
import java.rmi.server.ExportException;
import java.rmi.server.ObjID;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import sun.misc.GC;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.NewThreadAction;
import sun.security.action.GetLongAction;
/**
* Object table shared by all implementors of the Transport interface.
* This table maps object ids to remote object targets in this address
* space.
*
* @author Ann Wollrath
* @author Peter Jones
*/
public final class ObjectTable {
/** maximum interval between complete garbage collections of local heap */
private final static long gcInterval = // default 1 hour
AccessController.doPrivileged(
new GetLongAction("sun.rmi.dgc.server.gcInterval", 3600000));
/**
* lock guarding objTable and implTable.
* Holders MAY acquire a Target instance's lock or keepAliveLock.
*/
private static final Object tableLock = new Object();
/** tables mapping to Target, keyed from ObjectEndpoint and impl object */
private static final Map<ObjectEndpoint,Target> objTable =
new HashMap<>();
private static final Map<WeakRef,Target> implTable =
new HashMap<>();
/**
* lock guarding keepAliveCount, reaper, and gcLatencyRequest.
* Holders may NOT acquire a Target instance's lock or tableLock.
*/
private static final Object keepAliveLock = new Object();
/** count of non-permanent objects in table or still processing calls */
private static int keepAliveCount = 0;
/** thread to collect unreferenced objects from table */
private static Thread reaper = null;
/** queue notified when weak refs in the table are cleared */
static final ReferenceQueue<Object> reapQueue = new ReferenceQueue<>();
/** handle for GC latency request (for future cancellation) */
private static GC.LatencyRequest gcLatencyRequest = null;
/*
* Disallow anyone from creating one of these.
*/
private ObjectTable() {}
/**
* Returns the target associated with the object id.
*/
static Target getTarget(ObjectEndpoint oe) {
synchronized (tableLock) {
return objTable.get(oe);
}
}
/**
* Returns the target associated with the remote object
*/
public static Target getTarget(Remote impl) {
synchronized (tableLock) {
return implTable.get(new WeakRef(impl));
}
}
/**
* Returns the stub for the remote object <b>obj</b> passed
* as a parameter. This operation is only valid <i>after</i>
* the object has been exported.
*
* @return the stub for the remote object, <b>obj</b>.
* @exception NoSuchObjectException if the stub for the
* remote object could not be found.
*/
public static Remote getStub(Remote impl)
throws NoSuchObjectException
{
Target target = getTarget(impl);
if (target == null) {
throw new NoSuchObjectException("object not exported");
} else {
return target.getStub();
}
}
/**
* Remove the remote object, obj, from the RMI runtime. If
* successful, the object can no longer accept incoming RMI calls.
* If the force parameter is true, the object is forcibly unexported
* even if there are pending calls to the remote object or the
* remote object still has calls in progress. If the force
* parameter is false, the object is only unexported if there are
* no pending or in progress calls to the object.
*
* @param obj the remote object to be unexported
* @param force if true, unexports the object even if there are
* pending or in-progress calls; if false, only unexports the object
* if there are no pending or in-progress calls
* @return true if operation is successful, false otherwise
* @exception NoSuchObjectException if the remote object is not
* currently exported
*/
public static boolean unexportObject(Remote obj, boolean force)
throws java.rmi.NoSuchObjectException
{
synchronized (tableLock) {
Target target = getTarget(obj);
if (target == null) {
throw new NoSuchObjectException("object not exported");
} else {
if (target.unexport(force)) {
removeTarget(target);
return true;
} else {
return false;
}
}
}
}
/**
* Add target to object table. If it is not a permanent entry, then
* make sure that reaper thread is running to remove collected entries
* and keep VM alive.
*/
static void putTarget(Target target) throws ExportException {
ObjectEndpoint oe = target.getObjectEndpoint();
WeakRef weakImpl = target.getWeakImpl();
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE, "add object " + oe);
}
synchronized (tableLock) {
/**
* Do nothing if impl has already been collected (see 6597112). Check while
* holding tableLock to ensure that Reaper cannot process weakImpl in between
* null check and put/increment effects.
*/
if (target.getImpl() != null) {
if (objTable.containsKey(oe)) {
throw new ExportException(
"internal error: ObjID already in use");
} else if (implTable.containsKey(weakImpl)) {
throw new ExportException("object already exported");
}
objTable.put(oe, target);
implTable.put(weakImpl, target);
if (!target.isPermanent()) {
incrementKeepAliveCount();
}
}
}
}
/**
* Remove target from object table.
*
* NOTE: This method must only be invoked while synchronized on
* the "tableLock" object, because it does not do so itself.
*/
private static void removeTarget(Target target) {
// assert Thread.holdsLock(tableLock);
ObjectEndpoint oe = target.getObjectEndpoint();
WeakRef weakImpl = target.getWeakImpl();
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE, "remove object " + oe);
}
objTable.remove(oe);
implTable.remove(weakImpl);
target.markRemoved(); // handles decrementing keep-alive count
}
/**
* Process client VM signalling reference for given ObjID: forward to
* corresponding Target entry. If ObjID is not found in table,
* no action is taken.
*/
static void referenced(ObjID id, long sequenceNum, VMID vmid) {
synchronized (tableLock) {
ObjectEndpoint oe =
new ObjectEndpoint(id, Transport.currentTransport());
Target target = objTable.get(oe);
if (target != null) {
target.referenced(sequenceNum, vmid);
}
}
}
/**
* Process client VM dropping reference for given ObjID: forward to
* corresponding Target entry. If ObjID is not found in table,
* no action is taken.
*/
static void unreferenced(ObjID id, long sequenceNum, VMID vmid,
boolean strong)
{
synchronized (tableLock) {
ObjectEndpoint oe =
new ObjectEndpoint(id, Transport.currentTransport());
Target target = objTable.get(oe);
if (target != null)
target.unreferenced(sequenceNum, vmid, strong);
}
}
/**
* Increments the "keep-alive count".
*
* The "keep-alive count" is the number of non-permanent remote objects
* that are either in the object table or still have calls in progress.
* Therefore, this method should be invoked exactly once for every
* non-permanent remote object exported (a remote object must be
* exported before it can have any calls in progress).
*
* The VM is "kept alive" while the keep-alive count is greater than
* zero; this is accomplished by keeping a non-daemon thread running.
*
* Because non-permanent objects are those that can be garbage
* collected while exported, and thus those for which the "reaper"
* thread operates, the reaper thread also serves as the non-daemon
* VM keep-alive thread; a new reaper thread is created if necessary.
*/
static void incrementKeepAliveCount() {
synchronized (keepAliveLock) {
keepAliveCount++;
if (reaper == null) {
reaper = AccessController.doPrivileged(
new NewThreadAction(new Reaper(), "Reaper", false));
reaper.start();
}
/*
* While there are non-"permanent" objects in the object table,
* request a maximum latency for inspecting the entire heap
* from the local garbage collector, to place an upper bound
* on the time to discover remote objects that have become
* unreachable (and thus can be removed from the table).
*/
if (gcLatencyRequest == null) {
gcLatencyRequest = GC.requestLatency(gcInterval);
}
}
}
/**
* Decrements the "keep-alive count".
*
* The "keep-alive count" is the number of non-permanent remote objects
* that are either in the object table or still have calls in progress.
* Therefore, this method should be invoked exactly once for every
* previously-exported non-permanent remote object that both has been
* removed from the object table and has no calls still in progress.
*
* If the keep-alive count is decremented to zero, then the current
* reaper thread is terminated to cease keeping the VM alive (and
* because there are no more non-permanent remote objects to reap).
*/
static void decrementKeepAliveCount() {
synchronized (keepAliveLock) {
keepAliveCount--;
if (keepAliveCount == 0) {
if (!(reaper != null)) { throw new AssertionError(); }
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
reaper.interrupt();
return null;
}
});
reaper = null;
/*
* If there are no longer any non-permanent objects in the
* object table, we are no longer concerned with the latency
* of local garbage collection here.
*/
gcLatencyRequest.cancel();
gcLatencyRequest = null;
}
}
}
/**
* The Reaper thread waits for notifications that weak references in the
* object table have been cleared. When it receives a notification, it
* removes the corresponding entry from the table.
*
* Since the Reaper is created as a non-daemon thread, it also serves
* to keep the VM from exiting while there are objects in the table
* (other than permanent entries that should neither be reaped nor
* keep the VM alive).
*/
private static class Reaper implements Runnable {
public void run() {
try {
do {
// wait for next cleared weak reference
WeakRef weakImpl = (WeakRef) reapQueue.remove();
synchronized (tableLock) {
Target target = implTable.get(weakImpl);
if (target != null) {
if (!target.isEmpty()) {
throw new Error(
"object with known references collected");
} else if (target.isPermanent()) {
throw new Error("permanent object collected");
}
removeTarget(target);
}
}
} while (!Thread.interrupted());
} catch (InterruptedException e) {
// pass away if interrupted
}
}
}
}

View File

@@ -0,0 +1,342 @@
/*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.StreamCorruptedException;
import java.rmi.RemoteException;
import java.rmi.MarshalException;
import java.rmi.UnmarshalException;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteCall;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.misc.ObjectInputFilter;
import sun.rmi.runtime.Log;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.tcp.TCPEndpoint;
/**
* Stream-based implementation of the RemoteCall interface.
*
* @author Ann Wollrath
*/
@SuppressWarnings("deprecation")
public class StreamRemoteCall implements RemoteCall {
private ConnectionInputStream in = null;
private ConnectionOutputStream out = null;
private Connection conn;
private ObjectInputFilter filter = null;
private boolean resultStarted = false;
private Exception serverException = null;
public StreamRemoteCall(Connection c) {
conn = c;
}
public StreamRemoteCall(Connection c, ObjID id, int op, long hash)
throws RemoteException
{
try {
conn = c;
Transport.transportLog.log(Log.VERBOSE,
"write remote call header...");
// write out remote call header info...
// call header, part 1 (read by Transport)
conn.getOutputStream().write(TransportConstants.Call);
getOutputStream(); // creates a MarshalOutputStream
id.write(out); // object id (target of call)
// call header, part 2 (read by Dispatcher)
out.writeInt(op); // method number (operation index)
out.writeLong(hash); // stub/skeleton hash
} catch (IOException e) {
throw new MarshalException("Error marshaling call header", e);
}
}
/**
* Return the connection associated with this call.
*/
public Connection getConnection() {
return conn;
}
/**
* Return the output stream the stub/skeleton should put arguments/results
* into.
*/
public ObjectOutput getOutputStream() throws IOException {
return getOutputStream(false);
}
private ObjectOutput getOutputStream(boolean resultStream)
throws IOException
{
if (out == null) {
Transport.transportLog.log(Log.VERBOSE, "getting output stream");
out = new ConnectionOutputStream(conn, resultStream);
}
return out;
}
/**
* Release the outputStream Currently, will not complain if the
* output stream is released more than once.
*/
public void releaseOutputStream() throws IOException {
try {
if (out != null) {
try {
out.flush();
} finally {
out.done(); // always start DGC ack timer
}
}
conn.releaseOutputStream();
} finally {
out = null;
}
}
public void setObjectInputFilter(ObjectInputFilter filter) {
if (in != null) {
throw new IllegalStateException("set filter must occur before calling getInputStream");
}
this.filter = filter;
}
/**
* Get the InputStream the stub/skeleton should get results/arguments
* from.
*/
public ObjectInput getInputStream() throws IOException {
if (in == null) {
Transport.transportLog.log(Log.VERBOSE, "getting input stream");
in = new ConnectionInputStream(conn.getInputStream());
if (filter != null) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
ObjectInputFilter.Config.setObjectInputFilter(in, filter);
return null;
});
}
}
return in;
}
/**
* Release the input stream, this would allow some transports to release
* the channel early.
*/
public void releaseInputStream() throws IOException {
/* WARNING: Currently, the UnicastRef.java invoke methods rely
* upon this method not throwing an IOException.
*/
try {
if (in != null) {
// execute MarshalInputStream "done" callbacks
try {
in.done();
} catch (RuntimeException e) {
}
// add saved references to DGC table
in.registerRefs();
/* WARNING: The connection being passed to done may have
* already been freed.
*/
in.done(conn);
}
conn.releaseInputStream();
} finally {
in = null;
}
}
/**
* Discard any post-processing of refs the InputStream.
*/
public void discardPendingRefs() {
in.discardRefs();
}
/**
* Returns an output stream (may put out header information
* relating to the success of the call).
* @param success If true, indicates normal return, else indicates
* exceptional return.
* @exception StreamCorruptedException If result stream previously
* acquired
* @exception IOException For any other problem with I/O.
*/
public ObjectOutput getResultStream(boolean success) throws IOException {
/* make sure result code only marshaled once. */
if (resultStarted)
throw new StreamCorruptedException("result already in progress");
else
resultStarted = true;
// write out return header
// return header, part 1 (read by Transport)
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeByte(TransportConstants.Return);// transport op
getOutputStream(true); // creates a MarshalOutputStream
// return header, part 2 (read by client-side RemoteCall)
if (success) //
out.writeByte(TransportConstants.NormalReturn);
else
out.writeByte(TransportConstants.ExceptionalReturn);
out.writeID(); // write id for gcAck
return out;
}
/**
* Do whatever it takes to execute the call.
*/
@SuppressWarnings("fallthrough")
public void executeCall() throws Exception {
byte returnType;
// read result header
DGCAckHandler ackHandler = null;
try {
if (out != null) {
ackHandler = out.getDGCAckHandler();
}
releaseOutputStream();
DataInputStream rd = new DataInputStream(conn.getInputStream());
byte op = rd.readByte();
if (op != TransportConstants.Return) {
if (Transport.transportLog.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"transport return code invalid: " + op);
}
throw new UnmarshalException("Transport return code invalid");
}
getInputStream();
returnType = in.readByte();
in.readID(); // id for DGC acknowledgement
} catch (UnmarshalException e) {
throw e;
} catch (IOException e) {
throw new UnmarshalException("Error unmarshaling return header",
e);
} finally {
if (ackHandler != null) {
ackHandler.release();
}
}
// read return value
switch (returnType) {
case TransportConstants.NormalReturn:
break;
case TransportConstants.ExceptionalReturn:
Object ex;
try {
ex = in.readObject();
} catch (Exception e) {
discardPendingRefs();
throw new UnmarshalException("Error unmarshaling return", e);
}
// An exception should have been received,
// if so throw it, else flag error
if (ex instanceof Exception) {
exceptionReceivedFromServer((Exception) ex);
} else {
discardPendingRefs();
throw new UnmarshalException("Return type not Exception");
}
// Exception is thrown before fallthrough can occur
default:
if (Transport.transportLog.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"return code invalid: " + returnType);
}
throw new UnmarshalException("Return code invalid");
}
}
/**
* Routine that causes the stack traces of remote exceptions to be
* filled in with the current stack trace on the client. Detail
* exceptions are filled in iteratively.
*/
protected void exceptionReceivedFromServer(Exception ex) throws Exception {
serverException = ex;
StackTraceElement[] serverTrace = ex.getStackTrace();
StackTraceElement[] clientTrace = (new Throwable()).getStackTrace();
StackTraceElement[] combinedTrace =
new StackTraceElement[serverTrace.length + clientTrace.length];
System.arraycopy(serverTrace, 0, combinedTrace, 0,
serverTrace.length);
System.arraycopy(clientTrace, 0, combinedTrace, serverTrace.length,
clientTrace.length);
ex.setStackTrace(combinedTrace);
/*
* Log the details of a server exception thrown as a result of a
* remote method invocation.
*/
if (UnicastRef.clientCallLog.isLoggable(Log.BRIEF)) {
/* log call exception returned from server before it is rethrown */
TCPEndpoint ep = (TCPEndpoint) conn.getChannel().getEndpoint();
UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call " +
"received exception: [" + ep.getHost() + ":" +
ep.getPort() + "] exception: ", ex);
}
throw ex;
}
/*
* method to retrieve possible server side exceptions (which will
* be throw from exceptionReceivedFromServer(...) )
*/
public Exception getServerException() {
return serverException;
}
public void done() throws IOException {
/* WARNING: Currently, the UnicastRef.java invoke methods rely
* upon this method not throwing an IOException.
*/
releaseInputStream();
}
}

View File

@@ -0,0 +1,461 @@
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.rmi.Remote;
import java.rmi.NoSuchObjectException;
import java.rmi.dgc.VMID;
import java.rmi.server.ObjID;
import java.rmi.server.Unreferenced;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.NewThreadAction;
import sun.rmi.server.Dispatcher;
/**
* A target contains information pertaining to a remote object that
* resides in this address space. Targets are located via the
* ObjectTable.
*/
public final class Target {
/** object id for target */
private final ObjID id;
/** flag indicating whether target is subject to collection */
private final boolean permanent;
/** weak reference to remote object implementation */
private final WeakRef weakImpl;
/** dispatcher for remote object */
private volatile Dispatcher disp;
/** stub for remote object */
private final Remote stub;
/** set of clients that hold references to this target */
private final Vector<VMID> refSet = new Vector<>();
/** table that maps client endpoints to sequence numbers */
private final Hashtable<VMID, SequenceEntry> sequenceTable =
new Hashtable<>(5);
/** access control context in which target was created */
private final AccessControlContext acc;
/** context class loader in which target was created */
private final ClassLoader ccl;
/** number of pending/executing calls */
private int callCount = 0;
/** true if this target has been removed from the object table */
private boolean removed = false;
/**
* the transport through which this target was exported and
* through which remote calls will be allowed
*/
private volatile Transport exportedTransport = null;
/** number to identify next callback thread created here */
private static int nextThreadNum = 0;
/**
* Construct a Target for a remote object "impl" with
* a specific object id.
*
* If "permanent" is true, then the impl is pinned permanently
* (the impl will not be collected via distributed and/or local
* GC). If "on" is false, than the impl is subject to
* collection. Permanent objects do not keep a server from
* exiting.
*/
public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,
boolean permanent)
{
this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);
this.disp = disp;
this.stub = stub;
this.id = id;
this.acc = AccessController.getContext();
/*
* Fix for 4149366: so that downloaded parameter types unmarshalled
* for this impl will be compatible with types known only to the
* impl class's class loader (when it's not identical to the
* exporting thread's context class loader), mark the impl's class
* loader as the loader to use as the context class loader in the
* server's dispatch thread while a call to this impl is being
* processed (unless this exporting thread's context class loader is
* a child of the impl's class loader, such as when a registry is
* exported by an application, in which case this thread's context
* class loader is preferred).
*/
ClassLoader threadContextLoader =
Thread.currentThread().getContextClassLoader();
ClassLoader serverLoader = impl.getClass().getClassLoader();
if (checkLoaderAncestry(threadContextLoader, serverLoader)) {
this.ccl = threadContextLoader;
} else {
this.ccl = serverLoader;
}
this.permanent = permanent;
if (permanent) {
pinImpl();
}
}
/**
* Return true if the first class loader is a child of (or identical
* to) the second class loader. Either loader may be "null", which is
* considered to be the parent of any non-null class loader.
*
* (utility method added for the 1.2beta4 fix for 4149366)
*/
private static boolean checkLoaderAncestry(ClassLoader child,
ClassLoader ancestor)
{
if (ancestor == null) {
return true;
} else if (child == null) {
return false;
} else {
for (ClassLoader parent = child;
parent != null;
parent = parent.getParent())
{
if (parent == ancestor) {
return true;
}
}
return false;
}
}
/** Get the stub (proxy) object for this target
*/
public Remote getStub() {
return stub;
}
/**
* Returns the object endpoint for the target.
*/
ObjectEndpoint getObjectEndpoint() {
return new ObjectEndpoint(id, exportedTransport);
}
/**
* Get the weak reference for the Impl of this target.
*/
WeakRef getWeakImpl() {
return weakImpl;
}
/**
* Returns the dispatcher for this remote object target.
*/
Dispatcher getDispatcher() {
return disp;
}
AccessControlContext getAccessControlContext() {
return acc;
}
ClassLoader getContextClassLoader() {
return ccl;
}
/**
* Get the impl for this target.
* Note: this may return null if the impl has been garbage collected.
* (currently, there is no need to make this method public)
*/
Remote getImpl() {
return (Remote)weakImpl.get();
}
/**
* Returns true if the target is permanent.
*/
boolean isPermanent() {
return permanent;
}
/**
* Pin impl in target. Pin the WeakRef object so it holds a strong
* reference to the object to it will not be garbage collected locally.
* This way there is a single object responsible for the weak ref
* mechanism.
*/
synchronized void pinImpl() {
weakImpl.pin();
}
/**
* Unpin impl in target. Weaken the reference to impl so that it
* can be garbage collected locally. But only if there the refSet
* is empty. All of the weak/strong handling is in WeakRef
*/
synchronized void unpinImpl() {
/* only unpin if:
* a) impl is not permanent, and
* b) impl is not already unpinned, and
* c) there are no external references (outside this
* address space) for the impl
*/
if (!permanent && refSet.isEmpty()) {
weakImpl.unpin();
}
}
/**
* Enable the transport through which remote calls to this target
* are allowed to be set if it has not already been set.
*/
void setExportedTransport(Transport exportedTransport) {
if (this.exportedTransport == null) {
this.exportedTransport = exportedTransport;
}
}
/**
* Add an endpoint to the remembered set. Also adds a notifier
* to call back if the address space associated with the endpoint
* dies.
*/
synchronized void referenced(long sequenceNum, VMID vmid) {
// check sequence number for vmid
SequenceEntry entry = sequenceTable.get(vmid);
if (entry == null) {
sequenceTable.put(vmid, new SequenceEntry(sequenceNum));
} else if (entry.sequenceNum < sequenceNum) {
entry.update(sequenceNum);
} else {
// late dirty call; ignore.
return;
}
if (!refSet.contains(vmid)) {
/*
* A Target must be pinned while its refSet is not empty. It may
* have become unpinned if external LiveRefs only existed in
* serialized form for some period of time, or if a client failed
* to renew its lease due to a transient network failure. So,
* make sure that it is pinned here; this fixes bugid 4069644.
*/
pinImpl();
if (getImpl() == null) // too late if impl was collected
return;
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);
}
refSet.addElement(vmid);
DGCImpl.getDGCImpl().registerTarget(vmid, this);
}
}
/**
* Remove endpoint from remembered set. If set becomes empty,
* remove server from Transport's object table.
*/
synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)
{
// check sequence number for vmid
SequenceEntry entry = sequenceTable.get(vmid);
if (entry == null || entry.sequenceNum > sequenceNum) {
// late clean call; ignore
return;
} else if (strong) {
// strong clean call; retain sequenceNum
entry.retain(sequenceNum);
} else if (entry.keep == false) {
// get rid of sequence number
sequenceTable.remove(vmid);
}
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);
}
refSetRemove(vmid);
}
/**
* Remove endpoint from the reference set.
*/
synchronized private void refSetRemove(VMID vmid) {
// remove notification request
DGCImpl.getDGCImpl().unregisterTarget(vmid, this);
if (refSet.removeElement(vmid) && refSet.isEmpty()) {
// reference set is empty, so server can be garbage collected.
// remove object from table.
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE,
"reference set is empty: target = " + this);
}
/*
* If the remote object implements the Unreferenced interface,
* invoke its unreferenced callback in a separate thread.
*/
Remote obj = getImpl();
if (obj instanceof Unreferenced) {
final Unreferenced unrefObj = (Unreferenced) obj;
AccessController.doPrivileged(
new NewThreadAction(() -> {
Thread.currentThread().setContextClassLoader(ccl);
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
unrefObj.unreferenced();
return null;
}, acc);
}, "Unreferenced-" + nextThreadNum++, false, true)).start();
// REMIND: access to nextThreadNum not synchronized; you care?
}
unpinImpl();
}
}
/**
* Mark this target as not accepting new calls if any of the
* following conditions exist: a) the force parameter is true,
* b) the target's call count is zero, or c) the object is already
* not accepting calls. Returns true if target is marked as not
* accepting new calls; returns false otherwise.
*/
synchronized boolean unexport(boolean force) {
if ((force == true) || (callCount == 0) || (disp == null)) {
disp = null;
/*
* Fix for 4331349: unpin object so that it may be gc'd.
* Also, unregister all vmids referencing this target
* so target can be gc'd.
*/
unpinImpl();
DGCImpl dgc = DGCImpl.getDGCImpl();
Enumeration<VMID> enum_ = refSet.elements();
while (enum_.hasMoreElements()) {
VMID vmid = enum_.nextElement();
dgc.unregisterTarget(vmid, this);
}
return true;
} else {
return false;
}
}
/**
* Mark this target as having been removed from the object table.
*/
synchronized void markRemoved() {
if (!(!removed)) { throw new AssertionError(); }
removed = true;
if (!permanent && callCount == 0) {
ObjectTable.decrementKeepAliveCount();
}
if (exportedTransport != null) {
exportedTransport.targetUnexported();
}
}
/**
* Increment call count.
*/
synchronized void incrementCallCount() throws NoSuchObjectException {
if (disp != null) {
callCount ++;
} else {
throw new NoSuchObjectException("object not accepting new calls");
}
}
/**
* Decrement call count.
*/
synchronized void decrementCallCount() {
if (--callCount < 0) {
throw new Error("internal error: call count less than zero");
}
/*
* The "keep-alive count" is the number of non-permanent remote
* objects that are either in the object table or still have calls
* in progress. Therefore, this state change may affect the
* keep-alive count: if this target is for a non-permanent remote
* object that has been removed from the object table and now has a
* call count of zero, it needs to be decremented.
*/
if (!permanent && removed && callCount == 0) {
ObjectTable.decrementKeepAliveCount();
}
}
/**
* Returns true if remembered set is empty; otherwise returns
* false
*/
boolean isEmpty() {
return refSet.isEmpty();
}
/**
* This method is called if the address space associated with the
* vmid dies. In that case, the vmid should be removed
* from the reference set.
*/
synchronized public void vmidDead(VMID vmid) {
if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {
DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +
vmid + " from reference set");
}
sequenceTable.remove(vmid);
refSetRemove(vmid);
}
}
class SequenceEntry {
long sequenceNum;
boolean keep;
SequenceEntry(long sequenceNum) {
this.sequenceNum = sequenceNum;
keep = false;
}
void retain(long sequenceNum) {
this.sequenceNum = sequenceNum;
keep = true;
}
void update(long sequenceNum) {
this.sequenceNum = sequenceNum;
}
}

View File

@@ -0,0 +1,258 @@
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.IOException;
import java.io.ObjectOutput;
import java.rmi.MarshalException;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.LogStream;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import sun.rmi.runtime.Log;
import sun.rmi.server.Dispatcher;
import sun.rmi.server.UnicastServerRef;
/**
* Transport abstraction for enabling communication between different
* VMs.
*
* @author Ann Wollrath
*/
@SuppressWarnings("deprecation")
public abstract class Transport {
/** "transport" package log level */
static final int logLevel = LogStream.parseLevel(getLogLevel());
private static String getLogLevel() {
return java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("sun.rmi.transport.logLevel"));
}
/* transport package log */
static final Log transportLog =
Log.getLog("sun.rmi.transport.misc", "transport", Transport.logLevel);
/** References the current transport when a call is being serviced */
private static final ThreadLocal<Transport> currentTransport = new ThreadLocal<>();
/** ObjID for DGCImpl */
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
/** AccessControlContext for setting context ClassLoader */
private static final AccessControlContext SETCCL_ACC;
static {
Permissions perms = new Permissions();
perms.add(new RuntimePermission("setContextClassLoader"));
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
SETCCL_ACC = new AccessControlContext(pd);
}
/**
* Returns a <I>Channel</I> that generates connections to the
* endpoint <I>ep</I>. A Channel is an object that creates and
* manages connections of a particular type to some particular
* address space.
* @param ep the endpoint to which connections will be generated.
* @return the channel or null if the transport cannot
* generate connections to this endpoint
*/
public abstract Channel getChannel(Endpoint ep);
/**
* Removes the <I>Channel</I> that generates connections to the
* endpoint <I>ep</I>.
*/
public abstract void free(Endpoint ep);
/**
* Export the object so that it can accept incoming calls.
*/
public void exportObject(Target target) throws RemoteException {
target.setExportedTransport(this);
ObjectTable.putTarget(target);
}
/**
* Invoked when an object that was exported on this transport has
* become unexported, either by being garbage collected or by
* being explicitly unexported.
**/
protected void targetUnexported() { }
/**
* Returns the current transport if a call is being serviced, otherwise
* returns null.
**/
static Transport currentTransport() {
return currentTransport.get();
}
/**
* Verify that the current access control context has permission to accept
* the connection being dispatched by the current thread. The current
* access control context is passed as a parameter to avoid the overhead of
* an additional call to AccessController.getContext.
*/
protected abstract void checkAcceptPermission(AccessControlContext acc);
/**
* Sets the context class loader for the current thread.
*/
private static void setContextClassLoader(ClassLoader ccl) {
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
Thread.currentThread().setContextClassLoader(ccl);
return null;
}, SETCCL_ACC);
}
/**
* Service an incoming remote call. When a message arrives on the
* connection indicating the beginning of a remote call, the
* threads are required to call the <I>serviceCall</I> method of
* their transport. The default implementation of this method
* locates and calls the dispatcher object. Ordinarily a
* transport implementation will not need to override this method.
* At the entry to <I>tr.serviceCall(conn)</I>, the connection's
* input stream is positioned at the start of the incoming
* message. The <I>serviceCall</I> method processes the incoming
* remote invocation and sends the result on the connection's
* output stream. If it returns "true", then the remote
* invocation was processed without error and the transport can
* cache the connection. If it returns "false", a protocol error
* occurred during the call, and the transport should destroy the
* connection.
*/
public boolean serviceCall(final RemoteCall call) {
try {
/* read object id */
final Remote impl;
ObjID id;
try {
id = ObjID.read(call.getInputStream());
} catch (java.io.IOException e) {
throw new MarshalException("unable to read objID", e);
}
/* get the remote object */
Transport transport = id.equals(dgcID) ? null : this;
Target target =
ObjectTable.getTarget(new ObjectEndpoint(id, transport));
if (target == null || (impl = target.getImpl()) == null) {
throw new NoSuchObjectException("no such object in table");
}
final Dispatcher disp = target.getDispatcher();
target.incrementCallCount();
try {
/* call the dispatcher */
transportLog.log(Log.VERBOSE, "call dispatcher");
final AccessControlContext acc =
target.getAccessControlContext();
ClassLoader ccl = target.getContextClassLoader();
ClassLoader savedCcl = Thread.currentThread().getContextClassLoader();
try {
setContextClassLoader(ccl);
currentTransport.set(this);
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
public Void run() throws IOException {
checkAcceptPermission(acc);
disp.dispatch(impl, call);
return null;
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
} finally {
setContextClassLoader(savedCcl);
currentTransport.set(null);
}
} catch (IOException ex) {
transportLog.log(Log.BRIEF,
"exception thrown by dispatcher: ", ex);
return false;
} finally {
target.decrementCallCount();
}
} catch (RemoteException e) {
// if calls are being logged, write out exception
if (UnicastServerRef.callLog.isLoggable(Log.BRIEF)) {
// include client host name if possible
String clientHost = "";
try {
clientHost = "[" +
RemoteServer.getClientHost() + "] ";
} catch (ServerNotActiveException ex) {
}
String message = clientHost + "exception: ";
UnicastServerRef.callLog.log(Log.BRIEF, message, e);
}
/* We will get a RemoteException if either a) the objID is
* not readable, b) the target is not in the object table, or
* c) the object is in the midst of being unexported (note:
* NoSuchObjectException is thrown by the incrementCallCount
* method if the object is being unexported). Here it is
* relatively safe to marshal an exception to the client
* since the client will not have seen a return value yet.
*/
try {
ObjectOutput out = call.getResultStream(false);
UnicastServerRef.clearStackTraces(e);
out.writeObject(e);
call.releaseOutputStream();
} catch (IOException ie) {
transportLog.log(Log.BRIEF,
"exception thrown marshalling exception: ", ie);
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
public class TransportConstants {
/** Transport magic number: "JRMI"*/
public static final int Magic = 0x4a524d49;
/** Transport version number */
public static final short Version = 2;
/** Connection uses stream protocol */
public static final byte StreamProtocol = 0x4b;
/** Protocol for single operation per connection; no ack required */
public static final byte SingleOpProtocol = 0x4c;
/** Connection uses multiplex protocol */
public static final byte MultiplexProtocol = 0x4d;
/** Ack for transport protocol */
public static final byte ProtocolAck = 0x4e;
/** Negative ack for transport protocol (protocol not supported) */
public static final byte ProtocolNack = 0x4f;
/** RMI call */
public static final byte Call = 0x50;
/** RMI return */
public static final byte Return = 0x51;
/** Ping operation */
public static final byte Ping = 0x52;
/** Acknowledgment for Ping operation */
public static final byte PingAck = 0x53;
/** Acknowledgment for distributed GC */
public static final byte DGCAck = 0x54;
/** Normal return (with or without return value) */
public static final byte NormalReturn = 0x01;
/** Exceptional return */
public static final byte ExceptionalReturn = 0x02;
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.lang.ref.*;
import sun.rmi.runtime.Log;
/**
* WeakRef objects are used by the RMI runtime to hold potentially weak
* references to exported remote objects in the local object table.
*
* This class extends the functionality of java.lang.ref.WeakReference in
* several ways. The methods pin() and unpin() can be used to set
* whether the contained reference is strong or weak (it is weak upon
* construction). The hashCode() and equals() methods are overridden so
* that WeakRef objects hash and compare to each other according to the
* object identity of their referents.
*
* @author Ann Wollrath
* @author Peter Jones
*/
class WeakRef extends WeakReference<Object> {
/** value of the referent's "identity" hash code */
private int hashValue;
/** strong reference to the referent, for when this WeakRef is "pinned" */
private Object strongRef = null;
/**
* Create a new WeakRef to the given object.
*/
public WeakRef(Object obj) {
super(obj);
setHashValue(obj); // cache object's "identity" hash code
}
/**
* Create a new WeakRef to the given object, registered with a queue.
*/
public WeakRef(Object obj, ReferenceQueue<Object> q) {
super(obj, q);
setHashValue(obj); // cache object's "identity" hash code
}
/**
* Pin the contained reference (make this a strong reference).
*/
public synchronized void pin() {
if (strongRef == null) {
strongRef = get();
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE,
"strongRef = " + strongRef);
}
}
}
/**
* Unpin the contained reference (make this a weak reference).
*/
public synchronized void unpin() {
if (strongRef != null) {
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE,
"strongRef = " + strongRef);
}
strongRef = null;
}
}
/*
* Cache referent's "identity" hash code (so that we still have the
* value after the referent gets cleared).
*
* We cannot use the value from the object's hashCode() method, since
* if the object is of a remote class not extended from RemoteObject
* and it is trying to implement hashCode() and equals() so that it
* can be compared to stub objects, its own hash code could not have
* been initialized yet (see bugid 4102938). Also, object table keys
* based on server objects are indeed matched on object identity, so
* this is the correct hash technique regardless.
*/
private void setHashValue(Object obj) {
if (obj != null) {
hashValue = System.identityHashCode(obj);
} else {
hashValue = 0;
}
}
/**
* Always return the "identity" hash code of the original referent.
*/
public int hashCode() {
return hashValue;
}
/**
* Return true if "obj" is this identical WeakRef object, or, if the
* contained reference has not been cleared, if "obj" is another WeakRef
* object with the identical non-null referent. Otherwise, return false.
*/
public boolean equals(Object obj) {
if (obj instanceof WeakRef) {
if (obj == this)
return true;
Object referent = get();
return (referent != null) && (referent == ((WeakRef) obj).get());
} else {
return false;
}
}
}

View File

@@ -0,0 +1,423 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.*;
import java.net.*;
import java.util.Hashtable;
/**
* CGIClientException is thrown when an error is detected
* in a client's request.
*/
class CGIClientException extends Exception {
private static final long serialVersionUID = 8147981687059865216L;
public CGIClientException(String s) {
super(s);
}
public CGIClientException(String s, Throwable cause) {
super(s, cause);
}
}
/**
* CGIServerException is thrown when an error occurs here on the server.
*/
class CGIServerException extends Exception {
private static final long serialVersionUID = 6928425456704527017L;
public CGIServerException(String s) {
super(s);
}
public CGIServerException(String s, Throwable cause) {
super(s, cause);
}
}
/**
* CGICommandHandler is the interface to an object that handles a
* particular supported command.
*/
interface CGICommandHandler {
/**
* Return the string form of the command
* to be recognized in the query string.
*/
public String getName();
/**
* Execute the command with the given string as parameter.
*/
public void execute(String param) throws CGIClientException, CGIServerException;
}
/**
* The CGIHandler class contains methods for executing as a CGI program.
* The main function interprets the query string as a command of the form
* "<command>=<parameters>".
*
* This class depends on the CGI 1.0 environment variables being set as
* properties of the same name in this Java VM.
*
* All data and methods of this class are static because they are specific
* to this particular CGI process.
*/
public final class CGIHandler {
/* get CGI parameters that we need */
static int ContentLength;
static String QueryString;
static String RequestMethod;
static String ServerName;
static int ServerPort;
static {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
ContentLength =
Integer.getInteger("CONTENT_LENGTH", 0).intValue();
QueryString = System.getProperty("QUERY_STRING", "");
RequestMethod = System.getProperty("REQUEST_METHOD", "");
ServerName = System.getProperty("SERVER_NAME", "");
ServerPort = Integer.getInteger("SERVER_PORT", 0).intValue();
return null;
}
});
}
/* list of handlers for supported commands */
private static CGICommandHandler commands[] = {
new CGIForwardCommand(),
new CGIGethostnameCommand(),
new CGIPingCommand(),
new CGITryHostnameCommand()
};
/* construct table mapping command strings to handlers */
private static Hashtable<String, CGICommandHandler> commandLookup;
static {
commandLookup = new Hashtable<>();
for (int i = 0; i < commands.length; ++ i)
commandLookup.put(commands[i].getName(), commands[i]);
}
/* prevent instantiation of this class */
private CGIHandler() {}
/**
* Execute command given in query string on URL. The string before
* the first '=' is interpreted as the command name, and the string
* after the first '=' is the parameters to the command.
*/
public static void main(String args[])
{
try {
String command, param;
int delim = QueryString.indexOf("=");
if (delim == -1) {
command = QueryString;
param = "";
}
else {
command = QueryString.substring(0, delim);
param = QueryString.substring(delim + 1);
}
CGICommandHandler handler =
commandLookup.get(command);
if (handler != null)
try {
handler.execute(param);
} catch (CGIClientException e) {
e.printStackTrace();
returnClientError(e.getMessage());
} catch (CGIServerException e) {
e.printStackTrace();
returnServerError(e.getMessage());
}
else
returnClientError("invalid command.");
} catch (Exception e) {
e.printStackTrace();
returnServerError("internal error: " + e.getMessage());
}
System.exit(0);
}
/**
* Return an HTML error message indicating there was error in
* the client's request.
*/
private static void returnClientError(String message)
{
System.out.println("Status: 400 Bad Request: " + message);
System.out.println("Content-type: text/html");
System.out.println("");
System.out.println("<HTML>" +
"<HEAD><TITLE>Java RMI Client Error" +
"</TITLE></HEAD>" +
"<BODY>");
System.out.println("<H1>Java RMI Client Error</H1>");
System.out.println("");
System.out.println(message);
System.out.println("</BODY></HTML>");
System.exit(1);
}
/**
* Return an HTML error message indicating an error occurred
* here on the server.
*/
private static void returnServerError(String message)
{
System.out.println("Status: 500 Server Error: " + message);
System.out.println("Content-type: text/html");
System.out.println("");
System.out.println("<HTML>" +
"<HEAD><TITLE>Java RMI Server Error" +
"</TITLE></HEAD>" +
"<BODY>");
System.out.println("<H1>Java RMI Server Error</H1>");
System.out.println("");
System.out.println(message);
System.out.println("</BODY></HTML>");
System.exit(1);
}
}
/**
* "forward" command: Forward request body to local port on the server,
* and send response back to client.
*/
final class CGIForwardCommand implements CGICommandHandler {
public String getName() {
return "forward";
}
@SuppressWarnings("deprecation")
private String getLine (DataInputStream socketIn) throws IOException {
return socketIn.readLine();
}
public void execute(String param) throws CGIClientException, CGIServerException
{
if (!CGIHandler.RequestMethod.equals("POST"))
throw new CGIClientException("can only forward POST requests");
int port;
try {
port = Integer.parseInt(param);
} catch (NumberFormatException e) {
throw new CGIClientException("invalid port number.", e);
}
if (port <= 0 || port > 0xFFFF)
throw new CGIClientException("invalid port: " + port);
if (port < 1024)
throw new CGIClientException("permission denied for port: " +
port);
byte buffer[];
Socket socket;
try {
socket = new Socket(InetAddress.getLocalHost(), port);
} catch (IOException e) {
throw new CGIServerException("could not connect to local port", e);
}
/*
* read client's request body
*/
DataInputStream clientIn = new DataInputStream(System.in);
buffer = new byte[CGIHandler.ContentLength];
try {
clientIn.readFully(buffer);
} catch (EOFException e) {
throw new CGIClientException("unexpected EOF reading request body", e);
} catch (IOException e) {
throw new CGIClientException("error reading request body", e);
}
/*
* send to local server in HTTP
*/
try {
DataOutputStream socketOut =
new DataOutputStream(socket.getOutputStream());
socketOut.writeBytes("POST / HTTP/1.0\r\n");
socketOut.writeBytes("Content-length: " +
CGIHandler.ContentLength + "\r\n\r\n");
socketOut.write(buffer);
socketOut.flush();
} catch (IOException e) {
throw new CGIServerException("error writing to server", e);
}
/*
* read response
*/
DataInputStream socketIn;
try {
socketIn = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
throw new CGIServerException("error reading from server", e);
}
String key = "Content-length:".toLowerCase();
boolean contentLengthFound = false;
String line;
int responseContentLength = -1;
do {
try {
line = getLine(socketIn);
} catch (IOException e) {
throw new CGIServerException("error reading from server", e);
}
if (line == null)
throw new CGIServerException(
"unexpected EOF reading server response");
if (line.toLowerCase().startsWith(key)) {
if (contentLengthFound) {
throw new CGIServerException(
"Multiple Content-length entries found.");
} else {
responseContentLength =
Integer.parseInt(line.substring(key.length()).trim());
contentLengthFound = true;
}
}
} while ((line.length() != 0) &&
(line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
if (!contentLengthFound || responseContentLength < 0)
throw new CGIServerException(
"missing or invalid content length in server response");
buffer = new byte[responseContentLength];
try {
socketIn.readFully(buffer);
} catch (EOFException e) {
throw new CGIServerException(
"unexpected EOF reading server response", e);
} catch (IOException e) {
throw new CGIServerException("error reading from server", e);
}
/*
* send response back to client
*/
System.out.println("Status: 200 OK");
System.out.println("Content-type: application/octet-stream");
System.out.println("");
try {
System.out.write(buffer);
} catch (IOException e) {
throw new CGIServerException("error writing response", e);
}
System.out.flush();
}
}
/**
* "gethostname" command: Return the host name of the server as the
* response body
*/
final class CGIGethostnameCommand implements CGICommandHandler {
public String getName() {
return "gethostname";
}
public void execute(String param)
{
System.out.println("Status: 200 OK");
System.out.println("Content-type: application/octet-stream");
System.out.println("Content-length: " +
CGIHandler.ServerName.length());
System.out.println("");
System.out.print(CGIHandler.ServerName);
System.out.flush();
}
}
/**
* "ping" command: Return an OK status to indicate that connection
* was successful.
*/
final class CGIPingCommand implements CGICommandHandler {
public String getName() {
return "ping";
}
public void execute(String param)
{
System.out.println("Status: 200 OK");
System.out.println("Content-type: application/octet-stream");
System.out.println("Content-length: 0");
System.out.println("");
}
}
/**
* "tryhostname" command: Return a human readable message describing
* what host name is available to local Java VMs.
*/
final class CGITryHostnameCommand implements CGICommandHandler {
public String getName() {
return "tryhostname";
}
public void execute(String param)
{
System.out.println("Status: 200 OK");
System.out.println("Content-type: text/html");
System.out.println("");
System.out.println("<HTML>" +
"<HEAD><TITLE>Java RMI Server Hostname Info" +
"</TITLE></HEAD>" +
"<BODY>");
System.out.println("<H1>Java RMI Server Hostname Info</H1>");
System.out.println("<H2>Local host name available to Java VM:</H2>");
System.out.print("<P>InetAddress.getLocalHost().getHostName()");
try {
String localHostName = InetAddress.getLocalHost().getHostName();
System.out.println(" = " + localHostName);
} catch (UnknownHostException e) {
System.out.println(" threw java.net.UnknownHostException");
}
System.out.println("<H2>Server host information obtained through CGI interface from HTTP server:</H2>");
System.out.println("<P>SERVER_NAME = " + CGIHandler.ServerName);
System.out.println("<P>SERVER_PORT = " + CGIHandler.ServerPort);
System.out.println("</BODY></HTML>");
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import sun.rmi.runtime.Log;
/**
* The HttpAwareServerSocket class extends the java.net.ServerSocket
* class. It behaves like a ServerSocket, except that if
* the first four bytes of an accepted socket are the letters "POST",
* then it returns an HttpReceiveSocket instead of a java.net.Socket.
* This means that the accept method blocks until four bytes have been
* read from the new socket's input stream.
*/
class HttpAwareServerSocket extends ServerSocket {
/**
* Create a server socket on a specified port.
* @param port the port
* @exception IOException IO error when opening the socket.
*/
public HttpAwareServerSocket(int port) throws IOException
{
super(port);
}
/**
* Create a server socket, bind it to the specified local port
* and listen to it. You can connect to an annonymous port by
* specifying the port number to be 0. <i>backlog</i> specifies
* how many connection requests the system will queue up while waiting
* for the ServerSocket to execute accept().
* @param port the specified port
* @param backlog the number of queued connect requests pending accept
*/
public HttpAwareServerSocket(int port, int backlog) throws IOException
{
super(port, backlog);
}
/**
* Accept a connection. This method will block until the connection
* is made and four bytes can be read from the input stream.
* If the first four bytes are "POST", then an HttpReceiveSocket is
* returned, which will handle the HTTP protocol wrapping.
* Otherwise, a WrappedSocket is returned. The input stream will be
* reset to the beginning of the transmission.
* In either case, a BufferedInputStream will already be on top of
* the underlying socket's input stream.
* @exception IOException IO error when waiting for the connection.
*/
public Socket accept() throws IOException
{
Socket socket = super.accept();
BufferedInputStream in =
new BufferedInputStream(socket.getInputStream());
RMIMasterSocketFactory.proxyLog.log(Log.BRIEF,
"socket accepted (checking for POST)");
in.mark(4);
boolean isHttp = (in.read() == 'P') &&
(in.read() == 'O') &&
(in.read() == 'S') &&
(in.read() == 'T');
in.reset();
if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.BRIEF)) {
RMIMasterSocketFactory.proxyLog.log(Log.BRIEF,
(isHttp ? "POST found, HTTP socket returned" :
"POST not found, direct socket returned"));
}
if (isHttp)
return new HttpReceiveSocket(socket, in, null);
else
return new WrappedSocket(socket, in, null);
}
/**
* Return the implementation address and implementation port of
* the HttpAwareServerSocket as a String.
*/
public String toString()
{
return "HttpAware" + super.toString();
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.*;
import sun.rmi.runtime.Log;
/**
* The HttpInputStream class assists the HttpSendSocket and HttpReceiveSocket
* classes by filtering out the header for the message as well as any
* data after its proper content length.
*/
class HttpInputStream extends FilterInputStream {
/** bytes remaining to be read from proper content of message */
protected int bytesLeft;
/** bytes remaining to be read at time of last mark */
protected int bytesLeftAtMark;
/**
* Create new filter on a given input stream.
* @param in the InputStream to filter from
*/
public HttpInputStream(InputStream in) throws IOException
{
super(in);
if (in.markSupported())
in.mark(0); // prevent resetting back to old marks
// pull out header, looking for content length
DataInputStream dis = new DataInputStream(in);
String key = "Content-length:".toLowerCase();
boolean contentLengthFound = false;
String line;
do {
line = dis.readLine();
if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"received header line: \"" + line + "\"");
}
if (line == null)
throw new EOFException();
if (line.toLowerCase().startsWith(key)) {
if (contentLengthFound) {
throw new IOException(
"Multiple Content-length entries found.");
} else {
bytesLeft =
Integer.parseInt(line.substring(key.length()).trim());
contentLengthFound = true;
}
}
// The idea here is to go past the first blank line.
// Some DataInputStream.readLine() documentation specifies that
// it does include the line-terminating character(s) in the
// returned string, but it actually doesn't, so we'll cover
// all cases here...
} while ((line.length() != 0) &&
(line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
if (!contentLengthFound || bytesLeft < 0) {
// This really shouldn't happen, but if it does, shoud we fail??
// For now, just give up and let a whole lot of bytes through...
bytesLeft = Integer.MAX_VALUE;
}
bytesLeftAtMark = bytesLeft;
if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"content length: " + bytesLeft);
}
}
/**
* Returns the number of bytes that can be read with blocking.
* Make sure that this does not exceed the number of bytes remaining
* in the proper content of the message.
*/
public int available() throws IOException
{
int bytesAvailable = in.available();
if (bytesAvailable > bytesLeft)
bytesAvailable = bytesLeft;
return bytesAvailable;
}
/**
* Read a byte of data from the stream. Make sure that one is available
* from the proper content of the message, else -1 is returned to
* indicate to the user that the end of the stream has been reached.
*/
public int read() throws IOException
{
if (bytesLeft > 0) {
int data = in.read();
if (data != -1)
-- bytesLeft;
if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"received byte: '" +
((data & 0x7F) < ' ' ? " " : String.valueOf((char) data)) +
"' " + data);
}
return data;
}
else {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"read past content length");
return -1;
}
}
public int read(byte b[], int off, int len) throws IOException
{
if (bytesLeft == 0 && len > 0) {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"read past content length");
return -1;
}
if (len > bytesLeft)
len = bytesLeft;
int bytesRead = in.read(b, off, len);
bytesLeft -= bytesRead;
if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
"read " + bytesRead + " bytes, " + bytesLeft + " remaining");
}
return bytesRead;
}
/**
* Mark the current position in the stream (for future calls to reset).
* Remember where we are within the proper content of the message, so
* that a reset method call can recreate our state properly.
* @param readlimit how many bytes can be read before mark becomes invalid
*/
public void mark(int readlimit)
{
in.mark(readlimit);
if (in.markSupported())
bytesLeftAtMark = bytesLeft;
}
/**
* Repositions the stream to the last marked position. Make sure to
* adjust our position within the proper content accordingly.
*/
public void reset() throws IOException
{
in.reset();
bytesLeft = bytesLeftAtMark;
}
/**
* Skips bytes of the stream. Make sure to adjust our
* position within the proper content accordingly.
* @param n number of bytes to be skipped
*/
public long skip(long n) throws IOException
{
if (n > bytesLeft)
n = bytesLeft;
long bytesSkipped = in.skip(n);
bytesLeft -= bytesSkipped;
return bytesSkipped;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.*;
/**
* The HttpOutputStream class assists the HttpSendSocket and HttpReceiveSocket
* classes by providing an output stream that buffers its entire input until
* closed, and then it sends the complete transmission prefixed by the end of
* an HTTP header that specifies the content length.
*/
class HttpOutputStream extends ByteArrayOutputStream {
/** the output stream to send response to */
protected OutputStream out;
/** true if HTTP response has been sent */
boolean responseSent = false;
/**
* Begin buffering new HTTP response to be sent to a given stream.
* @param out the OutputStream to send response to
*/
public HttpOutputStream(OutputStream out) {
super();
this.out = out;
}
/**
* On close, send HTTP-packaged response.
*/
public synchronized void close() throws IOException {
if (!responseSent) {
/*
* If response would have zero content length, then make it
* have some arbitrary data so that certain clients will not
* fail because the "document contains no data".
*/
if (size() == 0)
write(emptyData);
DataOutputStream dos = new DataOutputStream(out);
dos.writeBytes("Content-type: application/octet-stream\r\n");
dos.writeBytes("Content-length: " + size() + "\r\n");
dos.writeBytes("\r\n");
writeTo(dos);
dos.flush();
// Do not close the underlying stream here, because that would
// close the underlying socket and prevent reading a response.
reset(); // reset byte array
responseSent = true;
}
}
/** data to send if the response would otherwise be empty */
private static byte[] emptyData = { 0 };
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.*;
import java.net.Socket;
import java.net.InetAddress;
/**
* The HttpReceiveSocket class extends the WrappedSocket class
* by removing the HTTP protocol packaging from the input stream and
* formatting the output stream as an HTTP response.
*
* NOTES:
*
* The output stream must be explicitly closed for the output to be
* sent, since the HttpResponseOutputStream needs to buffer the entire
* transmission to be able to fill in the content-length field of
* the HTTP header. Closing this socket will do this.
*
* The constructor blocks until the HTTP protocol header
* is received. This could be fixed, but I don't think it should be a
* problem because this object would not be created unless the
* HttpAwareServerSocket has detected the beginning of the header
* anyway, so the rest should be there.
*
* This socket can only be used to process one POST and reply to it.
* Another message would be received on a newly accepted socket anyway.
*/
public class HttpReceiveSocket extends WrappedSocket implements RMISocketInfo {
/** true if the HTTP header has pushed through the output stream yet */
private boolean headerSent = false;
/**
* Layer on top of a pre-existing Socket object, and use specified
* input and output streams.
* @param socket the pre-existing socket to use
* @param in the InputStream to use for this socket (can be null)
* @param out the OutputStream to use for this socket (can be null)
*/
public HttpReceiveSocket(Socket socket, InputStream in, OutputStream out)
throws IOException
{
super(socket, in, out);
this.in = new HttpInputStream(in != null ? in :
socket.getInputStream());
this.out = (out != null ? out :
socket.getOutputStream());
}
/**
* Indicate that this socket is not reusable.
*/
public boolean isReusable()
{
return false;
}
/**
* Get the address to which this socket is connected. "null" is always
* returned (to indicate an unknown address) because the originating
* host's IP address cannot be reliably determined: both because the
* request probably went through a proxy server, and because if it was
* delivered by a local forwarder (CGI script or servlet), we do NOT
* want it to appear as if the call is coming from the local host (in
* case the remote object makes access control decisions based on the
* "client host" of a remote call; see bugid 4399040).
*/
public InetAddress getInetAddress() {
return null;
}
/**
* Get an OutputStream for this socket.
*/
public OutputStream getOutputStream() throws IOException
{
if (!headerSent) { // could this be done in constructor??
DataOutputStream dos = new DataOutputStream(out);
dos.writeBytes("HTTP/1.0 200 OK\r\n");
dos.flush();
headerSent = true;
out = new HttpOutputStream(out);
}
return out;
}
/**
* Close the socket.
*/
public synchronized void close() throws IOException
{
getOutputStream().close(); // make sure response is sent
socket.close();
}
/**
* Return string representation of the socket.
*/
public String toString()
{
return "HttpReceive" + socket.toString();
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport.proxy;
import java.io.*;
/**
* The HttpSendInputStream class is used by the HttpSendSocket class as
* a layer on the top of the InputStream it returns so that it can be
* notified of attempts to read from it. This allows the HttpSendSocket
* to know when it should push across its output message.
*/
class HttpSendInputStream extends FilterInputStream {
/** the HttpSendSocket object that is providing this stream */
HttpSendSocket owner;
/**
* Create new filter on a given input stream.
* @param in the InputStream to filter from
* @param owner the HttpSendSocket that is providing this stream
*/
public HttpSendInputStream(InputStream in, HttpSendSocket owner)
throws IOException
{
super(in);
this.owner = owner;
}
/**
* Mark this stream as inactive for its owner socket, so the next time
* a read is attempted, the owner will be notified and a new underlying
* input stream obtained.
*/
public void deactivate()
{
in = null;
}
/**
* Read a byte of data from the stream.
*/
public int read() throws IOException
{
if (in == null)
in = owner.readNotify();
return in.read();
}
/**
* Read into an array of bytes.
* @param b the buffer into which the data is to be read
* @param off the start offset of the data
* @param len the maximum number of bytes to read
*/
public int read(byte b[], int off, int len) throws IOException
{
if (len == 0)
return 0;
if (in == null)
in = owner.readNotify();
return in.read(b, off, len);
}
/**
* Skip bytes of input.
* @param n the number of bytes to be skipped
*/
public long skip(long n) throws IOException
{
if (n == 0)
return 0;
if (in == null)
in = owner.readNotify();
return in.skip(n);
}
/**
* Return the number of bytes that can be read without blocking.
*/
public int available() throws IOException
{
if (in == null)
in = owner.readNotify();
return in.available();
}
/**
* Close the stream.
*/
public void close() throws IOException
{
owner.close();
}
/**
* Mark the current position in the stream.
* @param readlimit how many bytes can be read before mark becomes invalid
*/
public synchronized void mark(int readlimit)
{
if (in == null) {
try {
in = owner.readNotify();
}
catch (IOException e) {
return;
}
}
in.mark(readlimit);
}
/**
* Reposition the stream to the last marked position.
*/
public synchronized void reset() throws IOException
{
if (in == null)
in = owner.readNotify();
in.reset();
}
/**
* Return true if this stream type supports mark/reset.
*/
public boolean markSupported()
{
if (in == null) {
try {
in = owner.readNotify();
}
catch (IOException e) {
return false;
}
}
return in.markSupported();
}
}

Some files were not shown because too many files have changed in this diff Show More