All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.ree.eemws.kit.folders.InputTask Maven / Gradle / Ivy

Go to download

Client implementation of IEC 62325-504 technical specification. eemws-kit includes command line utilities to invoke the eem web services, as well as several GUI applications (browser, editor, ...)

The newest version!
/*
 * Copyright 2024 Redeia.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 *  by the Free Software Foundation, version 3 of the license.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTIBIILTY or FITNESS FOR A PARTICULAR PURPOSE. See GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program. If not, see
 * http://www.gnu.org/licenses/.
 *
 * Any redistribution and/or modification of this program has to make
 * reference to Redeia as the copyright owner of the program.
 */

package es.ree.eemws.kit.folders;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import es.ree.eemws.client.put.PutMessage;
import es.ree.eemws.core.utils.error.EnumErrorCatalog;
import es.ree.eemws.core.utils.file.FileUtil;
import es.ree.eemws.core.utils.iec61968100.EnumMessageStatus;
import es.ree.eemws.core.utils.iec61968100.FaultUtil;
import es.ree.eemws.core.utils.operations.put.PutOperationException;
import es.ree.eemws.core.utils.xml.XMLElementUtil;
import jakarta.xml.bind.JAXBException;

/**
 * InputTask. Checks for files in the input folder, read them and send to the
 * server.
 *
 * @author Redeia.
 * @version 2.1 01/01/2024
 */
public final class InputTask implements Runnable {

	/** Prefix added to identify response files. */
	private static final String RESPONSE_ID_PREFIX = "ack_"; //$NON-NLS-1$

	/** Object for checking messages sent by another module. */
	private final LockHandler lh;

	/** Put message operation object. */
	private PutMessage putMessage;

	/** This input task configuration set. */
	private InputConfigurationSet ics;

	/** This input set index (get it instead of asking ics each time). */
	private int icsIndex;

	/** Thread log system. */
	private static final Logger LOGGER = Logger.getLogger(InputTask.class.getName());

	/** Number of millisconds between file size checks. */
	private static final long SLEEP_BETWEEN_READS = 500;

	/**
	 * Creates a Input task in order to sent the files in the configured folder to a
	 * server.
	 *
	 * @param lockHandler Lock manager to synchronize the work.
	 * @param ic          This task configuration set.
	 */
	public InputTask(final LockHandler lockHandler, final InputConfigurationSet ic) {

		lh = lockHandler;
		ics = ic;
		icsIndex = ics.getIndex();
		putMessage = new PutMessage();
		putMessage.setEndPoint(ic.getInputUrlEndPoint());
		if (LOGGER.isLoggable(Level.INFO)) {
			LOGGER.info(ic.toString());
		}
	}

	/**
	 * Checks whether the given file is complete. Checks file size, waits for a
	 * while and checks its size again. If the size remains the same, the file is
	 * considered to be complete.
	 *
	 * @param file Reference to the input file.
	 * @return true When complete, false otherwise.
	 */
	private boolean isComplete(final File file) {

		var initialSize = file.length();
		long finalSize;
		var complete = false;

		if (initialSize > 0) {
			try {
				Thread.sleep(SLEEP_BETWEEN_READS);
			} catch (InterruptedException e) {
				LOGGER.finer("Wait interrupted"); // Don't mind! //$NON-NLS-1$
				Thread.currentThread().interrupt();
			}
			finalSize = file.length();

			complete = (finalSize == initialSize);
		}

		return complete;
	}

	/**
	 * Detects files in input folder. Sends them to the server
	 */
	@Override
	public void run() {

		String fileName = null;

		try {
			var f = new File(ics.getInputFolder());
			var files = f.listFiles();

			if (files != null) {
				boolean lockFile;
				StatusIcon.getStatus().setBusy();

				for (File file : files) {
					fileName = file.getName();
					lockFile = lh.tryLock(fileName);

					if (lockFile && isComplete(file)) {
						process(file);
					}

					lh.releaseLock(fileName);

				}

				StatusIcon.getStatus().setIdle();
			}
		} catch (Exception ex) {

			/*
			 * Defensive exception, if runnable task ends with exception won't be exectued
			 * againg!
			 */
			LOGGER.log(Level.SEVERE, MessageCatalog.MF_UNEXPECTED_ERROR_I.getMessage(icsIndex), ex);
		} finally {
			if (fileName != null) {
				lh.releaseLock(fileName);
			}
		}
	}

	/**
	 * Process file with name and contents passed as arguments.
	 *
	 * @param file Reference to the input file.
	 */
	private void process(final File file) {

		var fileName = file.getName();
		var fullFileName = file.getAbsolutePath();

		try {

			/* Send. */
			if (LOGGER.isLoggable(Level.INFO)) {
				LOGGER.info(MessageCatalog.MF_SENDING_MESSAGE.getMessage(icsIndex, fileName));
			}

			StringBuilder response;

			if (ics.isBinaryFolder()) {

				response = new StringBuilder(putMessage.put(fileName, FileUtil.readBinary(fullFileName)));

			} else {

				response = new StringBuilder(putMessage.put(new StringBuilder(FileUtil.readUTF8(fullFileName))));
			}

			if (LOGGER.isLoggable(Level.INFO)) {
				LOGGER.info(MessageCatalog.MF_SENT_MESSAGE.getMessage(icsIndex, fileName));
			}

			moveOnceProcessed(file);

			saveAndExecuteAck(file, response);

		} catch (PutOperationException ex) {

			/* Set status as failed, this is an exception!. */
			putMessage.getMessageMetaData().setStatus(EnumMessageStatus.FAILED);

			var code = ex.getCode();

			/* Soap Fault, save the fault message. */
			if (code.equals(EnumErrorCatalog.HAND_010.name())) {

				LOGGER.severe(MessageCatalog.MF_SERVER_RETURNS_FAULT.getMessage(icsIndex, fullFileName,
				        ex.getCause().getMessage()));
				saveAndExecuteAck(file, new StringBuilder(putMessage.getMessageMetaData().getRejectText()));

			} else {

				/* Magic folder needs a file, build a "fake" fault using the exception. */
				try {
					var fault = XMLElementUtil.element2String(XMLElementUtil
					        .obj2Element(FaultUtil.getFaultMessageFromException(ex.getMessage(), ex.getCode())));
					saveAndExecuteAck(file, new StringBuilder(fault));

				} catch (TransformerException | ParserConfigurationException | JAXBException e) {
					LOGGER.log(Level.SEVERE, MessageCatalog.MF_CANNOT_CREATE_FAULT_MSG.getMessage(icsIndex), e);
				}

				if (code.equals(EnumErrorCatalog.PUT_014.name())) {
					LOGGER.log(Level.SEVERE, MessageCatalog.MF_RETURNS_ERROR.getMessage(icsIndex, fullFileName), ex);
				} else {
					LOGGER.log(Level.SEVERE, MessageCatalog.MF_SERVER_RETURNS_ERROR.getMessage(icsIndex, fullFileName),
					        ex);
				}
			}

			moveOnceProcessed(file);

		} catch (IOException ioe) {
			LOGGER.log(Level.SEVERE, MessageCatalog.MF_CANNOT_READ_FILE.getMessage(icsIndex, fullFileName), ioe);
		}
	}

	/**
	 * Moves (copy + delete) the input file to the processed folder (if configured)
	 * once the file is sent. Depending on the java version, if source and
	 * destination are in different filesystems, move is not possible (so that, this
	 * is implemented as copy + delete)
	 *
	 * @param file Input file to be moved.
	 */
	private void moveOnceProcessed(final File file) {

		var execContext = ""; //$NON-NLS-1$

		try {
			var fileName = file.getName();
			var fullFileName = file.getAbsolutePath();

			if (ics.getProcessedFolder() != null) {
				var processedFilePath = ics.getProcessedFolder() + File.separator + fileName;
				execContext = MessageCatalog.MF_SAVING_PROCESS_FOLDER.getMessage(icsIndex, fullFileName,
				        processedFilePath);
				FileUtil.writeUTF8(processedFilePath, FileUtil.readUTF8(fullFileName));
			}

			execContext = MessageCatalog.MF_UNABLE_TO_DELETE_INPUT_FILE.getMessage(icsIndex, fullFileName);
			var f = new File(fullFileName);
			if (!f.delete()) {
				LOGGER.warning(execContext);
			}
		} catch (IOException ioe) {
			LOGGER.log(Level.SEVERE, execContext, ioe);
		}
	}

	/**
	 * Saves the given ack (could be a fault) into the configured ack folder(s).
	 * Then runs the configured scripts / programs
	 *
	 * @param file     Input file name (for log purposes)
	 * @param response Server response as string.
	 */
	private void saveAndExecuteAck(final File file, final StringBuilder response) {

		var execContext = ""; //$NON-NLS-1$
		var fileName = file.getName();
		var fullFileName = file.getAbsolutePath();

		try {

			String ackFilePath = null;
			EnumMessageStatus status;

			/* Response is saved in response folder. */
			if (ics.getAckFolder() != null) {
				ackFilePath = ics.getAckFolder() + File.separator + RESPONSE_ID_PREFIX + fileName;
				execContext = MessageCatalog.MF_SAVING_ACK_FOLDER.getMessage(icsIndex, fullFileName, ackFilePath);
				FileUtil.writeUTF8(ackFilePath, response.toString());
			}

			var metaData = putMessage.getMessageMetaData();
			status = metaData.getStatus();
			if (status == null) {
				status = EnumMessageStatus.OK;
			}

			if (status.equals(EnumMessageStatus.OK)) {

				/* Response is saved in response OK folder. */
				if (ics.getAckOkFolder() != null) {
					ackFilePath = ics.getAckOkFolder() + File.separator + RESPONSE_ID_PREFIX + fileName;
					execContext = MessageCatalog.MF_SAVING_ACK_OK_FOLDER.getMessage(icsIndex, fullFileName,
					        ackFilePath);
					FileUtil.writeUTF8(ackFilePath, response.toString());
				}

			} else /* Response is saved in response FAILED folder. */
			if (ics.getAckFailedFolder() != null) {
				ackFilePath = ics.getAckFailedFolder() + File.separator + RESPONSE_ID_PREFIX + fileName;
				execContext = MessageCatalog.MF_SAVING_ACK_FAILED_FOLDER.getMessage(icsIndex, fullFileName,
				        ackFilePath);
				FileUtil.writeUTF8(ackFilePath, response.toString());
			}

			/* If ack was saved and there is a program configured, run it... */
			if (ackFilePath != null) {
				if (status.equals(EnumMessageStatus.OK)) {
					if (ics.getAckOkProgramCmdLine() != null) {
						ProgramExecutor.execute(ics.getAckOkProgramCmdLine(), new File(ackFilePath), status.toString(),
						        null);
					}
				} else if (ics.getAckFailedProgramCmdLine() != null) {
					ProgramExecutor.execute(ics.getAckFailedProgramCmdLine(), new File(ackFilePath), status.toString(),
					        null);
				}
			}

		} catch (IOException ex) {
			LOGGER.log(Level.SEVERE, execContext, ex);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy