5

Spring Boot (1.2.7) web app (responds to SSL requests) works great running from Intellij IDEA.

Build JAR artifact to run from command line, throws error upon start

org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

Questions

  1. How can I avoid this error?
  2. If I need to declare an EmbeddedServletContainerFactory, how can I have its ServletContainer handle requests for my Controller?

Solutions Tried

Added EmbeddedservletContainerFactory as requested by error. Project now starts from JAR, but requests are not being received by Controller like it does when running from IntelliJ.

Here's what my Application.class looks like after adding the EmbeddedServletContainerFactory

package org.coursera.diabetes;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.support.ConnectionSource;
import diabetes.common.model.*;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class Application {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    private static final String dbUrl = "jdbc:h2:mem:testdb";

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

    }

    @Bean
    @Profile("production")
    public EmbeddedServletContainerFactory servletContainer(@Value("${keystore.file}") Resource keystoreFile) {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
        //factory.setPort(8443);
        String absoluteKeystoreFile = "";

        try {
            absoluteKeystoreFile = keystoreFile.getFile().getAbsolutePath();
            logger.info(String.format("******** keystore: %s", absoluteKeystoreFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        tomcat.addAdditionalTomcatConnectors(createSslConnector(absoluteKeystoreFile));
        return tomcat;
    }

    private Connector createSslConnector(String absKeystoreFile) {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        try {
            connector.setScheme("https");
            connector.setSecure(true);
            connector.setPort(8443);
            protocol.setSSLEnabled(true);
            protocol.setKeystoreFile(absKeystoreFile);
            protocol.setKeystorePass("changeit");
            protocol.setKeyAlias("tomcat");
            return connector;
        }
        catch (Exception ex) {
            throw new IllegalStateException("can't access keystore: [" + "keystore"
                    + "] or truststore: [" + "keystore" + "]", ex);
        }
    }
}

Notes

Spring Boot (web) application works great running from Intellij IDEA, responding to https requests without needing to do anything more than a few additional lines in application.properties:

security.require-ssl=true
server.port=8443
server.ssl.key-store=src/main/resources/private/keystore
server.ssl.key-store-password=changeit
server.ssl.key-password=changeit

To start JAR artifact from command line I use:

java -Dspring.profiles.active=production -Dkeystore.file=file:///$PWD/keystore -jar diabetes-spring.jar

JAR starts without error doing this, but https web requests (which work from Intellij) simply return Apache/tomcat 404 "The requested resource is not available".

EDITS

Running the JAR with -Ddebug outputs the following information (I'm omitting all the DEBUG messages for not finding config files application.properties / application / application-production.properties which don't seem pertitenent)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

2015-11-13 14:16:39.980  INFO 47600 --- [           main] org.coursera.diabetes.Applica
tion        : Starting Application on Bakers-MacBook-Pro.local with PID 47600 (/User
s/baker/coursera-android/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifa
cts/diabetes_spring_jar/diabetes-spring.jar started by blam in /Users/blam/coursera-and
roid/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifacts/diabetes_spring
_jar)
2015-11-13 14:16:39.983 DEBUG 47600 --- [           main] o.s.boot.SpringApplication
            : Loading source class org.coursera.diabetes.Application
2015-11-13 14:16:40.011 DEBUG 47600 --- [           main] o.s.b.c.c.ConfigFileApplicati
onListener  : Activated profiles production
2015-11-13 14:16:40.011 DEBUG 47600 --- [           main] o.s.b.c.c.ConfigFileApplicati
onListener  : Loaded config file 'file:./config/application.properties'
2015-11-13 14:16:40.020  INFO 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbe
ddedWebApplicationContext@23faf8f2: startup date [Fri Nov 13 14:16:40 WET 2015]; root o
f context hierarchy
2015-11-13 14:16:40.022 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Bean factory for org.springframework.boot.context.embedded.AnnotationConf
igEmbeddedWebApplicationContext@23faf8f2: org.springframework.beans.factory.support.Def
aultListableBeanFactory@6cc7b4de: defining beans [org.springframework.context.annotatio
n.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.inter
nalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequired
AnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProc
essor,application]; root of factory hierarchy
2015-11-13 14:16:40.220 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Unable to locate MessageSource with name 'messageSource': using default [
org.springframework.context.support.DelegatingMessageSource@20ce78ec]
2015-11-13 14:16:40.221 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Using ApplicationEventMulticaster [org.springframework.context.event.Simp
leApplicationEventMulticaster@393671df]
2015-11-13 14:16:40.226  WARN 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Exception encountered during context initialization - cancelling refresh
attempt

org.springframework.context.ApplicationContextException: Unable to start embedded conta
iner; nested exception is org.springframework.context.ApplicationContextException: Unab
le to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactor
y bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRe
fresh(EmbeddedWebApplicationContext.java:132)
        at org.springframework.context.support.AbstractApplicationContext.refresh(Abstr
actApplicationContext.java:474)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refr
esh(EmbeddedWebApplicationContext.java:117)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:68
9)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:958)
        at org.coursera.diabetes.Application.main(Application.java:40)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:182)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:155)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:129)
        ... 7 common frames omitted

2015-11-13 14:16:40.227  INFO 47600 --- [           main] .b.l.ClasspathLoggingApplicationListener : Application failed to start with classpath: [file:/Users/baker/coursera-android/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifacts/diabetes_spring_jar/diabetes-spring.jar]
2015-11-13 14:16:40.228 ERROR 47600 --- [           main] o.s.boot.SpringApplication               : Application startup failed

org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:132)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:117)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:689)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:958)
        at org.coursera.diabetes.Application.main(Application.java:40)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:182)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:155)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:129)
        ... 7 common frames omitted
Baker
  • 24,730
  • 11
  • 100
  • 106
  • Try putting things back the way they were and looking at the auto-configuration report (run with --debug) to see why the embedded servlet container isn't being auto-configured – Andy Wilkinson Nov 13 '15 at 09:40
  • @AndyWilkinson Thanks. Tried the -Ddebug option. Strangely while running the JAR no auto-configuration report is produced. (Output copied above.). Running --debug within IDEA, the report is generated as expected. I wonder if auto-configuration is somehow turned off when building JAR artifact. – Baker Nov 13 '15 at 14:57
  • How do you build jar? – jny Nov 13 '15 at 15:02
  • In IDEA -> Project Structure (Cmd + ;) -> Artifacts -> + button, -> JAR -> from Modules with Dependencies -> select main Module -> select Main class -> JAR files from libraries: extract to the target JAR -> directory for META-INF/MANIFEST.MF: /src/main/resources/ -> Build (in menu) -> Build artifacts -> [select name of jar] -> build – Baker Nov 13 '15 at 15:16

1 Answers1

0

In IntelliJ IDEA - Project Structure - Artifacts - Available Elements, all the Gradle dependency libraries need to be Extracted into Output Root for the JAR of a Spring Boot application to run properly from java command line execution.

After changing the JAR artifact to this, I could Build the JAR and run it from the command line JVM.

For the default TomcatEmbeddedServletContainerFactory to run on HTTPS/SSL port 8443 specify java command line arguments equivalent to what is required in application.properties.

i.e.

java -jar your-spring.jar --security.require-ssl=true --server.port=8443 --server.ssl.key-store=keystore --server.ssl.key-store-password=changeit --server.ssl.key-password=changeit

enter image description here

Baker
  • 24,730
  • 11
  • 100
  • 106