EncodingMapping encoding = encodings.getEncoding();
if (encoding == null || encoding.getName().equals(ContentEncodingRepository.IDENTITY)) {
return null;
}
String newPath = path + ".undertow.encoding." + encoding.getName();
Resource preCompressed = encoded.getResource(newPath);
if (preCompressed != null) {
return new ContentEncodedResource(preCompressed, encoding.getName());
}
final LockKey key = new LockKey(path, encoding.getName());
if (fileLocks.putIfAbsent(key, this) != null) {
//another thread is already compressing
//we don't do anything fancy here, just return and serve non-compressed content
return null;
}
FileChannel targetFileChannel = null;
FileChannel sourceFileChannel = null;
try {
//double check, the compressing thread could have finished just before we aquired the lock
preCompressed = encoded.getResource(newPath);
if (preCompressed != null) {
return new ContentEncodedResource(preCompressed, encoding.getName());
}
final File finalTarget = new File(encodedResourcesRoot, newPath);
final File tempTarget = new File(encodedResourcesRoot, newPath);
//horrible hack to work around XNIO issue
FileOutputStream tmp = new FileOutputStream(tempTarget);
try {
tmp.close();
} finally {
IoUtils.safeClose(tmp);
}
targetFileChannel = exchange.getConnection().getWorker().getXnio().openFile(tempTarget, FileAccess.READ_WRITE);
sourceFileChannel = exchange.getConnection().getWorker().getXnio().openFile(file, FileAccess.READ_ONLY);
StreamSinkConduit conduit = encoding.getEncoding().getResponseWrapper().wrap(new ImmediateConduitFactory<StreamSinkConduit>(new FileConduitTarget(targetFileChannel, exchange)), exchange);
final ConduitStreamSinkChannel targetChannel = new ConduitStreamSinkChannel(null, conduit);
long transfered = sourceFileChannel.transferTo(0, resource.getContentLength(), targetChannel);
targetChannel.shutdownWrites();
org.xnio.channels.Channels.flushBlocking(targetChannel);
if (transfered != resource.getContentLength()) {
UndertowLogger.REQUEST_LOGGER.error("Failed to write pre-cached file");
}
tempTarget.renameTo(finalTarget);
encoded.invalidate(newPath);
final Resource encodedResource = encoded.getResource(newPath);
return new ContentEncodedResource(encodedResource, encoding.getName());
} finally {
IoUtils.safeClose(targetFileChannel);
IoUtils.safeClose(sourceFileChannel);
fileLocks.remove(key);