Get the Code: https://github.com/iainporter/rest-java
In this post I will walk through the sign up and login services to demonstrate a vertical slice through the application. I will briefly discuss email verification and Social signup but won't delve into that until the next post. Session handling is also deferred until a later post.
User Sign up
To create a User we need to persist some basic information. At a bare minimum we need an email address and a password. Optionally we would like to capture perhaps a first and last name.
Once a user has signed up we need them to verify their email address. This is important foremost so that we can uniquely identify the user and provide the ability for them to safely reset their password, as well as other security features that depend on a valid email address being provided.
Most services will follow a similar development pattern to this one which is:
1. Define the domain model
2. Write the Repository interface to persist and retrieve the domain objects
3. Write a Service class to map the API object to the entity and CRUD logic to integrate with the Repository, as well as any other services in support of the particular API.
4. Define the API
5. Write the Resource Controller
The unit tests will test each layer of the application and the Integration Tests will test the full end to end calls through the application.
The User Domain Object
Entities are defined in the com.porterhead.rest.model package. Hibernate annotations are used to manage the joins and Spring Data to manage some of the basic functions common to all entities. All entities have an additional identifier which is a UUID. Some people have a preference to just use a sequential Long to identify an entity. In this application the primary keys are Longs but UUIDs are used in the API calls to persist and retrieve entities.
It comes down to whether you want your urls to look like this:
/user/1
or like this:
/user/38e1e415-acd2-42bb-8a3f-594f13e0d4ea
All entities that we wish to persist are annotated with:
@Entity
@Table(name="table name")
@Table(name="table name")
The class is added to the list of known entities in src/main/resources/META-INF/persistence.xml
The beans responsible for persistence are defined in src/main/resources/META-INF/spring/data-context.xml. There are bean profiles for dev, local, staging and production.
The User Repository
The repository tier uses Spring Data which is an excellent framework that takes care of most of the boilerplate headaches with CRUD functionality. The UserRepository class extends the JPARepository interface and has a few custom finder methods defined.
The User Service
The UserService interface defines a method for creating a new User:
public AuthenticatedUserToken createUser(CreateUserRequest request, Role role);
All entities remain within the boundary of the service layer and API objects pass between the Controllers and the services. This allows us to tightly define the contract between clients and the API. All request and responses are defined in the API package. It also prevents potential transaction and hibernate issues by accessing entity objects outside of a transaction. We can also pass API objects to multiple services without worrying about whether that service knows how to persist a particular entity.
It is worth stepping through the implementation of createUser as it will highlight several key features of how service methods are composed.
The method:
@Transactional public AuthenticatedUserToken createUser(CreateUserRequest request, Role role) { validate(request); User searchedForUser = userRepository.findByEmailAddress(request.getUser().getEmailAddress()); if (searchedForUser != null) { throw new DuplicateUserException(); } User newUser = createNewUser(request, role); AuthenticatedUserToken token = new AuthenticatedUserToken(newUser.getUuid().toString(), createAuthorizationToken(newUser).getToken()); userRepository.save(newUser); return token; }
First the API request is validated and if it fails an exception is thrown. This is a common pattern in service methods; to fail fast on an error. All Exceptions extend BaseWebApplicationException which defines an error payload for the response and an appropriate Http status code. In this particular case it will be a 400 error and in the case just below a 409 will be returned for the DuplicateUserException.
If no duplicate is found the a User entity is mapped from the request object and persisted.
Note that the password is saved as a one-way hash salted with the UUID of the User.
Finally an AuthenticatedUserToken object is mapped from the created entity and returned from the method.
See com.incept5.rest.service.UserServiceTest for the full suite of tests to run against this service.
If no duplicate is found the a User entity is mapped from the request object and persisted.
Note that the password is saved as a one-way hash salted with the UUID of the User.
Finally an AuthenticatedUserToken object is mapped from the created entity and returned from the method.
See com.incept5.rest.service.UserServiceTest for the full suite of tests to run against this service.
The User Resource Controller
Resource classes are responsible for marshalling and unmarshalling client requests and responses and coordinating calls to services. Resource classes do not access entities but instead use objects in the com.incept5.rest.api package to communicate with services.
There are some JAX-RS annotations to map urls to the correct resource:
The method for sign up is:
We specify that it is a POST and the @PermitAll means that the method is not access controlled. In a later post I will walk through customising a sign up for different types of users (customer, merchant, etc). The general sign up just assigns an authenticated role to a new user.
The logic flow is:
1. Call the User Service to handle the create request. If it is successful an API object will be returned with details of the new User.
2. Call the Email Verification Service to send a welcome email along with an embedded link to verify the email address. The details of this will be discussed in the next post. Note that this an asynchronous call and will return as soon as the Verification Service puts the request onto its queue.
3. A location uri for the new resource is created. This takes the form of user/<uuid>. We will see this in the next section when the API is tested with a curl request.
4. The response is gathered and returned. The Http response will be created (201) and the body will contain an AuthenticatedUserToken. Again I will go into more detail about that later.
The response:
That covers user sign up and login. In the next post I will discuss the email verification process.
There are some JAX-RS annotations to map urls to the correct resource:
@Path("/user")
@Component
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
@Component
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
The method for sign up is:
@PermitAll @POST public Response signupUser(CreateUserRequest request) { AuthenticatedUserToken token = userService.createUser(request, Role.authenticated); verificationTokenService.sendEmailRegistrationToken(token.getUserId()); URI location = uriInfo.getAbsolutePathBuilder().path(token.getUserId()).build(); return Response.created(location).entity(token).build(); }
We specify that it is a POST and the @PermitAll means that the method is not access controlled. In a later post I will walk through customising a sign up for different types of users (customer, merchant, etc). The general sign up just assigns an authenticated role to a new user.
The logic flow is:
1. Call the User Service to handle the create request. If it is successful an API object will be returned with details of the new User.
2. Call the Email Verification Service to send a welcome email along with an embedded link to verify the email address. The details of this will be discussed in the next post. Note that this an asynchronous call and will return as soon as the Verification Service puts the request onto its queue.
3. A location uri for the new resource is created. This takes the form of user/<uuid>. We will see this in the next section when the API is tested with a curl request.
4. The response is gathered and returned. The Http response will be created (201) and the body will contain an AuthenticatedUserToken. Again I will go into more detail about that later.
Testing the API for User sign up
There are in-memory unit tests in the com.porterhead.rest.resource package for testing the Resource classes but the surest way to test it is by running the code in a servlet container and firing real requests at it.
The integration tests are in the src/test/groovy directory. The tests are written in groovy and executed using the groovy rest client. UserIntegrationTest has tests for both failure and success scenarios.
Testing the API with curl
To test with curl we need to start up the war in a servlet container. The default profile in gradle.properties is dev but if we wanted to test against a real database and a real mail sender we could change that to local or staging and configure the necessary beans. I will discuss how to do that later.
Execute the following command:
gradle tomcatRun
Once the war has finished loading we can execute the following curl request:
curl -v -H "Content-Type: application/json" -X POST -d '{"user":{"firstName":"Foo","lastName":"Bar","emailAddress":"<your email address here>"}, "password":"password"}' http://localhost:8080/java-rest/user
You should see a response like this:
< HTTP/1.1 201 Created
< Server: Apache-Coyote/1.1
< Location: http://localhost:8080/java-rest/user/ff7ffcf0-cfe0-4c1e-9971-3b934612b154
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 12 Jan 2013 09:43:13 GMT
< * Connection #0 to host localhost left intact
{"userId":"ff7ffcf0-cfe0-4c1e-9971-3b934612b154","token":"5d1cf32e-e468-4a2c-92b6-9b0c377145f9"}
< Server: Apache-Coyote/1.1
< Location: http://localhost:8080/java-rest/user/ff7ffcf0-cfe0-4c1e-9971-3b934612b154
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 12 Jan 2013 09:43:13 GMT
< * Connection #0 to host localhost left intact
{"userId":"ff7ffcf0-cfe0-4c1e-9971-3b934612b154","token":"5d1cf32e-e468-4a2c-92b6-9b0c377145f9"}
I'll discuss how to use the response in the next post.
Now that we know sign up works we can test out login.
Try the following curl statement:
curl -v -H "Content-Type: application/json" -X POST -d '{"username":"<your email address here>","password":"password"}' http://localhost:8080/java-rest/user/login
The response:
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Location: http://localhost:8080/java-rest/user/7ef646ba-5e03-4d59-90a9-822daecf3428
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 12 Jan 2013 09:52:21 GMT
<
* Connection #0 to host localhost left intact
{"userId":"7ef646ba-5e03-4d59-90a9-822daecf3428","token":"164797a0-623f-427d-a469-b348e7e2bc59"}
< Server: Apache-Coyote/1.1
< Content-Location: http://localhost:8080/java-rest/user/7ef646ba-5e03-4d59-90a9-822daecf3428
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 12 Jan 2013 09:52:21 GMT
<
* Connection #0 to host localhost left intact
{"userId":"7ef646ba-5e03-4d59-90a9-822daecf3428","token":"164797a0-623f-427d-a469-b348e7e2bc59"}
That covers user sign up and login. In the next post I will discuss the email verification process.
Any idea why I get a internal 500 error when testing with curl ?
ReplyDeleteWhat gradle command did you use? And what was the curl statement that failed?
DeleteSorry for the slow response. I'm running the example you put on this page :
ReplyDeleteN:\GitHub> curl -v -H "Content-Type: application/json" -X POST -d '{"user":{"fir
stName":"Foo","lastName":"Bar","emailAddress":""}, "pas
sword":"password"}' http://localhost:8080/java-rest/user
* Adding handle: conn: 0xa50368
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0xa50368) send_pipe: 1, recv_pipe: 0
* Could not resolve host: email
* Closing connection 0
curl: (6) Could not resolve host: email
* Adding handle: conn: 0xa50368
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 1 (0xa50368) send_pipe: 1, recv_pipe: 0
* Could not resolve host: address
* Closing connection 1
curl: (6) Could not resolve host: address
curl: (3) [globbing] unmatched close brace/bracket at pos 6
* Adding handle: conn: 0xa5d3e0
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 2 (0xa5d3e0) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 8080 (#2)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#2)
> POST /java-rest/user HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
> Content-Type: application/json
> Content-Length: 52
>
* upload completely sent off: 52 out of 52 bytes
< HTTP/1.1 500 Internal Server Error
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Type: text/html;charset=utf-8
< Content-Length: 1063
< Date: Fri, 18 Apr 2014 17:19:56 GMT
< Connection: close
or rather :
Deletecurl -v -H "Content-Type: application/json" -X POST -d '{"user":{"firstName":"Foo","lastName":"Bar","emailAddress":"foobar@foobar.com"}, "password":"password"}' http://localhost:8080/java-rest/user
It looks like the error is caused by this on the server side :
ReplyDeleteorg.codehaus.jackson.JsonParseException: Unexpected character ('u' (code 117)): was expecting double-quote to start field name
Not sure why curl is mangling the JSON data being sent ? (quote problem it looks like)
Ok, on windows, you have to replace the outside single quotes with double quotes, and escalte the inside double quotes , like this :
ReplyDeletecurl -v -H "Content-Type: appli
cation/json" -X POST -d "{\"user\":{\"firstName\":\"Foo\",\"lastName\":\"Bar\",\
"emailAddress\":\"foobar@foobar.com\"}, \"password\":\"password\"}" http://l
ocalhost:8080/java-rest/user
Iain... thanks! Really amazing post! Huge contribution!
ReplyDeletecan you provide source code with maven.
ReplyDeleteI am execute gradle command gardle eclipse, this command execute successfully and also create eclipse project but when I import project in eclipse and run on server it will not run.
I assume you have installed the gradle plugin for eclipse - http://www.gradle.org/docs/current/userguide/eclipse_plugin.html
ReplyDeleteIf you have you should be able to execute any gradle command within eclipse that you can run from the command line.
What gradle command is failing for you?
This comment has been removed by the author.
ReplyDeleteThanks for quick replay
ReplyDeletebut when I import in eclipse got following exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/data-context.xml]: Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [META-INF/spring/data-context.xml]: Cannot resolve reference to bean 'database' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'database' is defined
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:299) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:295) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-4.0.0.M3.jar:4.0.0.M3]
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:741) ~[spring-context-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464) ~[spring-context-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384) ~[spring-web-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283) ~[spring-web-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111) ~[spring-web-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791) ~[na:na]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285) ~[na:na]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) ~[na:na]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) ~[na:na]
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) ~[na:na]
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:618) ~[na:na]
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:650) ~[na:7.0.29.A]
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1582) ~[na:7.0.29.A]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) ~[na:1.7.0_25]
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) ~[na:1.7.0_25]
at java.util.concurrent.FutureTask.run(FutureTask.java:166) ~[na:1.7.0_25]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ~[na:1.7.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ~[na:1.7.0_25]
at java.lang.Thread.run(Thread.java:724) ~[na:1.7.0_25]
That is due to no spring context being set.
ReplyDeleteDo a pull on the code in github and run again
Hi lain,
ReplyDeletethis is an excellent series of articles about the Restful in java. I have a question: why you assigned multiple sessions to each user in the code, but as shown in the table diagram each user was associated with zero or one session. I am a little bit confused about this issue.
Thanks
That was an error in the diagram. Thanks for pointing it out. I have updated it.
ReplyDeleteHi lain,
DeleteThanks for your reply. I have another three questions related to the session:
(1) The first is why don't you just create one session token for each user and update the lastUpdated field of the session token instead of creating a new session token every time a user login. Based on the lastUpdated field, you can easily expire the session if it has not been updated for a period of time.
(2) Besides, In the authorize method of the SessionTokenAuthorizationService class, you loop through all the session tokens that a user has, which is a little counterintuitive to me? I think each time a user has only one valid session, right? Could you please explain a little bit more on this to me?
(3) the last question is how do you utilize the SessionReaper class to delete expired sessions? Is there any mechanism to automatically to do this?
These might be rookie questions. But I really need help to clear these concepts up.
very appreciated
Thanks for the code review.
DeleteInitially I set up a User with multiple session tokens to support concurrent logins for the same account.
In retrospect if I was to code it again I would implement something similar to OAuth2 and use the same token for all logins for that account with a useful expiration policy.
If I get some time I'll probably rewrite it to do that.
Having said that I have not used the approach in this code base for a couple of years.
I use OAuth2
As for the SessionReaper I use a useful utility in spring-task. if you look in root-context.xml you will see the bean defined:
<bean id="sessionReaperCronJob" class="com.porterhead.rest.user.SessionReaper"/>
<task:scheduled-tasks scheduler="sessionReaperScheduler">
<task:scheduled ref="sessionReaperCronJob" method="cleanUpExpiredSessions" cron="0 0/1 * * * *"/>
</task:scheduled-tasks>
<task:scheduler id="sessionReaperScheduler" pool-size="1"/>
I have since replaced session tokens with a more OAuth-like AuthenticationToken approach.
DeleteSee the git commit for details.
I'm trying to modify the object returned on login, so in UserResource :
ReplyDeleteprivate Response getLoginResponse(AuthenticatedUserToken token) {...}
I've modified the AuthenticatedUserToken object but still, the response always just contains userID and sessionToken. Any idea ? is there anything that filters the response ?
Figured it out. I had to put "getter" methods in AuthenticatedUserToken for the new members. (not sure how that magically works but oh well)
DeleteHi. Thanks for these nice posts.
ReplyDeleteI have a question : when signing up a user, a token is created and persisted with the user when we do "userRepository.save(newUser);".
When login, we generate a new token, but I cannot see it being persisted. Why don't we? Am I missing something?
Also on login, should we check if user isVerified too?
Thanks
Good catch. That was a bug. The user if now saved if a new token is issued.
DeleteThe verification check for a user is meant more as means for restricting access to site resources if the user is not verified.
You would not want to prevent them logging in if they have not verified.
Thanks for such a quick response!
DeleteThis contribution is awesome, thanks a lot for sharing this project.
ReplyDeleteI didn't find the schema for importing the structure of the required MySQL tables (rest_user, rest_verification_token, etc.) at https://github.com/iainporter/rest-java. I would have expected to find in the schema folder along with the other *.sql files.
I've got so far a Jersey + spring-data + Hibernate + MySQL REST project I want to protect. Would you recommend me to focus on your GitHub project "rest-java" or "oauth2-provider" ? Which one are you currently the most working with?
The tables will be generated on startup
DeleteI would recommend the OAuth2 approach to security now as it has become the standard.
The oauth2-provider project uses MongoDB as the persistence repository for no other reason than I wanted to show use of something other than the usual hibernate/SQL approach.
If you want to use SQL for the OAuth objects then Spring has some handy defaults you can integrate.
Could you please mentor us about these handy methods that we can apply to your oauth2-provider project quickly?
DeleteThank you
you can replace the custom mongo implementation of TokenStore with the Spring JDBC version
Deletein token-store.xml
replace the token store bean reference :
<bean id="tokenStore" class="com.porterhead.oauth2.mongodb.OAuth2RepositoryTokenStore">
with:
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="tokenStoreDatasource"/>
</bean>
Mr Porter, Thank you for your response. When i applying your solution, occured an error. Have you got an idea about the issue?
ReplyDeleteBrgds
SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.authentication.dao.DaoAuthenticationProvider#0': Cannot resolve reference to bean 'userService' while setting bean property 'userDetailsService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.oauth2.provider.token.DefaultTokenServices com.trafficalarm.rest.configuration.UserConfiguration.tokenServices; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tokenServices' defined in class path resource [spring/oauth/oauth2-configuration.xml]: Cannot resolve reference to bean 'tokenStore' while setting bean property 'tokenStore'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tokenStore' defined in class path resource [spring/oauth/token-store.xml]: Cannot resolve reference to bean 'tokenStoreDatasource' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'tokenStoreDatasource' is defined
You have to provide this bean yourself:
Delete<constructor-arg ref="tokenStoreDatasource"/>
in the usual Spring way of configuring a JDBC datasource
If you need a script to create the schema you can find it here: https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2/src/test/resources
I got it. Thank you
DeleteI try to apply your solution into my existing project. I stucked with the error that i shared below. And i can not forward. However, I am sure that all complaint classes are actually exists under right package. Seems to be that i am missing something.
ReplyDeleteCould you plz lighten me? If you wanna see the project. Here it is=> https://github.com/webyildirim/trafikhaberci
thx
tomcat error log start such as:
SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.HierarchicalJsr250Voter] for bean with name 'roleVoter' defined in class path resource [spring/security/security-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.HierarchicalJsr250Voter
Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.HierarchicalJsr250Voter] for bean with name 'roleVoter' defined in class path resource [spring/security/security-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.HierarchicalJsr250Voter
Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.filter.spring.SpringCrossOriginResourceSharingFilter] for bean with name 'corsFilter' defined in class path resource [spring/oauth/oauth2-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.filter.spring.SpringCrossOriginResourceSharingFilter
Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.OAuthRestEntryPoint] for bean with name 'oauthRestEntryPoint' defined in class path resource [spring/oauth/oauth2-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.OAuthRestEntryPoint
May it be related with jdk1.7 usage?
DeleteThis comment has been removed by a blog administrator.
ReplyDelete