All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.server.handlers.resource.URLResource Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 io.undertow.server.handlers.resource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.xnio.IoUtils;
import io.undertow.UndertowLogger;
import io.undertow.io.IoCallback;
import io.undertow.io.Sender;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.DateUtils;
import io.undertow.util.ETag;
import io.undertow.util.MimeMappings;
import io.undertow.util.StatusCodes;

/**
 * @author Stuart Douglas
 */
public class URLResource implements Resource, RangeAwareResource {

    private final URL url;
    private final String path;

    private boolean connectionOpened = false;
    private Date lastModified;
    private Long contentLength;

    @Deprecated
    public URLResource(final URL url, URLConnection connection, String path) {
        this(url, path);
    }

    public URLResource(final URL url, String path) {
        this.url = url;
        this.path = path;
    }

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

    @Override
    public Date getLastModified() {
        openConnection();
        return lastModified;
    }

    private void openConnection() {
        if (!connectionOpened) {
            connectionOpened = true;
            URLConnection connection = null;
            try {
                try {
                    connection = url.openConnection();
                } catch (IOException e) {
                    lastModified = null;
                    contentLength = null;
                    return;
                }
                if (url.getProtocol().equals("jar")) {
                    connection.setUseCaches(false);
                    URL jar = ((JarURLConnection) connection).getJarFileURL();
                    lastModified = new Date(new File(jar.getFile()).lastModified());
                } else {
                    lastModified = new Date(connection.getLastModified());
                }
                contentLength = connection.getContentLengthLong();
            } finally {
                if (connection != null) {
                    try {
                        IoUtils.safeClose(connection.getInputStream());
                    } catch (IOException e) {
                        //ignore
                    }
                }
            }
        }
    }

    @Override
    public String getLastModifiedString() {
        return DateUtils.toDateString(getLastModified());
    }

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

    @Override
    public String getName() {
        String path = url.getPath();
        if (path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }
        int sepIndex = path.lastIndexOf("/");
        if (sepIndex != -1) {
            path = path.substring(sepIndex + 1);
        }
        return path;
    }

    @Override
    public boolean isDirectory() {
        Path file = getFilePath();
        if (file != null) {
            return Files.isDirectory(file);
        } else if (url.getPath().endsWith("/")) {
            return true;
        }
        return false;
    }

    @Override
    public List list() {
        List result = new LinkedList<>();
        Path file = getFilePath();
        try {
            if (file != null) {
                try (DirectoryStream stream = Files.newDirectoryStream(file)) {
                    if (stream != null) {
                        for (Path child : stream) {
                            result.add(new URLResource(child.toUri().toURL(), child.toString()));
                        }
                    } else {
                        UndertowLogger.ROOT_LOGGER.failedToListPathsForFile(file);
                    }
                }
            } else {
                UndertowLogger.ROOT_LOGGER.noSourceToListResourcesFrom();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    @Override
    public String getContentType(final MimeMappings mimeMappings) {
        final String fileName = 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(Sender sender, HttpServerExchange exchange, IoCallback completionCallback) {
        serveImpl(sender, exchange, -1, -1, false, completionCallback);
    }

    public void serveImpl(final Sender sender, final HttpServerExchange exchange, final long start, final long end, final boolean range, final IoCallback completionCallback) {

        class ServerTask implements Runnable, IoCallback {

            private InputStream inputStream;
            private byte[] buffer;

            long toSkip = start;
            long remaining = end - start + 1;

            @Override
            public void run() {
                if (range && remaining == 0) {
                    //we are done, just return
                    IoUtils.safeClose(inputStream);
                    completionCallback.onComplete(exchange, sender);
                    return;
                }
                if (inputStream == null) {
                    try {
                        inputStream = url.openStream();
                    } catch (IOException e) {
                        exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
                        return;
                    }
                    buffer = new byte[1024];//TODO: we should be pooling these
                }
                try {
                    int res = inputStream.read(buffer);
                    if (res == -1) {
                        //we are done, just return
                        IoUtils.safeClose(inputStream);
                        completionCallback.onComplete(exchange, sender);
                        return;
                    }
                    int bufferStart = 0;
                    int length = res;
                    if (range && toSkip > 0) {
                        //skip to the start of the requested range
                        //not super efficient, but what can you do
                        while (toSkip > res) {
                            toSkip -= res;
                            res = inputStream.read(buffer);
                            if (res == -1) {
                                //we are done, just return
                                IoUtils.safeClose(inputStream);
                                completionCallback.onComplete(exchange, sender);
                                return;
                            }
                        }
                        bufferStart = (int) toSkip;
                        length -= toSkip;
                        toSkip = 0;
                    }
                    if (range && length > remaining) {
                        length = (int) remaining;
                    }
                    sender.send(ByteBuffer.wrap(buffer, bufferStart, length), 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);
                IoUtils.safeClose(inputStream);
                if (!exchange.isResponseStarted()) {
                    exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
                }
                completionCallback.onException(exchange, sender, exception);
            }
        }

        ServerTask serveTask = new ServerTask();
        if (exchange.isInIoThread()) {
            exchange.dispatch(serveTask);
        } else {
            serveTask.run();
        }
    }

    @Override
    public Long getContentLength() {
        openConnection();
        return contentLength;
    }

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

    @Override
    public File getFile() {
        Path path = getFilePath();
        return path != null ? path.toFile() : null;
    }

    @Override
    public Path getFilePath() {
        if (url.getProtocol().equals("file")) {
            try {
                return Paths.get(url.toURI());
            } catch (URISyntaxException e) {
                return null;
            }
        }
        return null;
    }

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

    @Override
    public Path getResourceManagerRootPath() {
        return null;
    }

    @Override
    public URL getUrl() {
        return url;
    }

    @Override
    public void serveRange(Sender sender, HttpServerExchange exchange, long start, long end, IoCallback completionCallback) {
        serveImpl(sender, exchange, start, end, true, completionCallback);
    }

    @Override
    public boolean isRangeSupported() {
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy