/** * Contains the set of active module collector for * handling packaging, segmenting of payloads. */ package hk.hku.cecid.edi.sfrm.task; import java.io.IOException; import java.sql.Timestamp; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import javax.activation.DataSource; import hk.hku.cecid.edi.sfrm.spa.SFRMException; import hk.hku.cecid.edi.sfrm.spa.SFRMLog; import hk.hku.cecid.edi.sfrm.spa.SFRMProcessor; import hk.hku.cecid.edi.sfrm.activation.EmptyDataSource; import hk.hku.cecid.edi.sfrm.activation.FileRegionDataSource; import hk.hku.cecid.edi.sfrm.com.PackagedPayloads; import hk.hku.cecid.edi.sfrm.handler.OutgoingMessageHandler; import hk.hku.cecid.edi.sfrm.dao.SFRMPartnershipDVO; import hk.hku.cecid.edi.sfrm.dao.SFRMMessageDVO; import hk.hku.cecid.edi.sfrm.dao.SFRMMessageSegmentDVO; import hk.hku.cecid.edi.sfrm.pkg.SFRMConstant; import hk.hku.cecid.edi.sfrm.pkg.SFRMMessage; import hk.hku.cecid.edi.sfrm.pkg.SFRMMessageException; import hk.hku.cecid.piazza.commons.module.ActiveTaskAdaptor; // import hk.hku.cecid.piazza.commons.module.ActiveTaskModule; import hk.hku.cecid.piazza.commons.dao.DAOException; import hk.hku.cecid.piazza.commons.security.KeyStoreManager; /** * * * * Creation Date: 9/10/2006 * * @author Twinsen Tsang * @version 1.0.0 * @since 1.0.0 */ public class OutgoingSegmentTask extends ActiveTaskAdaptor { /** * The packaged payload. */ private final PackagedPayloads payload; /** * The sending payload of this tasks. */ private final SFRMMessageSegmentDVO segDVO; /** * The partnership record for the sending message. */ private final SFRMPartnershipDVO pDVO; /** * The message record for the sending message. */ private SFRMMessageDVO msgDVO; /** * The current retried time of sending. */ private int currentRetried; /** * The flag of message retry flag. */ private boolean retryEnabled; /** * The constant field for the suffix of the log. (each thread has different value). */ private final String SGT_LOG_SUFFIX; /** * Explicit Constructor.<br><br> * * @param sgtDVO * The payload need to be send out. * @param pDVO * The partnership record associated to this segment. * @param msgDVO * The message record associated to this segment. * @param payload * The packaged payloads * @since * 1.0.0 * @throws NullPointerException * If the message, partnership and segment is null. */ public OutgoingSegmentTask( SFRMMessageSegmentDVO sgtDVO, SFRMPartnershipDVO pDVO, SFRMMessageDVO msgDVO, PackagedPayloads payload) { // Error reporting. if (pDVO == null) throw new NullPointerException("Outgoing Segment Payloads Task: Missing partnership object"); if (sgtDVO == null) throw new NullPointerException("Outgoing Segment Payloads Task: Missing segment object"); if (msgDVO == null) throw new NullPointerException("Outgoing Segment Payloads Task: Missing message object"); if (payload == null && sgtDVO.getSegmentType().equals(SFRMConstant.MSGT_PAYLOAD)){ throw new NullPointerException("Outgoing Segment Payloads Task: Missing payload object for msg id: " + msgDVO.getMessageId()); } this.pDVO = pDVO; this.msgDVO = msgDVO; this.segDVO = sgtDVO; this.payload = payload; this.retryEnabled = (this.pDVO.getRetryMax() > 0); this.currentRetried = this.segDVO.getRetried(); this.SGT_LOG_SUFFIX = " msg id: " + this.segDVO.getMessageId() +" and sgt no: " + this.segDVO.getSegmentNo() +" and sgt type: " + this.segDVO.getSegmentType(); SFRMProcessor.getInstance().getLogger().debug(SGT_LOG_SUFFIX + "being addedd to task list"); } /** * Create a SFRM Message for sending.<br><br> * * The SFRM Message maybe sign, encrypt according * to the partnership configuration of this message. * * This is the first steps of this active task.<br><br> * * @return * A new SFRM message for sending. * @since * 1.0.0 * @throws DAOException * if database I/O Errors. * @throws SFRMMessageException * @throws NoSuchAlgorithmException * if the signing and encryption can't be found. * @throws UnrecoverableKeyException * @throws SMimeException * @throws SFRMException */ private SFRMMessage createSFRMMessage() throws IOException, DAOException, SFRMMessageException, NoSuchAlgorithmException, UnrecoverableKeyException, /*SMimeException, */SFRMException { // TODO: Refactoring. // Check if it is meta message. boolean isMeta = this.segDVO.getSegmentNo() == 0 && this.segDVO.getSegmentType(). equalsIgnoreCase(SFRMConstant.MSGT_META); boolean isPayload = this.segDVO.getSegmentType(). equalsIgnoreCase(SFRMConstant.MSGT_PAYLOAD); // Create return message. SFRMMessage sfrmMessage = new SFRMMessage(); // Construct the message header. Fill in general information sfrmMessage.setMessageID (this.segDVO.getMessageId()); sfrmMessage.setPartnershipId(this.msgDVO.getPartnershipId()); sfrmMessage.setSegmentNo (this.segDVO.getSegmentNo()); sfrmMessage.setSegmentType (this.segDVO.getSegmentType()); DataSource cacheSource = new EmptyDataSource(); if (isPayload || isMeta){ // Calculate the segment size. long size = this.segDVO.getSegmentEnd() - this.segDVO.getSegmentStart(); if (isMeta){ sfrmMessage.setTotalSize(this.msgDVO.getTotalSize()); sfrmMessage.setTotalSegment(this.msgDVO.getTotalSegment()); } sfrmMessage.setSegmentOffset(this.segDVO.getSegmentStart()); sfrmMessage.setSegmentLength(size); if (isPayload){ cacheSource = new FileRegionDataSource(payload.getRoot(), this.segDVO.getSegmentStart(), size); } } String contentType = payload == null ? SFRMConstant.DEFAULT_CONTENT_TYPE : payload.getContentType(); sfrmMessage.setContent(cacheSource, contentType); // Create SMIME Header. KeyStoreManager keyman = SFRMProcessor.getInstance().getKeyStoreManager(); // Generate checksum value using MD5 Hash algorithm. String mic = ""; // TUNE: if (isPayload){ mic = segDVO.getMD5Value(); } // Set the mic value and body part to the message. sfrmMessage.setMicValue(mic); // Setup up signing using MD5 or SHA1 if (this.msgDVO.getSignAlgorithm() != null){ SFRMProcessor.getInstance().getLogger().info(SFRMLog.OSPT_CALLER + SFRMLog.SIGNING_SGT + SGT_LOG_SUFFIX); sfrmMessage.sign(keyman.getX509Certificate(), keyman.getPrivateKey(), msgDVO.getSignAlgorithm()); sfrmMessage.setIsSigned(true); } // Setup up encrypting using RC2, DES if (this.msgDVO.getEncryptAlgorithm() != null){ SFRMProcessor.getInstance().getLogger().info(SFRMLog.OSPT_CALLER + SFRMLog.ENCRYPT_SGT + SGT_LOG_SUFFIX); sfrmMessage.encrypt(msgDVO.getPartnerX509Certificate(), msgDVO.getEncryptAlgorithm()); sfrmMessage.setIsEncrypted(true); } return sfrmMessage; } /** * Send a SFRM Message using Fast HTTP Connector.<br><br> * * This is step 2 of this active task.<br><br> * * @param message * The sfrm message to be sent. * @since * 1.0.3 */ private void sendSFRMMessage(SFRMMessage message) throws Exception { OutgoingMessageHandler.getInstance().sendMessage( message, msgDVO.getPartnerEndpoint(), msgDVO.getIsHostnameVerified(), msgDVO.getSignAlgorithm(), msgDVO.getEncryptAlgorithm(), msgDVO.getPartnerX509Certificate()); // Update the payload status to processed if the segment is receipt. this.segDVO.setStatus(SFRMConstant.MSGS_DELIVERED); this.segDVO.setProceedTimestamp(new Timestamp(System.currentTimeMillis())); SFRMProcessor.getInstance().getMessageSegmentHandler().getDAOInstance().persist(this.segDVO); this.retryEnabled = false; } /** * Execute the active task. * * @since 1.0.0 * * @see hk.hku.cecid.piazza.commons.module.ActiveTask#execute() */ public void execute() throws Exception { SFRMProcessor.getInstance().getLogger().debug("OutgoingSegmentTask Enter, Thread " + Thread.currentThread().getId()); // Log information. SFRMProcessor.getInstance().getLogger().info(SFRMLog.OSPT_CALLER + SFRMLog.OUTG_TASK + SGT_LOG_SUFFIX); // --------------------------------------------------------------------------- // Step 0: Check whether it has exceed the retries times. // --------------------------------------------------------------------------- if (this.currentRetried > this.getMaxRetries()) throw new SFRMMessageException( SFRMLog.OSPT_CALLER +" this sending segment has exceeding retries time: " + this.currentRetried + SGT_LOG_SUFFIX); // --------------------------------------------------------------------------- // Step 1: Check whether the message has been failed. // --------------------------------------------------------------------------- SFRMMessageDVO msgDVO = SFRMProcessor.getInstance().getMessageHandler() .retrieveMessage( this.segDVO.getMessageId(), this.msgDVO.getMessageBox()); if (msgDVO.getStatus().equalsIgnoreCase(SFRMConstant.MSGS_DELIVERY_FAILURE)){ SFRMProcessor.getInstance().getLogger().info( SFRMLog.OSPT_CALLER +" Failed msg with msg id:" + msgDVO.getMessageId() +" and sgt no: " + this.segDVO.getSegmentNo()); return; } // --------------------------------------------------------------------------- // Step 2: create the sfrm message for sending and cache it for retry // --------------------------------------------------------------------------- // FIXME: use back cache / create each times ? SFRMMessage sendMessage = null; sendMessage = this.createSFRMMessage(); // --------------------------------------------------------------------------- // Step 3: send the message. // --------------------------------------------------------------------------- this.sendSFRMMessage(sendMessage); sendMessage = null; } /** * Set the retries of active task.<br><br> * * The parameter <code>retried</code> is useless here * as we use the field "retried" in the database segment * table for reference. * * @since * 1.0.0 * * @param retried The number of times that has been tried. */ public void setRetried(int retried) { this.currentRetried++; try{ this.segDVO.setRetried(this.currentRetried); SFRMProcessor.getInstance().getMessageSegmentHandler().getDAOInstance() .persist(this.segDVO); } catch(DAOException daoe){ SFRMProcessor.getInstance().getLogger().error("Error in database", daoe); } } /** * @since * 1.0.0 * * @return return the max retries allowed for this active task. * */ public int getMaxRetries() { return this.pDVO.getRetryMax(); } /** * @since * 1.0.0 * * @return return the interval between each sending retry. */ public long getRetryInterval() { return this.pDVO.getRetryInterval(); } /** * @since * 1.0.0 * * @return return true if this task can be retried. */ public boolean isRetryEnabled() { return this.retryEnabled; } /** * The method is invoked upon the task fails to send.<br><br> * * The message segment and message will treat as FAIL. * with status DF (Delivery Failure).<br><br> * * Also, if the outgoing segment is a RECEIPT, * then the PAYLOAD segment corresponding to this * RECEIPT is also treated as FAIL. * * @param e * The failure cause. * @since * 1.0.0 */ public void onFailure(Throwable e) { SFRMProcessor.getInstance().getLogger().error( "Error in Outgoing Segmented Payload Task , Retried: " + Integer.toString(this.currentRetried) + " Max retried: " + Integer.toString(this.getMaxRetries()), e); // Unrecoverable exception if (!this.retryEnabled || this.currentRetried >= this.getMaxRetries()){ try { // --------------------------------------------------------------- // Step 0: Update the sfrm message record to be fail // --------------------------------------------------------------- this.msgDVO.setStatus(SFRMConstant.MSGS_PRE_DELIVERY_FAILED); this.msgDVO.setStatusDescription( "Segment: " + this.segDVO.getSegmentNo() + " has error: " + e.toString()); this.msgDVO.setCompletedTimestamp(new Timestamp(System.currentTimeMillis())); SFRMProcessor.getInstance().getMessageHandler().updateMessage(this.msgDVO); // --------------------------------------------------------------- // Step 1: Update the sfrm segment record to fail // --------------------------------------------------------------- this.segDVO.setStatus(SFRMConstant.MSGS_DELIVERY_FAILURE); this.segDVO.setCompletedTimestamp(new Timestamp(System.currentTimeMillis())); SFRMProcessor.getInstance().getMessageSegmentHandler().getDAOInstance().persist(this.segDVO); // --------------------------------------------------------------- // Step 2: clear all the cache. SFRMProcessor.getInstance().getMessageHandler().clearCache(this.msgDVO); SFRMProcessor.getInstance().getPartnershipHandler().clearCache( this.msgDVO.getPartnershipId(), this.msgDVO.getMessageId()); this.retryEnabled = false; } catch (Exception ex) { SFRMProcessor.getInstance().getLogger().fatal( "Unable to mark failure to outgoing SFRM message: " + this.msgDVO.getMessageId(), ex); } } else{ SFRMProcessor.getInstance().getLogger().error("Unknown Error", e); } } /* private void changeSetting(){ ActiveTaskModule am = (ActiveTaskModule) SFRMProcessor.getInstance().getModuleGroup().getModule("sfrm.outgoing.segment.collector"); am.getMonitor().setMaxThreadCount(15); } */ }