| /* |
| * 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 org.apache.yoko.orb.OB; |
| |
| import org.apache.yoko.orb.OCI.GiopVersion; |
| import org.omg.SendingContext.CodeBaseHelper; |
| |
| final class GIOPClient extends Client { |
| protected ORBInstance orbInstance_; // The ORB instance |
| |
| protected int nextRequestId_; // The next request ID |
| |
| protected java.lang.Object nextRequestIdMutex_ = new java.lang.Object(); // The |
| // next |
| // request |
| // ID |
| // mutex |
| |
| protected org.apache.yoko.orb.OCI.Connector connector_; // The connector |
| |
| protected GIOPConnection connection_; |
| |
| // |
| // Codesets SC |
| // |
| protected org.omg.IOP.ServiceContext codeSetSC_; |
| |
| protected org.omg.IOP.ServiceContext codeBaseSC_; |
| |
| protected boolean bidirWorker_; // is the worker bidir? |
| |
| protected boolean ownsWorker_; // does 'this' own the worker? |
| |
| protected boolean destroy_; // True if destroy() was called |
| |
| // ---------------------------------------------------------------------- |
| // GIOPClient private and protected member implementations |
| // ---------------------------------------------------------------------- |
| |
| protected void finalize() throws Throwable { |
| Assert._OB_assert(destroy_); |
| Assert._OB_assert(connection_ == null); |
| |
| super.finalize(); |
| } |
| |
| // |
| // uses the prepopulated connector_ (not connected) to do a lookup, |
| // checking if a bidir connection alias exists... it returns it if |
| // it does and returns null otherwise |
| // |
| protected GIOPConnection find_bidir_worker() { |
| try { |
| // |
| // Any transport that we want to query should exist when the |
| // server first receives a request from the client-side. This |
| // transport will have the ListenPointList populated inside of |
| // its TransportInfo. So we query the list of |
| // GIOPServerStarters for the correct transport and hopefully |
| // find a match if we want to use bidir |
| // |
| org.apache.yoko.orb.OBPortableServer.POAManagerFactory pmFactoryImpl = orbInstance_ |
| .getPOAManagerFactory(); |
| |
| // |
| // Obtain a list of POAs for this POAManager |
| // |
| org.omg.PortableServer.POAManager[] pmSeq = pmFactoryImpl.list(); |
| |
| for (int i = 0; i < pmSeq.length; i++) { |
| org.apache.yoko.orb.OBPortableServer.POAManager_impl poaImpl = (org.apache.yoko.orb.OBPortableServer.POAManager_impl) pmSeq[i]; |
| |
| // |
| // Get the server manager from the POA |
| // |
| org.apache.yoko.orb.OB.ServerManager sm = poaImpl |
| ._OB_getServerManager(); |
| |
| // |
| // get the list of servers from the server manager |
| // |
| org.apache.yoko.orb.OB.Server[] servSeq = sm.getServers(); |
| |
| // |
| // iterate these servers obtaining the GIOPServerStarter |
| // |
| for (int j = 0; j < servSeq.length; j++) { |
| org.apache.yoko.orb.OB.GIOPServer giopServer = (org.apache.yoko.orb.OB.GIOPServer) servSeq[j]; |
| |
| org.apache.yoko.orb.OB.GIOPServerStarter servStarter = giopServer |
| ._OB_getGIOPServerStarter(); |
| |
| // |
| // get the matching worker from the GIOPServerStarter |
| // |
| GIOPConnection gw = servStarter.getWorker(connectorInfo()); |
| |
| if (gw != null) |
| return gw; |
| } |
| } |
| } catch (ClassCastException ex) { |
| } |
| |
| // |
| // nothing was found to return |
| // |
| return null; |
| } |
| |
| // |
| // Get the worker. If the bool flag is true, and no worker exists, |
| // a new worker is created, with the timeout specified as second |
| // parameter. |
| // |
| protected synchronized GIOPConnection getWorker(boolean create, int t) { |
| if (destroy_) |
| throw new org.omg.CORBA.INITIALIZE(org.apache.yoko.orb.OB.MinorCodes |
| .describeInitialize(org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed), |
| org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed, |
| org.omg.CORBA.CompletionStatus.COMPLETED_NO); |
| |
| // |
| // first attempt to locate a reusable bidir connection |
| // |
| if (connection_ == null) { |
| connection_ = find_bidir_worker(); |
| |
| if (connection_ != null) { |
| // |
| // adjust the requestID to match the spec (even for |
| // clients, odd for servers) |
| // |
| if ((nextRequestId_ & 1) == 0) |
| nextRequestId_++; |
| ownsWorker_ = false; |
| connection_.activateClientSide(this); |
| |
| // |
| // log the reusing of the connection |
| // |
| CoreTraceLevels coreTraceLevels = orbInstance_ |
| .getCoreTraceLevels(); |
| if (coreTraceLevels.traceConnections() > 0) { |
| org.apache.yoko.orb.OCI.TransportInfo info = connection_ |
| .transport().get_info(); |
| String msg = "reusing established bidir connection\n"; |
| msg += info.describe(); |
| orbInstance_.getLogger().trace("outgoing", msg); |
| } |
| } |
| } |
| |
| // |
| // no bidir connection resolved so create one if the request |
| // calls for it |
| // |
| if (connection_ == null && create) { |
| // |
| // Trace connection attempt |
| // |
| CoreTraceLevels coreTraceLevels = orbInstance_.getCoreTraceLevels(); |
| if (coreTraceLevels.traceConnections() > 0) { |
| org.apache.yoko.orb.OCI.ConnectorInfo info = connector_ |
| .get_info(); |
| String msg = "trying to establish connection\n"; |
| msg += "timeout: "; |
| if (t >= 0) { |
| msg += t; |
| msg += "ms\n"; |
| } else |
| msg += "none\n"; |
| msg += info.describe(); |
| orbInstance_.getLogger().trace("outgoing", msg); |
| } |
| |
| // |
| // Create new transport, using the connector |
| // |
| // For symetry reasons, GIOPClientStarterThreaded should also be |
| // added, even though these classes only have a trivial |
| // functionality. Or perhaps the GIOPClientStarterThreaded tries to |
| // connect() in the backgound? Just an idea... |
| // |
| |
| org.apache.yoko.orb.OCI.Transport transport; |
| |
| if (t >= 0) { |
| transport = connector_.connect_timeout(t); |
| |
| // |
| // Was there a timeout? |
| // |
| if (transport == null) |
| throw new org.omg.CORBA.NO_RESPONSE("Connection timeout", |
| 0, org.omg.CORBA.CompletionStatus.COMPLETED_NO); |
| } else { |
| transport = connector_.connect(); |
| Assert._OB_assert(transport != null); |
| } |
| |
| // |
| // Create new worker |
| // |
| Assert._OB_assert(concModel_ == Threaded); |
| connection_ = new GIOPConnectionThreaded(orbInstance_, transport, |
| this); |
| ownsWorker_ = true; |
| |
| // |
| // bidirWorker_ means that this connection may be used to |
| // service requests so we need to set ourselves up as a |
| // server (to correct map the OAInterfaces) |
| // |
| if (bidirWorker_) |
| connection_.activateServerSide(); |
| } |
| |
| // |
| // Lazy initialization of codeSetSC_. We don't want to |
| // initialize codeSetSC_ in the constructor, in order to |
| // keep creation of GIOPClients, which might later on not |
| // be used, as light-weight as possible. |
| // |
| initServiceContexts(); |
| |
| return connection_; |
| } |
| |
| // |
| // initialize internal service contexts |
| private void initServiceContexts() { |
| if (codeSetSC_ == null) { |
| // |
| // Create CONV_FRAME::CodeSetContext |
| // |
| org.omg.CONV_FRAME.CodeSetContext ctx = new org.omg.CONV_FRAME.CodeSetContext(); |
| CodeConverters conv = codeConverters(); |
| |
| if (conv.outputCharConverter != null) |
| ctx.char_data = conv.outputCharConverter.getTo().rgy_value; |
| else |
| ctx.char_data = CodeSetDatabase.ISOLATIN1; |
| |
| if (conv.outputWcharConverter != null) |
| ctx.wchar_data = conv.outputWcharConverter.getTo().rgy_value; |
| else |
| ctx.wchar_data = orbInstance_.getNativeWcs(); |
| |
| // |
| // Create encapsulation for CONV_FRAME::CodeSetContext |
| // |
| org.apache.yoko.orb.OCI.Buffer buf = new org.apache.yoko.orb.OCI.Buffer(); |
| org.apache.yoko.orb.CORBA.OutputStream outCSC = new org.apache.yoko.orb.CORBA.OutputStream( |
| buf); |
| outCSC._OB_writeEndian(); |
| org.omg.CONV_FRAME.CodeSetContextHelper.write(outCSC, ctx); |
| |
| // |
| // Create service context containing the |
| // CONV_FRAME::CodeSetContext encapsulation |
| // |
| codeSetSC_ = new org.omg.IOP.ServiceContext(); |
| codeSetSC_.context_id = org.omg.IOP.CodeSets.value; |
| |
| int len = buf.length(); |
| byte[] data = buf.data(); |
| codeSetSC_.context_data = new byte[len]; |
| System.arraycopy(data, 0, codeSetSC_.context_data, 0, len); |
| } |
| if (codeBaseSC_ == null) { |
| |
| javax.rmi.CORBA.ValueHandler valueHandler = javax.rmi.CORBA.Util.createValueHandler(); |
| org.omg.SendingContext.CodeBase codeBase = CodeBaseHelper.narrow(valueHandler.getRunTimeCodeBase()); |
| |
| |
| org.apache.yoko.orb.OCI.Buffer buf = new org.apache.yoko.orb.OCI.Buffer(); |
| org.apache.yoko.orb.CORBA.OutputStream outCBC = new org.apache.yoko.orb.CORBA.OutputStream( |
| buf); |
| outCBC._OB_writeEndian(); |
| org.omg.SendingContext.CodeBaseHelper.write(outCBC, codeBase); |
| |
| codeBaseSC_ = new org.omg.IOP.ServiceContext(); |
| codeBaseSC_.context_id = org.omg.IOP.SendingContextRunTime.value; |
| |
| int len = buf.length(); |
| byte[] data = buf.data(); |
| codeBaseSC_.context_data = new byte[len]; |
| System.arraycopy(data, 0, codeBaseSC_.context_data, 0, len); |
| } |
| // |
| // NOTE: We don't initialize the INVOCATION_POLICIES service context |
| // here because the list of policies can change from one invocation to |
| // the next. Instead, we need to get the policies and build the |
| // service context each time we make an invocation. |
| // |
| } |
| |
| // ---------------------------------------------------------------------- |
| // GIOPClient package member implementations |
| // ---------------------------------------------------------------------- |
| |
| GIOPClient(ORBInstance orbInstance, |
| org.apache.yoko.orb.OCI.Connector connector, int concModel, |
| CodeConverters conv, boolean bidirEnable) { |
| super(concModel, conv); |
| orbInstance_ = orbInstance; |
| nextRequestId_ = 0; |
| connector_ = connector; |
| connection_ = null; |
| destroy_ = false; |
| bidirWorker_ = bidirEnable; |
| ownsWorker_ = true; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // GIOPClient public member implementations |
| // ---------------------------------------------------------------------- |
| |
| // |
| // Destroy the client |
| // |
| public void destroy(boolean terminate) { |
| GIOPConnection c = null; |
| |
| synchronized (this) { |
| // |
| // Don't destroy twice |
| // |
| if (destroy_) |
| return; |
| |
| // |
| // Set the destroy flag |
| // |
| destroy_ = true; |
| |
| // |
| // Use a copy of the worker, and destroy the worker outside |
| // the synchronization, to avoid deadlocks |
| // |
| c = connection_; |
| connection_ = null; |
| } |
| |
| // |
| // If there is a worker (and we exclusively own it) destroy it |
| // |
| if (c != null && ownsWorker_) |
| c.destroy(terminate); |
| } |
| |
| public synchronized void removeConnection(GIOPConnection connection) { |
| if (connection_ == connection) |
| connection_ = null; |
| } |
| |
| // |
| // Get a new request ID |
| // |
| public int requestId() { |
| synchronized (nextRequestIdMutex_) { |
| // |
| // In the case of BiDir connections, the client should use |
| // even numbered requestIds and the server should use odd |
| // numbered requestIds... the += 2 keeps this pattern intact |
| // assuming its correct at startup |
| // |
| return nextRequestId_ += 2; |
| } |
| } |
| |
| // |
| // get a list of ServiceContexts that have to be sent on an AMI router |
| // request |
| // |
| public org.omg.IOP.ServiceContext[] getAMIRouterSCL() { |
| // |
| // initialize the service contexts if they haven't already been |
| // |
| initServiceContexts(); |
| |
| org.omg.IOP.ServiceContext[] scl = new org.omg.IOP.ServiceContext[1]; |
| scl[0] = codeSetSC_; |
| |
| // |
| // return the list |
| // |
| return scl; |
| } |
| |
| // |
| // Get all profiles that are usable with this client |
| // |
| public org.apache.yoko.orb.OCI.ProfileInfo[] getUsableProfiles( |
| org.omg.IOP.IOR ior, org.omg.CORBA.Policy[] policies) { |
| // |
| // Get all profiles usable for the connector |
| // |
| org.apache.yoko.orb.OCI.ProfileInfo[] all = connector_ |
| .get_usable_profiles(ior, policies); |
| |
| // |
| // Filter out profiles which would require a different code converter |
| // |
| java.util.Vector vec = new java.util.Vector(); |
| for (int i = 0; i < all.length; i++) { |
| CodeConverters conv = CodeSetUtil.getCodeConverters(orbInstance_, |
| all[i]); |
| if (codeConverters().equals(conv)) |
| vec.addElement(all[i]); |
| } |
| |
| org.apache.yoko.orb.OCI.ProfileInfo[] result = new org.apache.yoko.orb.OCI.ProfileInfo[vec |
| .size()]; |
| vec.copyInto(result); |
| return result; |
| } |
| |
| // |
| // Get the OCI Connector info |
| // |
| public org.apache.yoko.orb.OCI.ConnectorInfo connectorInfo() { |
| return connector_.get_info(); |
| } |
| |
| // |
| // Get the OCI Transport info |
| // |
| public org.apache.yoko.orb.OCI.TransportInfo transportInfo() { |
| // |
| // Get the connection, but do not create a new one if there is none |
| // available |
| // |
| GIOPConnection connection = getWorker(false, -1); |
| |
| if (connection == null) |
| return null; |
| |
| org.apache.yoko.orb.OCI.Transport transport = connection.transport(); |
| return transport.get_info(); |
| } |
| |
| // |
| // Start a downcall, returning a downcall emitter and an |
| // OutputStream for marshalling a request |
| // |
| public DowncallEmitter startDowncall(Downcall down, |
| org.apache.yoko.orb.CORBA.OutputStreamHolder out) { |
| GIOPConnection connection = null; |
| try { |
| // |
| // Get the worker, creating a new one if there is none |
| // available |
| // |
| connection = getWorker(true, down.policies().connectTimeout); |
| } catch (org.omg.CORBA.SystemException ex) { |
| Assert |
| ._OB_assert(ex.completed == org.omg.CORBA.CompletionStatus.COMPLETED_NO); |
| down.setFailureException(ex); |
| return null; |
| } |
| |
| try { |
| // |
| // We only need to add a code set context if we're GIOP |
| // version 1.1 or higher, and if no messages have been sent so |
| // far |
| // |
| byte major = down.profileInfo().major; |
| byte minor = down.profileInfo().minor; |
| if (!connection.requestSent() && (major > 1 || minor >= 1)) { |
| CoreTraceLevels coreTraceLevels = orbInstance_ |
| .getCoreTraceLevels(); |
| if (coreTraceLevels.traceConnections() >= 2) { |
| CodeConverters conv = codeConverters(); |
| String msg = "sending transmission code sets"; |
| msg += "\nchar code set: "; |
| if (conv.outputCharConverter != null) |
| msg += conv.outputCharConverter.getTo().description; |
| else { |
| CodeSetInfo info = CodeSetDatabase.instance() |
| .getCodeSetInfo(orbInstance_.getNativeCs()); |
| msg += info.description; |
| } |
| msg += "\nwchar code set: "; |
| if (conv.outputWcharConverter != null) |
| msg += conv.outputWcharConverter.getTo().description; |
| else { |
| CodeSetInfo info = CodeSetDatabase.instance() |
| .getCodeSetInfo(orbInstance_.getNativeWcs()); |
| msg += info.description; |
| } |
| orbInstance_.getLogger().trace("outgoing", msg); |
| } |
| |
| Assert._OB_assert(codeSetSC_ != null); |
| down.addToRequestSCL(codeSetSC_); |
| |
| Assert._OB_assert(codeBaseSC_ != null); |
| down.addToRequestSCL(codeBaseSC_); |
| |
| } |
| |
| // |
| // I don't want to send BiDir related contexts if I'm not |
| // working with GIOP 1.2 or greater. |
| // |
| boolean validGIOPVersion = false; |
| if ((major > 1) || ((major == 1) && (minor >= 2))) |
| validGIOPVersion = true; |
| |
| if (validGIOPVersion |
| && (down.policies().biDirMode == org.omg.BiDirPolicy.BOTH.value)) { |
| org.apache.yoko.orb.OCI.Transport t = connection.transport(); |
| |
| org.omg.IOP.ServiceContext contexts[] = t.get_info() |
| .get_service_contexts(down.policies().value); |
| for (int i = 0; i < contexts.length; i++) |
| down.addToRequestSCL(contexts[i]); |
| } |
| |
| org.apache.yoko.orb.OCI.ProfileInfo profileInfo = down |
| .profileInfo(); |
| org.apache.yoko.orb.OCI.Buffer buf = new org.apache.yoko.orb.OCI.Buffer( |
| 12); |
| buf.pos(12); |
| out.value = new org.apache.yoko.orb.CORBA.OutputStream(buf, |
| codeConverters(), GiopVersion.get(profileInfo.major, profileInfo.minor)); |
| |
| // |
| // Create GIOP outgoing message |
| // |
| GIOPOutgoingMessage outgoing = new GIOPOutgoingMessage( |
| orbInstance_, out.value, profileInfo); |
| |
| // |
| // Write header |
| // |
| String op = down.operation(); |
| if (op.charAt(0) == '_' && op.equals("_locate")) |
| outgoing.writeLocateRequestHeader(down.requestId()); |
| else |
| outgoing.writeRequestHeader(down.requestId(), down.operation(), |
| down.responseExpected(), down.getRequestSCL()); |
| |
| return connection.emitterInterface(); |
| } catch (org.omg.CORBA.SystemException ex) { |
| Assert |
| ._OB_assert(ex.completed == org.omg.CORBA.CompletionStatus.COMPLETED_NO); |
| down.setFailureException(ex); |
| return null; |
| } |
| } |
| |
| // |
| // Checks whether this client is equal to another client |
| // |
| public boolean matches(Client other) { |
| if (!!!(other instanceof GIOPClient)) return false; |
| GIOPClient that = (GIOPClient) other; |
| |
| return this.connector_.equal(that.connector_) && this.codeConverters().equals(that.codeConverters()); |
| } |
| |
| // |
| // Force connection establishment |
| // |
| public void bind(int connectTimeout) { |
| // |
| // Get the connection, creating a new one if there is none |
| // available |
| // |
| getWorker(true, connectTimeout); |
| } |
| |
| // |
| // Determines whether this client supports twoway invocations |
| // |
| public boolean twoway() { |
| // |
| // Get the connection |
| // |
| GIOPConnection connection = getWorker(false, -1); |
| Assert._OB_assert(connection != null); |
| org.apache.yoko.orb.OCI.Transport transport = connection.transport(); |
| return transport.mode() == org.apache.yoko.orb.OCI.SendReceiveMode.SendReceive; |
| } |
| |
| // |
| // determines whether this GIOPClient exclusively owns its worker or |
| // if its shared with another Client/Server |
| // |
| public boolean sharedConnection() { |
| return !ownsWorker_; |
| } |
| } |