Not so long ago Spring presented a java based configuration for the Spring Security module. I try to migrate from XML to Java configuration.
Here is my test project: https://github.com/Fruzenshtein/security-spr
pom.xml was updated:
spring.version = 3.2.4.RELEASE
spring.security.version = 3.1.4.RELEASE
...
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>
<version>1.0.0.M1</version>
</dependency>
...
<repository>
<id>repository.springsource.milestone</id>
<name>SpringSource Milestone Repository</name>
<url>http://repo.springsource.org/milestone</url>
</repository>
Then I have added a new java config class instead of spring-security.xml
package com.sprsec.init;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import com.sprsec.service.CustomUserDetailsService;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.userDetailsService(new CustomUserDetailsService())
.authorizeUrls()
.antMatchers("/sec/moderation.html").hasRole("MODERATOR")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/user-login.html")
.defaultSuccessUrl("/success-login.html")
.failureUrl("/error-login.html")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/index.html");
}
}
After that I changed my Initializaer.class to:
package com.sprsec.init;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class Initializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Remark:
spring-security.xml was deleted
A line of code #ImportResource("classpath:spring-security.xml") was deleted in WebAppConfig.java
Filter declaration in web.xml was removed
When I try to run the application on a server I get:
SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webAppConfig': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.config.internalTransactionAdvisor' defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration.transactionAdvisor()] threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor.setAdvice(Lorg/aopalliance/aop/Advice;)V
Can someone give an advice what can be a reason of this?
You are probably running into some dependency conflict. Run maven dependency:tree to figure out which dependencies are used and find conflicting ones. Also the use of the maven-enforcer-plugin might help.
Looking at your sample project you are using Spring 3.1.3 and the same for Spring Security, you want to have seperate properties for spring and spring security.
<properties>
<hibernate.version>4.1.7.Final</hibernate.version>
<mysql.connector.version>5.1.21</mysql.connector.version>
<slf4j.version>1.6.6</slf4j.version>
<spring.version>3.2.4.RELEASE</spring.version>
<spring.security.version>3.1.4.RELEASE<spring.security.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
As for Spring Security you might want to try out the new 3.2.0.RC1 as that already includes the new spring security javaconfig with improvements and some convenience superclasses.
A final note on your configuration, as that is flawed, you are duplicating your bean instances as you are loading the same configuration twice (duplicating your whole configuration), both the ContextLoaderListener and DispatcherServlet use the WebAppConfig for configuration. This could effectively render your security useless.
Submitted a pull request which should give you some pointers on how to fix the issue at hand.
Related
I am trying to add JUnit tests to a Tomee web-application that uses CDI and JPA. Leaving out the different hurdles of the last two weeks (including very intensive research on stackoverflow as well as other sources), my problem appears to be quite concrete:
Running the unit test gives this error message:
org.apache.openejb.Injector$NoInjectionMetaDataException:
org.demo.service.GenericServiceTest : Annotate the class with #javax.annotation.ManagedBean so it can be discovered in the application scanning process
at org.apache.openejb.Injector.inject(Injector.java:54)
at org.apache.openejb.OpenEjbContainer$GlobalContext.bind(OpenEjbContainer.java:656)
...
This I don't understand, for I have annotated the class just as required. Any idea what I can do to resolve this?
(Or does the error refer to the injected class - GenericService? I cannot annotate this #ManagedBean, for it is already #Stateless).
Here are some details of my project:
gradle dependencies:
dependencies {
compile 'org.apache.commons:commons-lang3:3.3.2'
compile "com.googlecode.json-simple:json-simple:1.1.1"
compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.8.6'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.6'
compile 'log4j:log4j:1.2.16'
testCompile "org.apache.openejb:tomee-embedded:1.7.3"
compile "javax:javaee-api:7.0"
compile 'mysql:mysql-connector-java:5.1.16'
}
(I have put the testCompile inbetween, because there is a ticket stating the importance of the order: EJB testing with TomEE embedded EJBContainer api: java.lang.ClassFormatError exception)
My Test class is this:
package org.demo.service;
import java.io.File;
import java.util.Properties;
import javax.annotation.ManagedBean;
import javax.ejb.embeddable.EJBContainer;
import javax.inject.Inject;
import javax.naming.NamingException;
import org.apache.openejb.OpenEjbContainer;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
#ManagedBean
public class GenericServiceTest {
private static EJBContainer ejbContainer;
#Inject
private GenericService service;
#Test
public void test() throws NamingException {
System.out.println("service: " + service);
}
#BeforeClass
public static void start() {
Properties p = new Properties();
try {
p.put(EJBContainer.MODULES, new File("bin"));
} catch (Exception e1) {
e1.printStackTrace();
}
p.put(OpenEjbContainer.Provider.OPENEJB_ADDITIONNAL_CALLERS_KEY, GenericService.class.getName());
ejbContainer = javax.ejb.embeddable.EJBContainer.createEJBContainer(p);
}
#Before
public void inject() throws NamingException {
ejbContainer.getContext().bind("inject", this);
}
#AfterClass
public static void shutdownContainer() {
if (ejbContainer != null) {
ejbContainer.close();
}
}
}
Maybe I'm running totally in the wrong direction here - Please let me know if I should choose a different approach - All I want is to add unit tests to my web application (preferably without introducing Weld/JBoss or other implementations as alternatives to the implementations I already use in the application itself).
Thank you very much in advance!
I would recommand you to read http://tomee.apache.org/developer/testing/index.html , there are some examples
Regarding your test it uses openejb embedded and not tomee embedded and deploys bin/ as an application. the hack bind("inject") only works with classpath deployment (no module).
I am trying to use #PushEndpoint like in the Primefaces showcase:
http://www.primefaces.org/showcase/push/notify.xhtml
import javax.faces.application.FacesMessage;
import org.primefaces.push.annotation.OnMessage;
import org.primefaces.push.annotation.PushEndpoint;
import org.primefaces.push.impl.JSONEncoder;
#PushEndpoint("/notify")
public class NotifyResource {
#OnMessage(encoders = { JSONEncoder.class })
public FacesMessage onMessage(FacesMessage message) {
return message;
}
}
As soon as I use this example, my server won't start anymore:
SEVERE: CDI Beans module deployment failed
java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
The problem comes from the line
(encoders = { JSONEncoder.class })
Full stack trace: http://pastie.org/10936442
The problem was a ClassNotFoundException
java.lang.ClassNotFoundException: org.atmosphere.config.managed.Encoder
And Atmosphere is not included in primefaces.jar
Simply add the dependency
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-runtime</artifactId>
<version>2.2.6</version>
</dependency>
https://mvnrepository.com/artifact/org.atmosphere/atmosphere-runtime/2.2.6
I am struggling with this issue for days now and have no clue how to solve this. Any quick help will be grateful.
I need to convert LocalDate from JSON string which I am receiving from REST service build using apache CXF and jackson. I wrote custom ContextResolver and registered JavaTimeModule in Mapper object.
When I run the application, default constructor is called, that means it has been loaded, but getContext() method which returns ObjectMapper never gets called.
I have registered same ContextResolver in server and client side.
All dependencies are in place(jackson databind, core, annotation, datatype-jsr310).
I am able to fetch JSON response when I hit REST URI directly in browser. Issue comes when I call same URI annotated method from client code
Below is my client code.
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
#Provider //makes this bean a Provider
public class LocalDateObjectMapperContextResolver implements ContextResolver<ObjectMapper>{
private final ObjectMapper MAPPER;
public LocalDateObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
MAPPER.registerModule(new JavaTimeModule());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}
<jaxrs:client id="testclient"
serviceClass="package1.RESTService"
username="abc"
password="abc"
address="$serviceURL">
<jaxrs:features>
<bean class="org.apache.cxf.transport.common.gzip.GZIPFeature"/>
<cxf:logging/>
</jaxrs:features>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:client>
Same way, This contextResolver is registered on server side also under
<jaxrs:server>
.....
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:server>
Any reason why getContext is not called?
I also tried by extending ObjectMapper and registering javaTimeModule there, but dont know how to register customObjectMapper in Jackson flow. I just put default constructor for testing, And it does get called while application startup, but then again, No results, I still get same error.
Error: No suitable constructor found for type [simple type, class java.time.LocalDate]: can not instantiate from JSON object (need to add/enable type information?)
I had exactly the same problem #peeskillet describes in question comment.
I was using Jackson dependencies from version 2 and jackson-jaxrs from version 1.
All solved when moved all dependencies to version 2.
If you are using Maven you can add following two maven dependency.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
And Add following code snippet.
#Configuration
public class CxfConfig {
#Component
#javax.ws.rs.ext.Provider
public static class JacksonJaxbJsonProvider
extends com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider {
#Autowired
private ObjectMapper objectMapper;
#PostConstruct
public void init() {
objectMapper.registerModule(new Jdk8Module());
}
}
}
The following example works in a Java EE6 (Glassfish3) project of mine but failed after I switched to Java EE7 (Glassfish4). The HTTP request returns "500 Internal Error" without any message in the Glassfish server log. The project was setup using NetBeans8 as Maven Web Project and has no special dependencies, beans.xml or other configuration.
#RequestScoped
#Path("generic")
public class GenericResource {
#GET
#Path("ping")
#Produces(APPLICATION_JSON)
public List<String> debugPing() {
return Arrays.asList("pong");
}
And then:
$ curl -v http://localhost:8080/mavenproject2/webresources/generic/ping
> GET /mavenproject2/webresources/generic/ping HTTP/1.1
...
< HTTP/1.1 500 Internal Server Error
As as I understand, all REST handling is done by the Jackson reference implementation and that Jackson uses Jersey as underlaying JSON library. One of the two is supposed to have some kind of provider for all basic data types. Only custom made classes need a self written ObjectMapper. Are these concepts still correct?
It took me some hours but I finally solved this question myself.
First fact is that the Glassfish4 JAX-RS implementation "Jersey" as switched its underlying JSON library from Jackson 1.x to Eclipselink MOXy. The latter seems not be able to convert Lists, Arrays and arbitrary POJOs to JSON out of the box. Therefore I tried to force JAX-RS to use Jackson 2.x and disable MOXy.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
// This is Jackson 2.x, Jackson 1.x used org.codehaus.jackson!
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ApplicationPath("rest")
public class RestConfig extends Application {
private final static Logger log = LoggerFactory.getLogger(RestConfig.class);
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
log.info("Enabling custom Jackson JSON provider");
set.add(new JacksonJsonProvider() /* optionally add .configure(SerializationFeature.INDENT_OUTPUT, true) */);
return set;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> map = new HashMap<>();
log.info("Disabling MOXy JSON provider");
map.put("jersey.config.disableMoxyJson.server", true);
return map;
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
// ... add your own REST enabled classes here ...
return resources;
}
}
My pom.xml contains:
<dependency>
<!-- REST (Jackson as JSON mapper) -->
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<!-- REST (Jackson LowerCaseWithUnderscoresStrategy etc.) -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Hope this helps someone!
I'm throwing an exception MyCustomException from my application. (EJB Layer)
I've an exception mapper in web service layer which looks like following -
package net.webservices;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import net.common.MyCustomException;
#Provider
public class EJBExceptionMapper implements
ExceptionMapper<net.common.MyCustomException> {
public Response toResponse(MyCustomException exception) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
I've registered my mapper in web.xml of the web service layer as following -
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>net.webservices.EJBExceptionMapper</param-value>
</context-param>
The EJBExceptionMapper is not catching the MyCustomException. But instead its being caught by the catch block of the web service implementation.
What could be the problem?
Note: I don't want to register my ExceptionMapper manually using getProviderFactory().addExceptionMapper()
I don't know why your solution doesn't work (but I've never used RESTeasy, only Jersey). In any case, it would probably be simpler to extend WebApplicationException. That way, you don't have to register a provider:
public class MyCustomException extends WebApplicationException {
public MyCustomException() {
super(Response.status(Response.Status.BAD_REQUEST).build());
}
}
You need to throw exception (of type MyCustomException ) in the catch block and add a "Throws MyCustomException" to the method signature