/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.runtime.io.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.text.MessageFormat;
/**
* Installs a temporary file.
* @since 0.9.0
*/
public final class TemporaryFileInstaller {
private final byte[] bytes;
private final boolean executable;
private TemporaryFileInstaller(byte[] bytes, boolean executable) {
this.bytes = bytes;
this.executable = executable;
}
/**
* Creates a new instance.
* @param contents the file contents
* @param executable {@code true} to install as an executable file, otherwise {@code false}
* @return the created instance
* @throws IOException if failed to load contents
*/
public static TemporaryFileInstaller newInstance(InputStream contents, boolean executable) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buf = new byte[256];
while (true) {
int read = contents.read(buf);
if (read < 0) {
break;
}
output.write(buf, 0, read);
}
byte[] bytes = output.toByteArray();
return new TemporaryFileInstaller(bytes, executable);
}
/**
* Installs contents into the target file.
* @param target the target file
* @param reuse {@code true} to reuse installed file, otherwise {@code false}
* @return {@code true} if target file is successfully installed,
* or {@code false} if the file is already installed
* @throws IOException if error occurred while installing the target file
*/
public boolean install(File target, boolean reuse) throws IOException {
File parent = target.getAbsoluteFile().getParentFile();
if (parent.mkdirs() == false && parent.isDirectory() == false) {
throw new IOException(MessageFormat.format(
"failed to create a file: {0}",
target));
}
try (RandomAccessFile file = new RandomAccessFile(target, "rw"); //$NON-NLS-1$
FileLock lock = file.getChannel().lock(0, 0, false)) {
if (reuse && isReusable(target, file)) {
return false;
}
doInstall(target, file);
return true;
}
}
private boolean isReusable(File target, RandomAccessFile file) throws IOException {
if (executable && target.canExecute() == false) {
return false;
}
if (bytes.length != file.length()) {
return false;
}
int offset = 0;
file.seek(offset);
byte[] buf = new byte[256];
while (true) {
int read = file.read(buf);
if (read < 0) {
break;
}
for (int i = 0; i < read; i++) {
if (bytes[i + offset] != buf[i]) {
return false;
}
}
offset += read;
}
return true;
}
private void doInstall(File target, RandomAccessFile file) throws IOException {
file.setLength(0L);
file.write(bytes);
if (executable) {
target.setExecutable(true);
}
}
}