| /* |
| * Copyright (c) 1997, 2018 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. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package org.glassfish.enterprise.iiop.impl; |
| |
| |
| import jakarta.ejb.NoSuchObjectLocalException; |
| |
| import java.rmi.Remote; |
| |
| import java.security.AccessController ; |
| import java.security.PrivilegedAction ; |
| |
| import org.omg.CORBA.portable.Delegate; |
| |
| import org.glassfish.enterprise.iiop.api.RemoteReferenceFactory; |
| |
| import org.glassfish.enterprise.iiop.spi.EjbContainerFacade; |
| import org.glassfish.enterprise.iiop.util.S1ASThreadPoolManager; |
| |
| import org.omg.PortableServer.POA ; |
| import org.omg.PortableServer.Servant ; |
| import org.omg.PortableServer.ServantLocator ; |
| import org.omg.PortableServer.ServantLocatorPackage.CookieHolder ; |
| |
| import com.sun.logging.LogDomains; |
| |
| import com.sun.enterprise.deployment.EjbDescriptor; |
| |
| // TODO Only needed for checkpointing |
| // import com.sun.ejb.base.sfsb.util.EJBServerConfigLookup; |
| |
| import com.sun.corba.ee.spi.extension.ServantCachingPolicy; |
| import com.sun.corba.ee.spi.extension.ZeroPortPolicy; |
| import com.sun.corba.ee.spi.extension.CopyObjectPolicy; |
| import com.sun.corba.ee.spi.extension.RequestPartitioningPolicy; |
| import com.sun.corba.ee.spi.threadpool.ThreadPoolManager; |
| |
| import com.sun.corba.ee.spi.presentation.rmi.PresentationManager ; |
| |
| import com.sun.corba.ee.spi.presentation.rmi.StubAdapter; |
| import com.sun.corba.ee.spi.oa.rfm.ReferenceFactory ; |
| import com.sun.corba.ee.spi.oa.rfm.ReferenceFactoryManager ; |
| |
| import com.sun.corba.ee.spi.misc.ORBConstants; |
| import com.sun.corba.ee.org.omg.CORBA.SUNVMCID; |
| import com.sun.corba.ee.spi.ior.IOR; |
| import com.sun.corba.ee.spi.ior.ObjectKey; |
| import com.sun.corba.ee.spi.ior.TaggedProfile; |
| import com.sun.corba.ee.spi.orb.ORB; |
| |
| import com.sun.enterprise.util.Utility; |
| |
| import org.glassfish.pfl.dynamic.codegen.spi.Wrapper ; |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.logging.Level; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.IOException; |
| import java.io.NotSerializableException; |
| import javax.rmi.CORBA.Tie; |
| import javax.rmi.CORBA.Util; |
| import org.omg.CORBA.CompletionStatus; |
| import org.omg.CORBA.OBJECT_NOT_EXIST; |
| import org.omg.CORBA.Policy; |
| |
| /** |
| * This class implements the RemoteReferenceFactory interface for the |
| * RMI/IIOP ORB with POA (Portable Object Adapter). |
| * There is one instance of the POARemoteReferenceFactory for each |
| * EJB type. |
| * |
| * It also implements the preinvoke/postinvoke APIs in the |
| * POA's ServantLocator interface, which are called before/after |
| * every invocation (local or remote). |
| * It creates a RMI-IIOP-POA object reference (a stub) for every EJBObject |
| * and EJBHome in the EJB container. |
| * |
| * @author Kenneth Saks |
| */ |
| |
| public final class POARemoteReferenceFactory extends org.omg.CORBA.LocalObject |
| implements RemoteReferenceFactory, ServantLocator { |
| |
| static final int PASS_BY_VALUE_ID = 0; |
| static final int PASS_BY_REFERENCE_ID = 1; |
| |
| static final int OTS_POLICY_TYPE = SUNVMCID.value + 123; |
| static final int CSIv2_POLICY_TYPE = SUNVMCID.value + 124; |
| static final int REQUEST_DISPATCH_POLICY_TYPE = SUNVMCID.value + 125; |
| static final int SFSB_VERSION_POLICY_TYPE = SUNVMCID.value + 126; |
| |
| private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(LogDomains.CORBA_LOGGER); |
| private static final int GET_TIE_EXCEPTION_CODE = 9999; |
| |
| private EjbContainerFacade container; |
| private EjbDescriptor ejbDescriptor; |
| private final ClassLoader appClassLoader; |
| |
| private ORB orb; |
| private POAProtocolMgr protocolMgr; |
| private final PresentationManager presentationMgr; |
| |
| private ReferenceFactory ejbHomeReferenceFactory ; |
| private PresentationManager.StubFactory ejbHomeStubFactory; |
| private String ejbHomeRepositoryId; |
| |
| private ReferenceFactory ejbObjectReferenceFactory ; |
| private PresentationManager.StubFactory ejbObjectStubFactory; |
| private String ejbObjectRepositoryId; |
| |
| private String remoteBusinessIntf; |
| |
| // true if remote home view. false if remote business view. |
| // Used when getting target object for an invocation. |
| private final boolean isRemoteHomeView; |
| |
| private final String poaId_EJBHome; |
| private final String poaId_EJBObject; |
| |
| // The EJB key format with field-name(size in bytes): |
| // ----------------------------------------- |
| // | EJB ID(8) | INSTANCEKEY | INSTANCEKEY | |
| // | | LENGTH(4) | (unknown) | |
| // ----------------------------------------- |
| // The following are the offsets for the fields in the EJB key. |
| static final int EJBID_OFFSET = 0; |
| private static final int INSTANCEKEYLEN_OFFSET = 8; |
| private static final int INSTANCEKEY_OFFSET = 12; |
| |
| POARemoteReferenceFactory(EjbContainerFacade container, POAProtocolMgr protocolMgr, |
| ORB orb, boolean remoteHomeView, String id) { |
| |
| this.protocolMgr = protocolMgr; |
| this.orb = orb; |
| this.poaId_EJBHome = id + "-EJBHome"; |
| this.poaId_EJBObject = id + "-EJBObject"; |
| this.presentationMgr = ORB.getPresentationManager(); |
| this.container = container; |
| this.ejbDescriptor = container.getEjbDescriptor(); |
| this.isRemoteHomeView = remoteHomeView; |
| |
| appClassLoader = container.getClassLoader(); |
| |
| // NOTE: ReferenceFactory creation happens in setRepositoryIds. |
| } |
| |
| @Override |
| public int getCSIv2PolicyType() { |
| return CSIv2_POLICY_TYPE; |
| } |
| |
| /* |
| private String getRepositoryId(Class c) throws Exception { |
| |
| // Using PresentationManager to get repository ID will always work, |
| // independent of whether we have generated static RMI-IIOP stubs. |
| |
| PresentationManager.ClassData cData = presentationMgr.getClassData(c); |
| String[] typeIds = cData.getTypeIds(); |
| |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, ".getRepositoryId: {0}", typeIds[0]); |
| } |
| |
| // Repository id is always 1st element in array. |
| return typeIds[0]; |
| } |
| */ |
| |
| |
| @Override |
| public void setRepositoryIds(Class homeIntf, Class remoteIntf) { |
| PresentationManager.StubFactoryFactory sff = ORB.getStubFactoryFactory(); |
| |
| // Home |
| ejbHomeStubFactory = sff.createStubFactory(homeIntf.getName(), false, "", null, appClassLoader); |
| String[] ejbHomeTypeIds = ejbHomeStubFactory.getTypeIds(); |
| ejbHomeRepositoryId = ejbHomeTypeIds[0]; |
| |
| ejbObjectStubFactory = sff.createStubFactory(remoteIntf.getName(), false, "", null, appClassLoader); |
| |
| String[] ejbObjectTypeIds = ejbObjectStubFactory.getTypeIds(); |
| |
| ejbObjectRepositoryId = ejbObjectTypeIds[0]; |
| |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, ".setRepositoryIds:" + " " + "{0} {1}", |
| new Object[] {ejbHomeRepositoryId, ejbObjectRepositoryId}); |
| } |
| |
| try { |
| |
| ejbHomeReferenceFactory = createReferenceFactory(poaId_EJBHome, ejbHomeRepositoryId); |
| ejbObjectReferenceFactory = createReferenceFactory(poaId_EJBObject, ejbObjectRepositoryId); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| |
| if (!isRemoteHomeView) { |
| remoteBusinessIntf = remoteIntf.getName(); |
| } |
| } |
| |
| @Override |
| public void cleanupClass(Class clazz) { |
| try { |
| presentationMgr.flushClass(clazz); |
| } catch(Exception e) { |
| logger.log(Level.FINE, "cleanupClass error", e); |
| } |
| } |
| |
| private ReferenceFactory createReferenceFactory(String poaId, String repoid ) throws Exception { |
| try { |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, ".createReferenceFactory->: {0} {1}", new Object[] {poaId, repoid}); |
| } |
| |
| ReferenceFactoryManager rfm |
| = (ReferenceFactoryManager) orb.resolve_initial_references(ORBConstants.REFERENCE_FACTORY_MANAGER); |
| |
| List<Policy> policies = new ArrayList<>(); |
| |
| // Servant caching for local RMI-IIOP invocation performance |
| policies.add(ServantCachingPolicy.getPolicy()); |
| |
| // OTS Policy |
| policies.add(new OTSPolicyImpl()); |
| |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, ".createReferenceFactory: {0} {1}: {2}", |
| new Object[] {poaId, repoid, ejbDescriptor}); |
| |
| } |
| |
| // CSIv2 Policy |
| policies.add(new CSIv2Policy(ejbDescriptor)); |
| |
| String threadPoolName = container.getUseThreadPoolId(); |
| int threadPoolNumericID = 0; |
| boolean usePassByReference = container.getPassByReference(); |
| |
| if (usePassByReference) { |
| policies.add(new CopyObjectPolicy(PASS_BY_REFERENCE_ID)); |
| } |
| |
| if (threadPoolName != null) { |
| ThreadPoolManager threadPoolManager = S1ASThreadPoolManager.getThreadPoolManager(); |
| try { |
| threadPoolNumericID = threadPoolManager.getThreadPoolNumericId( |
| threadPoolName); |
| policies.add(new RequestPartitioningPolicy(threadPoolNumericID)); |
| } catch (Exception ex) { |
| logger.log(Level.WARNING, "Not using threadpool-request-partitioning...", ex); |
| } |
| } |
| |
| /** TODO |
| logger.log(Level.INFO, "POARemoteRefFactory checking if SFSBVersionPolicy need to be added"); |
| EJBServerConfigLookup ejbConfigLookup = |
| new EJBServerConfigLookup(ejbDescriptor); |
| boolean addSFSBVersionPolicy = EJBServerConfigLookup.needToAddSFSBVersionInterceptors(); |
| logger.log(Level.INFO, "POARemoteRefFactory addSFSBVersionPolicy? " + addSFSBVersionPolicy); |
| if (addSFSBVersionPolicy) { |
| if (container instanceof StatefulSessionContainer) { |
| StatefulSessionContainer sfsbCon = (StatefulSessionContainer) container; |
| if (sfsbCon.isHAEnabled()) { |
| policies.add(new SFSBVersionPolicy(ejbDescriptor.getUniqueId())); |
| } |
| } |
| } |
| **/ |
| |
| if (logger.isLoggable(Level.FINE)) { |
| String jndiName = ejbDescriptor.getJndiName(); |
| logger.log(Level.FINE, "Using Thread-Pool: [{0} ==> {1}] for jndi name: {2}", |
| new Object[] {threadPoolName, threadPoolNumericID, jndiName}); |
| logger.log(Level.FINE, "Pass by reference: [{0}] for jndi name: {1}", |
| new Object[] {usePassByReference, usePassByReference}); |
| } |
| |
| // DisableClearTextIIOP policy which sets IIOP Profile port to 0 |
| // if EJB allows only SSL invocations |
| if (ejbDescriptor.allMechanismsRequireSSL()) { |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.WARNING, ".createReferenceFactory: {0} {1}: adding ZeroPortPolicy", |
| new Object[] {poaId, repoid}); |
| } |
| policies.add(ZeroPortPolicy.getPolicy()); |
| } |
| |
| |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, ".createReferenceFactory: {0} {1}: policies: {2}", |
| new Object[] {poaId, repoid, policies}); |
| } |
| |
| ReferenceFactory rf = rfm.create(poaId, repoid, policies, this); |
| return rf; |
| } finally { |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.WARNING, ".createReferenceFactory<-: {0} {1}", new Object[] {poaId, repoid}); |
| } |
| } |
| } |
| |
| |
| @Override |
| public java.rmi.Remote createRemoteReference(byte[] instanceKey) { |
| return createRef(instanceKey, ejbObjectReferenceFactory, ejbObjectStubFactory, ejbObjectRepositoryId); |
| } |
| |
| |
| @Override |
| public Remote createHomeReference(byte[] homeKey) { |
| return createRef(homeKey, ejbHomeReferenceFactory, ejbHomeStubFactory, ejbHomeRepositoryId); |
| } |
| |
| |
| private void setClassLoader() { |
| ClassLoader cl; |
| SecurityManager sman = System.getSecurityManager(); |
| if (sman == null) { |
| cl = this.getClass().getClassLoader(); |
| } else { |
| cl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { |
| |
| @Override |
| public ClassLoader run() { |
| return this.getClass().getClassLoader(); |
| } |
| }); |
| } |
| |
| Wrapper._setClassLoader(cl); |
| } |
| |
| |
| private Remote createRef(byte[] instanceKey, ReferenceFactory rf, PresentationManager.StubFactory stubFactory, |
| String repoid) { |
| try { |
| PresentationManager.StubFactory stubFact = stubFactory; |
| org.omg.CORBA.Object ref = _createRef(rf, instanceKey, repoid); |
| |
| // Set the ClassLoader to the ClassLoader for this class, |
| // which is loaded by the OSGi bundle ClassLoader for the |
| // orb-iiop bundle, which depends on (among others) the |
| // glassfish-corba-codegen bundle, which contains the |
| // CodegenProxyStub class needed inside the makeStub call. |
| setClassLoader(); |
| |
| org.omg.CORBA.Object stub = stubFact.makeStub(); |
| Delegate delegate = StubAdapter.getDelegate(ref); |
| StubAdapter.setDelegate(stub, delegate); |
| |
| return (Remote) stub; |
| |
| } catch (Exception e) { |
| logger.log(Level.SEVERE, "iiop.createreference_exception", e.toString()); |
| throw new RuntimeException("Unable to create reference ", e); |
| } |
| } |
| |
| // NOTE: The repoid is only needed for logging. |
| private org.omg.CORBA.Object _createRef(ReferenceFactory rf, byte[] instanceKey, String repoid) throws Exception { |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, "\t\tIn POARemoteReferenceFactory._createRef, repositoryId = {0}", repoid); |
| } |
| |
| // Create the ejbKey using EJB's unique id + instanceKey |
| byte[] ejbKey = createEJBKey(ejbDescriptor.getUniqueId(), instanceKey); |
| |
| org.omg.CORBA.Object obj = rf.createReference(ejbKey); |
| |
| return obj; |
| } |
| |
| |
| private byte[] createEJBKey(long ejbId, byte[] instanceKey) { |
| byte[] ejbkey = new byte[INSTANCEKEY_OFFSET + instanceKey.length]; |
| |
| Utility.longToBytes(ejbId, ejbkey, EJBID_OFFSET); |
| Utility.intToBytes(instanceKey.length, ejbkey, INSTANCEKEYLEN_OFFSET); |
| System.arraycopy(instanceKey, 0, ejbkey, INSTANCEKEY_OFFSET, instanceKey.length); |
| return ejbkey; |
| } |
| |
| |
| /** |
| * Disconnect an EJBObject or EJBHome from the ORB. |
| */ |
| @Override |
| public void destroyReference(Remote remoteRef, Remote remoteObj) { |
| // Note: the POAs have the NON_RETAIN policy so they dont maintain |
| // any state for objects. We only need to unexport the object from |
| // the RMI/IIOP machinery. |
| // The following call also does tie.deactivate() for the remoteObj's tie |
| try { |
| Util.unexportObject(remoteObj); |
| } catch ( RuntimeException ex ) { |
| // A bug in Util.unexportObject causes this exception |
| // Ignore it. |
| } catch ( java.lang.Exception nsoe ){ |
| // eat it and ignore it. |
| } |
| } |
| |
| /** |
| * This is the implementation of ServantLocator.preinvoke() |
| * It is called from the POA before every remote invocation. |
| * Return a POA Servant (which is the RMI/IIOP Tie for EJBObject/EJBHome). |
| * @param ejbKey |
| * @param cookieHolder |
| */ |
| @Override |
| public Servant preinvoke(byte[] ejbKey, POA adapter, String operation, CookieHolder cookieHolder) |
| throws org.omg.PortableServer.ForwardRequest { |
| if (logger.isLoggable(Level.FINE)) { |
| logger.log(Level.FINE, "In preinvoke for operation:{0}", operation); |
| } |
| |
| // get instance key |
| int keyLen = Utility.bytesToInt(ejbKey, INSTANCEKEYLEN_OFFSET); |
| byte[] instanceKey = new byte[keyLen]; |
| System.arraycopy(ejbKey, INSTANCEKEY_OFFSET, instanceKey, 0, keyLen); |
| |
| Servant servant = null; |
| try { |
| while ( servant == null ) { |
| // get the EJBObject / EJBHome |
| Remote targetObj = container.getTargetObject(instanceKey, (isRemoteHomeView ? null : remoteBusinessIntf)); |
| |
| // This could be null in rare cases for sfsbs and entity |
| // beans. It would be preferable to push the retry logic |
| // within the sfsb container and entity container |
| // implementations of getTargetObject, but for now let's keep |
| // the looping logic the same as it has always been. |
| if( targetObj != null ) { |
| // get the Tie which is the POA Servant |
| //fix for bug 6484935 |
| @SuppressWarnings("unchecked") |
| Tie tie = (Tie)AccessController.doPrivileged( |
| new PrivilegedAction() { |
| @Override |
| public Tie run() { |
| return presentationMgr.getTie(); |
| } |
| }); |
| |
| tie.setTarget(targetObj); |
| servant = (Servant) tie; |
| } |
| } |
| } catch (NoSuchObjectLocalException e) { |
| logger.log(Level.SEVERE,"iiop.gettie_exception", e); |
| throw new OBJECT_NOT_EXIST( GET_TIE_EXCEPTION_CODE, |
| CompletionStatus.COMPLETED_NO); |
| } catch (RuntimeException e) { |
| logger.log(Level.SEVERE,"iiop.runtime_exception", e); |
| throw e; |
| } |
| |
| return servant; |
| } |
| |
| @Override |
| public void postinvoke(byte[] ejbKey, POA adapter, String operation, java.lang.Object cookie, Servant servant) { |
| Remote target = null; |
| if ( servant != null ) { |
| target = ((Tie)servant).getTarget(); |
| } |
| |
| // Always release, since that restores previous context class loader. |
| container.releaseTargetObject(target); |
| } |
| |
| |
| @Override |
| public void destroy() { |
| try { |
| ejbHomeReferenceFactory.destroy() ; |
| ejbObjectReferenceFactory.destroy() ; |
| ejbHomeReferenceFactory = null ; |
| ejbObjectReferenceFactory = null ; |
| |
| container = null; |
| ejbDescriptor = null; |
| |
| orb = null; |
| protocolMgr = null; |
| |
| } catch (Throwable th) { |
| logger.log(Level.SEVERE, "Exception during " + "POARemoteRefFactory::destroy()", th); |
| } |
| } |
| |
| @Override |
| public boolean hasSameContainerID(org.omg.CORBA.Object obj) throws Exception { |
| boolean result = false; |
| try { |
| IOR ior = (orb).getIOR(obj, false); |
| java.util.Iterator iter = ior.iterator(); |
| |
| byte[] oid = null; |
| if (iter.hasNext()) { |
| TaggedProfile profile = (TaggedProfile) iter.next(); |
| ObjectKey objKey = profile.getObjectKey(); |
| oid = objKey.getId().getId(); |
| } |
| |
| if (oid != null && oid.length > INSTANCEKEY_OFFSET) { |
| long cid = Utility.bytesToLong(oid, EJBID_OFFSET); |
| // To be really sure that is indeed a ref generated |
| // by our container we do the following checks |
| int keyLen = Utility.bytesToInt(oid, INSTANCEKEYLEN_OFFSET); |
| if (oid.length == keyLen + INSTANCEKEY_OFFSET) { |
| result = (cid == ejbDescriptor.getUniqueId() ); |
| } |
| if (logger.isLoggable(Level.FINE)) { |
| StringBuilder sbuf = new StringBuilder(); |
| sbuf.append("hasSameContainerID() result: ").append(result) |
| .append("; because ==> oid.length: ").append(oid.length) |
| .append("; instance-key-length: ").append(keyLen) |
| .append("; expected oid.length: ") |
| .append(keyLen).append("+").append(INSTANCEKEY_OFFSET) |
| .append("; myContainrID: ") |
| .append(ejbDescriptor.getUniqueId()) |
| .append("; obj.containerID: ") |
| .append(cid); |
| logger.log(Level.FINE, sbuf.toString()); |
| } |
| } else { |
| if (logger.isLoggable(Level.FINE)) { |
| if (oid == null) { |
| logger.log(Level.FINE, "hasSameContainerID() failed because oid=null"); |
| } else { |
| logger.log(Level.FINE, |
| "hasSameContainerID() failed because oid.length={0}; but INSTANCE_KEY_OFFSET= {1}", |
| new Object[] {oid.length, INSTANCEKEY_OFFSET}); |
| } |
| } |
| } |
| } catch (Exception ex) { |
| logger.log(Level.FINE, "Exception while checking for same containerID", ex); |
| throw ex; |
| } |
| |
| return result; |
| } |
| |
| private void writeObject(ObjectOutputStream out) throws IOException { |
| throw new NotSerializableException(); |
| } |
| |
| private void readObject(ObjectInputStream in) throws IOException { |
| throw new NotSerializableException(); |
| } |
| } |