fr.gael.dhus.olingo.v1.entity.Node.java Source code

Java tutorial

Introduction

Here is the source code for fr.gael.dhus.olingo.v1.entity.Node.java

Source

/*
 * Data Hub Service (DHuS) - For Space data distribution.
 * Copyright (C) 2013,2014,2015 GAEL Systems
 *
 * This file is part of DHuS software sources.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.gael.dhus.olingo.v1.entity;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import fr.gael.dhus.olingo.v1.V1Util;
import fr.gael.dhus.util.DownloadStreamCloserListener;
import org.apache.commons.net.io.CopyStreamAdapter;
import org.apache.commons.net.io.CopyStreamListener;
import org.apache.log4j.Logger;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import org.apache.olingo.odata2.api.processor.ODataSingleProcessor;

import fr.gael.dhus.database.object.User;
import fr.gael.dhus.datastore.processing.ProcessingUtils;
import fr.gael.dhus.network.RegulatedInputStream;
import fr.gael.dhus.network.TrafficDirection;
import fr.gael.dhus.olingo.v1.entityset.NodeEntitySet;
import fr.gael.dhus.olingo.v1.map.impl.NodesMap;
import fr.gael.dhus.util.DownloadActionRecordListener;
import fr.gael.drb.DrbAttribute;
import fr.gael.drb.DrbAttributeList;
import fr.gael.drb.DrbFactory;
import fr.gael.drb.DrbNode;
import fr.gael.drb.impl.spi.DrbNodeSpi;
import fr.gael.drb.impl.xml.XmlFactory;
import fr.gael.drb.value.Value;
import fr.gael.drbx.cortex.DrbCortexModel;

/**
 * The OData representation of a DRB Node.
 */
public class Node extends Item {
    private static Logger logger = Logger.getLogger(Node.class);

    private final long ONE_YEAR_MS = (long) 365.25 * 24 * 60 * 60 * 1000;

    private DrbNode drbNode;
    private String path;
    private String contentType;
    private Long contentLength;
    private fr.gael.dhus.olingo.v1.entity.Class itemClass;

    private Map<String, Node> nodes;
    private Object value;
    private Map<String, Attribute> attributes;
    private Integer childrenNumber;

    public Node(String path) {
        super(getNodeId(path));
        this.path = path;
    }

    public Node(DrbNode node) {
        super(getNodeId(node));
        if (node == null)
            throw new NullPointerException("Passed node cannot be null.");
        this.drbNode = node;
    }

    public void changeId(String id) {
        super.id = id;
    }

    private void initNode() {
        if (this.drbNode == null) {
            if (path == null) {
                throw new NullPointerException("Node path cannot be null.");
            }
            this.drbNode = DrbFactory.openURI(path);
            logger.debug("Initialized node : " + path);
        }
        if (this.drbNode == null)
            throw new NullPointerException("Node cannot be null");
    }

    @Override
    public String getName() {
        initNode();
        return drbNode.getName();
    }

    @Override
    public String getContentType() {
        initNode();
        if (contentType == null) {
            try {
                DrbCortexModel model = DrbCortexModel.getDefaultModel();
                contentType = model.getClassOf(drbNode).getLabel();
            } catch (Exception e) {
                contentType = "Item";
            }
        }
        return contentType;
    }

    @Override
    public Long getContentLength() {
        initNode();
        if (contentLength == null) {
            contentLength = -1L;
            if (hasStream()) {
                InputStream stream = getStream();
                if (stream instanceof FileInputStream) {
                    try {
                        contentLength = ((FileInputStream) stream).getChannel().size();
                    } catch (IOException e) {
                        // Error while accessing file size: using -1L
                    }
                }
                // Still not initialized ?
                if (contentLength == -1) {
                    DrbAttribute attr = this.drbNode.getAttribute("size");
                    if (attr != null) {
                        try {
                            contentLength = Long.decode(attr.getValue().toString());
                        } catch (NumberFormatException nfe) {
                            // Error in attribute...
                        }
                    }
                }
            } else {
                contentLength = 0L;
            }
        }
        return contentLength;
    }

    public Integer getChildrenNumber() {
        initNode();
        if (childrenNumber == null) {
            childrenNumber = drbNode.getChildrenCount();
        }
        return childrenNumber;
    }

    public Object getValue() {
        initNode();
        if (value == null) {
            Value val = drbNode.getValue();
            String s_value = null;
            if (val != null)
                s_value = cleanInvalidXmlChars(val.toString(), "");
            value = s_value;
        }
        return value;
    }

    public Map<String, Node> getNodes() {
        initNode();
        if (nodes == null) {
            if (drbNode.hasChild()) {
                nodes = new NodesMap(drbNode);
            } else {
                nodes = Collections.emptyMap();
            }
        }
        return nodes;
    }

    public Map<String, Attribute> getAttributes() {
        initNode();
        if (attributes == null) {
            DrbAttributeList attrs = drbNode.getAttributes();
            Map<String, Attribute> attributes = new HashMap<String, Attribute>();
            if ((attrs != null) && (attrs.getLength() > 0)) {
                for (int index = 0; index < attrs.getLength(); index++) {
                    DrbAttribute attr = attrs.item(index);
                    String value = (attr.getValue() == null) ? "" : attr.getValue().toString();
                    Attribute attribute = new Attribute(attr.getName(), value);
                    attributes.put(attr.getName(), attribute);
                }
            }
            this.attributes = Collections.unmodifiableMap(new HashMap<String, Attribute>(attributes));
        }
        return attributes;
    }

    /**
     * Retrieve the Class from this Node entity.
     * @return the Class entity.
     * @throws UnsupportedOperationException if the model cannot be computed.
     * @throws NullPointerException if this product does not related any class.
     */
    @Override
    public fr.gael.dhus.olingo.v1.entity.Class getItemClass() {
        initNode();
        if (this.itemClass == null) {
            try {
                itemClass = new fr.gael.dhus.olingo.v1.entity.Class(
                        ProcessingUtils.getItemClassUri(ProcessingUtils.getClassFromNode(drbNode)));
            } catch (Exception e) {
                //throw new UnsupportedOperationException("Cannot find Drb model.",e);
                // Item class not found: use drb root item URI.
                itemClass = new fr.gael.dhus.olingo.v1.entity.Class("http://www.gael.fr/drb#item");
            }
        }
        return this.itemClass;
    }

    /**
     * Calls the superclass entity response not aggregated to this response.
     * @param root_url
     * @return the item class response.
     */
    protected Map<String, Object> itemToEntityResponse(String root_url) {
        return super.toEntityResponse(root_url);
    }

    @Override
    public Map<String, Object> toEntityResponse(String root_url) {
        Map<String, Object> res = itemToEntityResponse(root_url);

        initNode();
        res.put(NodeEntitySet.CHILDREN_NUMBER, getChildrenNumber());
        res.put(NodeEntitySet.VALUE, getValue());
        res.put(NodeEntitySet.PATH, drbNode);

        return res;
    }

    @Override
    public Object getProperty(String prop_name) throws ODataException {
        initNode();
        if (prop_name.equals(NodeEntitySet.CHILDREN_NUMBER))
            return getChildrenNumber();

        if (prop_name.equals(NodeEntitySet.VALUE))
            return getValue();

        return super.getProperty(prop_name);
    }

    @Override
    public ODataResponse getEntityMedia(ODataSingleProcessor processor) throws ODataException {
        initNode();
        if (hasStream()) {
            try {
                User u = V1Util.getCurrentUser();
                String user_name = (u == null ? null : u.getUsername());

                InputStream is = new BufferedInputStream(getStream());

                RegulatedInputStream.Builder builder = new RegulatedInputStream.Builder(is,
                        TrafficDirection.OUTBOUND);
                builder.userName(user_name);

                CopyStreamAdapter adapter = new CopyStreamAdapter();
                CopyStreamListener recorder = new DownloadActionRecordListener(this.getId(), this.getName(), u);
                CopyStreamListener closer = new DownloadStreamCloserListener(is);
                adapter.addCopyStreamListener(recorder);
                adapter.addCopyStreamListener(closer);
                builder.copyStreamListener(adapter);
                is = builder.build();

                String etag = getName() + "-" + getContentLength();

                // A priori Node never change, so the lastModified should be as
                // far as possible than today.
                long last_modified = System.currentTimeMillis() - ONE_YEAR_MS;

                // If node is not a data file, it cannot be downloaded and set to -1
                // As a stream exists, this control is probably obsolete.
                long content_length = getContentLength() == 0 ? -1 : getContentLength();

                return V1Util.prepareMediaResponse(etag, getName(), getContentType(), last_modified, content_length,
                        processor.getContext(), is);
            } catch (Exception e) {
                throw new ODataException("An exception occured while creating the stream for node " + getName(), e);
            }
        } else {
            throw new ODataException("No stream for node " + getName());
        }
    }

    public boolean hasStream() {
        initNode();
        if (drbNode instanceof DrbNodeSpi) {
            return ((DrbNodeSpi) drbNode).hasImpl(InputStream.class);
        }
        return false;
    }

    public InputStream getStream() {
        initNode();
        if (drbNode instanceof DrbNodeSpi) {
            return (InputStream) ((DrbNodeSpi) drbNode).getImpl(InputStream.class);
        }
        return null;
    }

    public InputStream toXML() {
        initNode();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        XmlFactory.writeXML(drbNode, out);
        return new ByteArrayInputStream(out.toByteArray());
    }

    /**
     * Computes the unique identifier within this parent node context. The
     * retrieved identifier is computed from the XPath that includes the name of
     * this node, and the occurrence of this node within the parent if any.
     * 
     * @param node this node to compute the id.
     * @return the unique identifier within this node.
     */
    public static String getNodeId(String path) {
        String id = path;
        if (id.endsWith("/"))
            id = id.substring(0, id.length() - 1);
        if (id.contains("/")) {
            id = id.substring(id.lastIndexOf("/") + 1);
        }
        if ((id == null) || "".equals(id))
            id = (new File(path)).getName();
        return id;
    }

    public static String getNodeId(DrbNode node) {
        return getNodeId(node.getPath());
    }

    /**
     * From xml spec valid chars:<br>
     * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]<br>
     * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.<br>
     * 
     * @param text The String to clean
     * @param replacement The string to be substituted for each match
     * @return The resulting String
     */
    public static String cleanInvalidXmlChars(String text, String replacement) {
        String re = "[^\\x09\\x0A\\x0D\\x20-\\xD7FF\\xE000-\\xFFFD\\x10000-x10FFFF]";
        return text.replaceAll(re, replacement);
    }
}