WildFly - getting resource from WAR

2019-01-28 05:41发布

问题:

I am using the following method to get a resource from WAR file in WildFly:

this.getClass().getResource(relativePath)

It works when the application is deployed as exploded WAR. It used to work with compressed WAR, too. Yesterday, I did a clean and rebuild of project in Eclipse, and it just stopped working.

When I check the resource root:

logger.info(this.getClass().getResource("/").toExternalForm());

I get this:

file:/C:/JBoss/wildfly8.1.0.CR1/modules/system/layers/base/org/jboss/as/ejb3/main/timers/

So, no wonder it doesn't work. It probably has something to do with JBoss module loading, but I don't know if this is a bug or normal behavior.

I found various similar problems on StackOverflow, but no applicable solution. One of the suggestions is to use ServletContext like so:

@Resource
private WebServiceContext wsContext;
...
ServletContext servletContext = (ServletContext)this.wsContext.getMessageContext()
        .get(MessageContext.SERVLET_CONTEXT);
servletContext.getResource(resourcePath);

But, when I try to obtain MessageContext in this manner, I get an IllegalStateException. So I am basically stuck. Any ideas?

回答1:

I ran into this same problem, and rather than define the resource as a shared module, I ended up working around this by using a ServletContextListener in my WAR.

In the contextInitialized method, I got the ServletContext from the ServletContextEvent and used its getResource("/WEB-INF/myResource") to get the URL to the resource inside my WAR file. It appears that in the ServletContextListener, the .getResource() method resolves as expected rather than to the "/modules/system/layers/base/org/jboss/as/ejb3/main/timers/" url. That URL can then be stored in the ServletContext for later use by your servlets or in an injected ApplicationScoped CDI bean.

@WebListener
public class ServletInitializer implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            final ServletContext context = sce.getServletContext();
            final URL resourceUrl = context.getResource("/WEB-INF/myResource");
            context.setAttribute("myResourceURL", resourceUrl);
        } catch (final MalformedURLException e) {
            throw new AssertionError("Resource not available in WAR file", e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {}
}

or

@WebListener
public class ServletInitializer implements ServletContextListener {

    @Inject
    private SomeApplicationScopedBean myBean;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            final ServletContext context = sce.getServletContext();
            final URL resourceUrl = context.getResource("/WEB-INF/myResource");
            myBean.setResourceUrl(resourceUrl);
        } catch (final MalformedURLException e) {
            throw new AssertionError("Resource not available in WAR file", e);
        }  
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {}
}


回答2:

We had a similar problem and our fault was that we tried to access the static resource through the raw path instead of using the input stream the resource is providing - the following code works for us even when deploying a non-exploded .war-file.

final URL resource = this.getClass().getResource(FILE);

try (final InputStream inputStream = resource.openStream();
     final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
     final BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
    // Use bufferedReader to read the content
} catch (IOException e) {
    // ...
}


回答3:

I finally gave up and put my resource files in a new JBoss module, as described in this link.

https://community.jboss.org/wiki/HowToPutAnExternalFileInTheClasspath

It works, but the downside is that there are two deployment targets so things are more complicated. On the upside, the size of the WAR file is reduced, and I don't have to redeploy the application if only some of the resources have changed.



回答4:

I was recently trying to figure out how to access a file within my own war in Java. The following is how the java classes and resources are packaged in the war file:

WAR
 `-- WEB-INF
        `-- classes (where all the java classes are)
        `-- resourcefiles
                   `-- resourceFile1

My target file was resourceFile1. To get that file, I just did the following in code:

InputStream inStream = this.class.getClassLoader().getResourceAsStream("resourcefiles/resourceFile1");

In this case the resource files would need to be in the same folder as the classes folder containing the java classes. Hopefully others find this helpful.



回答5:

This sample code works for wildfly deployed and tested on openshift. I think it is a wildfly problem I downland wildfly and tried on local I also get the error.

Check sample project on github

import org.springframework.web.bind.annotation.RequestMethod;    
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;

@Controller
@RequestMapping
public class FileDownloadController {

    private static final Logger logger = LoggerFactory.getLogger(FileDownloadController.class);

    private static final String DOC_FILE = "file/ibrahim-karayel.docx";
    private static final String PDF_FILE = "file/ibrahim-karayel.pdf";


    @RequestMapping(value = "/download/{type}", method = RequestMethod.GET)
    public void downloadFile(HttpServletRequest request, HttpServletResponse response,
                             @PathVariable("type") String type) throws IOException {

        File file = null;
        InputStream inputStream;
        if (type.equalsIgnoreCase("doc")) {
            inputStream = getClass().getClassLoader().getResourceAsStream(DOC_FILE);
            file = new File(Thread.currentThread().getContextClassLoader().getResource(DOC_FILE).getFile());
        } else if (type.equalsIgnoreCase("pdf")) {
            inputStream = getClass().getClassLoader().getResourceAsStream(PDF_FILE);
            file = new File(Thread.currentThread().getContextClassLoader().getResource(PDF_FILE).getFile());
        } else{
            throw new FileNotFoundException();
        }
        if (file == null && file.getName() == null) {
            logger.error("File Not Found -> " + file);
            throw new FileNotFoundException();
        }

        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        if (mimeType == null) {
            System.out.println("mimetype is not detectable, will take default");
            mimeType = "application/octet-stream";
        }

        System.out.println("mimetype : " + mimeType);
        response.setContentType(mimeType);
        /* "Content-Disposition : inline" will show viewable types [like images/text/pdf/anything viewable by browser] right on browser
            while others(zip e.g) will be directly downloaded [may provide save as popup, based on your browser setting.]*/
        response.setHeader("Content-Disposition", String.format("inline; filename=\"" + file.getName() + "\""));

        /* "Content-Disposition : attachment" will be directly download, may provide save as popup, based on your browser setting*/
        //response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));

        response.setContentLength(inputStream.available());
        IOUtils.copy(inputStream, response.getOutputStream());
        response.flushBuffer();
        inputStream.close();
    }
}