Search code examples
javahtmlwicketwicket-6

Having a static "sub-site" in Wicket (i.e.: mounting a directory in Wicket)


I'm developing a web application using Wicket.

While most of the website is dynamically generated through wicket, I need to have a portion of the site be a normal "static" html website. Basically a small "subsite" inside the main website that is not managed by wicket at all, but that, instead, is just a collection of static content (html pages, css, images).

Can this be done? The idea would be to "mount" a certain subpath to point to the sub-site, but I don't know if this is even possible, as the mountResource() method wants a Resource as input.

EDIT: I need a solution that allow me to modify the static html files directly on the filesystem, thus the reason I was trying to "mount a directory" via wicket. I cannot simply put the pages in my webapp folder, since that way they end up inside the app's WAR file and every modification to the static pages would need a full deploy every time.

Any ideas?


Solution

  • Well, I implementet this myself in the end, using a dynamic resource. I'm not an expert of Wicket, so this may be a "bad" solution for some reason, but it seems to work. Posting the code here so other people can use it if they want:

    What I did was create this resource:

    public class DirectoryResolverResource implements IResource {
        private static final long serialVersionUID = 1L;
    
        private File servedDirectory;
        private String urlPrefix;
    
        //served directory is the directory you want to mount as a static sub-site
        //urlPrefix is the mountpoint where you're going to mount this resource, without the leading "/". E.g.: if you mount your directory in "/help" so that the sub-site URL is www.yoursite.com/pages/help the urlPrefix value must be "help"
        public DirectoryResolverResource(File servedDirectory, String urlPrefix) {
            super();
            if (servedDirectory == null || !servedDirectory.isDirectory()) {
                throw new IllegalArgumentException("Directory is null or doesn't exist");
            }
            this.servedDirectory = servedDirectory;
            this.urlPrefix = urlPrefix;
        }
    
        @Override
        public void respond(Attributes attributes) {
            Url url = attributes.getRequest().getUrl();
            String subPath = "";
            try {
                //we decode the URL by reversing the percent-encoding, so that filenames are properly resolved
                subPath = URLDecoder.decode(url.toString(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_BAD_REQUEST, "Encoding is invalid");
            }
    
            if (subPath.startsWith(urlPrefix)) {
                subPath = subPath.substring(urlPrefix.length());
            } else {
                throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Url is invalid");
            }
    
            File file = new File(servedDirectory.getAbsolutePath() + (subPath.startsWith("/") ? "" : "/") + subPath);
            if (file.isDirectory()) {
                // In case of a directory, redirect to the path ending in "/", otherwise browsers will fail to resolve relative paths in the page
                if (!subPath.endsWith("/")) {
                    throw new RedirectToUrlException("." + (subPath.isEmpty() ? "/" + urlPrefix : subPath) + "/", HttpServletResponse.SC_MOVED_PERMANENTLY);
                }
                // no specific file specified, try to return index.html
                file = new File(file.getAbsolutePath(), "index.html");
            }
            if (!file.exists() || file.isDirectory()) {
                // file not found
                throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_NOT_FOUND, "Resource not found");
            }
    
            if (!FSManager.isInSubDirectory(servedDirectory, file)) {
                // Security check: user is trying to escape the served directory via a non-canonical path
                throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_FORBIDDEN, "Access to this resource is forbidden");
            }
    
            // Serve the file
            FileResourceStream fileResourceStream = new FileResourceStream(file);
            ResourceStreamResource resource = new ResourceStreamResource(fileResourceStream);
            resource.respond(attributes);
        }
    
    }
    

    You can mount this resource like this:

    mountResource("/help", new ResourceReference("helpres") {
                    private static final long serialVersionUID = 1L;
    
                    @Override
                    public IResource getResource() {
                        return new DirectoryResolverResource(helpDir, "help");
                    }
                });
    

    Hope this is helpful to someone. Any improvements/comments/corrections/constructive criticisms are higly appreciated!

    NOTE: the isInSubDirectory() method just checks if a file is inside a certain directory tree. Won't bore you with the details, you can find implementations of such method here: Check if file is in (sub)directory