src.com.ibm.as400.access.SpooledFileOutputStreamImplRemote Maven / Gradle / Ivy
Show all versions of jt400-jdk8 Show documentation
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: SpooledFileOutputStreamImplRemote.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-2000 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.io.OutputStream;
import java.io.IOException;
/**
* The SpooledFileOutputStream class is used to write data into a server spooled file.
**/
class SpooledFileOutputStreamImplRemote
implements SpooledFileOutputStreamImpl
{
static final String DT_AUTO = "*AUTO";
static final String DT_PRTF = "*PRTF";
private byte[] buffer_ = new byte[4096]; // we'll buffer up to 4K before sending
private NPConversation conversation_;
private NPCPAttribute cpAttr_; // attributes to create spooled file with
private NPCPAttribute cpCPFMsg_; // any error messages come back here
private NPCPIDSplF cpIDSplF_; // ID codepoint of spooled file created
private NPCPIDOutQ cpIDOutQ_; // output queue ID (may be null)
private NPCPIDPrinterFile cpIDPrtrFile_; // printer file ID (may be null)
private NPCPSplFHandle cpSplFHndl_; // handle used for writes and close request
private boolean fCreatePending_;
private NPSystem npSystem_;
private int offset_ = 0; // current offset into buffer
private AS400ImplRemote sys_;
/**
* Constructs a SpooledFileOutputStream object.
* Use this object to create a new spooled file on the given system
* with the specified parameters.
* @param system The system on which to create the spooled file.
* @param options Optional. A print parameter list that contains
* a list of attributes with which to create the spooled file.
* The attributes set in options will
* override those attributes in the printer file that is used.
* The printer file used will be the one specified with the
* printerFile parameter, or if that parameter is null,
* it will be the default network print server printer file (QPNPSPRTF).
* If the output queue is specified in options, it
* will override any output queue passed in the outputQueue
* parameter.
* The following parameters may be set:
*
* -
* ATTR_ALIGN - Align page
*
*
-
* ATTR_BACK_OVERLAY - Back overlay integrated file system Name
*
*
-
* ATTR_BKOVL_DWN - Back overlay offset down
*
*
-
* ATTR_BKOVL_ACR - Back overlay offset across
*
*
-
* ATTR_CPI - Characters per inch
*
*
- (1)
* ATTR_CODEPAGE - Code page
*
*
-
* ATTR_CONTROLCHAR - Control character
*
*
-
* ATTR_CONVERT_LINEDATA - Convert line data
*
*
-
* ATTR_CORNER_STAPLE - Corner staple
*
*
-
* ATTR_COPIES - Copies
*
*
-
* ATTR_DAYS_UNTIL_EXPIRE - Days Until File Expires
*
*
-
* ATTR_DBCSDATA - User-specified DBCS data
*
*
-
* ATTR_DBCSEXTENSN - DBCS extension characters
*
*
-
* ATTR_DBCSROTATE - DBCS character rotation
*
*
-
* ATTR_DBCSCPI - DBCS characters per inch
*
*
-
* ATTR_DBCSSISO - DBCS SO/SI spacing
*
*
-
* ATTR_DFR_WRITE - Defer write
*
*
-
* ATTR_PAGRTT - Degree of page rotation
*
*
-
* ATTR_EDGESTITCH_NUMSTAPLES - Edge Stitch Number of Staples
*
*
-
* ATTR_EDGESTITCH_REF - Edge Stitch Reference
*
*
-
* ATTR_EDGESTITCH_REFOFF - Edge Stitch Reference Offset
*
*
-
* ATTR_ENDPAGE - Ending page
*
*
-
* ATTR_EXPIRATION_DATE - Expiration Date
*
*
- (2)
* ATTR_FILESEP - File separators
*
*
-
* ATTR_FOLDREC - Fold records
*
*
-
* ATTR_FONTID - Font identifier
*
*
-
* ATTR_FORM_DEFINITION - Form definition integrated file system name
*
*
-
* ATTR_FORMFEED - Form feed
*
*
-
* ATTR_FORMTYPE - Form type
*
*
-
* ATTR_FRONT_OVERLAY - Front overlay integrated file system same
*
*
-
* ATTR_FTOVL_ACR - Front overlay offset across
*
*
-
* ATTR_FTOVL_DWN - Front overlay offset down
*
*
- (1)
* ATTR_CHAR_ID - Graphic character set
*
*
-
* ATTR_JUSTIFY - Hardware justification
*
*
-
* ATTR_HOLD - Hold spool file
*
*
-
* ATTR_HOLDPNDSTS - Hold Pending Status
*
*
-
* ATTR_IPP_ATTR_CCSID - IPP Attributes-ccsid
*
*
-
* ATTR_IPP_JOB_ID - IPP Job ID
*
*
-
* ATTR_IPP_JOB_NAME - IPP Job Name
*
*
-
* ATTR_IPP_JOB_NAME_NL - IPP Job Name NL
*
*
-
* ATTR_IPP_JOB_ORIGUSER - IPP Job Originating User Name
*
*
-
* ATTR_IPP_JOB_ORIGUSER_NL - IPP Job Originating User Name NL
*
*
-
* ATTR_IPP PRINTER_NAME - IPP Printer Name
*
*
-
* ATTR_IPP_ATTR_NL - IPP Natural Language
*
*
-
* ATTR_JOBSYSTEM - Job System Name
*
*
-
* ATTR_LPI - Lines per inch
*
*
-
* ATTR_MAXRCDS - Maximum spooled output records
*
*
-
* ATTR_OUTPTY - Output priority
*
*
-
* ATTR_OUTPUT_QUEUE - Output queue integrated file system name
*
*
-
* ATTR_OVERFLOW - Overflow line number
*
*
-
* ATTR_PAGE_DEFINITION - Page definition integrated file system name
*
*
-
* ATTR_PAGELEN - Length of page
*
*
-
* ATTR_MEASMETHOD - Measurement method
*
*
-
* ATTR_PAGEWIDTH - Width of page
*
*
-
* ATTR_MULTIUP - Pages per side
*
*
-
* ATTR_POINTSIZE - Point size
*
*
-
* ATTR_FIDELITY - Print fidelity
*
*
-
* ATTR_DUPLEX - Print on both sides
*
*
-
* ATTR_PRTQUALITY - Print quality
*
*
-
* ATTR_PRTTEXT - Print text
*
*
-
* ATTR_PRINTER - Printer
*
*
-
* ATTR_PRTDEVTYPE - Printer device type
*
*
-
* ATTR_RPLUNPRT - Replace unprintable characters
*
*
-
* ATTR_RPLCHAR - Replacement character
*
*
-
* ATTR_SADDLESTITCH_NUMSTAPLES - Saddle Stitch Number of Staples
*
*
-
* ATTR_SADDLESTITCH_REF - Saddle Stitch Reference
*
*
-
* ATTR_SAVE - Save spooled file
*
*
-
* ATTR_SRCDRWR - Source drawer
*
*
-
* ATTR_SPOOL - Spool the data
*
*
-
* ATTR_SPOOLFILE - Spooled file name
*
*
-
* ATTR_SCHEDULE - Spooled output schedule
*
*
-
* ATTR_STARTPAGE - Starting page
*
*
-
* ATTR_UNITOFMEAS - Unit of measure
*
*
-
* ATTR_USERCMT - User comment
*
*
-
* ATTR_USERDATA - User data
*
*
-
* ATTR_SPLSCS - Spool SCS
*
*
-
* ATTR_USRDEFDATA - User defined data
*
*
- (3)
* ATTR_USRDEFOPT - User defined options
*
*
-
* ATTR_USER_DEFINED_OBJECT - User defined object integrated file system name
*
*
*
* Note 1: Code page and graphical character set are dependent upon each
* other. If you set one you must set the other.
*
* Note 2: The special value of *FILE is not allowed when creating a new
* spooled file.
*
* Note 3: Up to 4 user-defined options may be specified.
*
* @param printerFile Optional. The printer file that should be used
* to create the spooled file. This printer file
* must reside on the same server system that the
* spooled file is being created on.
* @param outputQueue Optional. The output queue on which to create the
* spooled file. The output queue must reside on
* the same server system that the spooled file
* is being created on.
* @exception AS400Exception If the server returns an error message.
* @exception AS400SecurityException If a security or authority error occurs.
* @exception ErrorCompletingRequestException If an error occurs before the request is completed.
* @exception IOException If an error occurs while communicating with the server.
* @exception InterruptedException If this thread is interrupted.
**/
public synchronized void createSpooledFileOutputStream(AS400Impl system,
PrintParameterList options,
PrinterFileImpl printerFile,
OutputQueueImpl outputQueue)
throws AS400Exception,
AS400SecurityException,
ErrorCompletingRequestException,
InterruptedException,
IOException
{
// Force the system param to connect if it isn't already
// so that we can check its system name against the
// system outputQueue and printerFile are on
// NPSystem npSystem = NPSystem.getSystem(system);
// NPConversation conv = npSystem.getConversation();
// npSystem.returnConversation(conv); // we're done with it
fCreatePending_ = true; // we haven't issued the create yet
sys_ = (AS400ImplRemote) system;
npSystem_ = NPSystem.getSystem(sys_);
conversation_ = npSystem_.getConversation();
cpCPFMsg_ = new NPCPAttribute();
cpIDSplF_ = new NPCPIDSplF();
cpSplFHndl_ = new NPCPSplFHandle();
// if the user passed in a printer file, get its ID
if (printerFile != null)
{
cpIDPrtrFile_ = (NPCPIDPrinterFile)((PrinterFileImplRemote) printerFile).getIDCodePoint();
}
// if the user passed in an output queue, get its ID
if (outputQueue != null)
{
cpIDOutQ_ = (NPCPIDOutQ)((OutputQueueImplRemote) outputQueue).getIDCodePoint();
}
//-------------------------------------------------------------
// figure out what data type we're using.
// If the user has specified nothing or *AUTO
// delay the open until we get some data to analyze.
// If the user has specified *PRTF, change it to be nothing and the server
// will use what is in the printer file.
//-------------------------------------------------------------
String strDataType = null;
cpAttr_ = new NPCPAttribute(); // we need our own copy because we may change things
if (options != null)
{
cpAttr_.addUpdateAttributes(options.getAttrCodePoint());
}
strDataType = cpAttr_.getStringValue(PrintObject.ATTR_PRTDEVTYPE);
if (strDataType == null)
{
// datastream type not specified, so use *AUTO
strDataType = DT_AUTO;
} else {
//-------------------------------------------------------------
// strip trailing nulls & uppercase user specified value.
//-------------------------------------------------------------
strDataType = strDataType.toUpperCase().trim();
}
//-------------------------------------------------------------
// strDataType now contains the data type to use, whether the user
// explicitly set it or we defaulted it.
// Need to see if we should delay the create here
// IF not
// Need to change *PRTF to ""
// IF pAttrs specified, change it to "".
// ELSE
// set create pending flag on
// ENDIF
//-------------------------------------------------------------
if (strDataType.equals(DT_AUTO))
{
fCreatePending_ = true; // data type is automatic so wait for create
} else {
if (strDataType.equals(DT_PRTF))
{
strDataType = "";
}
makeCreateRequest(strDataType);
}
}
/**
* Closes the stream.
* It must be called to release any resources associated with the stream.
* @exception IOException If an error occurs while communicating with the server.
**/
public synchronized void close()
throws IOException
{
if (conversation_ == null)
{
Trace.log(Trace.ERROR, "Conversation is null.");
throw new IOException();
}
// if there is any data pending. write it out
if (offset_ != 0)
{
makeWriteRequest(buffer_, 0, offset_);
offset_ = 0;
}
// if create is still pending here, then we have an empty spooled
// file, send it up anyway...
if (fCreatePending_)
{
makeCreateRequest(null);
}
NPDataStream closeReq = new NPDataStream(NPConstants.SPOOLED_FILE);
NPDataStream closeRep = new NPDataStream(NPConstants.SPOOLED_FILE);
// setup the close request data stream
closeReq.setAction(NPDataStream.CLOSE);
closeReq.addCodePoint(cpSplFHndl_);
// setup the close reply to catch the return codepoints
closeRep.addCodePoint(cpCPFMsg_);
try
{
// make the request
conversation_.makeRequest(closeReq, closeRep);
}
catch (Exception e)
{
Trace.log(Trace.ERROR, "Caught an Exception." + e.toString());
throw new IOException(e.toString());
}
finally
{
// if we still have a conversation, return it
if (conversation_ != null)
{
npSystem_.returnConversation(conversation_);
conversation_ = null;
}
}
} // close()
/**
* Closes the stream when garbage is collected.
* @exception Throwable If an error occurs.
**/
protected void finalize()
throws Throwable
{
// We must be very careful here to not try to receive a
// reply from the server - we are being called by the
// garbage collector which could be running on the
// background thread of the AS400Server object in our
// conversation. If we were to call back to the AS400Server
// to wait for a reply we could end up in a deadlock situation
// Any requests we make we'll discard the reply on
// if we still have a conversation
// they must not have sent the close yet
if (conversation_ != null)
{
// if we have sent the create request
if (!fCreatePending_)
{
// send up a close request and ignore the reply
NPDataStream closeReq = new NPDataStream(NPConstants.SPOOLED_FILE);
// setup the close request data stream
closeReq.setAction(NPDataStream.CLOSE);
closeReq.addCodePoint(cpSplFHndl_);
AS400Server server= conversation_.getServer();
if (server != null)
{
// @D closeReq.setHostCCSID(conversation_.getHostCCSID());
closeReq.setConverter(conversation_.getConverter());
server.sendAndDiscardReply(closeReq);
}
}
npSystem_.returnConversation(conversation_);
}
super.finalize(); // always call super.finalize()!
}
/** Flushes the stream. This will write any buffered output bytes.
* @exception IOException If an error occurs while communicating with the server.
**/
public synchronized void flush()
throws IOException
{
// send what we have, if any
if (offset_ != 0)
{
makeWriteRequest(buffer_, 0, offset_);
offset_ = 0;
}
}
/** Returns the spooled file that was created (or is being created) with
* this output stream.
* @return A reference to the spooled file object.
**/
public synchronized NPCPIDSplF getSpooledFile()
throws IOException
{
// @D SpooledFile sf = null;
NPCPIDSplF sfID = null;
// flush any data we have first
// if the file hasn't been closed already
if (conversation_ != null)
{
flush();
}
// if we've issued the create already
if (!fCreatePending_)
{
// return the spooled file that we created
// sf = new SpooledFile(sys_, cpIDSplF_, null); // @D (see below)
// The call to create the SpooledFile will be made on the proxy side
sfID = cpIDSplF_;
} else {
Trace.log(Trace.ERROR, "Spooled File has not been created.");
throw new
ExtendedIllegalStateException(ExtendedIllegalStateException.OBJECT_MUST_BE_OPEN);
}
// return sf; // @D
return sfID;
}
/**
* Generates the create request.
* @param strDataType The PRTDEVTYPE to use (*SCS, *AFPDS, *USERASCII)...
* @exception IOException If an error occurs while communicating with the server.
**/
private synchronized void makeCreateRequest(String strDataType)
throws IOException
{
if (conversation_ == null)
{
Trace.log(Trace.ERROR, "Conversation is null.");
throw new IOException();
}
//-------------------------------------------------------------
// if datatype is something (override the printer file
// set the datatype in the attributes
//-------------------------------------------------------------
if ((strDataType != null) &&
(!strDataType.equals(""))) // if strDataType is something
{
if (cpAttr_ == null)
{
cpAttr_ = new NPCPAttribute();
}
cpAttr_.setAttrValue(PrintObject.ATTR_PRTDEVTYPE, strDataType);
}
NPDataStream createReq = new NPDataStream(NPConstants.SPOOLED_FILE);
NPDataStream createRep = new NPDataStream(NPConstants.SPOOLED_FILE);
// setup the create request data stream
createReq.setAction(NPDataStream.CREATE);
if (cpAttr_ != null)
{
createReq.addCodePoint(cpAttr_);
}
if (cpIDOutQ_ != null)
{
createReq.addCodePoint(cpIDOutQ_);
}
if (cpIDPrtrFile_ != null)
{
createReq.addCodePoint(cpIDPrtrFile_);
}
// setup the create reply to catch the return codepoints
createRep.addCodePoint(cpSplFHndl_);
createRep.addCodePoint(cpCPFMsg_);
createRep.addCodePoint(cpIDSplF_);
try
{
// make the request
conversation_.makeRequest(createReq, createRep);
}
catch (Exception e)
{
Trace.log(Trace.ERROR, "Caught an Exception." + e.toString());
throw new IOException(e.toString());
}
fCreatePending_ = false; // reset the create flag
} // makeCreateRequest()
/**
* Generates the write request.
* @param buf The array of bytes to write.
* @param length The length of data to write.
* @exception IOException If an error occurs while communicating with the server.
**/
private synchronized void makeWriteRequest(byte[] buf,
int offset,
int len)
throws IOException
{
if (conversation_ == null)
{
Trace.log(Trace.ERROR, "Conversation is null.");
throw new IOException();
}
// if the create is still pending here then
// we should sniff the data and then issue the create
// request with the correct data type
if (fCreatePending_)
{
String strDataType = NPDataAnalyzer.sniff(buf, offset, len);
makeCreateRequest(strDataType);
}
//
// now make the write request
//
NPDataStream writeReq = new NPDataStream(NPConstants.SPOOLED_FILE);
NPDataStream writeRep = new NPDataStream(NPConstants.SPOOLED_FILE);
NPCPData cpData = new NPCPData();
cpData.setDataBuffer(buf, len, offset);
// setup the write request data stream
writeReq.setAction(NPDataStream.WRITE);
writeReq.addCodePoint(cpSplFHndl_);
writeReq.addCodePoint(cpData);
// setup the create reply to catch the return codepoints
writeRep.addCodePoint(cpCPFMsg_);
try
{
// make the request
conversation_.makeRequest(writeReq, writeRep);
}
catch (Exception e)
{
Trace.log(Trace.ERROR, "Caught an Exception." + e.toString());
throw new IOException(e.toString());
}
}
/**
* Writes up to length bytes of data from the byte array data,
* starting at offset, to this spooled file.
*
* @param data The data to be written.
* @param offset The start offset in the data.
* @param length The number of bytes that are written.
*
* @exception IOException If an error occurs while communicating with the server.
**/
public synchronized void write(byte data[], int offset, int length)
throws IOException
{
if (conversation_ == null)
{
Trace.log(Trace.ERROR, "SpooledFileOutputStream already closed.");
throw new IOException();
}
int currentSourceOffset = offset;
int dataLeftToSend = length;
while(dataLeftToSend > 0)
{
//-------------------------------------------------------------
// calculate the available buffer space left in the current
// buffer
//-------------------------------------------------------------
int availLen = buffer_.length - offset_;
if (availLen >= dataLeftToSend)
{
//-------------------------------------------------------------
// If we have enough to hold it all
// Move it all to the current buffer
//-------------------------------------------------------------
System.arraycopy(data, currentSourceOffset,
buffer_, offset_,
dataLeftToSend);
currentSourceOffset += dataLeftToSend;
offset_ += dataLeftToSend;
dataLeftToSend = 0;
} else {
if (availLen != 0)
{
//-------------------------------------------------------------
// If we have ANY room at all
// Move what we can from the callers buffer to
// the current write bitstream
//-------------------------------------------------------------
System.arraycopy(data, currentSourceOffset,
buffer_, offset_,
availLen);
currentSourceOffset += availLen;
offset_ += availLen;
dataLeftToSend -= availLen;
}
//-------------------------------------------------------------
// if there is any data in the bytestream, send it out
//-------------------------------------------------------------
if (offset_ != 0)
{
makeWriteRequest(buffer_, 0, offset_);
offset_ = 0;
}
}
} // end while
} // write(byte[], int, int)
}