Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WFLY-2687 Use VFS based resource loader for deployment overlays #5663

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package org.wildfly.extension.undertow.deployment;

import java.io.IOException;
import java.net.URL;
import java.util.Collection;

import io.undertow.server.handlers.resource.FileResourceManager;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.server.handlers.resource.ResourceChangeListener;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.server.handlers.resource.URLResource;
import org.jboss.vfs.VirtualFile;

/**
Expand Down Expand Up @@ -42,8 +40,7 @@ public Resource getResource(final String path) throws IOException {
for (VirtualFile overlay : overlays) {
VirtualFile child = overlay.getChild(p);
if (child.exists()) {
URL url = child.toURL();
return new URLResource(url, url.openConnection(), path);
return new VirtualFileResource(child, path);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package org.wildfly.extension.undertow.deployment;

import io.undertow.UndertowLogger;
import io.undertow.io.IoCallback;
import io.undertow.io.Sender;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.util.DateUtils;
import io.undertow.util.ETag;
import io.undertow.util.MimeMappings;
import org.jboss.vfs.VirtualFile;
import org.xnio.FileAccess;
import org.xnio.IoUtils;
import org.xnio.Pooled;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
* @author Stuart Douglas
*/
public class VirtualFileResource implements Resource {

private final VirtualFile file;
private final String path;

public VirtualFileResource(final VirtualFile file, String path) {
this.file = file;
this.path = path;
}

@Override
public String getPath() {
return path;
}

@Override
public Date getLastModified() {
return new Date(file.getLastModified());
}

@Override
public String getLastModifiedString() {
final Date lastModified = getLastModified();
if (lastModified == null) {
return null;
}
return DateUtils.toDateString(lastModified);
}

@Override
public ETag getETag() {
return null;
}

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

@Override
public boolean isDirectory() {
return file.isDirectory();
}

@Override
public List<Resource> list() {
final List<Resource> resources = new ArrayList<Resource>();
for (VirtualFile child : file.getChildren()) {
resources.add(new VirtualFileResource(child, path));
}
return resources;
}

@Override
public String getContentType(final MimeMappings mimeMappings) {
final String fileName = file.getName();
int index = fileName.lastIndexOf('.');
if (index != -1 && index != fileName.length() - 1) {
return mimeMappings.getMimeType(fileName.substring(index + 1));
}
return null;
}

@Override
public void serve(final Sender sender, final HttpServerExchange exchange, final IoCallback callback) {
abstract class BaseFileTask implements Runnable {
protected volatile FileChannel fileChannel;

protected boolean openFile() {
try {
fileChannel = exchange.getConnection().getWorker().getXnio().openFile(file.getPhysicalFile(), FileAccess.READ_ONLY);
} catch (FileNotFoundException e) {
exchange.setResponseCode(404);
callback.onException(exchange, sender, e);
return false;
} catch (IOException e) {
exchange.setResponseCode(500);
callback.onException(exchange, sender, e);
return false;
}
return true;
}
}

class ServerTask extends BaseFileTask implements IoCallback {

private Pooled<ByteBuffer> pooled;

@Override
public void run() {
if (fileChannel == null) {
if (!openFile()) {
return;
}
pooled = exchange.getConnection().getBufferPool().allocate();
}
if (pooled != null) {
ByteBuffer buffer = pooled.getResource();
try {
buffer.clear();
int res = fileChannel.read(buffer);
if (res == -1) {
//we are done
pooled.free();
IoUtils.safeClose(fileChannel);
callback.onComplete(exchange, sender);
return;
}
buffer.flip();
sender.send(buffer, this);
} catch (IOException e) {
onException(exchange, sender, e);
}
}

}

@Override
public void onComplete(final HttpServerExchange exchange, final Sender sender) {
if (exchange.isInIoThread()) {
exchange.dispatch(this);
} else {
run();
}
}

@Override
public void onException(final HttpServerExchange exchange, final Sender sender, final IOException exception) {
UndertowLogger.REQUEST_IO_LOGGER.ioException(exception);
if (pooled != null) {
pooled.free();
pooled = null;
}
IoUtils.safeClose(fileChannel);
if (!exchange.isResponseStarted()) {
exchange.setResponseCode(500);
}
callback.onException(exchange, sender, exception);
}
}

class TransferTask extends BaseFileTask {
@Override
public void run() {
if (!openFile()) {
return;
}

sender.transferFrom(fileChannel, new IoCallback() {
@Override
public void onComplete(HttpServerExchange exchange, Sender sender) {
try {
IoUtils.safeClose(fileChannel);
} finally {
callback.onComplete(exchange, sender);
}
}

@Override
public void onException(HttpServerExchange exchange, Sender sender, IOException exception) {
try {
IoUtils.safeClose(fileChannel);
} finally {
callback.onException(exchange, sender, exception);
}
}
});
}
}

BaseFileTask task = new TransferTask();
if (exchange.isInIoThread()) {
exchange.dispatch(task);
} else {
task.run();
}
}

@Override
public Long getContentLength() {
return file.getSize();
}

@Override
public String getCacheKey() {
return file.toString();
}

@Override
public File getFile() {
try {
return file.getPhysicalFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public File getResourceManagerRoot() {
return null;
}

@Override
public URL getUrl() {
try {
return file.toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}

}