You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2014/05/09 09:03:19 UTC
svn commit: r1593493 [10/24] - in /river/jtsk/skunk/qa_refactor/trunk: qa/
qa/src/com/sun/jini/test/impl/end2end/jssewrapper/
qa/src/com/sun/jini/test/impl/joinmanager/
qa/src/com/sun/jini/test/impl/mahalo/
qa/src/com/sun/jini/test/impl/outrigger/match...
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/mux/MuxServer.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/mux/MuxServer.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/mux/MuxServer.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/mux/MuxServer.java Fri May 9 07:03:18 2014
@@ -1,263 +1,266 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.sun.jini.jeri.internal.mux;
-
-import com.sun.jini.action.GetIntegerAction;
-import com.sun.jini.thread.Executor;
-import com.sun.jini.thread.GetThreadPoolAction;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.SocketChannel;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.jini.core.constraint.InvocationConstraints;
-import net.jini.io.UnsupportedConstraintException;
-import net.jini.jeri.InboundRequest;
-import net.jini.jeri.RequestDispatcher;
-import net.jini.security.Security;
-import net.jini.security.SecurityContext;
-
-/**
- * A MuxServer controls the server side of a multiplexed connection.
- *
- * @author Sun Microsystems, Inc.
- **/
-public class MuxServer extends Mux {
-
- /** initial inbound ration as server, default is 32768 */
- private static final int serverInitialInboundRation =
- ((Integer) AccessController.doPrivileged(new GetIntegerAction(
- "com.sun.jini.jeri.connection.mux.server.initialInboundRation",
- 32768))).intValue();
-
- /**
- * pool of threads for executing tasks with user code: used for
- * dispatching incoming requests to request dispatchers
- **/
- private static final Executor userThreadPool =
- (Executor) AccessController.doPrivileged(
- new GetThreadPoolAction(true));
-
- /** mux logger */
- private static final Logger logger =
- Logger.getLogger("net.jini.jeri.connection.mux");
-
- /** the request dispatcher to dispatch incoming requests to */
- private final RequestDispatcher requestDispatcher;
-
- /** the security context to dispatch incoming requests in */
- private final SecurityContext securityContext;
-
- /**
- * Initiates the server side of a multiplexed connection over the
- * given input/output stream pair.
- *
- * @param out the output stream of the underlying connection
- *
- * @param in the input stream of the underlying connection
- *
- * @param requestDispatcher the request dispatcher to dispatch
- * incoming requests received on this multiplexed connection to
- **/
- public MuxServer(OutputStream out, InputStream in,
- RequestDispatcher requestDispatcher)
- throws IOException
- {
- super(out, in, Mux.SERVER, serverInitialInboundRation, 1024);
-
- this.requestDispatcher = requestDispatcher;
- this.securityContext = Security.getContext();
- }
-
- public MuxServer(SocketChannel channel,
- RequestDispatcher requestDispatcher)
- throws IOException
- {
- super(channel, Mux.SERVER, serverInitialInboundRation, 1024);
-
- this.requestDispatcher = requestDispatcher;
- this.securityContext = Security.getContext();
- }
-
- /**
- * Shuts down this multiplexed connection. Requests in progress
- * will throw IOException for future I/O operations.
- *
- * On the client side, requests that were in progress will appear
- * to have been aborted with unknown partial execution status.
- *
- * @param message reason for shutdown to be included in
- * IOExceptions thrown from future I/O operations
- **/
- public void shutdown(String message) {
- synchronized (muxLock) {
- /*
- * Be graceful, if possible: i.e. if there are no busy sessions.
- * REMIND: this current implementation is extremely conservative,
- * because most previously-used sessions will still appear busy!
- *
- * We can only send a Shutdown message if we have already sent
- * the ServerConnectionHeader. The header may not have been sent
- * if, for example, the ServerEndpointListener was closed right
- * as a connection was accepted. REMIND: should/could we try to
- * send a ServerConnectionHeader here, in order to be able to
- * send the Shutdown message?
- */
- if (serverConnectionReady && busySessions.isEmpty()) {
- asyncSendShutdown(null);
- }
- setDown(message, null);
- }
- }
-
- /**
- * Shuts down this multiplexed connection only if there are no
- * requests in progress (i.e. requests that have been dispatched
- * to the request dispatcher but that have not been aborted or had
- * their response fully written to the client).
- *
- * @return true if the connection was shut down (because there
- * were no requests in progress), and false otherwise
- **/
- public boolean shutdownGracefully() {
- synchronized (muxLock) {
- if (busySessions.isEmpty()) {
- asyncSendShutdown(null);
- setDown("mux connection shut down gracefully", null);
- return true;
- } else {
- return false;
- }
- }
- }
-
- /**
- * Verifies that the calling context has all of the security
- * permissions necessary to receive a request on this connection.
- *
- * This method should be overridden by subclasses to implement the
- * desired behavior of the checkPermissions method for
- * InboundRequest instances generated for this connection.
- **/
- protected void checkPermissions() {
- }
-
- /**
- * Checks that the specified requirements are either fully or
- * partially satisfied by the constraints actually in force for
- * this connection, and returns any constraints that must be fully
- * or partially implemented by higher layers in order to fully
- * satisfy all of the specified requirements.
- *
- * This method should be overridden by subclasses to implement the
- * desired behavior of the checkConstraints method for
- * InboundRequest instances generated for this connection.
- **/
- protected InvocationConstraints
- checkConstraints(InvocationConstraints constraints)
- throws UnsupportedConstraintException
- {
- if (constraints.requirements().isEmpty()) {
- return InvocationConstraints.EMPTY;
- }
- throw new UnsupportedConstraintException(
- "cannot satisfy constraints: " + constraints);
- }
-
- /**
- * Populates the context collection with information representing
- * this connection (such as the client host).
- *
- * This method should be overridden by subclasses to implement the
- * desired behavior of the populateContext method for
- * InboundRequest instances generated for this connection.
- **/
- protected void populateContext(Collection context) {
- }
-
- /**
- * Handles message to open a new session over this connection.
- *
- * This method must NOT be invoked while synchronized on muxLock.
- **/
- void handleOpen(int sessionID) throws ProtocolException {
- assert !Thread.holdsLock(muxLock);
-
- Session session;
- synchronized (muxLock) {
- if (!busySessions.get(sessionID)) {
- dispatchNewRequest(sessionID);
- return;
- } else {
- session = (Session) sessions.get(Integer.valueOf(sessionID));
- assert session != null;
- }
- }
-
- session.handleOpen();
-
- synchronized (muxLock) {
- dispatchNewRequest(sessionID);
- }
- }
-
- private void dispatchNewRequest(int sessionID) throws ProtocolException {
- assert Thread.holdsLock(muxLock);
- if (muxDown) {
- throw new ProtocolException(
- "connection down, cannot add new session");
- }
- /*
- * REMIND: Here we might want to decide to reject the session,
- * if current conditions warrant.
- */
- final Session session = new Session(this, sessionID, Session.SERVER);
- addSession(sessionID, session);
- try {
- userThreadPool.execute(new Runnable() {
- public void run() {
- final InboundRequest request = session.getInboundRequest();
- try {
- AccessController.doPrivileged(securityContext.wrap(
- new PrivilegedAction() {
- public Object run() {
- requestDispatcher.dispatch(request);
- return null;
- }
- }), securityContext.getAccessControlContext());
- } finally {
- request.abort();
- }
- }
- }, "mux request dispatch");
- } catch (OutOfMemoryError e) { // assume out of threads
- try {
- logger.log(Level.WARNING,
- "could not create thread for request dispatch", e);
- } catch (Throwable t) {
- }
- // reject request but absorb exception to preserve connection
- session.abort();
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.jini.jeri.internal.mux;
+
+import com.sun.jini.action.GetIntegerAction;
+import com.sun.jini.thread.Executor;
+import com.sun.jini.thread.GetThreadPoolAction;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.SocketChannel;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.core.constraint.InvocationConstraints;
+import net.jini.io.UnsupportedConstraintException;
+import net.jini.jeri.InboundRequest;
+import net.jini.jeri.RequestDispatcher;
+import net.jini.security.Security;
+import net.jini.security.SecurityContext;
+
+/**
+ * A MuxServer controls the server side of a multiplexed connection.
+ *
+ * @author Sun Microsystems, Inc.
+ **/
+public class MuxServer extends Mux {
+
+ /** initial inbound ration as server, default is 32768 */
+ private static final int serverInitialInboundRation =
+ ((Integer) AccessController.doPrivileged(new GetIntegerAction(
+ "com.sun.jini.jeri.connection.mux.server.initialInboundRation",
+ 32768))).intValue();
+
+ /**
+ * pool of threads for executing tasks with user code: used for
+ * dispatching incoming requests to request dispatchers
+ **/
+ private static final Executor userThreadPool =
+ (Executor) AccessController.doPrivileged(
+ new GetThreadPoolAction(true));
+
+ /** mux logger */
+ private static final Logger logger =
+ Logger.getLogger("net.jini.jeri.connection.mux");
+
+ /** the request dispatcher to dispatch incoming requests to */
+ private final RequestDispatcher requestDispatcher;
+
+ /** the security context to dispatch incoming requests in */
+ private final SecurityContext securityContext;
+
+ /**
+ * Initiates the server side of a multiplexed connection over the
+ * given input/output stream pair.
+ *
+ * @param out the output stream of the underlying connection
+ *
+ * @param in the input stream of the underlying connection
+ *
+ * @param requestDispatcher the request dispatcher to dispatch
+ * incoming requests received on this multiplexed connection to
+ **/
+ public MuxServer(OutputStream out, InputStream in,
+ RequestDispatcher requestDispatcher)
+ throws IOException
+ {
+ super(out, in, Mux.SERVER, serverInitialInboundRation, 1024);
+
+ this.requestDispatcher = requestDispatcher;
+ this.securityContext = Security.getContext();
+ }
+
+ public MuxServer(SocketChannel channel,
+ RequestDispatcher requestDispatcher)
+ throws IOException
+ {
+ super(channel, Mux.SERVER, serverInitialInboundRation, 1024);
+
+ this.requestDispatcher = requestDispatcher;
+ this.securityContext = Security.getContext();
+ }
+
+ /**
+ * Shuts down this multiplexed connection. Requests in progress
+ * will throw IOException for future I/O operations.
+ *
+ * On the client side, requests that were in progress will appear
+ * to have been aborted with unknown partial execution status.
+ *
+ * @param message reason for shutdown to be included in
+ * IOExceptions thrown from future I/O operations
+ **/
+ public void shutdown(String message) {
+ synchronized (muxLock) {
+ /*
+ * Be graceful, if possible: i.e. if there are no busy sessions.
+ * REMIND: this current implementation is extremely conservative,
+ * because most previously-used sessions will still appear busy!
+ *
+ * We can only send a Shutdown message if we have already sent
+ * the ServerConnectionHeader. The header may not have been sent
+ * if, for example, the ServerEndpointListener was closed right
+ * as a connection was accepted. REMIND: should/could we try to
+ * send a ServerConnectionHeader here, in order to be able to
+ * send the Shutdown message?
+ */
+ if (serverConnectionReady && busySessions.isEmpty()) {
+ asyncSendShutdown(null);
+ }
+ setDown(message, null);
+ }
+ }
+
+ /**
+ * Shuts down this multiplexed connection only if there are no
+ * requests in progress (i.e. requests that have been dispatched
+ * to the request dispatcher but that have not been aborted or had
+ * their response fully written to the client).
+ *
+ * @return true if the connection was shut down (because there
+ * were no requests in progress), and false otherwise
+ **/
+ public boolean shutdownGracefully() {
+ synchronized (muxLock) {
+ if (busySessions.isEmpty()) {
+ asyncSendShutdown(null);
+ setDown("mux connection shut down gracefully", null);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Verifies that the calling context has all of the security
+ * permissions necessary to receive a request on this connection.
+ *
+ * This method should be overridden by subclasses to implement the
+ * desired behavior of the checkPermissions method for
+ * InboundRequest instances generated for this connection.
+ **/
+ protected void checkPermissions() {
+ }
+
+ /**
+ * Checks that the specified requirements are either fully or
+ * partially satisfied by the constraints actually in force for
+ * this connection, and returns any constraints that must be fully
+ * or partially implemented by higher layers in order to fully
+ * satisfy all of the specified requirements.
+ *
+ * This method should be overridden by subclasses to implement the
+ * desired behavior of the checkConstraints method for
+ * InboundRequest instances generated for this connection.
+ **/
+ protected InvocationConstraints
+ checkConstraints(InvocationConstraints constraints)
+ throws UnsupportedConstraintException
+ {
+ if (constraints.requirements().isEmpty()) {
+ return InvocationConstraints.EMPTY;
+ }
+ throw new UnsupportedConstraintException(
+ "cannot satisfy constraints: " + constraints);
+ }
+
+ /**
+ * Populates the context collection with information representing
+ * this connection (such as the client host).
+ *
+ * This method should be overridden by subclasses to implement the
+ * desired behavior of the populateContext method for
+ * InboundRequest instances generated for this connection.
+ **/
+ protected void populateContext(Collection context) {
+ }
+
+ /**
+ * Handles message to open a new session over this connection.
+ *
+ * This method must NOT be invoked while synchronized on muxLock.
+ **/
+ @Override
+ void handleOpen(int sessionID) throws ProtocolException {
+ assert !Thread.holdsLock(muxLock);
+
+ Session session;
+ synchronized (muxLock) {
+ if (!busySessions.get(sessionID)) {
+ dispatchNewRequest(sessionID);
+ return;
+ } else {
+ session = (Session) sessions.get(Integer.valueOf(sessionID));
+ assert session != null;
+ }
+ }
+
+ session.handleOpen();
+
+ synchronized (muxLock) {
+ dispatchNewRequest(sessionID);
+ }
+ }
+
+ private void dispatchNewRequest(int sessionID) throws ProtocolException {
+ assert Thread.holdsLock(muxLock);
+ if (muxDown) {
+ throw new ProtocolException(
+ "connection down, cannot add new session");
+ }
+ /*
+ * REMIND: Here we might want to decide to reject the session,
+ * if current conditions warrant.
+ */
+ final Session session = new Session(this, sessionID, Session.SERVER);
+ addSession(sessionID, session);
+ try {
+ userThreadPool.execute(new Runnable() {
+ @Override
+ public void run() {
+ final InboundRequest request = session.getInboundRequest();
+ try {
+ AccessController.doPrivileged(securityContext.wrap(
+ new PrivilegedAction() {
+ @Override
+ public Object run() {
+ requestDispatcher.dispatch(request);
+ return null;
+ }
+ }), securityContext.getAccessControlContext());
+ } finally {
+ request.abort();
+ }
+ }
+ }, "mux request dispatch");
+ } catch (OutOfMemoryError e) { // assume out of threads
+ try {
+ logger.log(Level.WARNING,
+ "could not create thread for request dispatch", e);
+ } catch (Throwable t) {
+ }
+ // reject request but absorb exception to preserve connection
+ session.abort();
+ }
+ }
+}
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/runtime/Util.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/runtime/Util.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/runtime/Util.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/jeri/internal/runtime/Util.java Fri May 9 07:03:18 2014
@@ -1,811 +1,811 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.sun.jini.jeri.internal.runtime;
-
-import com.sun.jini.collection.WeakIdentityMap;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.URL;
-import java.rmi.MarshalledObject;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.rmi.activation.ActivationID;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Permission;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.net.InetAddress;
-import java.rmi.server.ServerNotActiveException;
-import javax.security.auth.Subject;
-import net.jini.export.ServerContext;
-import net.jini.io.context.ClientHost;
-import net.jini.io.context.ClientSubject;
-import net.jini.io.context.ContextPermission;
-import net.jini.io.context.IntegrityEnforcement;
-import net.jini.security.proxytrust.TrustEquivalence;
-
-/**
- * Utility methods for implementing custom remote reference types.
- *
- * @author Sun Microsystems, Inc.
- *
- */
-public class Util {
-
- /** cache of tables mapping methods to hashes */
- private static TableCache methodToHash_TableCache = new TableCache(true);
-
- /** cache of valid proxy remote methods */
- private static Map proxyRemoteMethodCache = new WeakHashMap();
-
- /** parameter types for activatable constructor or activate method */
- private static Class[] paramTypes = {
- ActivationID.class, MarshalledObject.class
- };
-
- /** name of the resource containing prohibited proxy interfaces */
- private static final String prohibitedProxyInterfacesResource =
- "com/sun/jini/proxy/resources/" +
- "InvocationHandler.moreProhibitedProxyInterfaces";
-
- /** names of interfaces that proxies are prohibited from implementing */
- private static final Collection prohibitedProxyInterfaces =
- getProhibitedProxyInterfaces();
-
- /**
- * Appends the current thread's stack trace to the stack trace of the
- * given exception.
- *
- * This method is used for exceptions that have been unmarshalled as the
- * exceptional result of a remote method invocation, so that the
- * client-side stack trace gets recorded in the exception (while
- * preserving the server-side stack trace in the exception as well).
- *
- * Note that the (somewhat odd) names of this method and the method that
- * this one internally delegates to are significant because these methods
- * visually highlight the remote call boundary between the server-side
- * and the client-side portions to readers of the combined stack trace.
- */
- public static void exceptionReceivedFromServer(Throwable t) {
- __________EXCEPTION_RECEIVED_FROM_SERVER__________(t);
- }
-
- private static void __________EXCEPTION_RECEIVED_FROM_SERVER__________(
- Throwable t)
- {
- StackTraceElement[] serverTrace = t.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);
- t.setStackTrace(combinedTrace);
- }
-
- /**
- * Clear the stack trace of the given exception by replacing it with
- * an empty StackTraceElement array, and do the same for all of its
- * chained causative exceptions.
- *
- * This method is used when it is desired for the stack trace data of
- * an exception to be suppressed before the exception gets marshalled
- * to a remote virtual machine, perhaps for reasons of confidentiality
- * or performance.
- */
- public static void clearStackTraces(Throwable t) {
- StackTraceElement[] empty = new StackTraceElement[0];
- while (t != null) {
- t.setStackTrace(empty);
- t = t.getCause();
- }
- }
-
- /**
- * Marshals <code>value</code> to an <code>ObjectOutput</code> stream,
- * <code>out</code>, using RMI's serialization format for arguments or
- * return values. For primitive types, the primitive type's class should
- * be specified (i.e., for the type <code>int</code>, specify
- * <code>int.class</code>), and the primitive value should be wrapped in
- * instances of the appropriate wrapper class, such as
- * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
- *
- * @param type <code>Class</code> object for the value to be marshalled
- * @param value value to marshal
- * @param out stream to which the value is marshalled
- * @throws IOException if an I/O error occurs marshalling
- * the value to the output stream
- */
- public 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 AssertionError(
- "Unrecognized primitive type: " + type);
- }
- } else {
- out.writeObject(value);
- }
- }
-
- /**
- * Unmarshals a value of the specified <code>type</code> from the
- * <code>ObjectInput</code> stream, <code>in</code>, using RMI's
- * serialization format for arguments or return values and returns the
- * result. For primitive types, the primitive type's class should be
- * specified (i.e., for the primitive type <code>int</code>, specify
- * <code>int.class</code>).
- *
- * @param type <code>Class</code> object for the value to be unmarshalled
- * @param in stream from which the value is unmarshalled
- * @return value unmarshalled from the input stream
- * @throws IOException if an I/O error occurs marshalling
- * the value to the output stream
- * @throws ClassNotFoundException if the <code>type</code>'s
- * class could not be found
- */
- public 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 new Float(in.readFloat());
- } else if (type == double.class) {
- return new Double(in.readDouble());
- } else {
- throw new AssertionError(
- "Unrecognized primitive type: " + type);
- }
- } else {
- return in.readObject();
- }
- }
-
- /**
- * Computes the "method hash" of a remote method, <code>m</code>. The
- * method hash is a <code>long</code> containing the first 64 bits of the
- * SHA digest from the UTF encoded string of the method name followed by
- * its "method descriptor". See section 4.3.3 of The Java(TM) Virtual
- * Machine Specification for the definition of a "method descriptor".
- *
- * @param m remote method
- * @return the method hash
- */
- private 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);
- 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;
- }
-
- /**
- * Returns the method hash for the method <code>m</code>. Subsequent
- * calls to <code>getMethodHash</code> passing the same method argument
- * should be faster since this method caches internally the result of the
- * method to method hash mapping. The method hash is calculated using the
- * <code>computeMethodHash</code> method.
- *
- * @param m the remote method
- * @return the method hash for the method <code>m</code>
- */
- public static long getMethodHash(Method m) {
- Map table = methodToHash_TableCache.getTable(m.getDeclaringClass());
- Long hash = (Long) table.get(m);
- return hash.longValue();
- }
-
- private static class TableCache extends WeakHashMap {
- /**
- * if true, the tables map methods to method hashes; if false,
- * the tables map method hashes to methods
- */
- private boolean mapsMethodToHash;
-
- public TableCache(boolean mapsMethodToHash) {
- super();
- this.mapsMethodToHash = mapsMethodToHash;
- }
-
- public Map getTable(Class remoteClass) {
- SoftReference[] tableRef;
-
- /*
- * Method tables for remote classes are cached in a hash table
- * using weak references to hold the Class object keys, so that
- * the cache does not prevent the class from being unloaded, and
- * using soft references to hold the values, so that the computed
- * method tables will generally persist while no objects of the
- * remote class are exported, but their storage may be reclaimed
- * if necessary, and accidental reachability of the remote class
- * through its interfaces is avoided.
- */
- synchronized (this) {
- /*
- * Look up class in cache; add entry if not found.
- */
- tableRef = (SoftReference[]) get(remoteClass);
- if (tableRef == null) {
- tableRef = new SoftReference[] { null };
- put(remoteClass, tableRef);
- }
- }
-
- /*
- * Check cached reference to method table for this class;
- * if it is null, go and create the table.
- */
- synchronized (tableRef) {
- Map table = null;
- if (tableRef[0] != null) {
- table = (Map) tableRef[0].get();
- }
- if (table == null) {
- if (mapsMethodToHash) {
- /*
- * REMIND: if we hand out this table directly, via a
- * public API, we need to make this map "unmodifiable".
- */
- table = new LazyMethodToHash_Map();
- } else {
- throw new UnsupportedOperationException();
- }
- tableRef[0] = new SoftReference(table);
- }
- return table;
- }
- }
- }
-
-
- /**
- * 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);
- }
-
- private static class LazyMethodToHash_Map extends WeakHashMap {
-
- public LazyMethodToHash_Map() {
- super();
- }
-
- public synchronized Object get(Object key) {
- Object hash = super.get(key);
- if (hash == null) {
- Method method = (Method) key;
- hash = Long.valueOf(computeMethodHash(method));
- put(method, hash);
- }
- return (Long) hash;
- }
- }
-
- /*
- * The following static methods are related to the creation of the
- * "method table" for a remote class, which maps method hashes to
- * the appropriate Method objects for the class's remote methods.
- */
-
- /**
- * Returns 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(TM) Virtual Machine Specification for
- * the definition of a "method descriptor".
- */
- public 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();
- }
-
- /**
- * Returns the descriptor of a particular type, as appropriate for either
- * a parameter type 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 virtual machine 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 an array containing the remote interfaces implemented
- * by the given class.
- *
- * @throws IllegalArgumentException if remoteClass implements
- * any illegal remote interfaces
- * @throws NullPointerException if remoteClass is null
- */
- public static Class[] getRemoteInterfaces(Class remoteClass) {
- ArrayList list = new ArrayList();
- getRemoteInterfaces(list, remoteClass);
- return (Class []) 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 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);
- }
- }
- }
- }
-
- /**
- * Throws IllegalArgumentException if any superinterface of c declares a
- * method with the same name and parameter types as m that does not
- * declare RemoteException or a superclass in its throws clause, or if
- * any superinterface of c has its name in prohibitedProxyInterfaces.
- */
- public static void checkProxyRemoteMethod(Class c, Method m) {
- WeakIdentityMap map;
- synchronized (proxyRemoteMethodCache) {
- SoftReference ref = (SoftReference) proxyRemoteMethodCache.get(c);
- map = (ref == null) ? null : (WeakIdentityMap) ref.get();
- if (map == null && ref != null) {
- map = new WeakIdentityMap();
- proxyRemoteMethodCache.put(c, new SoftReference(map));
- }
- }
- if (map == null) {
- checkProhibitedProxyInterfaces(c);
- synchronized (proxyRemoteMethodCache) {
- SoftReference ref =
- (SoftReference) proxyRemoteMethodCache.get(c);
- map = (ref == null) ? null : (WeakIdentityMap) ref.get();
- if (map == null) {
- map = new WeakIdentityMap();
- proxyRemoteMethodCache.put(c, new SoftReference(map));
- }
- }
- }
- synchronized (map) {
- if (map.get(m) != null) {
- return;
- }
- }
- checkExceptions(c, m.getName(), m.getParameterTypes());
- synchronized (map) {
- map.put(m, Boolean.TRUE);
- }
- }
-
- /**
- * Throws IllegalArgumentException if any superinterface of c declares a
- * method with the given name and parameter types that does not declare
- * RemoteException or a superclass in its throws clause.
- */
- private static void checkExceptions(Class c, String name, Class[] types) {
- Class[] ifaces = c.getInterfaces();
- for (int i = ifaces.length; --i >= 0; ) {
- try {
- checkMethod(ifaces[i].getMethod(name, types));
- checkExceptions(ifaces[i], name, types);
- } catch (NoSuchMethodException e) {
- }
- }
- }
-
- /**
- * Returns collection of prohibited proxy interfaces read from resources.
- */
- private static Collection getProhibitedProxyInterfaces() {
- Collection names = new HashSet();
- names.add("javax.management.MBeanServerConnection");
- Enumeration resources;
- try {
- resources = ClassLoader.getSystemResources(
- prohibitedProxyInterfacesResource);
- } catch (IOException e) {
- throw new ExceptionInInitializerError(
- new IOException(
- "problem getting resources: " +
- prohibitedProxyInterfacesResource).initCause(e));
- }
- while (resources.hasMoreElements()) {
- URL url = (URL) resources.nextElement();
- try {
- InputStream in = url.openStream();
- try {
- BufferedReader r =
- new BufferedReader(new InputStreamReader(in, "utf-8"));
- while (true) {
- String s = r.readLine();
- if (s == null) {
- break;
- }
- int i = s.indexOf('#');
- if (i >= 0) {
- s = s.substring(0, i);
- }
- s = s.trim();
- int n = s.length();
- if (n == 0) {
- continue;
- }
- char prev = '.';
- for (i = 0; i < n; i++) {
- char c = s.charAt(i);
- if (prev == '.' ?
- !Character.isJavaIdentifierStart(c) :
- !(Character.isJavaIdentifierPart(c) ||
- (c == '.' && i < n - 1)))
- {
- throw new ExceptionInInitializerError(
- "illegal interface name in " +
- url + ": " + s);
- }
- prev = c;
- }
- names.add(s);
- }
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- }
- }
- } catch (IOException e) {
- throw new ExceptionInInitializerError(
- new IOException("problem reading " + url).initCause(e));
- }
- }
- return names;
- }
-
- /**
- * Throws IllegalArgumentException if any superinterface of c has its
- * name in prohibitedProxyInterfaces.
- */
- private static void checkProhibitedProxyInterfaces(Class c) {
- Class[] ifaces = c.getInterfaces();
- for (int i = ifaces.length; --i >= 0; ) {
- String name = ifaces[i].getName();
- if (prohibitedProxyInterfaces.contains(name)) {
- throw new IllegalArgumentException(
- "prohibited proxy interface encountered: " + name);
- }
- checkProhibitedProxyInterfaces(ifaces[i]);
- }
- }
-
- /**
- * 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);
- }
-
- /**
- * Returns true either if both arguments are null or if an
- * invocation of Object.equals on "subject" with "object" as the
- * argument returns true; returns false otherwise;
- **/
- public static boolean equals(Object subject, Object object) {
- return subject == null ? object == null : subject.equals(object);
- }
-
- /**
- * Returns true either if both arguments are null or if both
- * arguments refer to objects of the same class and an invocation
- * of Object.equals on "subject" with "object" as the argument
- * returns true; returns false otherwise.
- *
- * This method is used to compare to possibly-null references for
- * object equality when neither object's class is trusted, with
- * the restriction that only objects of the same class can be
- * considered equal.
- **/
- public static boolean sameClassAndEquals(Object subject, Object object) {
- return subject == null ? object == null :
- object != null &&
- subject.getClass() == object.getClass() &&
- subject.equals(object);
- }
-
- /**
- * Returns true either if both arguments are null of if "subject"
- * is an instance of TrustEquivalence and an invocation of
- * TrustEquivalence.checkTrustEquivalence on "subject" with
- * "object" as the argument returns true; returns false otherwise.
- **/
- public static boolean checkTrustEquivalence(Object subject,
- Object object)
- {
- return subject == null ? object == null :
- subject instanceof TrustEquivalence &&
- ((TrustEquivalence) subject).checkTrustEquivalence(object);
- }
-
- /**
- * Returns true if proxy2 is a generated Proxy (proxy1 is assumed to
- * be one) and the classes of both proxies implement the same ordered
- * list of interfaces, and returns false otherwise.
- */
- public static boolean sameProxyClass(Object proxy1, Object proxy2) {
- return (proxy1.getClass() == proxy2.getClass() ||
- (Proxy.isProxyClass(proxy2.getClass()) &&
- equalInterfaces(proxy1, proxy2)));
- }
-
- /**
- * Returns true if the interfaces implemented by obj1's class
- * are the same (and in the same order) as obj2's class.
- */
- public static boolean equalInterfaces(Object obj1, Object obj2) {
- Class[] intf1 = obj1.getClass().getInterfaces();
- Class[] intf2 = obj2.getClass().getInterfaces();
- if (intf1.length != intf2.length) {
- return false;
- } else {
- for (int i = 0; i < intf1.length; i++) {
- if (intf1[i] != intf2[i]) {
- return false;
- }
- }
- return true;
- }
- }
-
- public static void populateContext(Collection context, InetAddress addr) {
- if (context == null) {
- throw new NullPointerException("context is null");
- }
- if (addr != null) {
- context.add(new ClientHostImpl(addr));
- }
- }
-
- public static void populateContext(Collection context, Subject s) {
- if (context == null) {
- throw new NullPointerException("context is null");
- }
- context.add(new ClientSubjectImpl(s));
- }
-
- public static void populateContext(Collection context, boolean integrity) {
- if (context == null) {
- throw new NullPointerException("context is null");
- }
- context.add(new IntegrityEnforcementImpl(integrity));
- }
-
- private static class ClientHostImpl
- implements ClientHost
- {
- private final InetAddress addr;
- public ClientHostImpl(InetAddress addr) { this.addr = addr; }
- public InetAddress getClientHost() { return addr; }
- }
-
- private static class ClientSubjectImpl
- implements ClientSubject
- {
- private final Subject s;
- private static final Permission getClientSubjectPerm =
- new ContextPermission("net.jini.io.context.ClientSubject.getClientSubject");
-
- public ClientSubjectImpl(Subject s) { this.s = s; }
- public Subject getClientSubject() {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(getClientSubjectPerm);
- }
- return s;
- }
- }
-
- private static class IntegrityEnforcementImpl
- implements IntegrityEnforcement
- {
- private final boolean integrity;
- public IntegrityEnforcementImpl(boolean integrity) {
- this.integrity = integrity;
- }
- public boolean integrityEnforced() { return integrity; }
- }
-
- public static InetAddress getClientHost() throws ServerNotActiveException {
- ClientHost ch = (ClientHost)
- ServerContext.getServerContextElement(ClientHost.class);
- return (ch != null) ? ch.getClientHost() : null;
- }
-
- public static String getClientHostString()
- throws ServerNotActiveException
- {
- InetAddress addr = getClientHost();
- return (addr != null) ? addr.toString() : null;
- }
-
- public static Subject getClientSubject() throws ServerNotActiveException {
- ClientSubject cs = (ClientSubject)
- ServerContext.getServerContextElement(ClientSubject.class);
- return (cs != null) ? cs.getClientSubject() : null;
- }
-
- /**
- * Check for permission to access the package of the specified class.
- *
- * @throws SecurityException if a security manager exists and invoking
- * its <code>checkPackageAccess</code> method with the package name of
- * the specified class throws a <code>SecurityException</code>
- */
- public static void checkPackageAccess(Class type) {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- String name = type.getName();
- int i = name.lastIndexOf('.');
- if (i != -1) {
- security.checkPackageAccess(name.substring(0, i));
- }
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.jini.jeri.internal.runtime;
+
+import com.sun.jini.collection.WeakIdentityMap;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.rmi.MarshalledObject;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationID;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.net.InetAddress;
+import java.rmi.server.ServerNotActiveException;
+import javax.security.auth.Subject;
+import net.jini.export.ServerContext;
+import net.jini.io.context.ClientHost;
+import net.jini.io.context.ClientSubject;
+import net.jini.io.context.ContextPermission;
+import net.jini.io.context.IntegrityEnforcement;
+import net.jini.security.proxytrust.TrustEquivalence;
+
+/**
+ * Utility methods for implementing custom remote reference types.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+public class Util {
+
+ /** cache of tables mapping methods to hashes */
+ private static TableCache methodToHash_TableCache = new TableCache(true);
+
+ /** cache of valid proxy remote methods */
+ private static Map proxyRemoteMethodCache = new WeakHashMap();
+
+ /** parameter types for activatable constructor or activate method */
+ private static Class[] paramTypes = {
+ ActivationID.class, MarshalledObject.class
+ };
+
+ /** name of the resource containing prohibited proxy interfaces */
+ private static final String prohibitedProxyInterfacesResource =
+ "com/sun/jini/proxy/resources/" +
+ "InvocationHandler.moreProhibitedProxyInterfaces";
+
+ /** names of interfaces that proxies are prohibited from implementing */
+ private static final Collection prohibitedProxyInterfaces =
+ getProhibitedProxyInterfaces();
+
+ /**
+ * Appends the current thread's stack trace to the stack trace of the
+ * given exception.
+ *
+ * This method is used for exceptions that have been unmarshalled as the
+ * exceptional result of a remote method invocation, so that the
+ * client-side stack trace gets recorded in the exception (while
+ * preserving the server-side stack trace in the exception as well).
+ *
+ * Note that the (somewhat odd) names of this method and the method that
+ * this one internally delegates to are significant because these methods
+ * visually highlight the remote call boundary between the server-side
+ * and the client-side portions to readers of the combined stack trace.
+ */
+ public static void exceptionReceivedFromServer(Throwable t) {
+ __________EXCEPTION_RECEIVED_FROM_SERVER__________(t);
+ }
+
+ private static void __________EXCEPTION_RECEIVED_FROM_SERVER__________(
+ Throwable t)
+ {
+ StackTraceElement[] serverTrace = t.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);
+ t.setStackTrace(combinedTrace);
+ }
+
+ /**
+ * Clear the stack trace of the given exception by replacing it with
+ * an empty StackTraceElement array, and do the same for all of its
+ * chained causative exceptions.
+ *
+ * This method is used when it is desired for the stack trace data of
+ * an exception to be suppressed before the exception gets marshalled
+ * to a remote virtual machine, perhaps for reasons of confidentiality
+ * or performance.
+ */
+ public static void clearStackTraces(Throwable t) {
+ StackTraceElement[] empty = new StackTraceElement[0];
+ while (t != null) {
+ t.setStackTrace(empty);
+ t = t.getCause();
+ }
+ }
+
+ /**
+ * Marshals <code>value</code> to an <code>ObjectOutput</code> stream,
+ * <code>out</code>, using RMI's serialization format for arguments or
+ * return values. For primitive types, the primitive type's class should
+ * be specified (i.e., for the type <code>int</code>, specify
+ * <code>int.class</code>), and the primitive value should be wrapped in
+ * instances of the appropriate wrapper class, such as
+ * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
+ *
+ * @param type <code>Class</code> object for the value to be marshalled
+ * @param value value to marshal
+ * @param out stream to which the value is marshalled
+ * @throws IOException if an I/O error occurs marshalling
+ * the value to the output stream
+ */
+ public 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 AssertionError(
+ "Unrecognized primitive type: " + type);
+ }
+ } else {
+ out.writeObject(value);
+ }
+ }
+
+ /**
+ * Unmarshals a value of the specified <code>type</code> from the
+ * <code>ObjectInput</code> stream, <code>in</code>, using RMI's
+ * serialization format for arguments or return values and returns the
+ * result. For primitive types, the primitive type's class should be
+ * specified (i.e., for the primitive type <code>int</code>, specify
+ * <code>int.class</code>).
+ *
+ * @param type <code>Class</code> object for the value to be unmarshalled
+ * @param in stream from which the value is unmarshalled
+ * @return value unmarshalled from the input stream
+ * @throws IOException if an I/O error occurs marshalling
+ * the value to the output stream
+ * @throws ClassNotFoundException if the <code>type</code>'s
+ * class could not be found
+ */
+ public 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 AssertionError(
+ "Unrecognized primitive type: " + type);
+ }
+ } else {
+ return in.readObject();
+ }
+ }
+
+ /**
+ * Computes the "method hash" of a remote method, <code>m</code>. The
+ * method hash is a <code>long</code> containing the first 64 bits of the
+ * SHA digest from the UTF encoded string of the method name followed by
+ * its "method descriptor". See section 4.3.3 of The Java(TM) Virtual
+ * Machine Specification for the definition of a "method descriptor".
+ *
+ * @param m remote method
+ * @return the method hash
+ */
+ private 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);
+ 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;
+ }
+
+ /**
+ * Returns the method hash for the method <code>m</code>. Subsequent
+ * calls to <code>getMethodHash</code> passing the same method argument
+ * should be faster since this method caches internally the result of the
+ * method to method hash mapping. The method hash is calculated using the
+ * <code>computeMethodHash</code> method.
+ *
+ * @param m the remote method
+ * @return the method hash for the method <code>m</code>
+ */
+ public static long getMethodHash(Method m) {
+ Map table = methodToHash_TableCache.getTable(m.getDeclaringClass());
+ Long hash = (Long) table.get(m);
+ return hash.longValue();
+ }
+
+ private static class TableCache extends WeakHashMap {
+ /**
+ * if true, the tables map methods to method hashes; if false,
+ * the tables map method hashes to methods
+ */
+ private boolean mapsMethodToHash;
+
+ public TableCache(boolean mapsMethodToHash) {
+ super();
+ this.mapsMethodToHash = mapsMethodToHash;
+ }
+
+ public Map getTable(Class remoteClass) {
+ SoftReference[] tableRef;
+
+ /*
+ * Method tables for remote classes are cached in a hash table
+ * using weak references to hold the Class object keys, so that
+ * the cache does not prevent the class from being unloaded, and
+ * using soft references to hold the values, so that the computed
+ * method tables will generally persist while no objects of the
+ * remote class are exported, but their storage may be reclaimed
+ * if necessary, and accidental reachability of the remote class
+ * through its interfaces is avoided.
+ */
+ synchronized (this) {
+ /*
+ * Look up class in cache; add entry if not found.
+ */
+ tableRef = (SoftReference[]) get(remoteClass);
+ if (tableRef == null) {
+ tableRef = new SoftReference[] { null };
+ put(remoteClass, tableRef);
+ }
+ }
+
+ /*
+ * Check cached reference to method table for this class;
+ * if it is null, go and create the table.
+ */
+ synchronized (tableRef) {
+ Map table = null;
+ if (tableRef[0] != null) {
+ table = (Map) tableRef[0].get();
+ }
+ if (table == null) {
+ if (mapsMethodToHash) {
+ /*
+ * REMIND: if we hand out this table directly, via a
+ * public API, we need to make this map "unmodifiable".
+ */
+ table = new LazyMethodToHash_Map();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ tableRef[0] = new SoftReference(table);
+ }
+ return table;
+ }
+ }
+ }
+
+
+ /**
+ * 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);
+ }
+
+ private static class LazyMethodToHash_Map extends WeakHashMap {
+
+ public LazyMethodToHash_Map() {
+ super();
+ }
+
+ public synchronized Object get(Object key) {
+ Object hash = super.get(key);
+ if (hash == null) {
+ Method method = (Method) key;
+ hash = Long.valueOf(computeMethodHash(method));
+ put(method, hash);
+ }
+ return (Long) hash;
+ }
+ }
+
+ /*
+ * The following static methods are related to the creation of the
+ * "method table" for a remote class, which maps method hashes to
+ * the appropriate Method objects for the class's remote methods.
+ */
+
+ /**
+ * Returns 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(TM) Virtual Machine Specification for
+ * the definition of a "method descriptor".
+ */
+ public 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();
+ }
+
+ /**
+ * Returns the descriptor of a particular type, as appropriate for either
+ * a parameter type 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 virtual machine 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 an array containing the remote interfaces implemented
+ * by the given class.
+ *
+ * @throws IllegalArgumentException if remoteClass implements
+ * any illegal remote interfaces
+ * @throws NullPointerException if remoteClass is null
+ */
+ public static Class[] getRemoteInterfaces(Class remoteClass) {
+ ArrayList list = new ArrayList();
+ getRemoteInterfaces(list, remoteClass);
+ return (Class []) 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 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);
+ }
+ }
+ }
+ }
+
+ /**
+ * Throws IllegalArgumentException if any superinterface of c declares a
+ * method with the same name and parameter types as m that does not
+ * declare RemoteException or a superclass in its throws clause, or if
+ * any superinterface of c has its name in prohibitedProxyInterfaces.
+ */
+ public static void checkProxyRemoteMethod(Class c, Method m) {
+ WeakIdentityMap map;
+ synchronized (proxyRemoteMethodCache) {
+ SoftReference ref = (SoftReference) proxyRemoteMethodCache.get(c);
+ map = (ref == null) ? null : (WeakIdentityMap) ref.get();
+ if (map == null && ref != null) {
+ map = new WeakIdentityMap();
+ proxyRemoteMethodCache.put(c, new SoftReference(map));
+ }
+ }
+ if (map == null) {
+ checkProhibitedProxyInterfaces(c);
+ synchronized (proxyRemoteMethodCache) {
+ SoftReference ref =
+ (SoftReference) proxyRemoteMethodCache.get(c);
+ map = (ref == null) ? null : (WeakIdentityMap) ref.get();
+ if (map == null) {
+ map = new WeakIdentityMap();
+ proxyRemoteMethodCache.put(c, new SoftReference(map));
+ }
+ }
+ }
+ synchronized (map) {
+ if (map.get(m) != null) {
+ return;
+ }
+ }
+ checkExceptions(c, m.getName(), m.getParameterTypes());
+ synchronized (map) {
+ map.put(m, Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Throws IllegalArgumentException if any superinterface of c declares a
+ * method with the given name and parameter types that does not declare
+ * RemoteException or a superclass in its throws clause.
+ */
+ private static void checkExceptions(Class c, String name, Class[] types) {
+ Class[] ifaces = c.getInterfaces();
+ for (int i = ifaces.length; --i >= 0; ) {
+ try {
+ checkMethod(ifaces[i].getMethod(name, types));
+ checkExceptions(ifaces[i], name, types);
+ } catch (NoSuchMethodException e) {
+ }
+ }
+ }
+
+ /**
+ * Returns collection of prohibited proxy interfaces read from resources.
+ */
+ private static Collection getProhibitedProxyInterfaces() {
+ Collection names = new HashSet();
+ names.add("javax.management.MBeanServerConnection");
+ Enumeration resources;
+ try {
+ resources = ClassLoader.getSystemResources(
+ prohibitedProxyInterfacesResource);
+ } catch (IOException e) {
+ throw new ExceptionInInitializerError(
+ new IOException(
+ "problem getting resources: " +
+ prohibitedProxyInterfacesResource).initCause(e));
+ }
+ while (resources.hasMoreElements()) {
+ URL url = (URL) resources.nextElement();
+ try {
+ InputStream in = url.openStream();
+ try {
+ BufferedReader r =
+ new BufferedReader(new InputStreamReader(in, "utf-8"));
+ while (true) {
+ String s = r.readLine();
+ if (s == null) {
+ break;
+ }
+ int i = s.indexOf('#');
+ if (i >= 0) {
+ s = s.substring(0, i);
+ }
+ s = s.trim();
+ int n = s.length();
+ if (n == 0) {
+ continue;
+ }
+ char prev = '.';
+ for (i = 0; i < n; i++) {
+ char c = s.charAt(i);
+ if (prev == '.' ?
+ !Character.isJavaIdentifierStart(c) :
+ !(Character.isJavaIdentifierPart(c) ||
+ (c == '.' && i < n - 1)))
+ {
+ throw new ExceptionInInitializerError(
+ "illegal interface name in " +
+ url + ": " + s);
+ }
+ prev = c;
+ }
+ names.add(s);
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ } catch (IOException e) {
+ throw new ExceptionInInitializerError(
+ new IOException("problem reading " + url).initCause(e));
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Throws IllegalArgumentException if any superinterface of c has its
+ * name in prohibitedProxyInterfaces.
+ */
+ private static void checkProhibitedProxyInterfaces(Class c) {
+ Class[] ifaces = c.getInterfaces();
+ for (int i = ifaces.length; --i >= 0; ) {
+ String name = ifaces[i].getName();
+ if (prohibitedProxyInterfaces.contains(name)) {
+ throw new IllegalArgumentException(
+ "prohibited proxy interface encountered: " + name);
+ }
+ checkProhibitedProxyInterfaces(ifaces[i]);
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Returns true either if both arguments are null or if an
+ * invocation of Object.equals on "subject" with "object" as the
+ * argument returns true; returns false otherwise;
+ **/
+ public static boolean equals(Object subject, Object object) {
+ return subject == null ? object == null : subject.equals(object);
+ }
+
+ /**
+ * Returns true either if both arguments are null or if both
+ * arguments refer to objects of the same class and an invocation
+ * of Object.equals on "subject" with "object" as the argument
+ * returns true; returns false otherwise.
+ *
+ * This method is used to compare to possibly-null references for
+ * object equality when neither object's class is trusted, with
+ * the restriction that only objects of the same class can be
+ * considered equal.
+ **/
+ public static boolean sameClassAndEquals(Object subject, Object object) {
+ return subject == null ? object == null :
+ object != null &&
+ subject.getClass() == object.getClass() &&
+ subject.equals(object);
+ }
+
+ /**
+ * Returns true either if both arguments are null of if "subject"
+ * is an instance of TrustEquivalence and an invocation of
+ * TrustEquivalence.checkTrustEquivalence on "subject" with
+ * "object" as the argument returns true; returns false otherwise.
+ **/
+ public static boolean checkTrustEquivalence(Object subject,
+ Object object)
+ {
+ return subject == null ? object == null :
+ subject instanceof TrustEquivalence &&
+ ((TrustEquivalence) subject).checkTrustEquivalence(object);
+ }
+
+ /**
+ * Returns true if proxy2 is a generated Proxy (proxy1 is assumed to
+ * be one) and the classes of both proxies implement the same ordered
+ * list of interfaces, and returns false otherwise.
+ */
+ public static boolean sameProxyClass(Object proxy1, Object proxy2) {
+ return (proxy1.getClass() == proxy2.getClass() ||
+ (Proxy.isProxyClass(proxy2.getClass()) &&
+ equalInterfaces(proxy1, proxy2)));
+ }
+
+ /**
+ * Returns true if the interfaces implemented by obj1's class
+ * are the same (and in the same order) as obj2's class.
+ */
+ public static boolean equalInterfaces(Object obj1, Object obj2) {
+ Class[] intf1 = obj1.getClass().getInterfaces();
+ Class[] intf2 = obj2.getClass().getInterfaces();
+ if (intf1.length != intf2.length) {
+ return false;
+ } else {
+ for (int i = 0; i < intf1.length; i++) {
+ if (intf1[i] != intf2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static void populateContext(Collection context, InetAddress addr) {
+ if (context == null) {
+ throw new NullPointerException("context is null");
+ }
+ if (addr != null) {
+ context.add(new ClientHostImpl(addr));
+ }
+ }
+
+ public static void populateContext(Collection context, Subject s) {
+ if (context == null) {
+ throw new NullPointerException("context is null");
+ }
+ context.add(new ClientSubjectImpl(s));
+ }
+
+ public static void populateContext(Collection context, boolean integrity) {
+ if (context == null) {
+ throw new NullPointerException("context is null");
+ }
+ context.add(new IntegrityEnforcementImpl(integrity));
+ }
+
+ private static class ClientHostImpl
+ implements ClientHost
+ {
+ private final InetAddress addr;
+ public ClientHostImpl(InetAddress addr) { this.addr = addr; }
+ public InetAddress getClientHost() { return addr; }
+ }
+
+ private static class ClientSubjectImpl
+ implements ClientSubject
+ {
+ private final Subject s;
+ private static final Permission getClientSubjectPerm =
+ new ContextPermission("net.jini.io.context.ClientSubject.getClientSubject");
+
+ public ClientSubjectImpl(Subject s) { this.s = s; }
+ public Subject getClientSubject() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(getClientSubjectPerm);
+ }
+ return s;
+ }
+ }
+
+ private static class IntegrityEnforcementImpl
+ implements IntegrityEnforcement
+ {
+ private final boolean integrity;
+ public IntegrityEnforcementImpl(boolean integrity) {
+ this.integrity = integrity;
+ }
+ public boolean integrityEnforced() { return integrity; }
+ }
+
+ public static InetAddress getClientHost() throws ServerNotActiveException {
+ ClientHost ch = (ClientHost)
+ ServerContext.getServerContextElement(ClientHost.class);
+ return (ch != null) ? ch.getClientHost() : null;
+ }
+
+ public static String getClientHostString()
+ throws ServerNotActiveException
+ {
+ InetAddress addr = getClientHost();
+ return (addr != null) ? addr.toString() : null;
+ }
+
+ public static Subject getClientSubject() throws ServerNotActiveException {
+ ClientSubject cs = (ClientSubject)
+ ServerContext.getServerContextElement(ClientSubject.class);
+ return (cs != null) ? cs.getClientSubject() : null;
+ }
+
+ /**
+ * Check for permission to access the package of the specified class.
+ *
+ * @throws SecurityException if a security manager exists and invoking
+ * its <code>checkPackageAccess</code> method with the package name of
+ * the specified class throws a <code>SecurityException</code>
+ */
+ public static void checkPackageAccess(Class type) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ String name = type.getName();
+ int i = name.lastIndexOf('.');
+ if (i != -1) {
+ security.checkPackageAccess(name.substring(0, i));
+ }
+ }
+ }
+}
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/TxnManagerImplInitializer.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/TxnManagerImplInitializer.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/TxnManagerImplInitializer.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/TxnManagerImplInitializer.java Fri May 9 07:03:18 2014
@@ -65,13 +65,15 @@ import org.apache.river.impl.thread.Name
*/
class TxnManagerImplInitializer {
/* Default tuning parameters for thread pool */
- int settlerthreads = 150;
+ int cores = Runtime.getRuntime().availableProcessors();
+ int settlerthreads = 15*cores;
long settlertimeout = 1000 * 15;
float settlerload = 1.0F;
- int taskthreads = 50;
+ int taskthreads = 5*cores;
long tasktimeout = 1000 * 15;
float taskload = 1.0F;
- ConcurrentMap<Long, TxnManagerTransaction> txns = new ConcurrentHashMap<Long, TxnManagerTransaction>();
+ ConcurrentMap<Long, TxnManagerTransaction> txns =
+ new ConcurrentHashMap<Long, TxnManagerTransaction>();
/* Retrieve values from properties. */
ActivationSystem activationSystem = null;
boolean activationPrepared = false;
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mercury/MailboxImpl.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mercury/MailboxImpl.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mercury/MailboxImpl.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mercury/MailboxImpl.java Fri May 9 07:03:18 2014
@@ -3777,15 +3777,13 @@ class MailboxImpl implements MailboxBack
/** Registration expiration thread code */
private class ExpirationThread extends InterruptedStatusThread implements TimeConstants {
- /** Create a daemon thread and start it */
public ExpirationThread() {
super("ExpirationThread");
- setDaemon(true);
+ setDaemon(false);
if (expirationLogger.isLoggable(Level.FINEST)) {
expirationLogger.log(Level.FINEST,
"ExpirationThread started ...");
}
- //start();
}
/**
@@ -4952,11 +4950,13 @@ class MailboxImpl implements MailboxBack
*/
private class SnapshotThread extends InterruptedStatusThread {
- /** Create a daemon thread */
+ /**
+ * Ensure thread is non daemon to avoid jvm terminating it during
+ * a snapshot.
+ */
public SnapshotThread() {
super("SnapshotThread");
- setDaemon(true);
-// start();
+ setDaemon(false);
}
public void run() {