| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.sessions.coordination.rmi; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Method; |
| import java.net.InetAddress; |
| import java.rmi.Naming; |
| import java.rmi.NoSuchObjectException; |
| import java.rmi.server.UnicastRemoteObject; |
| |
| import javax.naming.Context; |
| |
| import org.eclipse.persistence.exceptions.RemoteCommandManagerException; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.sessions.coordination.RemoteConnection; |
| import org.eclipse.persistence.internal.sessions.coordination.rmi.RMIRemoteCommandConnection; |
| import org.eclipse.persistence.internal.sessions.coordination.rmi.RMIRemoteCommandConnectionImpl; |
| import org.eclipse.persistence.internal.sessions.coordination.rmi.RMIRemoteConnection; |
| import org.eclipse.persistence.sessions.coordination.RemoteCommandManager; |
| import org.eclipse.persistence.sessions.coordination.ServiceId; |
| import org.eclipse.persistence.sessions.coordination.TransportManager; |
| |
| /** |
| * <p> |
| * <b>Purpose</b>: Provide an RMI transport implementation for RCM. |
| * </p><p> |
| * <b>Description</b>: This class manages the RMI remote connections to other |
| * RCM service instances and posts the local RMI connection to this service instance |
| * in a name service so that other RCM service instances can connect to it. |
| * </p> |
| * @author Steven Vo |
| * @since OracleAS TopLink 10<i>g</i> (9.0.4) |
| */ |
| public class RMITransportManager extends TransportManager { |
| |
| private Method narrow; |
| private Constructor constructor; |
| |
| public RMITransportManager(RemoteCommandManager rcm) { |
| this.rcm = rcm; |
| this.initialize(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create and return an RMI remote connection to the specified service |
| */ |
| @Override |
| public RemoteConnection createConnection(ServiceId connectionServiceId) { |
| RemoteConnection connection = null; |
| |
| if (namingServiceType == REGISTRY_NAMING_SERVICE) { |
| connection = createConnectionFromRegistry(connectionServiceId.getId(), connectionServiceId.getURL()); |
| |
| } else if (namingServiceType == JNDI_NAMING_SERVICE) { |
| connection = createConnectionFromJNDI(connectionServiceId.getId(), connectionServiceId.getURL()); |
| } |
| if (connection != null) { |
| connection.setServiceId(connectionServiceId); |
| } |
| return connection; |
| } |
| |
| /** |
| * INTERNAL: |
| * Look the specified remote object up in JNDI and return a Connection to it. |
| */ |
| protected RemoteConnection createConnectionFromJNDI(String remoteObjectIdentifier, String hostURL) { |
| Object[] args = { remoteObjectIdentifier, hostURL }; |
| rcm.logDebug("looking_up_remote_conn_in_jndi", args); |
| try { |
| Context context = getRemoteHostContext(hostURL); |
| |
| //Use JNDI lookup(), rather than the RMI version, |
| //AND replace the Java remote interface cast with a call to javax.rmi.PortableRemoteObject.narrow(): |
| if (narrow != null) { |
| return new RMIRemoteConnection((RMIRemoteCommandConnection) narrow.invoke( |
| null, context.lookup(remoteObjectIdentifier), RMIRemoteCommandConnection.class)); |
| } else { |
| return new RMIRemoteConnection((RMIRemoteCommandConnection)context.lookup(remoteObjectIdentifier)); |
| } |
| } catch (Exception e) { |
| try { |
| rcm.handleException(RemoteCommandManagerException.errorLookingUpRemoteConnection(remoteObjectIdentifier, hostURL, e)); |
| } catch (Exception ex2) { |
| // Must catch this exception and log a debug message |
| rcm.logDebug("unable_to_look_up_remote_conn_in_jndi", args); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Look the specified remote object up in the RMIRegistry and return a Connection to it. |
| */ |
| protected RemoteConnection createConnectionFromRegistry(String remoteObjectIdentifier, String hostURL) { |
| String formattedUrl = formatURLforRegistry(hostURL, remoteObjectIdentifier); |
| Object[] args = { formattedUrl }; |
| rcm.logDebug("looking_up_remote_conn_in_registry", args); |
| try { |
| return new RMIRemoteConnection((RMIRemoteCommandConnection)Naming.lookup(formattedUrl)); |
| } catch (Exception e) { |
| try { |
| rcm.handleException(RemoteCommandManagerException.errorLookingUpRemoteConnection(remoteObjectIdentifier, hostURL, e)); |
| } catch (Exception ex2) { |
| // Must catch this exception and log a debug message |
| rcm.logDebug("unable_to_look_up_remote_conn_in_registry", args); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Create the local command connection for this transport in a naming service and |
| * return it. |
| */ |
| @Override |
| public void createLocalConnection() { |
| if (namingServiceType == REGISTRY_NAMING_SERVICE) { |
| createLocalConnectionInRegistry(); |
| |
| } else if (namingServiceType == JNDI_NAMING_SERVICE) { |
| createLocalConnectionInJNDI(); |
| } |
| if (localConnection != null) { |
| localConnection.setServiceId(rcm.getServiceId()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Put the local command connection of this transport in JNDI and return it |
| */ |
| protected void createLocalConnectionInJNDI() { |
| try { |
| // Register the remote connection in JNDI naming service |
| RMIRemoteCommandConnection remoteConnectionObject; |
| if (narrow != null) { |
| if (constructor == null) { |
| try { |
| constructor = PrivilegedAccessHelper.getConstructorFor( |
| Class.forName("org.eclipse.persistence.internal.sessions.coordination.rmi.iiop.RMIRemoteCommandConnectionImpl"), |
| new Class[] {RemoteCommandManager.class}, false); |
| } catch (ReflectiveOperationException e) { |
| throw RemoteCommandManagerException.errorInitCorba("javax.rmi.PortableRemoteObject", e); |
| } |
| } |
| try { |
| remoteConnectionObject = (RMIRemoteCommandConnection) PrivilegedAccessHelper.invokeConstructor(constructor, new Object[] {rcm}); |
| } catch (ReflectiveOperationException e) { |
| // TODO Auto-generated catch block |
| throw RemoteCommandManagerException.errorInitCorba("javax.rmi.PortableRemoteObject", e); |
| } |
| } else { |
| remoteConnectionObject = new RMIRemoteCommandConnectionImpl(rcm); |
| } |
| Object[] args = { rcm.getServiceId().getId() }; |
| rcm.logDebug("register_local_connection_in_jndi", args); |
| getLocalHostContext().rebind(rcm.getServiceId().getId(), remoteConnectionObject); |
| localConnection = new RMIRemoteConnection(remoteConnectionObject); |
| } catch (Exception exception) { |
| rcm.handleException(RemoteCommandManagerException.errorBindingConnection(rcm.getServiceId().toString(), exception)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Put the local command connection of this transport in the Registry and return it |
| */ |
| protected RemoteConnection createLocalConnectionInRegistry() { |
| String fullURL = formatURLforRegistry(rcm.getServiceId().getURL(), rcm.getServiceId().getId()); |
| |
| try { |
| // Register the remote connection in RMI Registry naming service |
| RMIRemoteCommandConnectionImpl remoteConnectionObject = new RMIRemoteCommandConnectionImpl(rcm); |
| Object[] args = { fullURL }; |
| rcm.logDebug("register_local_connection_in_registry", args); |
| Naming.rebind(fullURL, remoteConnectionObject); |
| localConnection = new RMIRemoteConnection(remoteConnectionObject); |
| } catch (Exception exception) { |
| rcm.handleException(RemoteCommandManagerException.errorBindingConnection(fullURL, exception)); |
| } |
| return localConnection; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the context used for looking up in local JNDI. |
| */ |
| public Context getLocalHostContext() { |
| return getContext(getLocalContextProperties()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Format the URL so that it can be used to look up the RMI Registry. |
| */ |
| private String formatURLforRegistry(String url, String serviceName) { |
| if (url == null) { |
| return null; |
| } |
| String fullURL = url; |
| |
| if ((fullURL.endsWith("/") || fullURL.endsWith("\\"))) { |
| fullURL = fullURL.substring(0, fullURL.length() - 1); |
| } |
| return fullURL + "/" + serviceName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the default local URL for JNDI lookups |
| */ |
| public String getDefaultLocalUrl() { |
| try { |
| // Look up the local host name and paste it in a default URL |
| String localHost = InetAddress.getLocalHost().getHostName(); |
| if (narrow != null) { |
| return DEFAULT_IIOP_URL_PROTOCOL + "::" + localHost + ":" + DEFAULT_IIOP_URL_PORT; |
| } else { |
| return DEFAULT_URL_PROTOCOL + "://" + localHost + ":" + DEFAULT_URL_PORT; |
| } |
| } catch (IOException exception) { |
| throw RemoteCommandManagerException.errorGettingHostName(exception); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize default properties for RMI. |
| */ |
| @Override |
| public void initialize() { |
| super.initialize(); |
| // URL is not required when in a cluster, do not default. |
| //if (rcm.getServiceId().getURL() == null) { |
| // rcm.getServiceId().setURL(getDefaultLocalUrl()); |
| //} |
| namingServiceType = DEFAULT_NAMING_SERVICE; |
| } |
| |
| /** |
| * ADVANCED: |
| * Remove the local connection from remote accesses. The implementation removes the local connection from JNDI or the |
| * RMI registry, un-exports it from the RMI runtime, and sets it to null. |
| * This method is invoked internally by EclipseLink when the RCM is shutdown and should not be invoked by user's application. |
| */ |
| @Override |
| public void removeLocalConnection() { |
| String unbindName = null; |
| try { |
| if (namingServiceType == REGISTRY_NAMING_SERVICE) { |
| unbindName = formatURLforRegistry(rcm.getServiceId().getURL(), rcm.getServiceId().getId()); |
| Naming.unbind(unbindName); |
| } else if (namingServiceType == JNDI_NAMING_SERVICE) { |
| unbindName = rcm.getServiceId().getId(); |
| getLocalHostContext().unbind(unbindName); |
| } else { |
| return; |
| } |
| } catch (Exception exception) { |
| rcm.handleException(RemoteCommandManagerException.errorUnbindingLocalConnection(unbindName, exception)); |
| } finally { |
| // unexport the local connection from the RMI runtime |
| RMIRemoteConnection localConnection = (RMIRemoteConnection)getConnectionToLocalHost(); |
| if (localConnection != null) { |
| RMIRemoteCommandConnection commandConnection = localConnection.getConnection(); |
| if (commandConnection != null) { |
| try { |
| UnicastRemoteObject.unexportObject(commandConnection, true); |
| } catch (NoSuchObjectException nso) { |
| // if the object isn't exported, ignore this exception since cleanup is being performed |
| } |
| } |
| this.localConnection = null; |
| } |
| } |
| } |
| |
| public void setIsRMIOverIIOP(boolean b) { |
| if (b) { |
| try { |
| narrow = PrivilegedAccessHelper.getDeclaredMethod(Class.forName("javax.rmi.PortableRemoteObject"), "narrow", new Class[] {Object.class, Class.class}); |
| } catch (ReflectiveOperationException e) { |
| // TODO Auto-generated catch block |
| throw RemoteCommandManagerException.errorInitCorba("javax.rmi.PortableRemoteObject", e); |
| } |
| } else { |
| narrow = null; |
| } |
| |
| } |
| } |