com.ibm.as400.access.AS400ThreadedServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400 Show documentation
Show all versions of jt400 Show documentation
The Open Source version of the IBM Toolbox for Java
The newest version!
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400ThreadedServer.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2003 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.io.IOException;
import java.net.SocketException;
import java.util.Hashtable;
final class AS400ThreadedServer extends AS400Server implements Runnable
{
private static int threadCount_ = 0;
private AS400ImplRemote system_;
private boolean disconnecting_ = false;
private Hashtable replyStreams_;
private Hashtable instanceReplyStreams_ = new Hashtable();
private Thread readDaemon_ = null;
private IOException readDaemonException_ = null;
private RuntimeException unlikelyException_ = null;
private DataStream exchangeAttrReply_ = null;
// Vectors are slow, but Object arrays are big, so we implement our own hashtables to compromise.
private final ReplyList replyList_ = new ReplyList();
private static final class DataStreamCollection
{
DataStream[] chain_;
DataStreamCollection(DataStream ds)
{
chain_ = new DataStream[] { ds };
}
}
private static final class ReplyList
{
final DataStreamCollection[] streams_ = new DataStreamCollection[16];
private DiscardList discardList_;
final void add(DataStream ds)
{
int id = ds.getCorrelation();
if (discardList_.remove(id))
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "ReplyList: Discarded datastream:", id);
ClassDecoupler.freeDBReplyStream(ds); ds = null;
return;
}
int hash = ds.getCorrelation() % 16;
// Use the collection object for synchronization to prevent bottlenecks.
DataStreamCollection coll = streams_[hash];
if (coll == null)
{
streams_[hash] = new DataStreamCollection(ds);
return;
}
synchronized (coll)
{
DataStream[] chain = coll.chain_;
int max = chain.length;
for (int i = 0; i < max; ++i)
{
if (chain[i] == null)
{
chain[i] = ds;
return;
}
}
DataStream[] newChain = new DataStream[max * 2];
System.arraycopy(chain, 0, newChain, 0, max);
newChain[max] = ds;
coll.chain_ = newChain;
}
}
final DataStream remove(int correlation)
{
int hash = correlation % 16;
DataStreamCollection coll = streams_[hash];
if (coll == null) return null;
// Use the collection object for synchronization to prevent bottlenecks.
synchronized (coll)
{
DataStream[] chain = coll.chain_;
for (int i = 0; i < chain.length; ++i)
{
if (chain[i] != null && chain[i].getCorrelation() == correlation)
{
DataStream ds = chain[i];
// Move up the remaining entries because chained replies have the same correlation ID and need to remain in chronological order.
if (i + 1 < chain.length)
{
System.arraycopy(chain, i + 1, chain, i, chain.length - i - 1);
}
// Set last element to null.
chain[chain.length - 1] = null;
return ds;
}
}
return null;
}
}
void setDiscardList(DiscardList discardList)
{
discardList_ = discardList;
}
}
private final DiscardList discardList_ = new DiscardList();
private static final class DiscardList
{
int[] ids_ = new int[8];
final Object idsLock_ = new Object();
private ReplyList replyList_;
final void add(int correlation)
{
DataStream ds = replyList_.remove(correlation);
if (ds != null)
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "DiscardList: Discarded datastream:", correlation);
ClassDecoupler.freeDBReplyStream(ds); ds = null;
return;
}
synchronized (idsLock_)
{
final int max = ids_.length;
for (int i = 0; i < max; ++i)
{
if (ids_[i] == 0)
{
ids_[i] = correlation; // We don't allow 0 as a valid correlation ID.
return;
}
}
int[] newIds = new int[max * 2];
System.arraycopy(ids_, 0, newIds, 0, max);
newIds[max] = correlation;
ids_ = newIds;
}
}
final boolean remove(int correlation)
{
final int max = ids_.length;
for (int i = 0; i < max; ++i)
{
if (ids_[i] == correlation)
{
ids_[i] = 0;
return true;
}
}
return false;
}
void setReplyList(ReplyList replyList)
{
replyList_ = replyList;
}
}
private int lastCorrelationId_ = 0;
private class CorrelationIdLock extends Object {}
private class ReceiveLock extends Object {}
private final CorrelationIdLock correlationIdLock_ = new CorrelationIdLock();
private final ReceiveLock receiveLock_ = new ReceiveLock();
AS400ThreadedServer(AS400ImplRemote system, int service, SocketContainer socket, String jobString) throws IOException
{
system_ = system;
service_ = service;
jobString_ = jobString;
socket_ = socket;
connectionID_ = socket_.hashCode();
inStream_ = socket_.getInputStream();
outStream_ = socket_.getOutputStream();
replyStreams_ = AS400Server.replyStreamsHashTables[service];
discardList_.setReplyList(replyList_);
replyList_.setDiscardList(discardList_);
String jobID;
if (jobString != null && jobString.length() != 0) jobID = jobString;
else jobID = AS400.getServerName(service) + "/" + (++threadCount_);
readDaemon_ = new Thread(this, "AS400 Read Daemon [system:"+system.getSystemName() + ";job:" + jobID + "]");
readDaemon_.setDaemon(true);
readDaemon_.start();
}
// Print is the only service that uses this method.
@Override
final void addInstanceReplyStream(DataStream replyStream)
{
instanceReplyStreams_.put(replyStream, replyStream);
}
// Print is the only service that uses this method.
@Override
final void clearInstanceReplyStreams()
{
instanceReplyStreams_.clear();
}
@Override
final void forceDisconnect()
{
disconnecting_ = true;
if (readDaemonException_ == null)
readDaemonException_ = new ConnectionDroppedException(ConnectionDroppedException.DISCONNECT_RECEIVED);
if (service_ == AS400.DATABASE || service_ == AS400.COMMAND
|| service_ == AS400.CENTRAL|| service_ == AS400.SIGNON || service_ == AS400.HOSTCNN)
{
AS400EndJobDS endjob = new AS400EndJobDS(AS400Server.getServerId(service_));
if (Trace.traceOn_) endjob.setConnectionID(connectionID_);
try {
endjob.write(outStream_);
}
catch (IOException e) {
Trace.log(Trace.ERROR, "Send end job data stream failed:", e);
}
}
Trace.log(Trace.INFORMATION , "forceDisconnect calling readDaemon_.interrupt");
readDaemon_.interrupt();
try {
socket_.close();
}
catch (IOException e) {
Trace.log(Trace.ERROR, "Socket close failed:", e);
}
// Wait for thread to end. This is necessary to help socket descriptor from being reused.
try {
readDaemon_.join();
}
catch (InterruptedException e) {
Trace.log(Trace.ERROR, "Thread join failed:", e);
}
}
@Override
public final DataStream getExchangeAttrReply()
{
return exchangeAttrReply_;
}
@Override
final boolean isConnected()
{
return readDaemonException_ == null && unlikelyException_ == null;
}
@Override
final int newCorrelationId()
{
synchronized (correlationIdLock_)
{
// Don't allow 0 as a valid correlation ID.
if (++lastCorrelationId_ == 0) lastCorrelationId_ = 1;
return lastCorrelationId_;
}
}
@Override
final DataStream receive(int correlationId) throws IOException, InterruptedException
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "AS400Server.receive from job " + getJobString());
synchronized (receiveLock_)
{
while (true)
{
// Changed March 2018 to give priority to exceptions..
// Otherwise exceptions are being lost.
if (readDaemonException_ != null) {
Trace.log(Trace.ERROR, "receive(): Read daemon exception:", readDaemonException_);
throw readDaemonException_;
}
if (unlikelyException_ != null) {
Trace.log(Trace.ERROR, "receive(): Read daemon exception:", unlikelyException_);
throw unlikelyException_;
}
DataStream ds = replyList_.remove(correlationId);
if (ds != null)
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "receive(): Valid reply found:", correlationId);
if (DBDSPool.monitor) {
if (ds instanceof DBReplyRequestedDS)
((DBReplyRequestedDS) ds).setAllocatedLocation();
}
return ds;
}
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "receive(): Reply not found. Waiting...");
receiveLock_.wait();
}
}
}
@Override
public void run()
{
while (readDaemonException_ == null && unlikelyException_ == null)
{
try
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Waiting for reply...");
DataStream reply = null;
// If client access server, construct ClientAccessDataStream.
if (service_ != AS400.RECORDACCESS)
reply = ClientAccessDataStream.construct(inStream_, instanceReplyStreams_, replyStreams_, system_, connectionID_);
else // Construct a DDMDataStream.
reply = ClassDecoupler.constructDDMDataStream(inStream_, replyStreams_, system_, connectionID_);
// Note: the thread is blocked on the above call if the inputStream has nothing to receive.
int correlation = reply.getCorrelation();
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Adding reply:", correlation);
replyList_.add(reply);
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Notifying threads.");
synchronized (receiveLock_)
{
receiveLock_.notifyAll(); // Notify all waiting threads.
}
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Threads notified.");
}
catch (IOException e)
{
if (Trace.traceOn_)
{
if (disconnecting_ && e instanceof SocketException) {
// It's an expected consequence of a client-initiated disconnect.
Trace.log(Trace.DIAGNOSTIC, "run(): Caught SocketException during disconnect:", e);
} else
Trace.log(Trace.ERROR, "run(): Caught IOException:", e);
}
// At this point, all waiting threads must be notified that the connection has ended...
if (readDaemonException_ == null)
readDaemonException_ = e;
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Notifying threads after IOException.");
synchronized (receiveLock_)
{
receiveLock_.notifyAll(); // Notify all waiting threads.
}
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Threads notified after IOException.");
}
catch (RuntimeException e)
{
if (Trace.traceOn_) Trace.log(Trace.ERROR, "run(): Caught RuntimeException:", e);
if (unlikelyException_ == null)
unlikelyException_ = e;
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Notifying threads after RuntimeException.");
synchronized (receiveLock_)
{
receiveLock_.notifyAll();
}
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "run(): Threads notified after RuntimeException.");
}
//@socket2 removed finally close because we were doing double closes.
//@socket2finally
//@socket2{
// Since we've fallen out of the while loop, we can reasonably assume that the socket is broken (based on the loop condition).
// Ensure the socket doesn't get left open.
//@socket2 if (!isConnected()) // Check it this way, in case we change the meaning of isConnected() in the future.
//{
//@socket2 try { socket_.close(); }
//@socket2 catch (Throwable t) {
//@socket2 Trace.log(Trace.ERROR, "Socket close failed:", t);
//@socket2 }
//@socket2 }
//@socket2}
}
}
@Override
final int send(DataStream requestStream) throws IOException
{
if (Trace.traceOn_) {
Trace.log(Trace.DIAGNOSTIC, "send(): send request to job " + getJobString());
requestStream.setConnectionID(connectionID_);
}
if (readDaemonException_ != null) {
Trace.log(Trace.ERROR, "Read daemon generated exception:", readDaemonException_);
throw readDaemonException_;
}
if (unlikelyException_ != null) {
Trace.log(Trace.ERROR, "Read daemon generated exception:", unlikelyException_);
throw unlikelyException_;
}
int correlationID = newCorrelationId();
requestStream.setCorrelation(correlationID);
requestStream.write(outStream_);
return correlationID;
}
@Override
final void send(DataStream requestStream, int correlationId) throws IOException
{
if (Trace.traceOn_) {
Trace.log(Trace.DIAGNOSTIC, "send(): send request to job " + getJobString());
requestStream.setConnectionID(connectionID_);
}
if (readDaemonException_ != null) {
Trace.log(Trace.ERROR, "Read daemon generated exception:", readDaemonException_);
throw readDaemonException_;
}
if (unlikelyException_ != null) {
Trace.log(Trace.ERROR, "Read daemon generated exception:", unlikelyException_);
throw unlikelyException_;
}
requestStream.setCorrelation(correlationId);
requestStream.write(outStream_);
}
@Override
final void sendAndDiscardReply(DataStream requestStream) throws IOException
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "send and discard(): ...");
int correlationID = send(requestStream);
discardList_.add(correlationID);
}
@Override
final void sendAndDiscardReply(DataStream requestStream,int correlationID) throws IOException
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "send and discard(): ...");
send(requestStream,correlationID);
discardList_.add(correlationID);
}
@Override
public final DataStream sendAndReceive(DataStream requestStream) throws IOException, InterruptedException
{
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "send and receive(): ...");
int correlationID = send(requestStream);
return receive(correlationID);
}
@Override
public final synchronized DataStream sendExchangeAttrRequest(DataStream req) throws IOException, InterruptedException
{
if (exchangeAttrReply_ == null)
exchangeAttrReply_ = sendAndReceive(req);
return exchangeAttrReply_;
}
@Override
public void setExchangeAttrReply(DataStream xChgAttrReply) {
exchangeAttrReply_ = xChgAttrReply;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy