Creating custom PostAuthorize method in Spring Security - java

I am trying to create a a custom method for use in Pre/Post Authorize calls like this:
public class CustomLSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler{
public CustomSecurityExpressionHandler(){
super();
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation){
CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
return root;
}
}
and
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private Object target;
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
public boolean testDecision(String test){
System.out.println("Printing:"+test+"\n");
return true;
}
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
public Object getFilterObject() {
return filterObject;
}
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
public Object getReturnObject() {
return returnObject;
}
void setThis(Object target) {
this.target = target;
}
public Object getThis() {
return target;
}
public boolean hasPermission(Object permission) {
try {
return super.hasPermission(null, null, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object permission) {
return super.hasPermission(null, null, permission);
}
#Override
public boolean hasPermission(Object targetId, String targetType, Object permission) {
try {
return super.hasPermission(targetId, targetType, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object targetId, String targetType, Object permission) {
return super.hasPermission(targetId, targetType, permission);
}
#Override
public boolean hasPermission(Object target, Object permission) {
try {
return super.hasPermission(target, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object target, Object permission) {
return super.hasPermission(target, permission);
}
}
As seen above I have added the new method testDecision(String), which I can successfully use in my preAuthorize call as below:
#PreAuthorize("testDecision('TestString')")
Event getEvent(int eventId);
But when I call it in the context of a PostAuthorize as:
#PostAuthorize("testDecision('TestString')")
Event getEvent(int eventId);
I get a ClassCastException:
SEVERE: Servlet.service() for servlet [Spring MVC Dispatcher Servlet] in context with path [/myapp] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: com.example.CustomSecurityExpressionRoot cannot be cast to org.springframework.security.access.expression.method.MethodSecurityExpressionRoot] with root cause
java.lang.ClassCastException: com.example.CustomSecurityExpressionRoot cannot be cast to org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
at org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler.setReturnObject(DefaultMethodSecurityExpressionHandler.java:156)
at org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice.after(ExpressionBasedPostInvocationAdvice.java:49)
at org.springframework.security.access.prepost.PostInvocationAdviceProvider.decide(PostInvocationAdviceProvider.java:38)
at org.springframework.security.access.intercept.AfterInvocationProviderManager.decide(AfterInvocationProviderManager.java:73)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterInvocation(AbstractSecurityInterceptor.java:282)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy15.getEvent(Unknown Source)
(..truncated..)
Anyone can help me figure out what I am doing wrong?

It seems you are on an older version of Spring Security. As of Spring Security 3.1.5+ SEC-2245 is fixed & you can create your own expression root and implement MethodSecurityExpressionOperations.

The class CustomSecurityExpressionRoot must extends MethodSecurityExpressionRoot!
(implementing MethodSecurityExpressionOperations) is not enough.
Unfortunately MethodSecurityExpressionRoot is a package protected class.
Therfore you need to put CustomSecurityExpressionRoot in the same package (org.springframework.security.access.expression.method)
or you use the following class as super class for your CustomSecurityExpressionRoot (that is what I do in my projects)
ExtensibleMethodSecurityExpressionRoot:
package org.springframework.security.access.expression.method;
import org.springframework.security.core.Authentication;
/** Makes the class {#link MethodSecurityExpressionRoot} public to other packages. */
public class ExtensibleMethodSecurityExpressionRoot extends MethodSecurityExpressionRoot {
/**
* Instantiates a new extensible method security expression root.
* #param a the Authentication
*/
public ExtensibleMethodSecurityExpressionRoot(final Authentication a) {
super(a);
}
}
My complete way is this:
ExtensibleMethodSecurityExpressionHandler to change the evaluation root context:
package org.springframework.security.access.expression.method;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.method.defaultexpression.DefaultMethodSecuritiyExpressionRootFactory;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
/**
* This class is the same like {#link MethodSecurityExpressionHandler} but its evaluation
* root context can be exchanged.
* To use an other evaluation root context, set an other {#link #methodSecurityExpRootFactory}.
*
*/
public class ExtensibleMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
/** The parameter name discoverer. */
private ParameterNameDiscoverer parameterNameDiscoverer;
/** The permission evaluator. */
private PermissionEvaluator permissionEvaluator;
/** The trust resolver. */
private AuthenticationTrustResolver trustResolver;
/** The expression parser. */
private ExpressionParser expressionParser;
/** The method security expression root factory. */
private MethodSecurityExpressionRootFactory<?> methodSecurityExpRootFactory;
/** The role hierarchy. */
private RoleHierarchy roleHierarchy;
/**
* Instantiates a new extensible method security expression handler.
*/
public ExtensibleMethodSecurityExpressionHandler() {
this.parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
this.permissionEvaluator = new DenyAllPermissionEvaluator();
this.trustResolver = new AuthenticationTrustResolverImpl();
this.expressionParser = new SpelExpressionParser();
this.methodSecurityExpRootFactory = new DefaultMethodSecuritiyExpressionRootFactory();
}
/**
* Uses a {#link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
* configures it with a {#link MethodSecurityExpressionRoot} instance as the expression root object.
*
* #param auth the auth
* #param mi the mi
* #return the evaluation context
*/
#Override
public EvaluationContext createEvaluationContext(final Authentication auth, final MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth,
mi,
this.parameterNameDiscoverer);
MethodSecurityExpressionRoot root = this.methodSecurityExpRootFactory.createMethodSecurityExpressionRoot(auth);
root.setTrustResolver(this.trustResolver);
root.setPermissionEvaluator(this.permissionEvaluator);
root.setRoleHierarchy(this.roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
/*
* (non-Javadoc)
*
* #see
* org.springframework.security.access.expression.method.MethodSecurityExpressionHandler#filter(java.lang.Object,
* org.springframework.expression.Expression, org.springframework.expression.EvaluationContext)
*/
#Override
#SuppressWarnings({ "unchecked", "rawtypes" })
public Object filter(final Object filterTarget, final Expression filterExpression, final EvaluationContext ctx) {
MethodSecurityExpressionRoot rootObject = (MethodSecurityExpressionRoot) ctx.getRootObject().getValue();
List retainList;
if (filterTarget instanceof Collection) {
Collection collection = (Collection) filterTarget;
retainList = new ArrayList(collection.size());
for (Object filterObject : (Collection) filterTarget) {
rootObject.setFilterObject(filterObject);
if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
retainList.add(filterObject);
}
}
collection.clear();
collection.addAll(retainList);
return filterTarget;
}
if (filterTarget.getClass().isArray()) {
Object[] array = (Object[]) filterTarget;
retainList = new ArrayList(array.length);
for (Object element : array) {
rootObject.setFilterObject(element);
if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
retainList.add(element);
}
}
Object[] filtered = (Object[]) Array.newInstance(filterTarget.getClass().getComponentType(),
retainList.size());
for (int i = 0; i < retainList.size(); i++) {
filtered[i] = retainList.get(i);
}
return filtered;
}
throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
}
/*
* (non-Javadoc)
*
* #see org.springframework.security.access.expression.method.MethodSecurityExpressionHandler#getExpressionParser()
*/
#Override
public ExpressionParser getExpressionParser() {
return this.expressionParser;
}
/**
* Sets the parameter name discoverer.
*
* #param parameterNameDiscoverer the new parameter name discoverer
*/
public void setParameterNameDiscoverer(final ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* Sets the permission evaluator.
*
* #param permissionEvaluator the new permission evaluator
*/
public void setPermissionEvaluator(final PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
/**
* Sets the trust resolver.
*
* #param trustResolver the new trust resolver
*/
public void setTrustResolver(final AuthenticationTrustResolver trustResolver) {
this.trustResolver = trustResolver;
}
/*
* (non-Javadoc)
*
* #see
* org.springframework.security.access.expression.method.MethodSecurityExpressionHandler#setReturnObject(java.lang
* .Object, org.springframework.expression.EvaluationContext)
*/
#Override
public void setReturnObject(final Object returnObject, final EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
/**
* Sets the role hierarchy.
*
* #param roleHierarchy the new role hierarchy
*/
public void setRoleHierarchy(final RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
/**
* Gets the method security expression root factory.
*
* #return the method security expression root factory
*/
public MethodSecurityExpressionRootFactory<?> getMethodSecurityExpressionRootFactory() {
return this.methodSecurityExpRootFactory;
}
/**
* Sets the method security expression root factory.
*
* #param methodSecurityExpressionRootFactory the new method security expression root factory
*/
public void setMethodSecurityExpressionRootFactory(
final MethodSecurityExpressionRootFactory<?> methodSecurityExpressionRootFactory) {
this.methodSecurityExpRootFactory = methodSecurityExpressionRootFactory;
}
/**
* Gets the parameter name discoverer.
*
* #return the parameter name discoverer
*/
public ParameterNameDiscoverer getParameterNameDiscoverer() {
return this.parameterNameDiscoverer;
}
/**
* Gets the permission evaluator.
*
* #return the permission evaluator
*/
public PermissionEvaluator getPermissionEvaluator() {
return this.permissionEvaluator;
}
/**
* Gets the trust resolver.
*
* #return the trust resolver
*/
public AuthenticationTrustResolver getTrustResolver() {
return this.trustResolver;
}
/**
* Gets the role hierarchy.
*
* #return the role hierarchy
*/
public RoleHierarchy getRoleHierarchy() {
return this.roleHierarchy;
}
/**
* Sets the expression parser.
*
* #param expressionParser the new expression parser
*/
public void setExpressionParser(final ExpressionParser expressionParser) {
this.expressionParser = expressionParser;
}
}
MethodSecurityExpressionRootFactory:
package org.springframework.security.access.expression.method;
import org.springframework.security.core.Authentication;
/**
* Factory Class/Template Class-Pattern: Template Class interface to create different expression root objects.
*
* #param <T> the {#link ExtensibleMethodSecurityExpressionRoot} created by this factory.
*/
public interface MethodSecurityExpressionRootFactory<T extends ExtensibleMethodSecurityExpressionRoot> {
/**
* Creates a new MethodSecurityExpressionRoot object.
*
* #param authentication the authentication
* #return the extensible method security expression root
*/
T createMethodSecurityExpressionRoot(final Authentication authentication);
}
DefaultMethodSecuritiyExpressionRootFactory: only needed if one want to use the ExtensibleMethodSecurityExpression handler without own extension
package org.springframework.security.access.expression.method.defaultexpression;
import org.springframework.security.access.expression.method.ExtensibleMethodSecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionRootFactory;
import org.springframework.security.core.Authentication;
/**
* Create the default {#link ExtensibleMethodSecurityExpressionRoot} expression root.
*/
public class DefaultMethodSecuritiyExpressionRootFactory implements
MethodSecurityExpressionRootFactory<ExtensibleMethodSecurityExpressionRoot> {
#Override
public ExtensibleMethodSecurityExpressionRoot createMethodSecurityExpressionRoot(final Authentication auth) {
return new ExtensibleMethodSecurityExpressionRoot(auth);
}
}
Example Customized Method Expression Root
package com.queomedia.vwcotool.infrastructure.security.spring;
import org.springframework.security.access.expression.method.ExtensibleMethodSecurityExpressionRoot;
import org.springframework.security.core.Authentication;
public class VwCoToolMethodSecurityExpressionRoot extends ExtensibleMethodSecurityExpressionRoot {
private Authentication a;
public MyMethodSecurityExpressionRoot(final Authentication a) {
super(a);
this.a = a;
}
public isXXX(final DomainObject x){
return x.getCreator().getName().equals(a.getPrincipal());
}
}

Related

Abstract class for builder pattern

I have existing model classes that always use builder pattern like this:
public class Model {
public static class Builder {
private boolean isValid;
private List<String> errorMessagesOrNull;
public Builder setIsValid(final boolean isValid) {
this.isValid = isValid;
return this;
}
public Builder setErrorMessages(final List<String> errorMessages) {
this.errorMessagesOrNull = errorMessages;
return this;
}
public List<String> getErrorMessages() {
return this.errorMessagesOrNull == null ? new ArrayList<>() : this.errorMessagesOrNull;
}
public Model Build() {
return new Model(this);
}
}
private boolean isValid;
private List<String> errorMessages;
private Model(final Builder builder) {
this.isValid = builder.isValid;
this.errorMessages = builder.getErrorMessages();
}
public boolean getIsValid() {
return isValid;
}
public List<String> getErrorMessages() {
return errorMessages;
}
}
As you see, the model classes always have isValid and errorMessages. I want to write an abstract class to minimize the repeated logic for those model classes.
So I came up like this abstract class:
public abstract class AbstractModel<T extends AbstractModel<T>> {
public static abstract class Builder<T> {
private boolean isValid;
private List<String> errorMessagesOrNull;
public Builder<T> setIsValid(final boolean isValid) {
this.isValid = isValid;
return this;
}
public Builder<T> setErrorMessages(final List<String> errorMessages) {
this.errorMessagesOrNull = errorMessages;
return this;
}
public List<String> getErrorMessages() {
return this.errorMessagesOrNull == null ? new ArrayList<>() : this.errorMessagesOrNull;
}
public abstract T Build();
}
private boolean isValid;
private List<String> errorMessages;
private AbstractModel(final Builder<T> builder) {
this.isValid = builder.isValid;
this.errorMessages = builder.getErrorMessages();
}
public boolean getIsValid() {
return isValid;
}
public List<String> getErrorMessages() {
return errorMessages;
}
}
But it's not really working as I intended. When I extends the abstract class:
public class Model extends AbstractModel<Model> {
// Empty here since all fields are extended
}
I cannot do something like:
Model model = new Model.Builder.setIsValid(true).Build();
I want the abstract class has Builder static class, so that I don't need to write the static class Builder every time.
Please advise.
You also need to implement the Builder.
public class Model extends AbstractModel<Model>{
private Model(final Builder builder) {
super(builder);
}
public static class Builder2 extends AbstractModel.Builder<Model> {
#Override
public Model Build() {
return new Model(this);
}
}
}
then it possible to call
Model model = new Model.Builder2().Build();
EDIT
Also, the constructor of AbstractBuilder also must be protected.
protected AbstractModel(final Builder<? extends Builder<T>> builder) {
this.isValid = builder.isValid;
this.errorMessages = builder.getErrorMessages();
}
I think that there is a huge flaw in your logic. The program itself doesn't really make any sense at all. Why do you construct a Model with the Builder class in the first place? I think it is better to show you how you should have written your program, instead of just "bodging" it together. Alright, let us start with the Model class.
Let's say the Model class cannot be constructed without a Builder. Would it then make sense to add the Builder class into the Model class? Short answer: no, it wouldn't. Instead, the Builder class should contain the Model class as a non-static internal class.
/**
* The {#code Builder} can construct new instances of the {#code Model} class.
*
* #see Model
*/
public class Builder
{
private final String[] log;
/**
* The {#code Model} class can do something. You can only construct it through a {#code Builder}.
*
* #see Builder
*/
public class Model
{
private final Builder builder;
/**
* Constructs a new {#code Model} with the specified argument.
*
* #param builder the {#code Builder} that constructed the model.
*/
public Model(final Builder builder)
{
this.builder = builder;
}
/**
* Returns the associated {#code Builder}.
*
* #return the builder that constructed the model.
*/
public Builder getBuilder()
{
return this.builder;
}
}
/**
* Constructs a new instance of the {#code Builder} class with the specified argument.
*
* #param log the log of the {#code Builder}.
*/
public Builder(final String... log)
{
this.log = log;
}
/**
* Tries to {#code build} a new instance of the {#code Model} class.
*
* #return the constructed {#code Model}.
*/
public Model build()
{
return new Model(this);
}
/**
* Returns the log of the {#code Builder}.
*
* #return an log.
*/
public String[] getLog()
{
return this.log;
}
/**
* Determines whether or not the {#code Builder} is valid.
*
* #return {#code true} when the specified {#code log} is not {#code null}; {#code false} otherwise.
*/
public boolean isValid()
{
return this.log != null;
}
}
No class other than the Builder can construct a Model. However, if you construct a new instance of the Builder class and get the result of invoking the build method, you'll have access to all public variables and methods.
If you know want to construct a Model, you can do that just like that:
Builder.Model model = new Builder().build();
If you don't want the Builder. prefix, just add an import statement that imports the Model class.
import organisation.projectname.pathToBuilder.Builder.Model;

Integrating Query dsl with Reactive Mongo Repository in spring 5

Spring 5 comes with a reactive implementation for spring data mongo , before the i was able to extend my mongo repository with QuerydslPredicateExecutor
#Repository
public interface AccountRepo extends MongoRepository<Account,String>
,QuerydslPredicateExecutor<Account>
if i try this
#Repository
public interface AccountRepo extends
ReactiveMongoRepository<Account,String>,
QuerydslPredicateExecutor<Account>
my application does not start it because :
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property exists found for type Account!
is there a way around this
here is the account class
package com.devop.models;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document
#Data
public class Account {
#Id
String id;
String accountName;
String accountNumber;
String schemeCode;
String openDate;
String accountCategory;
String accountCurrency = "Naira";
String accountSecondaryCategory;
String receiveSmsAlert;
String recieveEmailAlert;
String cifId;
}
here is the interface AccountRepo
package com.devop.mongoRepo;
import com.devop.models.Account;
import org.springframework.data.mongodb.repository.*;
import org.springframework.data.mongodb.repository.support*;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#Repository
public interface AccountRepo extends
ReactiveMongoRepository<Account,String>
,QuerydslPredicateExecutor<Account> {
Flux<Account> findAccountByAccountNameContains(String accountName);
Mono<Account> findAccountByAccountNumberEquals(String accountNumber);
}
You could use ReactiveQuerydslPredicateExecutor. See example for more information https://github.com/spring-projects/spring-data-examples/tree/master/mongodb/querydsl
I found a workaround solution using a Custom ReactiveMongoRepositoryFactoryBean and ReactiveMongoRepositoryFactory, it could help someone else that is struggling with that
First you need to use repositoryFactoryBeanClass in the reactive annotation.
#EnableReactiveMongoRepositories(repositoryFactoryBeanClass = CustomReactiveMongoRepositoryFactoryBean.class)
#Configuration
public class ReactiveMongoDbHack {
}
CustomReactiveMongoRepositoryFactoryBean class
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
public class CustomReactiveMongoRepositoryFactoryBean extends ReactiveMongoRepositoryFactoryBean {
public CustomReactiveMongoRepositoryFactoryBean(Class repositoryInterface) {
super(repositoryInterface);
}
#Override
protected RepositoryFactorySupport getFactoryInstance(ReactiveMongoOperations operations) {
return new CustomReactiveMongoRepositoryFactory(operations);
}
}
CustomReactiveMongoRepositoryFactory class
import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT;
public class CustomReactiveMongoRepositoryFactory extends ReactiveMongoRepositoryFactory {
private static MongoOperations operations;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
public CustomReactiveMongoRepositoryFactory(ReactiveMongoOperations operations) {
super(operations);
this.mappingContext = operations.getConverter().getMappingContext();
}
//TODO Must set MongoOperations(MongoTemplate)
public static void setOperations(MongoOperations operations) {
CustomReactiveMongoRepositoryFactory.operations = operations;
}
#Override
protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryComposition.RepositoryFragments fragments = RepositoryComposition.RepositoryFragments.empty();
boolean isQueryDslRepository = QUERY_DSL_PRESENT
&& QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());
if (isQueryDslRepository) {
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType(),
metadata);
fragments = fragments.append(RepositoryFragment.implemented(
getTargetRepositoryViaReflection(QuerydslMongoPredicateExecutor.class, entityInformation, operations)));
}
return fragments;
}
private <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> domainClass,
#Nullable RepositoryMetadata metadata) {
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
return MongoEntityInformationSupport.<T, ID> entityInformationFor(entity,
metadata != null ? metadata.getIdType() : null);
}
}
MongoEntityInformationSupport class copied from spring-data-mongodb
import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Support class responsible for creating {#link MongoEntityInformation} instances for a given
* {#link MongoPersistentEntity}.
*
* #author Christoph Strobl
* #author Mark Paluch
* #since 1.10
*/
final class MongoEntityInformationSupport {
private MongoEntityInformationSupport() {}
/**
* Factory method for creating {#link MongoEntityInformation}.
*
* #param entity must not be {#literal null}.
* #param idType can be {#literal null}.
* #return never {#literal null}.
*/
#SuppressWarnings("unchecked")
static <T, ID> MongoEntityInformation<T, ID> entityInformationFor(MongoPersistentEntity<?> entity,
#Nullable Class<?> idType) {
Assert.notNull(entity, "Entity must not be null!");
MappingMongoEntityInformation<T, ID> entityInformation = new MappingMongoEntityInformation<T, ID>(
(MongoPersistentEntity<T>) entity, (Class<ID>) idType);
return ClassUtils.isAssignable(Persistable.class, entity.getType())
? new PersistableMongoEntityInformation<>(entityInformation) : entityInformation;
}
}
PersistableMongoEntityInformation class also copied from spring-data-mongodb
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
/**
* {#link MongoEntityInformation} implementation wrapping an existing {#link MongoEntityInformation} considering
* {#link Persistable} types by delegating {#link #isNew(Object)} and {#link #getId(Object)} to the corresponding
* {#link Persistable#isNew()} and {#link Persistable#getId()} implementations.
*
* #author Christoph Strobl
* #author Oliver Gierke
* #since 1.10
*/
#RequiredArgsConstructor
class PersistableMongoEntityInformation<T, ID> implements MongoEntityInformation<T, ID> {
private final #NonNull MongoEntityInformation<T, ID> delegate;
/*
* (non-Javadoc)
* #see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
*/
#Override
public String getCollectionName() {
return delegate.getCollectionName();
}
/*
* (non-Javadoc)
* #see org.springframework.data.mongodb.repository.MongoEntityInformation#getIdAttribute()
*/
#Override
public String getIdAttribute() {
return delegate.getIdAttribute();
}
/*
* (non-Javadoc)
* #see org.springframework.data.repository.core.EntityInformation#isNew(java.lang.Object)
*/
#Override
#SuppressWarnings("unchecked")
public boolean isNew(T t) {
if (t instanceof Persistable) {
return ((Persistable<ID>) t).isNew();
}
return delegate.isNew(t);
}
/*
* (non-Javadoc)
* #see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object)
*/
#Override
#SuppressWarnings("unchecked")
public ID getId(T t) {
if (t instanceof Persistable) {
return ((Persistable<ID>) t).getId();
}
return delegate.getId(t);
}
/*
* (non-Javadoc)
* #see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
*/
#Override
public Class<ID> getIdType() {
return delegate.getIdType();
}
/*
* (non-Javadoc)
* #see org.springframework.data.repository.core.support.EntityMetadata#getJavaType()
*/
#Override
public Class<T> getJavaType() {
return delegate.getJavaType();
}
}
Then your Reactive Repository with Query DSL would work.
public interface AssetRepository extends ReactiveCrudRepository<Asset, String>, QuerydslPredicateExecutor<Asset> {
}
PS: It must need to set MongoOperations at CustomReactiveMongoRepositoryFactory.setOperations, what I did was to use the auto config classes(MongoAutoConfiguration and MongoDataAutoConfiguration) for Mongo then using #PostConstruct in a method to set it.

Null pointer exception while event occur

Exception when scheduled timer event occur.....
When i use SetEvent se = new SetEvent();
se.startArchiveEvent();
then i getting following exception....
Exception in thread "Timer-0" java.lang.NullPointerException
at com.metacube.metice.dao.AbstractDao.getSession(AbstractDao.java:47)
at com.metacube.metice.dao.AbstractDao.createEntityCriteria(AbstractDao.java:120)
at com.metacube.metice.dao.NoticeDaoImpl.archiveExpiredNotices(NoticeDaoImpl.java:162)
at com.metacube.metice.service.ArchiveEvent.run(ArchiveEvent.java:15)
at java.util.TimerThread.mainLoop(Unknown Source)
at java.util.TimerThread.run(Unknown Source)
following is the related classes
ArchiveEvent.java
package com.metacube.metice.service;
import java.util.TimerTask;
import org.springframework.beans.factory.annotation.Autowired;
import com.metacube.metice.dao.NoticeDao;
import com.metacube.metice.dao.NoticeDaoImpl;
public class ArchiveEvent extends TimerTask{
// #Autowired
NoticeDao noticeDao = new NoticeDaoImpl();
#Override
public void run() {
noticeDao.archiveExpiredNotices();
}
}
SetEvent.java
package com.metacube.metice.service;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletContextEvent;
public class SetEvent{
public void startArchiveEvent(){
TimerTask archiveNotices = new ArchiveEvent();
Timer timer = new Timer();
timer.scheduleAtFixedRate(archiveNotices, nextMidNight(), 1000*60*60*24);
System.out.println("Automated archive Notice event started[period 24 Hours]...");
}
private static Date nextMidNight(){
Calendar cal = Calendar.getInstance();
Calendar midNight = new GregorianCalendar(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH),cal.get(Calendar.DATE),16,43,00);
System.out.println(midNight.getTime());
return midNight.getTime();
}
}
AbstractDao.java
package com.metacube.metice.dao;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
/**
* #name AbstractDao
* #description : This is abstract generic class which performs all hibernate
* CRUD operation of persistant class into database
* #author Banwari
*
* #param <PK>
* : primary key field of persistent class
* #param <T>
* : name of pesrsitent class type
* #date 03-Dec-2015
*/
#Transactional
public abstract class AbstractDao<PK extends Serializable, T> {
/* Generic class type varible */
private final Class<T> persistentClass;
/* default constructor which makes generic class to specific persitent class */
#SuppressWarnings("unchecked")
public AbstractDao() {
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass()
.getGenericSuperclass()).getActualTypeArguments()[1];
}
/* SessionFactory class object */
#Autowired
private SessionFactory sessionFactory;
/**
* This method for getting hibernate Session object reference
*
* #return current session
*/
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
/**
* This method for getting persistent object from database for primary key
* primary key
*
* #param key
* : Primary Key type of persistent class
* #return persistent object
*/
#SuppressWarnings("unchecked")
public T getByKey(PK key) {
return (T) getSession().get(persistentClass, key);
}
/**
* This method save object into database
*
* #param entity
* : is object which have to be save into the database
* #throws HibernateException
*/
public void save(T entity) throws HibernateException {
getSession().save(entity);
}
/**
* This method persist object into database
*
* #param entity
* : is object which have to be persist into the database
* #throws HibernateException
*/
public void persist(T entity) throws HibernateException {
getSession().persist(entity);
}
/**
* This method saveOrUpdate object into database
*
* #param entity
* : is object which have to be save or update into the database
* #throws HibernateException
*/
public void saveOrUpdate(T entity) throws HibernateException {
getSession().saveOrUpdate(entity);
}
/**
* This method delete object from database
*
* #param entity
* : is object which have to be delete from the database
* #throws HibernateException
*/
public void delete(T entity) {
getSession().delete(entity);
}
/**
* This method update object into database
*
* #param entity
* : is object which have to be update into the database
* #throws HibernateException
*/
public void update(T entity) throws HibernateException{
getSession().update(entity);
}
/**
* This method for getting Criteria object refernce
* #return Criteria for the persistentClass
*/
protected Criteria createEntityCriteria() {
return getSession().createCriteria(persistentClass);
}
}

XML to Object Unmarshalling using JAXB

I need to Unmarshal XML to Java Object, I have tried with below code. Its create an Object but set all value as a null. code for same:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Dispatch implements java.io.Serializable {
private Integer dispatchId;
private Order order;
/**
* #return the dispatchId
*/
public Integer getDispatchId() {
return dispatchId;
}
/**
* #param dispatchId
* the dispatchId to set
*/
public void setDispatchId(Integer dispatchId) {
this.dispatchId = dispatchId;
}
/**
* #return the order
*/
public Order getOrder() {
return order;
}
/**
* #param order
* the order to set
*/
public void setOrder(Order order) {
this.order = order;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return ""+this.dispatchId;
}
}
I have Dispatch Class with other sub class, i need to convert XML to Java Object. Code for same:
Public class UnmarshalExample {
public static void main(String[] args) {
String xmlString = "<ns1:dispatch xmlns:ns1=\"http://service.order.com\"><ns1:dispatchId>1</ns1:dispatchId><ns1:order><ns1:totalAmount>1000.0</ns1:totalAmount></ns1:order></ns1:dispatch>";
Dispatch dispatch = (Dispatch) JAXB.unmarshal(
new StringReader(xmlString), Dispatch.class);
System.out.println(dispatch);
}
}
As a output it will return null.
Can any one tell me whats wrong thing in my code?
That is odd, I pasted you code and ran and it produces dispatchId 1:
Dispatch class:
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Dispatch implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer dispatchId;
/**
* #return the dispatchId
*/
public Integer getDispatchId() {
return dispatchId;
}
/**
* #param dispatchId
* the dispatchId to set
*/
public void setDispatchId(Integer dispatchId) {
this.dispatchId = dispatchId;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return ""+this.dispatchId;
}
}
Test class
import java.io.*;
import javax.xml.bind.JAXB;
public class Test {
public static void main(String[] args) {
String xmlString = "<ns1:dispatch xmlns:ns1=\"http://service.order.com\"><ns1:dispatchId>1</ns1:dispatchId><ns1:order><ns1:totalAmount>1000.0</ns1:totalAmount></ns1:order></ns1:dispatch>";
Dispatch dispatch = (Dispatch) JAXB.unmarshal(
new StringReader(xmlString), Dispatch.class);
System.out.println(dispatch);
}
}
output is
1
The order is of the type Order. We don't see any code of this class.
This is probably the cause of it as well. You may not provide decent data for the Order to be constructed.
To see how namespaces are used: http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html

Spring Data Rest -Disable self links(HAL) in Json

I am newbie to spring-data-rest. In my Application when i make a rest call i am getting json which consist on self-links and links to foreign key tables. But i want json instead on links. I generated my code using telosys tool generator. Here is my JSON what i am gettin when i made a REST call to the "merchandise" table in database.:
{
"id" : 1,
"rate": 300,
"type": "item",
"shortDescription": "test",
"longDescription": "test test",
"_links": {
"self": {
"href": "http://localhost:8080/sportsrest/merchandises/1"
},
"listOfMerchandiseAttribute": {
"href": "http://localhost:8080/sportsrest/merchandises/1/listOfMerchandiseAttribute"
},
}
}
But in instead of getting link of "listOfMerchandiseAttribute" i want to get JSON of listOfMerchandiseAttribute. listOfMerchandiseAttribute is my another table in database
That is i want my json like this:
{
"id": 1,
"rate": 300,
"type": "item",
"shortDescription": "test",
"longDescription": "test test",
"_links": {
"self": {
"href": "http://localhost:8080/sportsrest/merchandises/1"
},
"listOfMerchandiseAttribute": {
"id": 1,
"attributeName": "testname",
"attributeValue": 50
},
}
}
When i search on google i got some results and changed the ApplicationConfig.java file according to that but still i am getting links instead of JSON. Here is My ApplicationConfig file.
ApplicationConfig.java
/*
* Created on 19 Mar 2015 ( Time 14:41:07 )
* Generated by Telosys Tools Generator ( version 2.1.0 )
*/
package co.vitti.sports;
import org.springframework.context.MessageSource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.http.MediaType;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import co.vitti.sports.validator.BinaryValidator;
import co.vitti.sports.validator.CustomerPackageBalValidator;
import co.vitti.sports.validator.MerchandiseItemValidator;
import co.vitti.sports.validator.CustomerPackageValidator;
import co.vitti.sports.validator.InvoiceValidator;
import co.vitti.sports.validator.UserRoleValidator;
import co.vitti.sports.validator.SportValidator;
import co.vitti.sports.validator.MerchandiseTypeValidator;
import co.vitti.sports.validator.BookingValidator;
import co.vitti.sports.validator.TenantValidator;
import co.vitti.sports.validator.PaymentModeValidator;
import co.vitti.sports.validator.CourtValidator;
import co.vitti.sports.validator.MerchandisePackageValidator;
import co.vitti.sports.validator.CartValidator;
import co.vitti.sports.validator.MigrationValidator;
import co.vitti.sports.validator.TenantSportValidator;
import co.vitti.sports.repository.converter.TenantSportKeyConverter;
import co.vitti.sports.validator.TenantPaymentmodeValidator;
import co.vitti.sports.repository.converter.TenantPaymentmodeKeyConverter;
import co.vitti.sports.validator.MerchandiseAttributeValidator;
import co.vitti.sports.repository.converter.MerchandiseAttributeKeyConverter;
import co.vitti.sports.validator.CartItemValidator;
import co.vitti.sports.validator.MerchandiseValidator;
import co.vitti.sports.validator.UserValidator;
import co.vitti.sports.validator.TimeslotValidator;
import co.vitti.sports.validator.RoleValidator;
import org.springframework.core.convert.support.ConfigurableConversionService;
/**
* Application configuration.
* ( messages resources, validators)
* #author Telosys Tools Generator
*
*/
#Configuration
#ComponentScan(basePackageClasses = ApplicationConfig.class)
#EnableWebMvc
public class ApplicationConfig extends RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config){
config.setDefaultMediaType((MediaType) MediaType.parseMediaTypes("application/x-spring-data-verbose+json"));
}
#Bean
public DataSource dataSource(){
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
/**
* Message Ressource declaration.
* #return MessageRessource
*/
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("i18n/messages");
source.setUseCodeAsDefaultMessage(true);
return source;
}
/**
* Validator declaration for Binary
* #return the BinaryValidator
*/
#Bean
public BinaryValidator beforeCreateBinaryValidator() {
return new BinaryValidator();
}
/**
* Validator declaration for CustomerPackageBal
* #return the CustomerPackageBalValidator
*/
#Bean
public CustomerPackageBalValidator beforeCreateCustomerPackageBalValidator() {
return new CustomerPackageBalValidator();
}
/**
* Validator declaration for MerchandiseItem
* #return the MerchandiseItemValidator
*/
#Bean
public MerchandiseItemValidator beforeCreateMerchandiseItemValidator() {
return new MerchandiseItemValidator();
}
/**
* Validator declaration for CustomerPackage
* #return the CustomerPackageValidator
*/
#Bean
public CustomerPackageValidator beforeCreateCustomerPackageValidator() {
return new CustomerPackageValidator();
}
/**
* Validator declaration for Invoice
* #return the InvoiceValidator
*/
#Bean
public InvoiceValidator beforeCreateInvoiceValidator() {
return new InvoiceValidator();
}
/**
* Validator declaration for UserRole
* #return the UserRoleValidator
*/
#Bean
public UserRoleValidator beforeCreateUserRoleValidator() {
return new UserRoleValidator();
}
/**
* Validator declaration for Sport
* #return the SportValidator
*/
#Bean
public SportValidator beforeCreateSportValidator() {
return new SportValidator();
}
/**
* Validator declaration for MerchandiseType
* #return the MerchandiseTypeValidator
*/
#Bean
public MerchandiseTypeValidator beforeCreateMerchandiseTypeValidator() {
return new MerchandiseTypeValidator();
}
/**
* Validator declaration for Booking
* #return the BookingValidator
*/
#Bean
public BookingValidator beforeCreateBookingValidator() {
return new BookingValidator();
}
/**
* Validator declaration for Tenant
* #return the TenantValidator
*/
#Bean
public TenantValidator beforeCreateTenantValidator() {
return new TenantValidator();
}
/**
* Validator declaration for PaymentMode
* #return the PaymentModeValidator
*/
#Bean
public PaymentModeValidator beforeCreatePaymentModeValidator() {
return new PaymentModeValidator();
}
/**
* Validator declaration for Court
* #return the CourtValidator
*/
#Bean
public CourtValidator beforeCreateCourtValidator() {
return new CourtValidator();
}
/**
* Validator declaration for MerchandisePackage
* #return the MerchandisePackageValidator
*/
#Bean
public MerchandisePackageValidator beforeCreateMerchandisePackageValidator() {
return new MerchandisePackageValidator();
}
/**
* Validator declaration for Cart
* #return the CartValidator
*/
#Bean
public CartValidator beforeCreateCartValidator() {
return new CartValidator();
}
/**
* Validator declaration for Migration
* #return the MigrationValidator
*/
#Bean
public MigrationValidator beforeCreateMigrationValidator() {
return new MigrationValidator();
}
/**
* Validator declaration for TenantSport
* #return the TenantSportValidator
*/
#Bean
public TenantSportValidator beforeCreateTenantSportValidator() {
return new TenantSportValidator();
}
/**
* Validator declaration for TenantPaymentmode
* #return the TenantPaymentmodeValidator
*/
#Bean
public TenantPaymentmodeValidator beforeCreateTenantPaymentmodeValidator() {
return new TenantPaymentmodeValidator();
}
/**
* Validator declaration for MerchandiseAttribute
* #return the MerchandiseAttributeValidator
*/
#Bean
public MerchandiseAttributeValidator beforeCreateMerchandiseAttributeValidator() {
return new MerchandiseAttributeValidator();
}
/**
* Validator declaration for CartItem
* #return the CartItemValidator
*/
#Bean
public CartItemValidator beforeCreateCartItemValidator() {
return new CartItemValidator();
}
/**
* Validator declaration for Merchandise
* #return the MerchandiseValidator
*/
#Bean
public MerchandiseValidator beforeCreateMerchandiseValidator() {
return new MerchandiseValidator();
}
/**
* Validator declaration for User
* #return the UserValidator
*/
#Bean
public UserValidator beforeCreateUserValidator() {
return new UserValidator();
}
/**
* Validator declaration for Timeslot
* #return the TimeslotValidator
*/
#Bean
public TimeslotValidator beforeCreateTimeslotValidator() {
return new TimeslotValidator();
}
/**
* Validator declaration for Role
* #return the RoleValidator
*/
#Bean
public RoleValidator beforeCreateRoleValidator() {
return new RoleValidator();
}
/**
* Add all converters for composite keys
*/
#Override
protected void configureConversionService(ConfigurableConversionService conversionService) {
super.configureConversionService(conversionService);
conversionService.addConverter(this.tenantsportKeyConverter());
conversionService.addConverter(this.tenantpaymentmodeKeyConverter());
conversionService.addConverter(this.merchandiseattributeKeyConverter());
}
/**
* Converter for the composite key in the TenantSport entity
* #return the converter
*/
#Bean
public TenantSportKeyConverter tenantsportKeyConverter() {
return new TenantSportKeyConverter();
}
/**
* Converter for the composite key in the TenantPaymentmode entity
* #return the converter
*/
#Bean
public TenantPaymentmodeKeyConverter tenantpaymentmodeKeyConverter() {
return new TenantPaymentmodeKeyConverter();
}
/**
* Converter for the composite key in the MerchandiseAttribute entity
* #return the converter
*/
#Bean
public MerchandiseAttributeKeyConverter merchandiseattributeKeyConverter() {
return new MerchandiseAttributeKeyConverter();
}
// equivalents for <mvc:resources/> tags
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/app/**").addResourceLocations("/app/")
.setCachePeriod(31556926);
}
// equivalent for <mvc:default-servlet-handler/> tag
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Can anyone please help me to solve my issue and get the JSON instead on links.
Thanks in Advance.
You should make sure that listOfMerchandiseAttribute is a member of your domain class. Then default converting to JSON should have it there.
And Spring Data REST will use what-ever you current representation is and add Hypermedia.
See also Disable Hypertext Application Language (HAL) in JSON?
You could use Excerpts (especially made for this scenario). Because the Spring example is so eloquent and it's silly to just copy it here I'll just point it: https://docs.spring.io/spring-data/rest/docs/3.1.x/reference/html/#projections-excerpts.excerpting-commonly-accessed-data.
But just for the record and your convenience I'll paste the main parts of the spring example:
#Projection(name = "inlineAddress", types = { Person.class })
interface InlineAddress {
String getFirstName();
String getLastName();
Address getAddress();
}
see at Projection javadoc that types means The type the projection type is bound to.
The excerpt could be used this way:
#RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}
in order to get this:
{
"firstName" : "Frodo",
"lastName" : "Baggins",
"address" : {
"street": "Bag End",
"state": "The Shire",
"country": "Middle Earth"
},
"_links": ...
}
For you merchandise is the equivalent of Person and the listOfMerchandiseAttribute is the equivalent of address.
How to get rid of _links
Check my answer at Disable Hypertext Application Language (HAL) in JSON?.
Final notes
Spring is evolving so be aware that this works with at least:
spring-data-rest-webmvc 3.1.3.RELEASE
or, if you prefeer spring boot version:
spring-boot-starter-parent 2.1.1.RELEASE
A cleaner way to do it would be to set the following property to false in application.properties along with creating Projections
spring.hateoas.use-hal-as-default-json-media-type=false

Categories