0

I'm having some trouble trying to set up a RESTFUL API using Spring Framework and Spring Security + Jersey.

I could deploy a Jersey rest api and request resources successfully but when I also implemented Spring (Framework + Spring security) things are getting out of hand.

I've worked before with old Spring versions where configuration was made with XML files, but I would like to play and learn how to config it with Java annotations only. I'm having trouble with this as I can't get past applicationContext.xml. I simply don't want to put an XML file, I just want to configure my small rest api webapp with Java annotations only.

When I added Spring Security config lines to my web.xml and tried to request any resource, this error appeared:

No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?

So, I also added this snippet to my web.xml:

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Then, I created a new configuration class for Spring, a simple one:

@Configuration
@ComponentScan({
    "com.myapp.rest.api", 
    "com.myapp.business.services"
})
public class SpringApplicationConfig {

}

I restarted my Tomcat again and tried to request any resource, then it started to complain about not finding the applicationContext.xml file, which I didn't provide, but I don't know why it is still trying to find it if I have another class with @Configuration annotation. How can I configure Spring without using applicationContext.xml?

Note: I am aware of Spring MVC and Spring Boot, but I'm currently playing with Jersey and using Spring Framework for dependency injection, also Spring Security for auth and security stuff obviously.

Below my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container, 
    see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.myapp.rest.api</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <!-- SPRING SECURITY -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

And my pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.myapp</groupId>
    <artifactId>api</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>api</name>

    <build>
        <finalName>my api</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <inherited>true</inherited>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- JERSEY (2) -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
        </dependency>
        <!-- Soporte JSON -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
        </dependency>

    <!-- SPRING + SPRING SECURITY -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring-security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring-security.version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet-api.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- JACKSON - JSON -->
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!--  LOG4J2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!--  JWT TOKEN -->
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>${jose.jwt.version}</version>
        </dependency>


    </dependencies>


    <properties>
        <jose.jwt.version>4.39.2</jose.jwt.version>
        <spring-security.version>4.2.3.RELEASE</spring-security.version>
        <spring.version>4.3.9.RELEASE</spring.version>
        <log4j.version>2.8.2</log4j.version>
        <jersey.version>2.9.1</jersey.version>
        <jackson.version>2.8.9</jackson.version>
        <servlet-api.version>3.1.0</servlet-api.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>
JorgeGRC
  • 1,032
  • 2
  • 18
  • 37
  • https://stackoverflow.com/a/32357991/2587435 – Paul Samsotha Jul 13 '17 at 14:58
  • @peeskillet got rid of the errors when trying your workaround by creating a dummy applicationContext.xml. I think this could be the answer to this question but some other issues with `Spring Security` configuration arised and I need to solve them first to verify that in fact your answer worked (: – JorgeGRC Jul 13 '17 at 15:16

1 Answers1

1

You are missing some important points in web.xml

 // For java config, you don't want to use .xml 
  // you have to include AnnotationConfigWebApplicationContext
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
      </param-value>
    </context-param>

    // Location of your Configuration class
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>my.package.AppConfig</param-value>
    </context-param>

    // To load context
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    // Spring Servlet
    <servlet>
        <servlet-name>SpringApplication</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
   // Your controllers
        <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>my.package.controllers.HomeController</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    // Servlet url mapping
    <servlet-mapping>
        <servlet-name>SpringApplication</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

Adding controller class

@Component
@Path("/home")
public class HomeController {

UPDATE : As it was told that author of question is interested in just Spring DI, not involving Spring MVC, below is simple config bootstrapping WebApplicationInitializer to configure the ServletContext programmatically, without web.xml. Note that this would require Servlet 3.0+ container.

public class ApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext)  throws ServletException {

        AnnotationConfigWebApplicationContext context 
          = new AnnotationConfigWebApplicationContext();

        servletContext.addListener(new ContextLoaderListener(context));
        servletContext.setInitParameter(
          "contextConfigLocation", "com.package.my");
    }
}
fg78nc
  • 4,774
  • 3
  • 19
  • 32
  • this also worked as http://stackoverflow.com/a/32357991/2587435. Errors are gone but now I need to face some issues with `Spring Security` configuration. Actually I think this is the answer that I was looking for, but I usually get confused when using spring because I don't think it's easy to configure. I only took the `contextClass` and `contextConfigLocation` from your snipped to get it to work, because I'm not using `Spring MVC`, I'm using `Jersey`. Do you know if with Spring Security is also needed to do something similar to get rid of `applicationContext-security.xml` ? – JorgeGRC Jul 13 '17 at 15:21
  • I see, I thought MVC might be useful, as you build your project further. – fg78nc Jul 13 '17 at 15:23
  • What do you mean with 'further'? I think I'll give `Spring MVC` a go soon, but right now I need to use `Jersey` – JorgeGRC Jul 13 '17 at 15:24
  • In my understanding, MVC is core of web project, allowing you to build things up. If you just need Spring DI, it would be different configuration. – fg78nc Jul 13 '17 at 15:25
  • Yes, that's true, but as I only needed a REST API I thought it could be overkill to use a full MVC framework, but I have to admit that it looks easy to configure and get things up running quickly – JorgeGRC Jul 13 '17 at 15:27
  • I don't think there is a overkill, you need to configure and load servlets anyway. There is no overhead. – fg78nc Jul 13 '17 at 15:27
  • 1
    @JorgeGRC I have updated my answer with simple configuration without web.xml – fg78nc Jul 13 '17 at 15:33