8

I'm new to Spring framework and Spring Boot.
I've implemented a very simple RESTful Spring Boot web application.
You can see the nearly full source code in another question: Spring Boot: How to externalize JDBC datasource configuration?

How can the app service external static HTML, css js files?
For example, the directory structure may be as follows:

MyApp\
   MyApp.jar (this is the Spring Boot app that services the static files below)
   static\
       index.htm
       images\
           logo.jpg
       js\
           main.js
           sub.js
       css\
           app.css
       part\
           main.htm
           sub.htm

I've read the method to build a .WAR file that contains static HTML files, but since it requires rebuild and redeploy of WAR file even on single HTML file modification, that method is unacceptable.

An exact and concrete answer is preferable since my knowledge of Spring is very limited.

Community
  • 1
  • 1
zeodtr
  • 10,645
  • 14
  • 43
  • 60
  • 1
    What is it that doesn't work? Classpath resources in /static will be served as static resources by default (also in /public and a couple of other places), so you should be able to run your app and load /css/app.css for example. – Dave Syer Nov 19 '13 at 15:44
  • @DaveSyer Actually I found that it works, when I run the MyApp.jar in the MyApp directory. I verified it when I read the source code of org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory. It has 'static' in COMMON_DOC_ROOTS. (See https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java?source=cc) But I could not find any documentation about it, so I thought that including static files in the WAR file is the only method. – zeodtr Nov 20 '13 at 00:27
  • @DaveSyer And I wanted to change the directory for the static files to anywhere I wanted, regardless of classpath or current directory. The directory structure of my question is only an example(which is accidentally supported by the framework). So I posted another question to view the problem from another angle. (See http://stackoverflow.com/questions/20069130/list-of-properties-available-for-application-properties-in-spring-boot) – zeodtr Nov 20 '13 at 00:32

3 Answers3

6

I see from another of your questions that what you actually want is to be able to change the path to static resources in your application from the default values. Leaving aside the question of why you would want to do that, there are several possible answers.

  • One is that you can provide a normal Spring MVC @Bean of type WebMvcConfigurerAdapter and use the addResourceHandlers() method to add additional paths to static resources (see WebMvcAutoConfiguration for the defaults).
  • Another approach is to use the ConfigurableEmbeddedServletContainerFactory features to set the servlet context root path.
  • The full "nuclear option" for that is to provide a @Bean definition of type EmbeddedServletContainerFactory that set up the servlet container in the way you want it. If you use one of the existing concrete implementations they extend the Abstract* class that you already found, so they even have a setter for a property called documentRoot. You can also do a lot of common manipulations using a @Bean of type EmbeddedServletContainerCustomizer.
Luke Singham
  • 1,536
  • 2
  • 20
  • 38
Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • Thank you for your answer. So my approach in my another question is the last one of your suggestion. For now, I will stick to it(since it works). After studying more of the Spring and Spring Boot maybe I change the approach. – zeodtr Nov 20 '13 at 11:18
  • 1
    Regarding the reason why I want to change the default location of the static files, it is just for the flexibility for development and deploy environment. Maybe I don't know enough of the convention of the Spring framework (even Java) for now. – zeodtr Nov 20 '13 at 11:18
  • Note, that for spring-boot 2.x these classes changed their names. Now it is `ConfigurableTomcatWebServerFactory`, `TomcatServletWebServerFactory` and `TomcatServletWebServerFactoryCustomizer`. – Ruslan Stelmachenko Feb 20 '19 at 02:33
4

Is enough if you specify '-cp .' option in command 'java -jar blabla.jar' and in current directory is 'static' directory

pper
  • 41
  • 2
  • I found this answer more simple, no code change needed. But for it to work as expected, the OP need to rename its `static` folder to `public`, because spring boot (v1.1.6 here) will look for a `public` folder in the classpath. – Pierre-David Belanger Sep 26 '14 at 12:19
  • This solution was perfect for frontend development, just add a runtime dependency in the IDE. Then you can edit html/javascript stuff without having to repackage/restart the backend. – osundblad Jan 22 '15 at 15:44
0

Take a look at this Dave Syer's answer implementation.

You can set the document root directory which will be used by the web context to serve static files using ConfigurableEmbeddedServletContainer.setDocumentRoot(File documentRoot).

Working example:

package com.example.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Paths;

@Configuration
public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
    private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);

    private final Environment env;
    private static final String STATIC_ASSETS_FOLDER_PARAM = "static-assets-folder";
    private final String staticAssetsFolderPath;

    public WebConfigurer(Environment env, @Value("${" + STATIC_ASSETS_FOLDER_PARAM + ":}") String staticAssetsFolderPath) {
        this.env = env;
        this.staticAssetsFolderPath = staticAssetsFolderPath;
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        if (env.getActiveProfiles().length > 0) {
            log.info("Web application configuration, profiles: {}", (Object[]) env.getActiveProfiles());
        }
        log.info(STATIC_ASSETS_FOLDER_PARAM + ": '{}'", staticAssetsFolderPath);
    }

    private void customizeDocumentRoot(ConfigurableEmbeddedServletContainer container) {
        if (!StringUtils.isEmpty(staticAssetsFolderPath)) {
            File docRoot;
            if (staticAssetsFolderPath.startsWith(File.separator)) {
                docRoot = new File(staticAssetsFolderPath);
            } else {
                final String workPath = Paths.get(".").toUri().normalize().getPath();
                docRoot = new File(workPath + staticAssetsFolderPath);
            }
            if (docRoot.exists() && docRoot.isDirectory()) {
                log.info("Custom location is used for static assets, document root folder: {}",
                        docRoot.getAbsolutePath());
                container.setDocumentRoot(docRoot);
            } else {
                log.warn("Custom document root folder {} doesn't exist, custom location for static assets was not used.",
                        docRoot.getAbsolutePath());
            }
        }
    }

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        customizeDocumentRoot(container);
    }

}

Now you can customize your app with command line and profiles (src\main\resources\application-myprofile.yml):

> java -jar demo-0.0.1-SNAPSHOT.jar --static-assets-folder="myfolder"
> java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=myprofile
Community
  • 1
  • 1
kinjelom
  • 6,105
  • 3
  • 35
  • 61