spring-boot: spring-boot jersey based application : can not start without errors using java -jar command

It works fine if I run it using:

OK: mvn spring-boot:run

but not relying on the spring-boot maven plugin and with a plain:

NOK : java -jar target/spring-boot-jersey-test-0.0.1-SNAPSHOT.jar

For the 2nd case, App starts but the following errors are visible during startup

[2014-08-07 12:54:47.531] boot - 7632 ERROR [localhost-startStop-1] --- [/]: Exception starting filter servletContainer
com.sun.jersey.core.spi.scanning.ScannerException: IO error when scanning jar jar:file:/Users/boostrack/spring-boot-jersey-test/target/spring-boot-jersey-test-0.0.1-SNAPSHOT.jar!/lib/jersey-client-1.11.jar!/com/sun/jersey
    at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.scan(JarZipSchemeScanner.java:82)
    at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:223)
    at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:139)
    at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80)
    at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104)
    at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78)
    at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89)
    at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:700)
    at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)
    at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:727)
    at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
    at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:109)
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4809)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5485)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.FileNotFoundException: /Users/boostrack/spring-boot-jersey-test-0.0.1-SNAPSHOT.jar!/lib/jersey-client-1.11.jar (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:131)
    at java.io.FileInputStream.<init>(FileInputStream.java:87)
    at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:90)
    at sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:188)
    at java.net.URL.openStream(URL.java:1038)
    at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.closing(JarZipSchemeScanner.java:123)
    at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.scan(JarZipSchemeScanner.java:75)
    ... 22 more
[2014-08-07 12:54:47.558] boot - 7632 ERROR [localhost-startStop-1] --- StandardContext: Error filterStart
[2014-08-07 12:54:47.560] boot - 7632 ERROR [localhost-startStop-1] --- StandardContext: Context [] startup failed due to previous errors

and the application does not respond as expected.

PS: did not tested how it works with gradle plugin

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Comments: 20 (6 by maintainers)

Most upvoted comments

I use the following code (that works as spring beans) jersey 1:

import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ws.rs.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.sun.jersey.api.core.DefaultResourceConfig;

@Component
public class RestApplication extends DefaultResourceConfig {

    private static final Logger log = LoggerFactory.getLogger(RestApplication.class);

    public RestApplication() {
        getFeatures().put("com.sun.jersey.api.json.POJOMappingFeature", true);
    }

    @Autowired
    ApplicationContext appCtx;

    @PostConstruct
    public void setup() {
        log.info("Rest classes found:");
        Map<String,Object> beans = appCtx.getBeansWithAnnotation(Path.class);
        for (Object o : beans.values()) {
            log.info(" -> " + o.getClass().getName());
            getSingletons().add(o);
        }
    }

}

and

    @Autowired
    RestApplication restApplication;

    @Bean
    public ServletRegistrationBean jersey() {
        ServletRegistrationBean bean = new ServletRegistrationBean();
        bean.setServlet(new ServletContainer(restApplication));
        bean.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
        bean.setUrlMappings(Arrays.asList("/rest/*"));
        return bean;
    }

jersery2:

import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Path;

import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.sun.jersey.api.core.ResourceConfig;

@Component
@ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {

    private static final Logger log = LoggerFactory.getLogger(JerseyConfig.class);

    @Autowired
    ApplicationContext appCtx;

    @PostConstruct
    public void setup() {
        log.info("Rest classes found:");
        Map<String,Object> beans = appCtx.getBeansWithAnnotation(Path.class);
        for (Object o : beans.values()) {
            log.info(" -> " + o.getClass().getName());
            register(o);
        }
    }

}

In the future please ask this sort of question on StackOverflow using the spring-boot tag.

It looks like you’ve configured Jersey to scan the com.sun.jersey package. The jars containing that package are nested within your application’s jar file and Jersey appears to be unable to scan a nested jar.

If you need Jersey to scan the com.sun.jersey package then you can configure Boot’s Maven plugin to unpack the jars containing that package:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <requiresUnpack>
                <dependency>
                        <groupId>com.sun.jersey</groupId>
                        <artifactId>jersey-server</artifactId>
                </dependency>
                <dependency>
                        <groupId>com.sun.jersey</groupId>
                        <artifactId>jersey-core</artifactId>
                </dependency>
                <dependency>
                        <groupId>com.sun.jersey</groupId>
                        <artifactId>jersey-servlet</artifactId>
                </dependency>
        </requiresUnpack>
    </configuration>
</plugin>

See the documentation for more details.

Instead of using the packages(“com.example”) from Jersey, I use my own method (no spring needed):

public void scan(String... packages) {
    for (String pack : packages) {
        Reflections reflections = new Reflections(pack);
        reflections.getTypesAnnotatedWith(Provider.class)
                .parallelStream()
                .forEach((clazz) -> {
                    logger.debug("New provider registered: " + clazz.getName());
                    register(clazz);
                });
        reflections.getTypesAnnotatedWith(Path.class)
                .parallelStream()
                .forEach((clazz) -> {
                    logger.debug("New resource registered: " + clazz.getName());
                    register(clazz);
                });
    }
}

With Spring Boot (+ Jersey 2) it can look like separate config class:

@Configuration
public class RestBeansConfiguration {
    private static final Logger LOG = LoggerFactory.getLogger(RestBeansConfiguration.class);

    @Inject
    private ApplicationContext appCtx;

    @Bean
    public ResourceConfigCustomizer jersey() {
        return config -> {
            LOG.info("Jersey resource classes found:");
            appCtx.getBeansWithAnnotation(Path.class).forEach((name, resource) -> {
                LOG.info(" -> {}", resource.getClass().getName());
                config.register(resource);
            });
        };
    }
}

thx @matejsp for hint!

Are there any work around?

Yes, please see the release notes.