package gov.va.med.esr.service.impl;


import gov.va.med.esr.common.model.ee.IRS1095B;
import gov.va.med.esr.common.model.ee.MECPeriod;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.TransmissionStatus;
import gov.va.med.esr.common.model.messaging.IrsTransmissionLogData;
import gov.va.med.esr.common.model.messaging.IrsTransmissionLogDetailEntry;
import gov.va.med.esr.common.model.messaging.IrsTransmissionLogEntry;
import gov.va.med.esr.common.model.party.SimpleAddress;
import gov.va.med.esr.service.IRSWebServiceDelegate;
import gov.va.med.esr.service.LookupService;
import gov.va.med.fw.persistent.DAOOperations;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.esr.common.util.CxfOutInterceptor;
import java.security.SecureRandom;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.Holder;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.attachment.ByteDataSource;
import org.apache.cxf.feature.Feature;
import org.apache.cxf.jaxb.JAXBDataBinding;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.message.Attachment;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.common.gzip.GZIPFeature;
import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import us.gov.treasury.irs.common.BinaryFormatCodeType;
import us.gov.treasury.irs.common.ErrorMessageDetailType;
import us.gov.treasury.irs.common.TINRequestTypeCodeType;
import us.gov.treasury.irs.ext.aca.air.ty16.ACABulkBusinessHeaderRequestType;
import us.gov.treasury.irs.ext.aca.air.ty16.ACABulkReqTrnsmtStsReqGrpDtlType;
import us.gov.treasury.irs.ext.aca.air.ty16.ACATrnsmtManifestReqDtlType;
import us.gov.treasury.irs.ext.aca.air.ty16.BusinessAddressGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.BusinessNameType;
import us.gov.treasury.irs.ext.aca.air.ty16.CompanyInformationGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.CorrectedRecordInfoGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.EmployerCoveredIndividualType;
import us.gov.treasury.irs.ext.aca.air.ty16.ForeignAddressGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.Form1094BUpstreamDetailType;
import us.gov.treasury.irs.ext.aca.air.ty16.Form1095BUpstreamDetailType;
import us.gov.treasury.irs.ext.aca.air.ty16.IssuerInfoGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.MonthIndGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.OtherCompletePersonNameType;
import us.gov.treasury.irs.ext.aca.air.ty16.ResponsibleIndividualGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.TransmissionTypeCdType;
import us.gov.treasury.irs.ext.aca.air.ty16.TransmitterErrorDetailGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.StateType;
import us.gov.treasury.irs.ext.aca.air.ty16.TransmissionStatusCodeType;
import us.gov.treasury.irs.ext.aca.air.ty16.USAddressGrpType;
import us.gov.treasury.irs.ext.aca.air.ty16.VendorInformationGrpType;
import us.gov.treasury.irs.msg.form1094_1095bctransmittermessage.FormBCTrnsmtSubmissionDtlType;
import us.gov.treasury.irs.msg.form1094_1095btransmitterupstreammessage.Form109495BTrnsmtUpstreamType;
import us.gov.treasury.irs.msg.irsacabulkrequesttransmitter.ACABulkRequestTransmitterType;
import us.gov.treasury.irs.msg.irstransmitterstatusrequest.ACABulkRequestTransmitterStatusDetailRequestType;
import us.gov.treasury.irs.msg.irstransmitterstatusrequest.ACABulkRequestTransmitterStatusDetailResponseType;
import us.gov.treasury.irs.srv.acabulkrequesttransmitterstatus.ACATransmitterStatusReqPortType;
import us.gov.treasury.irs.srv.gettransmitterbulkrequest.*;


public class IRSWebServiceDelegateImpl  extends AbstractComponent implements IRSWebServiceDelegate {


	private static final long serialVersionUID = 8534310803323475694L;
	private static final String EIN = "741612229";
	//00-0000201
	private static final String AATS_EIN = "000000211";
	private static final String AATS_CONTROL_EIN = "WORK";
	private static final String AATS_CONTROL_LEGAL_NAME = "WORK";
	private static final String LEGAL_NAME = "DEPARTMENT OF VETERANS AFFAIRS";
	//private static final String BUSINESS_TYPE = "FGAN";
	private static final String BUSINESS_STREET_ADDRESS = "1800 G ST NW RM 402B";
	private static final String BUSINESS_CITY = "WASHINGTON";
	private static final String BUSINESS_STATE = "DC";
	private static final String BUSINESS_ZIP = "20460";
	private static final String BUSINESS_PHONE = "2022715400";
	private static final String FORM_TYPE_CODE = "1094/1095B";
	//bulk submits can take up to 15 mins in prod but we shall not wait that long. 5 mins max.
	private static final long SUBMIT_TIMEOUT = 300000;
	private static final int MAX_RETRY = 5;
	private static final String APP_ID = "SYS12";
	private static final String TYPE_CODE = "T";
	private static final String SUBMIT_ORIGINAL = "O";
	private static final String SUBMIT_CORRECTION = "C";
	private static final String SUBMIT_REPLACEMENT = "R";
	private static final String WSU_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
	private static final String ACABUSINESSHEADER_NS = "urn:us:gov:treasury:irs:msg:acabusinessheader";
	private static final String ACABUSINESSHEADER_NS_ACK = "urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest";
	private static final String ACATRANSMITTERMANIFESTREQDTL_NS = "urn:us:gov:treasury:irs:ext:aca:air:ty16";



	//always only one 1094 container
	private static final BigInteger PAYER_COUNT = BigInteger.valueOf(1);

	private String irsServiceBulkEndPoint = null;
	private String irsServiceStatusEndPoint = null;

	private LookupService lookupService = null;

	private String irsProcessingCode = null;

	private String tempFileLocation = null;

	DataHandler handler;

	DataHandler attachName;

	Attachment formDataFile;


	//path and name to the cert file to be used for digitally signing the soap header
	private String irsSignatureFile = null;

	private String transmissionControlCode = null;
	private String softwareId = null;
	private String clientSystemId = null;
	private String irsContactFname = null;
	private String irsContactLname = null;
	private String irsContactPhone = null;

	private DAOOperations genericDAO;

	/**
	 * Main entry for original submission bulk entries to the IRS
	 * You should not invoke this service directly from outside context of the IRSTransmissionService
     * @param transmission log entry
     */
	 public String transmitOriginal(IrsTransmissionLogEntry transmission) throws ServiceException {

		 return generateTransmitterBulkSubmission(transmission, null);

	 }

	 /*
	  * Standard business header used by both the bulk submits and the Ack service.
	  */
	 private ACABulkBusinessHeaderRequestType getBusinessHeader() throws ServiceException {

		 us.gov.treasury.irs.ext.aca.air.ty16.ObjectFactory f = new us.gov.treasury.irs.ext.aca.air.ty16.ObjectFactory();
		 ACABulkBusinessHeaderRequestType bHeader = f.createACABulkBusinessHeaderRequestType();

		 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
		 sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		 String date = sdf.format(new Date());
		 XMLGregorianCalendar xmlCal = null;

		try {
			xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(date);
		} catch (DatatypeConfigurationException e) {
			logger.error("Data Type Error in IRS Transmit:", e);
		}

		 UUID id = UUID.randomUUID();
		 String utid = id + ":" + APP_ID + ":" + this.getTransmissionControlCode() + "::" + TYPE_CODE;

		 bHeader.setTimestamp(xmlCal);
		 bHeader.setUniqueTransmissionId(utid);


		 return bHeader;
	 }


	 /*
	  * primary worker for constructing the IRS bulk submit payload from the
	  * transmission log entry objects, that have been pre-committed
	  *
	  */
	private String generateTransmitterBulkSubmission(IrsTransmissionLogEntry transmission, String originalReceipt) throws ServiceException{

		 try {

			 //data file attachment, root container for all 1094 and 1095B form entries
			 ACABulkRequestTransmitterType filebody = new ACABulkRequestTransmitterType();

			 ACATrnsmtManifestReqDtlType headerManifest = new ACATrnsmtManifestReqDtlType();

			 setCommonHeader(transmission, headerManifest);

			 //send the original receipt if correction or replacing a previous transmisison
			 if (transmission.getCorrectionInd().equalsIgnoreCase(SUBMIT_REPLACEMENT) && originalReceipt != null) {
				 headerManifest.setOriginalReceiptId(StringUtils.split(originalReceipt, "|")[0]);
			 }

			 //1094<form type>_Request_<TCC>_<Date>T<TimeStamp>Z.xml
			 //1094B_Request_BY01G_20140101T010102000Z.xml
			 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'");
			 sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
			 String date = sdf.format(new Date());

			 String fileName = "1094B_Request_" + this.getTransmissionControlCode() + "_" + date + ".xml";

			 if (transmission.getIrsTransmissionLogData() != null && transmission.getIrsTransmissionLogData().size() > 0 &&
					 transmission.getIrsTransmissionLogData().iterator().next().getRequestData() != null) {

				 //create data handler from binary data source of the pre constructed xml data
				 ByteDataSource bulkFile = new ByteDataSource(transmission.getIrsTransmissionLogData().iterator().next().getRequestData().getBytes(), "text/xml;");
				 bulkFile.setContentType("text/xml;");
				 bulkFile.setName(fileName);
				 handler = new DataHandler(bulkFile);

				 //create attachment from DH
				 //formDataFile = AttachmentUtil.createMtomAttachmentFromDH(true, handler, "", 100);

				 filebody.setVersion("1.0");

				 filebody.setBulkExchangeFile(handler);

				 //compute checksum and size
				 headerManifest.setChecksumAugmentationNum(org.apache.commons.codec.digest.DigestUtils.md5Hex
						 (transmission.getIrsTransmissionLogData().iterator().next().getRequestData().getBytes()));
				 headerManifest.setAttachmentByteSizeNum(BigInteger.valueOf(transmission.getIrsTransmissionLogData().iterator().next().getRequestData().getBytes().length));

			 } else {
				 //we have no data somehow, no point to continue
				 throw new ServiceException("Form Data file does not exist in IRS Bulk Submission Request");
			 }

			 headerManifest.setDocumentSystemFileNm(fileName);

			 ACABulkBusinessHeaderRequestType busHeaderDetail = new ACABulkBusinessHeaderRequestType();

			 busHeaderDetail = getBusinessHeader();
			 Holder<ACABulkBusinessHeaderRequestType> holder = new Holder<ACABulkBusinessHeaderRequestType>(busHeaderDetail);

			 String responseStatus = "";
			 String errorMsg = "";
			 String status = "";
			 String receiptId = "";

			 //gzip compression is mandatory on the IRS service
		 	 GZIPFeature gz = new GZIPFeature();
		 	 gz.setThreshold(0);
		 	 gz.setForce(true);

		 	 List<Feature> features = new ArrayList<Feature>();
		 	 features.add(gz);

		 	 //create client from jax factory to avoid conflict with com.sun.xml.ws
		 	 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		 	 factory.setAddress(this.getIrsServiceBulkEndPoint());
		 	 factory.setServiceClass(BulkRequestTransmitterPortType.class);
		 	 factory.setFeatures(features);

		 	 JAXBDataBinding jxBinding = new JAXBDataBinding();

		 	 factory.setDataBinding(jxBinding);

		 	 BulkRequestTransmitterPortType port = (BulkRequestTransmitterPortType) factory.create();

		 	 org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy.getClient(port);

		 	 //setup properties, namespaces, attachments and interceptors
		 	 this.clientBulkRequestSetup(client);

		 	 //generate security header and digital signature on required elements
		 	 this.addSignature(client, true, true, true);

			us.gov.treasury.irs.msg.irsacabulkrequesttransmitter.ACABulkRequestTransmitterResponseType response =
					new us.gov.treasury.irs.msg.irsacabulkrequesttransmitter.ACABulkRequestTransmitterResponseType();
			try {

				response = port.bulkRequestTransmitter(filebody, null, null, holder, headerManifest);
				if (response == null)
					throw new ServiceException("IRS Transmit Request Failure: No Response Recieved");

			} catch (Exception ex) {
				StringWriter sw = new StringWriter();
				ex.printStackTrace(new PrintWriter(sw));
				String exceptionDetails = sw.toString();
				logger.error("Exception during IRS bulk request service call:" + exceptionDetails);
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.FAILED.getCode()));
				transmission.setErrorDescription("No Receipt ID Provided in Submission Response");

				throw new ServiceException("IRS Transmit Request Failure:" + ex.getMessage());
			}

			receiptId = response.getReceiptId();
			if (response.getErrorMessageDetail() != null) {
				ErrorMessageDetailType error = response.getErrorMessageDetail();
				if (error != null) {
					errorMsg += error.getErrorMessageCd() + ":";
					errorMsg += error.getErrorMessageTxt();
				}
			}
			TransmissionStatusCodeType c = response.getTransmissionStatusCd();
			status = c.value();

			responseStatus = receiptId + "^" + status + "^" + errorMsg;

			return responseStatus;

		 } catch (Exception ex) {
			 StringWriter sw = new StringWriter();
				ex.printStackTrace(new PrintWriter(sw));
				String exceptionDetails = sw.toString();
				logger.error("Exception during IRS bulk request service call:" + exceptionDetails);
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.FAILED.getCode()));
				transmission.setErrorDescription("No Receipt ID Provided in Submission Response");

				throw new ServiceException("IRS Transmit Request Failure:" + ex.getMessage());
		}


	 }


	 /*
	  * Mock response for testing/internal validation only
	  *
	 private ACABulkRequestTransmitterResponse mockBulkResponse() {

		 ACABulkRequestTransmitterResponse response =
				 new ACABulkRequestTransmitterResponse();

		 final Random rnd = new Random();
		 String res = "1095B-15-" + String.valueOf(rnd.nextInt());
		 response.setReceiptId(res);

		 response.setTransmissionStatusCd(TransmissionStatusCodeType.PROCESSING);

		 ErrorMessageDetail d = new ErrorMessageDetail();
		 d.setErrorMessageCd("AIR101589");
		 d.setErrorMessageTxt("Invalid TCC");
		 response.setErrorMessageDetail(d);

		 return response;
	 }*/


	/*
	 * generic marshaling utility for any of the jax object factories
	 */
	 @SuppressWarnings("rawtypes")
	private String buildXmlDocument(JAXBElement document) {

		  Class<?> clazz = document.getValue().getClass();
		  try {
		    JAXBContext context =
		        JAXBContext.newInstance(clazz.getPackage().getName());
		    Marshaller m = context.createMarshaller();
		    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		    m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
		    StringWriter sw = new StringWriter();
		    m.marshal(document, sw);
		    String xml = sw.toString();

		    return xml;

		  } catch (JAXBException e) {
		    logger.error("Error building xml doc " + e);

		  }
		  return null;
	}

 	/**
	 * Main entry for replacing failed bulk batch submissions
	 * You should not invoke this service directly from outside context of the IRSTransmissionService
     * @param transmission log entry
     * @param original receipt of the batch that is being replaced
     */
	 public String retransmitBatchSubmission(IrsTransmissionLogEntry transmission, String originalReceipt) throws ServiceException {
		 //entire batch was expired or rejected, retransmit with original 1095B data and original receipt
		 return generateTransmitterBulkSubmission(transmission, originalReceipt);

	 }

 	/**
	 * Main entry for replacing failed individual person submissions
	 * You should not invoke this service directly from outside context of the IRSTransmissionService
     * @param transmission log entry
     * @param original unique record id of the person entry that is being replaced
     */
	 public String retransmitPersonSubmission(IrsTransmissionLogEntry transmission, String originalReceipt) throws ServiceException {
		 return generateTransmitterBulkSubmission(transmission, originalReceipt);

	 }


 	/**
	 * Main entry for requesting acknowledgment of a previous submitted entry in processing status
	 * You should not invoke this service directly from outside context of the IRSTransmissionService
     * @param transmission log entry
     */
	public void requestProcessingStatus(IrsTransmissionLogEntry transmission) throws ServiceException {


		if (transmission == null || transmission.getReceiptId() == null || transmission.getReceiptId().isEmpty()) {
			return;
		}

		String receipt = transmission.getReceiptId();

		try {

			//only the business header needed for acks

			ACABulkBusinessHeaderRequestType busHeaderDetail = new ACABulkBusinessHeaderRequestType();
			busHeaderDetail = getBusinessHeader();
			Holder<ACABulkBusinessHeaderRequestType> holder = new Holder<ACABulkBusinessHeaderRequestType>(busHeaderDetail);

			ACABulkRequestTransmitterStatusDetailRequestType requestBody = new ACABulkRequestTransmitterStatusDetailRequestType();

			ACABulkReqTrnsmtStsReqGrpDtlType reqDetail = new ACABulkReqTrnsmtStsReqGrpDtlType();

			reqDetail.setReceiptId(receipt);

			requestBody.setACABulkReqTrnsmtStsReqGrpDtl(reqDetail);

			ACABulkRequestTransmitterStatusDetailResponseType response =
					new ACABulkRequestTransmitterStatusDetailResponseType();

			 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		 	 factory.setAddress(this.getIrsServiceStatusEndPoint());
		 	 factory.setServiceClass(ACATransmitterStatusReqPortType.class);

		 	 JAXBDataBinding jxBinding = new JAXBDataBinding();
		 	 factory.setDataBinding(jxBinding);

		 	 ACATransmitterStatusReqPortType port = (ACATransmitterStatusReqPortType) factory.create();

		 	 org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy.getClient(port);
		 	 client.getInInterceptors().add(new GZIPInInterceptor());


		 	 //*********for local dev only, do NOT leave this enabled in upper env
		 	 //client.getOutInterceptors().add(new CxfOutInterceptor());
		 	 //***************

		 	 //generate security header and digital signature on required elements
		 	 this.addSignature(client, true, true, false);

		 	 //TODO remove?
		 	 HTTPConduit http = (HTTPConduit) client.getConduit();
		 	 HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
		 	 httpClientPolicy.setContentType("text/xml");
		 	 httpClientPolicy.setAcceptEncoding("gzip,deflate");
		 	 http.setClient(httpClientPolicy);

			try {
				// if ack request fails or no-response, just log it
				//eat the exception and consider as still processing
				//it will keep trying again each ack interval until it reaches expired status
				//response = mockResponse(receipt);
				response = port.getACATransmitterStatusReqOperation(requestBody, null, null, holder);

				if (response == null || response.getACABulkRequestTransmitterResponse() == null ||
						response.getACABulkRequestTransmitterResponse().getTransmissionStatusCd() == null) {
					logger.error("Exception during IRS request acknowlegement service call: No Response Returned");

					return;
				}
			} catch (Exception ex) {

				 StringWriter sw = new StringWriter();
				 ex.printStackTrace(new PrintWriter(sw));
				 String exceptionDetails = sw.toString();
				 logger.error("Exception during IRS request acknowlegement service call:" + exceptionDetails);
				return;
			}

			TransmissionStatusCodeType respType =
					response.getACABulkRequestTransmitterResponse().getTransmissionStatusCd();

			transmission.setAckDate(holder.value.getTimestamp().toGregorianCalendar().getTime());
			transmission.setAckId(holder.value.getUniqueTransmissionId().split(":")[0]);

			//transmission is already set to processing status by default
			//if not complete yet do nothing, it will check again in the next interval
			if (respType == TransmissionStatusCodeType.PROCESSING) {
				return;
			}

			//check each possible status return type
			if (respType == TransmissionStatusCodeType.ACCEPTED) {
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.ACCEPTED.getCode()));

				transmission.setErrorDescription(null);

			} else if (respType == TransmissionStatusCodeType.NOT_FOUND) {
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.NOT_FOUND.getCode()));

				transmission.setErrorDescription("Requested Receipt ID Not Found");
			} else if (respType == TransmissionStatusCodeType.REJECTED) {
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.REJECTED.getCode()));

				//error may be here or in the data file attachment; or both
				if (response.getACABulkRequestTransmitterResponse().getErrorMessageDetail() != null) {
					transmission.setErrorDescription(response.getACABulkRequestTransmitterResponse().getErrorMessageDetail().getErrorMessageCd()
							+ ":" + response.getACABulkRequestTransmitterResponse().getErrorMessageDetail().getErrorMessageTxt());
				}

				String recordErr = "";

				if (response.getACABulkReqTrnsmtStsRespGrpDtl() != null) {

					DataHandler dh = response.getACABulkReqTrnsmtStsRespGrpDtl().getBulkExchangeFile();
					//may or may not have an error data file attached, if so it has to be 1094 entry error
					if (dh != null) {

						List<TransmitterErrorDetailGrpType> errorList = getErrorsFromBulkFile(dh, transmission);

						Iterator<TransmitterErrorDetailGrpType> errorIt = errorList.iterator();

						while (errorIt.hasNext()) {
							TransmitterErrorDetailGrpType curError = errorIt.next();

							List<ErrorMessageDetailType> errList = curError.getErrorMessageDetail();

					    	if (errList != null && errList.size() > 0) {
					    		if (errList.size() == 1) {
					    			ErrorMessageDetailType firstErr = errList.get(0);
					    			recordErr = firstErr.getErrorMessageCd() + ":" + firstErr.getErrorMessageTxt();
					    		} else {
					    			for (int i = 0; i < errList.size(); i++) {
					    				ErrorMessageDetailType cur = errList.get(i);
					    				recordErr += cur.getErrorMessageCd();
					    				recordErr += ":";
					    			}
					    		}


					    		recordErr = transmission.getErrorDescription() + ":" + recordErr;

					    		if (recordErr.length() > 900) recordErr = recordErr.substring(0, 900) + " :TRUNCATED";;
					    		transmission.setErrorDescription(recordErr);
					    	}

						}
					}
				}


			} else if (respType == TransmissionStatusCodeType.ACCEPTED_WITH_ERRORS ||
					respType == TransmissionStatusCodeType.PARTIALLY_ACCEPTED) {
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.ACCEPTED_WITH_ERRORS.getCode()));

				transmission.setErrorDescription(null);

				DataHandler dh = response.getACABulkReqTrnsmtStsRespGrpDtl().getBulkExchangeFile();

				List<TransmitterErrorDetailGrpType> errorList = getErrorsFromBulkFile(dh, transmission);

			    //go through error list, each one find the referenced sequence in the details list and set error message
			    Iterator<TransmitterErrorDetailGrpType> errorIt = errorList.iterator();

			    while (errorIt.hasNext()) {
			    	try {

			    		TransmitterErrorDetailGrpType curError = errorIt.next();
				    	String uniqueId = curError.getUniqueRecordId();

				    	//1095B-15-00000098|1|27
				    	if (uniqueId == null || uniqueId.isEmpty() || StringUtils.split(uniqueId, "|").length < 3) {
				    		logger.error("Invalid Unique Record Id encountered in IRS data file: " + uniqueId);
				    		continue;
				    	}

				    	int sequence = Integer.valueOf((StringUtils.split(uniqueId, "|")[2]));

				    	//if multiple errors, concat just the codes
				    	//one error, save code plus description
				    	List<ErrorMessageDetailType> errList = curError.getErrorMessageDetail();


				    	String recordErr = "";
				    	if (errList != null && errList.size() > 0) {
				    		if (errList.size() == 1) {
				    			ErrorMessageDetailType firstErr = errList.get(0);
				    			recordErr = firstErr.getErrorMessageCd() + ":" + firstErr.getErrorMessageTxt();
				    		} else {
				    			for (int i = 0; i < errList.size(); i++) {
				    				ErrorMessageDetailType cur = errList.get(i);
				    				recordErr += cur.getErrorMessageCd();
				    				recordErr += ":";
				    			}
				    		}

				    		if (recordErr.length() > 900) recordErr = recordErr.substring(0, 900) + " :TRUNCATED";

				    		//find the detail sequence and set the error
				    		Set<IrsTransmissionLogDetailEntry> detailSet = transmission.getIrsTransmissionLogDetails();

					    	Iterator<IrsTransmissionLogDetailEntry> set = detailSet.iterator();

					    	while (set.hasNext()) {
					    		IrsTransmissionLogDetailEntry cur = set.next();
					    		if (cur.getRecordSequence() == sequence) {
					    			String err = recordErr;
					    			if (err.length() > 900) err = err.substring(0, 900) + " :TRUNCATED";
					    			cur.setErrorDescription(err);
					    			break;
					    		}
					    	}
				    	}

			    	} catch (Exception ex) {
			    		logger.error("Error processing form data file records: " + ex.getMessage());
			    		continue;
			    	}
			    }
			} else {
				//status was returned but no match to any known type
				//should never happen
				transmission.setErrorDescription("Unknown Status Type Returned:" + respType.value());
				transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
						TransmissionStatus.class, TransmissionStatus.FAILED.getCode()));
			}

		} catch (Exception e) {
			transmission.setErrorDescription("Unknown Error in Requesting Acknowledgements:" + e.getMessage());
			transmission.setTransmissionStatus((TransmissionStatus) this.lookupService.getByCode(
					TransmissionStatus.class, TransmissionStatus.FAILED.getCode()));
			logger.error("RPC Exception in IRSAcknolegement request: " + e.getMessage());
		}

	 }

	/*
	 * Marshals the response data file attachment to
	 * xml response data and to an error detail list to process
	 *
	 */
	 @SuppressWarnings("unchecked")
	private List<TransmitterErrorDetailGrpType> getErrorsFromBulkFile(DataHandler dh, IrsTransmissionLogEntry transmission) {
		 List<TransmitterErrorDetailGrpType> errorList = null;
		 java.io.InputStream istream = null;
		 try {
		 	FormBCTrnsmtSubmissionDtlType errRoot =
					new FormBCTrnsmtSubmissionDtlType();

			us.gov.treasury.irs.msg.form1094_1095bctransmittermessage.ObjectFactory rFac =
					new us.gov.treasury.irs.msg.form1094_1095bctransmittermessage.ObjectFactory();

			JAXBContext context =
		        JAXBContext.newInstance(us.gov.treasury.irs.msg.form1094_1095bctransmittermessage.ObjectFactory.class);

			Unmarshaller m = context.createUnmarshaller();
		    Marshaller ms = context.createMarshaller();
		    ms.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		    StringWriter sw = new StringWriter();

		    istream = dh.getInputStream();
		    //errRoot = ((JAXBElement<FormBCTrnsmtSubmissionDtlType>)m.unmarshal(istream)).getValue();

		    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		    dbf.setExpandEntityReferences(false);
		    dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
		    dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

		    DocumentBuilder db = dbf.newDocumentBuilder();
		    Document document = db.parse(istream);
		    errRoot = ((JAXBElement<FormBCTrnsmtSubmissionDtlType>)m.unmarshal(document)).getValue();

		    ms.marshal(rFac.createFormBCTransmitterSubmissionDtl(errRoot), sw);
		    String responseXml = sw.toString();
		    transmission.getIrsTransmissionLogData().iterator().next().setResponseData(responseXml);

		    errorList = errRoot.getACATransmitterSubmissionDetail().getTransmitterErrorDetailGrp();

		 } catch (JAXBException ex) {
			 logger.error("Error parsing bulk error data file from IRS: " + ex.getMessage());
		 } catch (IOException ex) {
			 logger.error("Error parsing bulk error data file from IRS: " + ex.getMessage());
		 } catch (ParserConfigurationException ex) {
			 logger.error("Error parsing bulk error data file from IRS: " + ex.getMessage());
		} catch (SAXException ex) {
			logger.error("Error parsing bulk error data file from IRS: " + ex.getMessage());
		} finally {
			 if (istream != null) {
				 org.apache.commons.io.IOUtils.closeQuietly(istream);
			 }
		 }

		 return errorList;
	 }

	 /*
	  * Mock response for testing or internal validation only
	  *
	 private ACABulkRequestTransmitterStatusDetailResponse mockResponse(String receipt) {
		 ACABulkRequestTransmitterStatusDetailResponse response =
				 new ACABulkRequestTransmitterStatusDetailResponse();

		 ACABulkReqTrnsmtStsRespGrpDtl det =
				 new ACABulkReqTrnsmtStsRespGrpDtl();

		 //TransmissionStatusCdType
		 us.gov.treasury.irs.ext.aca.air.ty16.ACABulkRequestTransmitterResponse type = new us.gov.treasury.irs.ext.aca.air.ty16.ACABulkRequestTransmitterResponse();

		 type.setTransmissionStatusCd(TransmissionStatusCodeType.ACCEPTED);
		 type.setErrorMessageDetail(null);
		 type.setReceiptId(receipt);

		 response.setACABulkRequestTransmitterResponse(type);


		 DataHandler handler = new DataHandler(new FileDataSource(this.getTempFileLocation() + "errorFile.xml"));
		 det.setBulkExchangeFile(handler);

		 response.setACABulkReqTrnsmtStsRespGrpDtl(det);

		 return response;
	 }*/

	 /*
	  * used for test scenarios and annual renewal of VA software ID in all T(test) IRS env
	  * SSN range must map to valid name control code or transmit is rejected
	  */
	 private String generateTestSsn() {
		 String ssn = "000000";

		 //Random rand = new Random();
		 SecureRandom rand = new SecureRandom();


		 int suffix = rand.nextInt((900 - 100) + 1) + 100;



		 //int suffix = ThreadLocalRandom.current().nextInt(100, 900 + 1);

		 ssn += Integer.toString(suffix);

		 return ssn;
	 }


	 /*
	  * used for test scenarios and annual renewal of VA software ID in all T(test) IRS env
	  * SSN range must map to valid name control code or transmit is rejected
	  */
	 private String getNameControl(String ssn) {
		 String nameControl = "";
		 //688656013

		 /*
		  *           SSN RANGE             NAME CONTROL
			000-00-0001 through 000-00-0100 HERR
			000-00-0101 through 000-00-0200 MART
			000-00-0201 through 000-00-0300 WILL
			000-00-0301 through 000-00-0400 SOUT
			000-00-0401 through 000-00-0500 GAVI
			000-00-0501 through 000-00-0600 DAVI
			000-00-0601 through 000-00-0700 NICH
			000-00-0701 through 000-00-0800 CAME
			000-00-0801 through 000-00-0900 JONE
			7
			000-00-0901 through 000-00-1000 SCOT
		  */

		 if (ssn == null  || ssn.length() != 9) {
			 return nameControl;
		 }

		 String pos = ssn.substring(6, 7);

		 String[] nameControls = new String[] {"HERR","MART","WILL","SOUT","GAVI","DAVI","NICH","CAME","JONE","SCOT"};

		 int nc = Integer.valueOf(pos);

		 if (ssn.endsWith("00")) {
			 nc = nc - 1;
		 }


		 nameControl = nameControls[nc];

		 return nameControl;
	 }

	/**
	 * Builds the xml payload for the entire form data file
	 * containing all 1094 and 1095B submissions in the batch request
	 * You should not invoke this service directly from outside context of the IRSTransmissionService
     * @param batch list of individual 1095B entries in the submission
     * @param correctionInd identifies in the batch contains Corrected or Replacement records for previous submissions
     * @param taxYear the tax year to process
     * @param originalReceipt the identifier of the original submissions that are being corrected or replaced
     */
	 public IrsTransmissionLogData buildRequestDataFile(List<IRS1095B> batch, String correctionInd, String taxYear, String originalReceipt, List<IRS1095B> errorList) throws ServiceException {

		 Form1094BUpstreamDetailType transmitter = new Form1094BUpstreamDetailType();

		 us.gov.treasury.irs.msg.form1094_1095btransmitterupstreammessage.ObjectFactory f = new us.gov.treasury.irs.msg.form1094_1095btransmitterupstreammessage.ObjectFactory();

		 Form109495BTrnsmtUpstreamType rootContainer = f.createForm109495BTrnsmtUpstreamType();

		 //per IRS guide these values are required always to be the same
		 transmitter.setRecordType("String");
		 transmitter.setLineNum(BigInteger.valueOf(0));
		 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
			 transmitter.setBusinessNameControlTxt(AATS_CONTROL_EIN);
		 }


		 IrsTransmissionLogData requestData = new IrsTransmissionLogData();
		 String correctedInd = "0";
		 if (correctionInd.equalsIgnoreCase(SUBMIT_CORRECTION)) {
			 correctedInd = "1";
		 }


		 try {
			 //always only one 1094 in the bulk transmit
			 transmitter.setSubmissionId(PAYER_COUNT.toString());

			 /*if (correctionInd.equalsIgnoreCase(SUBMIT_REPLACEMENT) && originalReceipt != null) {
				 transmitter.setOriginalUniqueSubmissionId(StringUtils.split(originalReceipt, "|")[0] + "|1");
			 }*/

			 transmitter.setTaxYr(DatatypeFactory.newInstance().newXMLGregorianCalendar(taxYear).toString());
			 BusinessNameType busName = new BusinessNameType();
			 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
				 busName.setBusinessNameLine1Txt(AATS_CONTROL_LEGAL_NAME + LEGAL_NAME);
			 } else {
				 busName.setBusinessNameLine1Txt(LEGAL_NAME);
			 }
			 transmitter.setBusinessName(busName);
			 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
				 transmitter.setEmployerEIN(AATS_EIN);
			 } else {
				 transmitter.setEmployerEIN(EIN);
			 }

			 OtherCompletePersonNameType name = new OtherCompletePersonNameType();

			 name.setPersonFirstNm(this.getIrsContactFname());
			 name.setPersonLastNm(this.getIrsContactLname());

			 transmitter.setContactNameGrp(name);
			 transmitter.setContactPhoneNum(this.getIrsContactPhone());

			 BusinessAddressGrpType busAddress = new BusinessAddressGrpType();
			 USAddressGrpType us = new USAddressGrpType();
			 us.setAddressLine1Txt(BUSINESS_STREET_ADDRESS);
			 us.setCityNm(BUSINESS_CITY);

			 us.setUSStateCd(StateType.fromValue(BUSINESS_STATE));
			 us.setUSZIPCd(BUSINESS_ZIP);
			 busAddress.setUSAddressGrp(us);
			 busAddress.setForeignAddressGrp(null);

			 transmitter.setMailingAddressGrp(busAddress);

			 //*******End 1094**************

			 //*********Start 1095 details list************
			 Iterator<IRS1095B> it = batch.iterator();
			 int pos = 1;
			 while (it.hasNext()) {
				 Form1095BUpstreamDetailType personDetail = new Form1095BUpstreamDetailType();

				 String testSSN = generateTestSsn();
				 String testNameControl = getNameControl(testSSN);

				 IRS1095B cur = (IRS1095B)it.next();

				 try {
					 personDetail.setRecordType("String");
					 personDetail.setLineNum(BigInteger.valueOf(0));

					 personDetail.setRecordId((BigInteger.valueOf(pos)).toString());
					 personDetail.setCorrectedInd(correctedInd);
					 personDetail.setTaxYr(DatatypeFactory.newInstance().newXMLGregorianCalendar(taxYear).toString());

					 ResponsibleIndividualGrpType rig = new ResponsibleIndividualGrpType();

					 if (cur.getSSN() != null && !cur.getSSN().isEmpty()) {
							rig.setTINRequestTypeCd(TINRequestTypeCodeType.INDIVIDUAL_TIN);
							if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
								rig.setSSN(testSSN);
							} else {
								rig.setSSN(cur.getSSN());
							}

					}

					 if (getDob(cur.getDOB()) != null) {

						 rig.setBirthDt(getDob(cur.getDOB()).toString());
					 }
					 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
						 rig.setPersonNameControlTxt(testNameControl);
					 }

					 OtherCompletePersonNameType personName = new OtherCompletePersonNameType();
					 if (cur.getFirstName() != null) {
						 personName.setPersonFirstNm(cur.getFirstName());
					 } else {
						 personName.setPersonFirstNm(this.getIrsContactFname());
					 }

					 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
						 personName.setPersonLastNm(testNameControl + cur.getLastName());
					 } else {
						 personName.setPersonLastNm(cur.getLastName());
					 }
					 if (cur.getMiddleName() != null && !cur.getMiddleName().isEmpty()) {
						 personName.setPersonMiddleNm(cur.getMiddleName());
					 }
					 if (cur.getNameSuffix() != null && !cur.getNameSuffix().isEmpty()) {
						 personName.setSuffixNm(cur.getNameSuffix());
					 }

					 stripBadCharsAndEscapeXmlChars(personName);

					 rig.setResponsibleIndividualName(personName);

					 if (correctionInd.equalsIgnoreCase(SUBMIT_CORRECTION) && originalReceipt != null) {
						 CorrectedRecordInfoGrpType correction = new CorrectedRecordInfoGrpType();
						 correction.setCorrectedUniqueRecordId(originalReceipt);
						 correction.setCorrectedRecordPayeeName(personName);
						 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
							 correction.setCorrectedRecordPayeeTIN(testSSN);
						 } else {
							 correction.setCorrectedRecordPayeeTIN(cur.getSSN());
						 }

						 personDetail.setCorrectedRecordInfoGrp(correction);

					 }

					 BusinessAddressGrpType personAddress = new BusinessAddressGrpType();
					 boolean badAddress = false;
					 //defect in IRS validation causes entire batch to be rejected if even one record has one missing address field
					 //workaround until that's fixed is to default missing field to the same field in the business address
					 if (cur.getAddress() != null) {
						 if ((cur.getAddress().getCountry() != null && cur.getAddress().getCountry().equals(Country.CODE_USA.getName())) ||
								 (cur.getAddress().getState() != null && cur.getAddress().getZipCode() != null)) {
							 USAddressGrpType add = new USAddressGrpType();

							 add.setAddressLine1Txt(cur.getAddress().getLine1() != null && cur.getAddress().getLine1().trim().length() > 0 ? cur.getAddress().getLine1():busAddress.getUSAddressGrp().getAddressLine1Txt());
							 if (cur.getAddress().getLine2() != null && !cur.getAddress().getLine2().isEmpty()) {
								 add.setAddressLine2Txt(cur.getAddress().getLine2());
							 }
							 add.setCityNm(cur.getAddress().getCity() != null && cur.getAddress().getCity().trim().length() > 0 ? cur.getAddress().getCity():busAddress.getUSAddressGrp().getCityNm());
							 if (cur.getAddress().getState() != null && cur.getAddress().getState().trim().length() > 0) {
								 try {
									 add.setUSStateCd(StateType.fromValue(cur.getAddress().getState()));
								 } catch (Exception ex) {
									 //throw new Exception("INVALID STATE CODE for US Address:" + cur.getAddress().getState());
									 badAddress = true;
								 }
							 } else {
								 add.setUSStateCd(busAddress.getUSAddressGrp().getUSStateCd());
							 }
							 add.setUSZIPCd(cur.getAddress().getZipCode() != null && cur.getAddress().getZipCode().trim().length() > 0 ? cur.getAddress().getZipCode():busAddress.getUSAddressGrp().getUSZIPCd());
							 if (cur.getAddress().getZipPlus4() != null && !cur.getAddress().getZipPlus4().isEmpty()) {
								 add.setUSZIPExtensionCd(cur.getAddress().getZipPlus4());
							 }

							 stripBadCharsAndEscapeXmlChars(add);
							 personAddress.setUSAddressGrp(add);
							 personAddress.setForeignAddressGrp(null);

						 } else {
							 ForeignAddressGrpType add = new ForeignAddressGrpType();
							 add.setAddressLine1Txt(cur.getAddress().getLine1() != null && cur.getAddress().getLine1().trim().length() > 0 ? cur.getAddress().getLine1() : busAddress.getUSAddressGrp().getAddressLine1Txt());
							 if (cur.getAddress().getLine2() != null && !cur.getAddress().getLine2().isEmpty()) {
								 add.setAddressLine2Txt(cur.getAddress().getLine2());
							 }

							 add.setCityNm(cur.getAddress().getCity() != null && cur.getAddress().getCity().trim().length() > 0 ? cur.getAddress().getCity() : busAddress.getUSAddressGrp().getCityNm());

							 if (cur.getAddress().getCountry() != null && cur.getAddress().getCountry().trim().length() > 0) {
								 add.setCountryNm(cur.getAddress().getCountry());
							 } else {
								 badAddress = true;
							 }

							 add.setForeignPostalCd(cur.getAddress().getPostalCode());
							 if (cur.getAddress().getProvince() != null && !cur.getAddress().getProvince().isEmpty()) {
								 add.setForeignProvinceNm(cur.getAddress().getProvince());
							 }

							 stripBadCharsAndEscapeXmlChars(add);
							 personAddress.setForeignAddressGrp(add);
							 personAddress.setUSAddressGrp(null);
						 }
					 } else {
						 personAddress = busAddress;
					 }

					 if ((personAddress.getUSAddressGrp() == null && personAddress.getForeignAddressGrp() == null) || badAddress) {
						 personAddress = busAddress;
					 }


					 rig.setMailingAddressGrp(personAddress);

					 rig.setPolicyOriginCd("C");

					 personDetail.setResponsibleIndividualGrp(rig);

					 //Issuer Info Group
					 IssuerInfoGrpType issuer = new IssuerInfoGrpType();
					 issuer.setBusinessName(busName);
					 issuer.setContactPhoneNum(BUSINESS_PHONE);
					 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
						 issuer.setEIN(AATS_EIN);
					 } else {
						 issuer.setEIN(EIN);
					 }

					 issuer.setMailingAddressGrp(busAddress);
					 issuer.setTINRequestTypeCd(TINRequestTypeCodeType.BUSINESS_TIN);
					 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
						issuer.setBusinessNameControlTxt(AATS_CONTROL_EIN);
					 }

					 personDetail.setIssuerInfoGrp(issuer);

					 //Covered Individual
					 EmployerCoveredIndividualType personInfo = new EmployerCoveredIndividualType();
					 if (getDob(cur.getDOB()) != null) {
						 personInfo.setBirthDt(getDob(cur.getDOB()).toString());
					 }
					 personInfo.setCoveredIndividualName(personName);



					 if (cur.getSSN() != null && !cur.getSSN().isEmpty()) {
						 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
							 personInfo.setSSN(testSSN);
						 } else {
							 personInfo.setSSN(cur.getSSN());
						 }

						 personInfo.setTINRequestTypeCd(TINRequestTypeCodeType.INDIVIDUAL_TIN);
					 }

					 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
						 personInfo.setPersonNameControlTxt(testNameControl);
					 }

					 if (cur.getMecPeriod().isCoveredAll12Months()) {
						 personInfo.setCoveredIndividualAnnualInd("1");
					 } else {
						 personInfo.setCoveredIndividualAnnualInd("0");
					 }

					 MonthIndGrpType period = new MonthIndGrpType();

					 period.setJanuaryInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("1") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("1") ? "1" : "0");
					 period.setFebruaryInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("2") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("2") ? "1" : "0");
					 period.setMarchInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("3") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("3") ? "1" : "0");
					 period.setAprilInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("4") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("4") ? "1" : "0");
					 period.setMayInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("5") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("5") ? "1" : "0");
					 period.setJuneInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("6") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("6") ? "1" : "0");
					 period.setJulyInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("7") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("7") ? "1" : "0");
					 period.setAugustInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("8") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("8") ? "1" : "0");
					 period.setSeptemberInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("9") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("9") ? "1" : "0");
					 period.setOctoberInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("10") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("10") ? "1" : "0");
					 period.setNovemberInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("11") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("11") ? "1" : "0");
					 period.setDecemberInd((boolean) cur.getMecPeriod().getCoverageMonths().containsKey("12") && (Boolean) cur.getMecPeriod().getCoverageMonths().get("12") ? "1" : "0");

					 personInfo.setCoveredIndividualMonthlyIndGrp(period);


					 if (personDetail.getCorrectedInd().equalsIgnoreCase("1")) {
						 if (personInfo.getCoveredIndividualAnnualInd().equalsIgnoreCase("1") || monthsRemaining(period)) {
							 personDetail.getCoveredIndividualGrp().add(personInfo);
						 }
					 } else {
						 personDetail.getCoveredIndividualGrp().add(personInfo);
					 }


				 } catch (Exception ex) {
					 //something goes wrong before it's added, remove it from the batch
					 StringWriter sw = new StringWriter();
					 ex.printStackTrace(new PrintWriter(sw));
					 String exceptionDetails = sw.toString();

					 if (exceptionDetails.contains("INVALID STATE CODE for US Address")) {
						 cur.setErrorText(exceptionDetails.substring(0,59));
					 } else {
						 cur.setErrorText(exceptionDetails);
					 }
					 errorList.add(cur);

					 logger.error("Unknown Exception in building IRS data file entry for entry " + exceptionDetails);
					 it.remove();
					 continue;
				 }

				 transmitter.getForm1095BUpstreamDetail().add(personDetail);
				 pos++;
			 }

			 rootContainer.getForm1094BUpstreamDetail().add(transmitter);

			 transmitter.setForm1095BAttachedCnt(BigInteger.valueOf(transmitter.getForm1095BUpstreamDetail().size()).toString());

			 String xml = buildXmlDocument(f.createForm109495BTransmittalUpstream(rootContainer));

			 requestData.setRequestData(xml);


		} catch (DatatypeConfigurationException e) {
			logger.error("DataTypeError in BuildDataFile:" + e.getMessage(), e);
		} catch (Exception ex) {
			logger.error("Error in BuildDataFile:" + ex.getMessage(), ex);
		}

		return requestData;

	 }

	 private boolean monthsRemaining(MonthIndGrpType period) {

		 if (period.getJanuaryInd().equalsIgnoreCase("1") ||
				 period.getFebruaryInd().equalsIgnoreCase("1") ||
				 period.getMarchInd().equalsIgnoreCase("1") ||
				 period.getAprilInd().equalsIgnoreCase("1") ||
				 period.getMayInd().equalsIgnoreCase("1") ||
				 period.getJuneInd().equalsIgnoreCase("1") ||
				 period.getJulyInd().equalsIgnoreCase("1") ||
				 period.getAugustInd().equalsIgnoreCase("1") ||
				 period.getSeptemberInd().equalsIgnoreCase("1") ||
				 period.getOctoberInd().equalsIgnoreCase("1") ||
				 period.getNovemberInd().equalsIgnoreCase("1") ||
				 period.getDecemberInd().equalsIgnoreCase("1")) {
			 return true;

		 }

		 return false;
	 }

	 /**
	  * Takes an address object. Uses Java reflection to find String-type fields that have setters and getters.
	  * For all such fields that are found the field values are changed by stripping out some characters and
	  * character sequences that IRS rejects. Also special XML key characters are escaped.
	  * @param address object
	  */
	 public void stripBadCharsAndEscapeXmlChars(Object obj) throws Exception {
		String charsToStripOut = "--|#";	//strip all "--" and all "#" (specify as regex)

		Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();	//returns all class fields, even private and protected
		String values[] = {""};
        Class methodParams[] = {String.class};	//used to look for a method that takes a String parameter

        for (Field myField : fields) {
        	if (myField.getType().isAssignableFrom(String.class)) {	//if a String-type field
        		String capitalizedFieldName = Character.toUpperCase(myField.getName().charAt(0)) + myField.getName().substring(1);
        		try {
					Method getter = clazz.getMethod("get" + capitalizedFieldName, null);
					Method setter = clazz.getMethod("set" + capitalizedFieldName, methodParams);
					//strip bad characters. Several passes may be necessary if each string transformation leaves the resultant string with bad char sequences
					String fieldValue = (String) getter.invoke(obj, null);
					if (fieldValue != null && !fieldValue.isEmpty()) {
		        		String oldFieldValue = "";
		        		for (int i = 0; !oldFieldValue.equals(fieldValue) && i < 5; i++) {
		        			oldFieldValue = fieldValue;
		        			fieldValue = fieldValue.replaceAll(charsToStripOut, "");
		        		}
		        		//escape XML characters
		        		values[0] = StringEscapeUtils.escapeXml(fieldValue);
		        		setter.invoke(obj, (Object[])values);	//set the field to the new value
					}
				} catch (NoSuchMethodException e) {
					// if the field doesn't have the setter and getter we're looking for,
					//we'll drop into this catch, means we shouldn't change this field
				} catch (IllegalAccessException e) {
					logger.error("Unable to set address field:" + e.getMessage(), e);
				} catch (SecurityException e) {

				} catch (IllegalArgumentException e) {
					logger.error("Unable to set address field:" + e.getMessage(), e);
				} catch (InvocationTargetException e) {
					logger.error("Unable to set address field:" + e.getMessage(), e);
				}
        	}
        }
	 }

	 /*
	  * Utility for converting imprecise dob to xml
	  *
	  */
	 private XMLGregorianCalendar getDob(String dob) {
		Date dt = null;
		try {
			if (dob != null && dob.length() > 3) {
				 if (dob.length() == 8) {
					 SimpleDateFormat f = new SimpleDateFormat("yyyyMMdd");
					 dt = f.parse(dob);
				 } else if (dob.length() == 6) {
					 SimpleDateFormat f = new SimpleDateFormat("yyyyMM");
					 dt = f.parse(dob);
				 } else if (dob.length() == 4) {
					 SimpleDateFormat f = new SimpleDateFormat("yyyy");
					 dt = f.parse(dob);
				 } else {
					 return null;
				 }
				 GregorianCalendar cal = new GregorianCalendar();
				 cal.setTime(dt);


				 XMLGregorianCalendar xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendarDate
						 (cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH), 0);
				 xmlDate.setTimezone(DatatypeConstants.FIELD_UNDEFINED);

				 return xmlDate;
			 }
		} catch (Exception ex) {
			return null;
		}
		return null;
	 }

	 public ArrayList<IRS1095B> convertAllEntries(String dataFile) {
		 ArrayList<IRS1095B> listing = new ArrayList<IRS1095B>();

		 String xml = dataFile;

		 Form109495BTrnsmtUpstreamType result = JAXB.unmarshal(new StringReader(xml), Form109495BTrnsmtUpstreamType.class);

		 //always only one 1094 entry
		 List<Form1095BUpstreamDetailType> details = result.getForm1094BUpstreamDetail().get(0).getForm1095BUpstreamDetail();

		 Form1095BUpstreamDetailType entry = null;
		 IRS1095B submission = null;
		 MECPeriod period = new MECPeriod();
		 HashMap coverageMonths = new HashMap();

		 for (Form1095BUpstreamDetailType object : details) {

		         entry = object;

		         if (entry != null) {

					submission = new IRS1095B();

					submission.setSSN(entry.getResponsibleIndividualGrp().getSSN());
					if (entry.getResponsibleIndividualGrp().getBirthDt() != null) {
						submission.setDOB(entry.getResponsibleIndividualGrp().getBirthDt().toString());
					}

					SimpleAddress address = new SimpleAddress();
					if (entry.getResponsibleIndividualGrp().getMailingAddressGrp() != null) {

						USAddressGrpType add = entry.getResponsibleIndividualGrp().getMailingAddressGrp().getUSAddressGrp();

						if (add != null) {

							address.setCity(add.getCityNm());
							address.setLine1(add.getAddressLine1Txt());
							address.setLine2(add.getAddressLine2Txt());
							address.setState(add.getUSStateCd() != null ? add.getUSStateCd().name() : "DC");
							address.setZipCode(add.getUSZIPCd());
							address.setZipPlus4(add.getUSZIPExtensionCd());
						} else {
							ForeignAddressGrpType fadd = entry.getResponsibleIndividualGrp().getMailingAddressGrp().getForeignAddressGrp();
							if (fadd != null) {
								address.setCity(fadd.getCityNm());
								address.setCountry(fadd.getCountryNm());
								address.setLine1(fadd.getAddressLine1Txt());
								address.setLine2(fadd.getAddressLine2Txt());
								address.setPostalCode(fadd.getForeignPostalCd());
								address.setProvince(fadd.getForeignProvinceNm());
							}
						}


						submission.setAddress(address);
					}

					listing.add(submission);

				}


		    }


		 return listing;
	 }

 	/**
	 * Finds single person entry from stored bulk transmission payload entry
	 * and unmarshals xml to usable object rest of the system can use
     * @param dataFile the dataFile entry containing the original CLOB xml data for the transmission
     * @param sequence the record sequence in this data file entry that is to be retrieved
     */
	 @SuppressWarnings({ "unchecked", "rawtypes" })
	public IRS1095B convertSingleEntry(IrsTransmissionLogData dataFile, int sequence) throws ServiceException {

		if (dataFile == null) return new IRS1095B();

	 	String xml = dataFile.getRequestData();

		Form1095BUpstreamDetailType entry = null;
		IRS1095B submission = null;
		MECPeriod period = new MECPeriod();
		HashMap coverageMonths = new HashMap();

		try {

			if (xml != null && !xml.isEmpty()) {

				Form109495BTrnsmtUpstreamType result = JAXB.unmarshal(new StringReader(xml), Form109495BTrnsmtUpstreamType.class);

				//always only one 1094 entry
				List<Form1095BUpstreamDetailType> details = result.getForm1094BUpstreamDetail().get(0).getForm1095BUpstreamDetail();

				for (Form1095BUpstreamDetailType object : details) {
			        //if (object.getRecordId().intValue() == sequence) {
					if (StringUtils.isNotEmpty(object.getRecordId()) && Integer.parseInt(object.getRecordId()) == sequence) {
			            entry = object;
			            break;
			        }
			    }

				if (entry != null) {

					submission = new IRS1095B();

					SimpleAddress address = new SimpleAddress();
					if (entry.getResponsibleIndividualGrp().getMailingAddressGrp() != null) {

						USAddressGrpType add = entry.getResponsibleIndividualGrp().getMailingAddressGrp().getUSAddressGrp();

						if (add != null && add.getUSStateCd() != null) {

							address.setCity(add.getCityNm());
							address.setLine1(add.getAddressLine1Txt());
							address.setLine2(add.getAddressLine2Txt());
							address.setState(add.getUSStateCd().name());
							address.setZipCode(add.getUSZIPCd());
							address.setZipPlus4(add.getUSZIPExtensionCd());
						} else {
							ForeignAddressGrpType fadd = entry.getResponsibleIndividualGrp().getMailingAddressGrp().getForeignAddressGrp();
							if (fadd != null) {
								address.setCity(fadd.getCityNm());
								address.setCountry(fadd.getCountryNm());
								address.setLine1(fadd.getAddressLine1Txt());
								address.setLine2(fadd.getAddressLine2Txt());
								address.setPostalCode(fadd.getForeignPostalCd());
								address.setProvince(fadd.getForeignProvinceNm());
							}
						}


						submission.setAddress(address);
					}

					period.setCoveredAll12Months(entry.getCoveredIndividualGrp().get(0).getCoveredIndividualAnnualInd().equalsIgnoreCase("0") ? false : true);
					coverageMonths.put("1", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getJanuaryInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("2", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getFebruaryInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("3", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getMarchInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("4", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getAprilInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("5", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getMayInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("6", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getJuneInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("7", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getJulyInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("8", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getAugustInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("9", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getSeptemberInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("10", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getOctoberInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("11", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getNovemberInd().equalsIgnoreCase("1") ? true : false));
					coverageMonths.put("12", (entry.getCoveredIndividualGrp().get(0).getCoveredIndividualMonthlyIndGrp().getDecemberInd().equalsIgnoreCase("1") ? true : false));

					period.setCoverageMonths(coverageMonths);
					submission.setMecPeriod(period);

				}

			}

		} catch (Exception ex) {
			StringWriter sw = new StringWriter();
			 ex.printStackTrace(new PrintWriter(sw));
			 String exceptionDetails = sw.toString();
			logger.error("Error converting XML data request to object: " + ex.getMessage() + exceptionDetails);

		}

		 return submission;
	 }

	public LookupService getLookupService() {
		return lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}

	public String getIrsProcessingCode() {
		return irsProcessingCode;
	}

	public void setIrsProcessingCode(String irsProcessingCode) {
		this.irsProcessingCode = irsProcessingCode;
	}

	public DAOOperations getGenericDAO() {
		return genericDAO;
	}

	public void setGenericDAO(DAOOperations genericDAO) {
		this.genericDAO = genericDAO;
	}

	public String getIrsSignatureFile() {
		return irsSignatureFile;
	}

	public void setIrsSignatureFile(String irsSignatureFile) {
		this.irsSignatureFile = irsSignatureFile;
	}

	public String getTransmissionControlCode() {
		return transmissionControlCode;
	}

	public void setTransmissionControlCode(String transmissionControlCode) {
		this.transmissionControlCode = transmissionControlCode;
	}

	public String getSoftwareId() {
		return softwareId;
	}

	public String getIrsContactFname() {
		return irsContactFname;
	}

	public void setIrsContactFname(String irsContactFname) {
		this.irsContactFname = irsContactFname;
	}

	public String getIrsContactLname() {
		return irsContactLname;
	}

	public void setIrsContactLname(String irsContactLname) {
		this.irsContactLname = irsContactLname;
	}

	public String getIrsContactPhone() {
		return irsContactPhone;
	}

	public void setIrsContactPhone(String irsContactPhone) {
		this.irsContactPhone = irsContactPhone;
	}

	public void setSoftwareId(String systemId) {
		this.softwareId = systemId;
	}

	/*
	 * sets all common manifest header elements
	 *
	 */
	 private void setCommonHeader(IrsTransmissionLogEntry transmission, ACATrnsmtManifestReqDtlType headerManifest) {

		 try {

			 headerManifest.setPaymentYr(DatatypeFactory.newInstance().newXMLGregorianCalendar(transmission.getTaxYear()));
			 //headerManifest.getACATransmitterManifestReqDtl().setp

			 //set 0 as current filing year, 1 as correction for previous year
			 Calendar prevYear = Calendar.getInstance();
			 prevYear.add(Calendar.YEAR, -1);
			 int curTaxYear = prevYear.get(Calendar.YEAR);
			 if (Integer.valueOf(transmission.getTaxYear()) < curTaxYear) {
				 headerManifest.setPriorYearDataInd("1");
			 } else {
				 headerManifest.setPriorYearDataInd("0");
			 }

			 //EIN
			 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
				 headerManifest.setEIN(AATS_EIN);
			 } else {
				 headerManifest.setEIN(EIN);
			 }


			 //O, C, R
			 if (transmission.getCorrectionInd().equalsIgnoreCase(SUBMIT_ORIGINAL)) {
				 headerManifest.setTransmissionTypeCd(TransmissionTypeCdType.O);
			 } else if (transmission.getCorrectionInd().equalsIgnoreCase(SUBMIT_CORRECTION)) {
				 headerManifest.setTransmissionTypeCd(TransmissionTypeCdType.C);
			 } else {
				 headerManifest.setTransmissionTypeCd(TransmissionTypeCdType.R);
			 }

			 //Test or Production Indicator
			 headerManifest.setTestFileCd(this.getIrsProcessingCode());


			 //Transmitter Name
			 BusinessNameType busName = new BusinessNameType();
			 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
				 busName.setBusinessNameLine1Txt(AATS_CONTROL_LEGAL_NAME + LEGAL_NAME);
			 } else {
				 busName.setBusinessNameLine1Txt(LEGAL_NAME);
			 }
			 headerManifest.setTransmitterNameGrp(busName);

			 OtherCompletePersonNameType name = new OtherCompletePersonNameType();
			 name.setPersonFirstNm(this.getIrsContactFname());
			 name.setPersonLastNm(this.getIrsContactLname());

			 BusinessAddressGrpType busAddress = new BusinessAddressGrpType();
			 USAddressGrpType us = new USAddressGrpType();
			 us.setAddressLine1Txt(BUSINESS_STREET_ADDRESS);
			 us.setCityNm(BUSINESS_CITY);
			 us.setUSStateCd(StateType.fromValue(BUSINESS_STATE));


			 us.setUSZIPCd(BUSINESS_ZIP);
			 busAddress.setUSAddressGrp(us);
			 busAddress.setForeignAddressGrp(null);

			 CompanyInformationGrpType company = new CompanyInformationGrpType();
			 if (this.getIrsProcessingCode().equalsIgnoreCase("T")) {
				 company.setCompanyNm(AATS_CONTROL_LEGAL_NAME + LEGAL_NAME);
			 } else {
				 company.setCompanyNm(LEGAL_NAME);
			 }
			 company.setContactNameGrp(name);
			 company.setContactPhoneNum(this.getIrsContactPhone());
			 company.setMailingAddressGrp(busAddress);
			 headerManifest.setCompanyInformationGrp(company);

			 VendorInformationGrpType inhouse = new VendorInformationGrpType();
			 inhouse.setContactNameGrp(name);
			 inhouse.setContactPhoneNum(this.getIrsContactPhone());
			 inhouse.setVendorCd("I");
			 headerManifest.setVendorInformationGrp(inhouse);

			 //counts
			 headerManifest.setTotalPayerRecordCnt(PAYER_COUNT.toString());

			 headerManifest.setTotalPayeeRecordCnt(BigInteger.valueOf(transmission.getIrsTransmissionLogDetails().size()).toString());

			 //sw ID
			 headerManifest.setSoftwareId(this.getSoftwareId());

			 headerManifest.setFormTypeCd(FORM_TYPE_CODE);

			 headerManifest.setBinaryFormatCd(BinaryFormatCodeType.APPLICATION_XML);


		} catch (Exception e) {
			logger.error("Data Type Error in IRS Transmit: ", e);
		}

	 }

	 //setup properties, namespaces, attachments and interceptors
	 private void clientBulkRequestSetup (org.apache.cxf.endpoint.Client client) {

	 	 Map<String, String> nsMap = new HashMap();
	 	 nsMap.put("ns1", "urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter");
	 	 nsMap.put("ns2", "urn:us:gov:treasury:irs:common");
	 	 client.getRequestContext().put("soap.env.ns.map", nsMap);


	 	 client.getRequestContext().put(Message.MTOM_ENABLED, Boolean.TRUE);
	 	 client.getRequestContext().put(Message.MTOM_THRESHOLD, 100);
	 	 client.getInInterceptors().add(new GZIPInInterceptor());
	 	 client.getOutInterceptors().add(new GZIPOutInterceptor());

	 	 HTTPConduit http = (HTTPConduit) client.getConduit();
	 	 HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
	 	 httpClientPolicy.setContentType("text/xml");
	 	 httpClientPolicy.setAcceptEncoding("deflate");
	 	 httpClientPolicy.setReceiveTimeout(SUBMIT_TIMEOUT);
	 	 httpClientPolicy.setConnectionTimeout(SUBMIT_TIMEOUT);
	 	 httpClientPolicy.setAutoRedirect(true);
	 	 httpClientPolicy.setMaxRetransmits(MAX_RETRY);
	 	 http.setClient(httpClientPolicy);

	 	 //*************
	 	 //For Dev/Test only, comment this line past Dev env
	 	 // do NOT leave this enabled in upper env
	 	 //this logs the payload and mime headers to a text file in the esr domain server directory
	 	 //client.getOutInterceptors().add(new CxfOutInterceptor());
	 	 //************
	 }

	 //generates outbound WSS4J configured outbound message signature interceptor
	 //used by both the bulk submit and the status request services
	 private void addSignature(org.apache.cxf.endpoint.Client client, boolean busHeader, boolean timestamp, boolean manifest) {

		 Map<String, Object> signatureProps = new HashMap<String, Object>();
		 String sigparts = "";

		 String businessElement = "";
		 String statusBody = "";

		 if (manifest) {
			 businessElement = "{Element}{" + ACABUSINESSHEADER_NS + "}ACABusinessHeader;";
		 } else {
			 businessElement = "{Element}{" + ACABUSINESSHEADER_NS_ACK + "}ACABusinessHeader;";
			 statusBody = "{Element}{" + ACABUSINESSHEADER_NS_ACK + "}ACABulkRequestTransmitterStatusDetailRequest;";
		 }
		 String timestampElement = "{Element}{" + WSU_NS + "}Timestamp;";
		 String manifestElement = "{Element}{" + ACATRANSMITTERMANIFESTREQDTL_NS + "}ACATransmitterManifestReqDtl;";

		 if (busHeader) sigparts += businessElement;
		 if (timestamp) sigparts += timestampElement;
		 if (manifest) sigparts += manifestElement;
		 sigparts += statusBody;

		 //the headers and timestamp must be digitally signed by the IdenTrust certificate
		 //that is registered to VA account TCC at the IRS
		 signatureProps.put(WSHandlerConstants.ACTION,
			    WSHandlerConstants.TIMESTAMP + " " +
			    WSHandlerConstants.SIGNATURE);

		 signatureProps.put(WSHandlerConstants.USER, "es-irs.DNS   ");
		 signatureProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, UTPasswordCallback.class.getName());
		 signatureProps.put(WSHandlerConstants.SIGNATURE_PARTS, sigparts);
		 signatureProps.put(WSHandlerConstants.SIG_ALGO, "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
		 signatureProps.put(WSHandlerConstants.SIG_KEY_ID, "X509KeyIdentifier");
		 signatureProps.put(WSHandlerConstants.IS_BSP_COMPLIANT, "false");
		 signatureProps.put(WSHandlerConstants.ADD_INCLUSIVE_PREFIXES, "false");

		 Properties cryptoProperties = new Properties();
		 cryptoProperties.put("org.apache.ws.security.crypto.provider","org.apache.ws.security.components.crypto.Merlin");
		 cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.password", System.getProperty("irs.sign.key"));
		 cryptoProperties.put("org.apache.ws.security.crypto.merlin.file", this.getIrsSignatureFile());
		 cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.type", "jks");
		 cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.alias", "es-irs.DNS   ");
		 client.getRequestContext().put("cryptoProperties", cryptoProperties);
		 client.getRequestContext().put(WSHandlerConstants.SIG_PROP_REF_ID,"cryptoProperties");
		 signatureProps.put(WSHandlerConstants.SIG_PROP_REF_ID, "cryptoProperties");


		 WSS4JOutInterceptor sec = new WSS4JOutInterceptor(signatureProps);
		 client.getOutInterceptors().add(sec);

	 }

	public String getClientSystemId() {
		return clientSystemId;
	}

	public void setClientSystemId(String clientSystemId) {
		this.clientSystemId = clientSystemId;
	}

	public String getTempFileLocation() {
		return tempFileLocation;
	}

	public void setTempFileLocation(String tempFileLocation) {
		this.tempFileLocation = tempFileLocation;
	}

	public String getIrsServiceBulkEndPoint() {
		return irsServiceBulkEndPoint;
	}

	public void setIrsServiceBulkEndPoint(String irsServiceBulkEndPoint) {
		this.irsServiceBulkEndPoint = irsServiceBulkEndPoint;
	}

	public String getIrsServiceStatusEndPoint() {
		return irsServiceStatusEndPoint;
	}

	public void setIrsServiceStatusEndPoint(String irsServiceStatusEndPoint) {
		this.irsServiceStatusEndPoint = irsServiceStatusEndPoint;
	}

}
