PIPReader.java

/*******************************************************************************
 * Copyright 2018 IIT-CNR
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 ******************************************************************************/
package it.cnr.iit.ucs.pipreader;

import it.cnr.iit.ucs.exceptions.PIPException;
import it.cnr.iit.ucs.journaling.JournalBuilder;
import it.cnr.iit.ucs.journaling.JournalingInterface;
import it.cnr.iit.ucs.obligationmanager.ObligationInterface;
import it.cnr.iit.ucs.pip.PIPBase;
import it.cnr.iit.ucs.pip.PIPSubscriberTimer;
import it.cnr.iit.ucs.properties.components.PipProperties;
import it.cnr.iit.utility.FileUtility;
import it.cnr.iit.utility.errorhandling.Reject;
import it.cnr.iit.xacml.Attribute;
import oasis.names.tc.xacml.core.schema.wd_17.RequestType;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * The task this PIP is to read data from a file when requested.
 * The Path to reach the file is passed as parameter to the pip.
 *
 * @author Antonio La Marra, Alessandro Rosetti
 */
public class PIPReader extends PIPBase {

    private static final Logger log = Logger.getLogger(PIPReader.class.getName());
    private JournalingInterface journal;

    public static final String FILE_PATH_KEYWORD = "FILE_PATH";

    /**
     * Map having attributeId as key and the file path as value
     */
    private final Map<String, String> filePathMap = new HashMap<>();

    public PIPReader(PipProperties properties) {
        super(properties);
        Reject.ifFalse(init(properties), "Error initialising pip : " + properties.getId());
    }

    @Override
    public boolean init(PipProperties properties) {
        try {
            for (Map<String, String> attributeMap : properties.getAttributes()) {

                Attribute attribute = new Attribute();
                buildAttribute(attribute, attributeMap);

                // extract the file name from the additionalProperties, as value of the entry with
                // the key matching this attributeId
                String filePath = properties.getAdditionalProperties().get(attribute.getAttributeId());
                Reject.ifNull(filePath, "Missing file path");
                Reject.ifTrue(filePath.equals(""), "Empty file path");
                setFilePath(attribute.getAttributeId(), filePath);

                addAttribute(attribute);

                journal = JournalBuilder.build(properties);

                // timer for polling the value of the attribute
                PIPSubscriberTimer subscriberTimer = new PIPSubscriberTimer(this);
                subscriberTimer.setRate(properties.getRefreshRate());
                subscriberTimer.start();
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * Retrieve the value of the attribute passed as argument.
     * If the value is not of type environment, this method uses the additionalInformation
     * (which has to be set beforehand) to select the right value from the file.
     * Then, it sets the value within the attribute, and finally returns a String
     * containing such a value.
     */
    @Override
    public String retrieve(Attribute attribute) throws PIPException {

        String filePath = this.filePathMap.get(attribute.getAttributeId());
        String value;
        if (isEnvironmentCategory(attribute)) {
            value = read(filePath);
        } else {
            value = read(filePath, attribute.getAdditionalInformation());
        }
        attribute.setValue(attribute.getDataType(), value);
        return value;
    }

    /**
     * Effective retrieval of the monitored value.
     *
     * @return the requested value
     * @throws PIPException if the value cannot be retrieved
     */
    private String read(String filePath) throws PIPException {
        try {
            Path path = Paths.get(filePath);
            // TODO UCS-33 NOSONAR
            String value = new String(Files.readAllBytes(path));
            journal.logString(formatJournaling(value));
            return value;
        } catch (IOException e) {
            throw new PIPException("Attribute Manager error : " + e.getMessage());
        }
    }

    /**
     * Effective retrieval of the monitored value looking for the line containing a filter.
     * Note that each line of the file MUST be composed of two fields separated
     * by either one or more spaces or tab, e.g.:
     * filter attribute
     * filter   attribute
     * filter\tattribute.
     *
     * @param filter the string to be used to search for the item we're interested into
     * @return the requested value
     * @throws PIPException if the value cannot be retrieved
     */
    private String read(String filePath, String filter) throws PIPException {
        // TODO UCS-33 NOSONAR
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            for (String line; (line = br.readLine()) != null; ) {
                String key = line.split("\\s+")[0];
                if (key.equals(filter)) {
                    String value = line.split("\\s+")[1];
                    journal.logString(formatJournaling(value, filter));
                    return value;
                }
            }
        } catch (Exception e) {
            throw new PIPException("Attribute Manager error: " + e.getMessage());
        }
        throw new PIPException("Attribute Manager: unable to retrieve a value for filter: \"" + filter + "\"\n");
    }

    private void setFilePath(String attributeId, String filePath) {
        String absFilePath = FileUtility.findFileAbsPathUsingClassLoader(filePath);
        if (absFilePath != null) {
            this.filePathMap.put(attributeId, absFilePath);
        } else {
            this.filePathMap.put(attributeId, filePath);
        }
        Reject.ifNull(this.filePathMap.get(attributeId));
    }

    private String formatJournaling(String... strings) {
        StringBuilder logStringBuilder = new StringBuilder();
        logStringBuilder.append("VALUE READ: ").append(strings[0]);

        if (strings.length > 1) {
            logStringBuilder.append(" FOR FILTER: ").append(strings[1]);
        }
        return logStringBuilder.toString();
    }

    @Override
    public void update(String data) throws PIPException {
// fixme: who is supposed to call this?
//        try {
//            Path path = Paths.get( filePath );
//            Files.write( path, data.getBytes() );
//        } catch( IOException e ) {
//            log.severe( "Error updating attribute : " + e.getMessage() );
//        }
        System.err.println("update() method not implemented");
    }

    @Override
    public void retrieve(RequestType request,
                         List<Attribute> attributeRetrievals) {
        log.severe("Multiple retrieve is unimplemented");
    }

    @Override
    public void subscribe(RequestType request,
                          List<Attribute> attributeRetrieval) {
        log.severe("Multiple subscribe is unimplemented");
    }

    @Override
    public void performObligation(ObligationInterface obligation) {
        if (obligation != null) {
            log.severe("Perform obligation is unimplemented");
        }
    }


    @Override
    protected Logger getLogger() {
        return log;
    }
}