
org.structr.cloud.message.FileNodeDataContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of structr-ui Show documentation
Show all versions of structr-ui Show documentation
Structr is an open source framework based on the popular Neo4j graph database.
The newest version!
/**
* Copyright (C) 2010-2016 Structr GmbH
*
* This file is part of Structr .
*
* Structr 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.
*
* Structr 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 Structr. If not, see .
*/
package org.structr.cloud.message;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.structr.cloud.CloudConnection;
import org.structr.common.error.FrameworkException;
import org.structr.core.graph.SyncCommand;
import org.structr.dynamic.File;
/**
* Transport data container for file nodes
*
*
*/
public class FileNodeDataContainer extends NodeDataContainer {
private static final Logger logger = Logger.getLogger(FileNodeDataContainer.class.getName());
private transient java.io.File temporaryFile = null;
private transient OutputStream outputStream = null;
private long fileSize = 0;
public FileNodeDataContainer() throws FrameworkException {
super();
}
public FileNodeDataContainer(final File fileNode) throws FrameworkException {
super(fileNode, 0);
this.fileSize = fileNode.getSize();
}
@Override
public void onRequest(CloudConnection serverConnection) throws IOException, FrameworkException {
serverConnection.beginFile(this);
sendKeepalive(serverConnection);
}
@Override
public void onResponse(CloudConnection clientConnection) throws IOException, FrameworkException {
}
/**
* Adds a chunk of data to this container's temporary file, creating the file if it does not exist yet.
*
* @param chunk the chunk to add
*/
public void addChunk(FileNodeChunk chunk) {
// check file size
if (this.fileSize > 0) {
if (chunk.getFileSize() != this.fileSize) {
throw new IllegalStateException("File size mismatch while adding chunk. Expected " + fileSize + ", received " + chunk.getFileSize());
}
} else {
this.fileSize = chunk.getFileSize();
}
// check sequence number
if (chunk.getSequenceNumber() != this.sequenceNumber) {
throw new IllegalStateException("Sequence number mismatch while adding chunk. Expected " + sequenceNumber + ", received " + chunk.getSequenceNumber());
} else {
sequenceNumber++;
}
// TODO: check chunk checksum here?
try {
if (temporaryFile == null) {
temporaryFile = java.io.File.createTempFile("structr", "file");
outputStream = new FileOutputStream(temporaryFile);
}
if (outputStream != null) {
outputStream.write(chunk.getBinaryContent());
}
} catch (Throwable t) {
logger.log(Level.WARNING, "", t);
}
}
/**
* Flushes and closes the temporary file after receiving it from a remote structr instance. This method is called when the cloud service recevies a FileNodeEndChunk
.
*/
public void flushAndCloseTemporaryFile() {
// TODO: check file checksum here?!
if (outputStream != null) {
try {
outputStream.flush();
outputStream.close();
} catch (Throwable t) {
logger.log(Level.WARNING, "", t);
}
} else {
logger.log(Level.WARNING, "outputStream was null, fileSize: " + fileSize + "..");
}
}
/**
* Renames / moves the temporary file to its final location. This method is called when the cloud service recevies a FileNodeEndChunk
.
*
* @param finalPath the final path of this file
* @return whether the renaming/moving was successful
*/
public boolean persistTemporaryFile(String finalPath) throws IOException {
boolean ret = false;
if (temporaryFile != null) {
final java.io.File finalFile = new java.io.File(finalPath);
final Path source = temporaryFile.toPath();
final Path dest = finalFile.toPath();
// create parent directories
finalFile.mkdirs();
// move file from tmp to final destination
Files.move(source, dest, StandardCopyOption.REPLACE_EXISTING);
}
return (ret);
}
public long getFileSize() {
return (this.fileSize);
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
@Override
protected void deserializeFrom(DataInputStream inputStream) throws IOException {
this.fileSize = (Long)SyncCommand.deserialize(inputStream);
super.deserializeFrom(inputStream);
}
@Override
protected void serializeTo(DataOutputStream outputStream) throws IOException {
SyncCommand.serialize(outputStream, fileSize);
super.serializeTo(outputStream);
}
// ----- public static methods -----
/**
* Creates and returns an Iterable instance whose iterator creates instances of the given file.
*
* @param fileNode the node to read from
* @param chunkSize the desired chunk size
* @return an Iterable that generates FileNodeChunks
*/
public static Iterable getChunks(final File fileNode, final int chunkSize) {
return (new Iterable() {
@Override
public Iterator iterator() {
return (new ChunkIterator(fileNode, chunkSize));
}
});
}
// ----- nested classes -----
/**
* An iterator that creates FileNodeChunks
while reading a large file from disk.
*/
private static class ChunkIterator implements Iterator {
private InputStream inputStream = null;
private File fileNode = null;
private int sequenceNumber = 0;
private long fileSize = 0;
private int chunkSize = 0;
public ChunkIterator(File fileNode, int chunkSize) {
this.fileNode = fileNode;
this.fileSize = fileNode.getSize();
this.chunkSize = chunkSize;
this.inputStream = fileNode.getInputStream();
}
@Override
public boolean hasNext() {
boolean ret = false;
if (inputStream != null) {
try {
ret = inputStream.available() > 0;
if (!ret) {
inputStream.close();
}
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception in ChunkIterator: {0}", t);
}
}
return (ret);
}
@Override
public FileNodeChunk next() {
FileNodeChunk chunk = null;
if (inputStream != null) {
try {
int available = inputStream.available();
int readSize = available < chunkSize ? available : chunkSize;
chunk = new FileNodeChunk(fileNode.getUuid(), fileSize, sequenceNumber, readSize);
inputStream.read(chunk.getBuffer(), 0, readSize);
sequenceNumber++;
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception in ChunkIterator: {0}", t);
}
}
return (chunk);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy