Passing application context to JSR 303 ConstraintValidator - bean-validation

Some of the JSR-3validation I want to do is context sensitive.
For example, my web application has a "year" context for each request. When persisting a new bean, I want to be able to constrain the year property to be within year of the current request context.
#InCurrentContextYear
private LocalDate activationDate;
The current year is contained in a request object that I want to pass (in some way) into the constraint validator. For example:
public class InCurrentContextYearValidator
implements ConstraintValidator<InCurrentContextYear, LocalDate>{
#Override
public void initialize(InCurrentContextYear constraintAnnotation) {}
#Override
public boolean isValid(
LocalDate value,
ConstraintValidatorContext context,
Request request) {
return DateUtil.isInRequestYear(value, request)
}
}
Now I could do this with thread-local storage, but I'm wondering if there's a better way.
I do recognize that this is getting somewhat outside the domain of "bean validation" but it seems like using annotations for such things is a defensible approach. Annotations allow me to keep my beans as POJOs, and service as a nice way to document the rules of the properties.
So why should I need to have an entirely separate mechanism to do essentially the same thing as bean validation?

Related

Keep #RequestScoped context when receiving an async CDI event

I want to switch from firing CDI beans synchronous to asynchronous to be able to work stuff parallel.
event.fire(myObject) -> event.fireAsync(myObject)
As I currently use the request context to know what tenant the current process is about, I am confronted with the problem, that the #RequestScoped context is lost in a #ObservesAsync method. Therefor I don't know anymore to what db to persist etc. I could provide the necessary information in the cdi event object and recreate the requestcontext manually after recieving, but this would bloat my object and clutter my code.
Is there a way to simply keep the request context for a async cdi event?
Request scoped objects are not required to be thread-safe and usually are not. For that reason, request context is never automatically propagated across threads. For asynchronous events, indeed you should put all the necessary data into the event object.
You are of course not the first person to ask about this. There's been attempts to define an API/SPI for context propagation (MicroProfile Context Propagation, Jakarta Concurrency), including CDI request context, but they only work correctly in case of sequential processing with thread jumps (common in non-blocking/reactive programming). If you try to [ab]use context propagation for concurrent processing, you're signing up for troubles. For the latest discussion about this, see https://github.com/jakartaee/cdi/issues/474
I actually switched to using interfaces. This gives me more control and makes the code more understandable:
abstract class Publisher<T>{
#All
#Inject
private List<EventConsumer<T>> eventConsumers;
#Inject
private ContextInfo contextInfo;
#Inject
private MutableContextInfo mutableContextInfo;
...
public void publishEvent(T event){
String myContextInfo= contextInfo.getMyContextInfo();
eventConsumers.forEach(consumer -> notifyAsync(consumer, receivedObject, myContextInfo))
}
private void notifyAsync(EventConsumer<T> consumer, T object, String myContextInfo) {
Uni.createFrom()
.voidItem()
.subscribeAsCompletionStage()
.thenAccept(voidItem -> notifyConsumer(consumer, object, myContextInfo));
}
/**
* Method needs to be public to be able to activate request context on self invocation
*/
#ActivateRequestContext
public void notifyConsumer(EventConsumer<T> consumer, T object, String myContextInfo) {
mutableContextInfo.setMyContextInfo(myContextInfo);
try {
consumer.onEvent(object);
} catch (RuntimeException ex) {
log.error("Error while promoting object to eventconsumer", ex);
}
}
}

What are my options to force users of my API to implement a static method?

I am currently using the BLoC pattern to implement firebase/firestore functionality. It seems common to implement a model, an entity (which is basically the model hold by the database) and a repository which handels requests of the program.
To convert the model into the entity and vice versa the model requires a toEntity and fromEntity method. I want my class to look like this:
#immutable
abstract class DatabaseModel {
final id;
DatabaseEntity toEntity();
DatabaseModel({#required this.id});
static DatabaseModel fromEntity(DatabaseEntity entity);
}
However, this is not possible. Dart does not allow static methods without a function body as mentioned in Can one declare a static method within an abstract class, in Dart?, based on the fact that static methods aren't inherited anyways.
I thought about implementing the fromEntity method as a member method of DatabaseModel model with an additional static method which calls the member method, like this:
abstract class DatabaseModel {
final id;
DatabaseEntity toEntity();
DatabaseModel({#required this.id});
static DatabaseModel createFromEntity(DatabaseEntity entity, DatabaseModel instance) {
return instance.fromEntity(entity);
}
DatabaseModel fromEntity(DatabaseEntity entity);
}
But because of the following drawbacks I decided against using this method:
The inheriting class cannot longer be #immutable
Creating a new instance is not really straight forward since e.g. an object of class YouTubeComment would be instantiated like this - DatabaseModel.createFromEntity(entity, YouTubeComment()).
This requires the inheriting class to implement an empty constructor to create the instance.
Since it is not possible to implement a static method or an appropriate constructor, Is there a way to signal the developer that it is intended to implement a static method?

Using ninject with repositories, in mvc 5 ef 6

I set up my first mvc project with ninject, and im not sure if i understand this fully. I have the following simple setup.
I am using entity framework 6 as my orm.
Customer repository
public class CustomerRepository : ICustomerRepository
{
private readonly ApplicationDbContext db;
public CustomerRepository(ApplicationDbContext db)
{
this.db = db;
}
public IEnumerable<Customer> GetAll()
{
return this.db.Customers.ToList();
}
}
ICustomerRepository
public interface ICustomerRepository
{
IEnumerable<Customer> GetAll();
}
Ninject
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICustomerRepository>().To<CustomerRepository>().InRequestScope();
kernel.Bind<ICustomerDetailsRepository>().To<CustomerDetailsRepository>().InRequestScope();
kernel.Bind<ApplicationDbContext>().To<ApplicationDbContext>().InRequestScope();
}
Controller
public HomeController(ICustomerRepository customerRepository, ICustomerDetailsRepository customerDetailsRepository)
{
this.customerRepository = customerRepository;
this.customerDetailsRepository = customerDetailsRepository;
}
As you can see, i am calling both repositories from the same controller. Both repositories are setup exactly the same way.
Will both my repositories use the same dbcontext when requested, and will it automaticly be disposed afterwards??
This is not a real life setup. I made it really basic to try and understand how ninject work.
The fact that you configured your binding to be InRequestScope means that the requested object will be created the first time it's resolved after a new request starts, and for every subsequent resolutions of the same object within the same request, you will get the same instance.
Keep in mind that the lifetime of the request is determined by the lifetime of the HttpContext.Current object.
Just for reference:
As you can see here:
InThreadScope matches the lifetime of your object to the lifetime of System.Threading.Thread.CurrentThread
InSingletonScope matches the lifetime of your object to the lifetime of Ninject's Kernel
InTransientScope matches the lifetime of your object to the lifetime of null
Update
Regarding your comment about people implementing Dispose():
Even if you don't dispose your objects manually, when the dependency injection container disposes your object, it calls the dispose method if it implements IDisposable

Testing with autowired dependency like session in Validator

I have few custom annotations defined on fields of an object like:
public class Person{
#Accountname
String email;
}
Implementation class of #Accountname:
#Autowired ValidationService service;
#Autowired ClientSession clientSession;
#Override
public boolean isValid(final String email, final ConstraintValidatorContext ctx) {
if(!service.isAccountValid(email, clientSession)){
return false;
}
}
I am trying to write junits for these annotations.
#Test
public void validEmailTest()
{
person.setEmail("abc#xyz.com");
Set<ConstraintViolation<Person>> violations = validatorInstance.getValidator().validateProperty(person, "email");
Assert.assertEquals(1, violations.size());
}
But its throwing this error when I execute the test:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.clientSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:34)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.getTarget(CglibAopProxy.java:663)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:614)
at org.hibernate.validator.internal.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:308)
... 45 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
... 54 more
Any idea on how to write junits if a validator class has a dependency on services like session etc.?
This should be tested separately (units).
The real logic that validates is in your ValidationService, so test it there, in AccountnameValidator test only the logic that is in there, injecting your dependencies:
#Mock ValidationService service;
#Mock ClientSession clientSession;
#InjectMocks AccountnameValidator av = new AccountnameValidator()
//initialize mocks
//example test
when(service.isAccountValid(email, clientSession)).thenReturn(true);
boolean result = av.isValid(email, ctx);
assertTrue(result);
And finally if you want you can validate presence of the annotation in Person class on email field using reflection.

set database pulled application constants on startup

I have a lot of dropdown menus in my application. The contents of these dropdown menus is read from the database.
Currently, to avoid pulling these values back every-time I want to use them, I pull them back once when I first need them and store them in the session after that.
This is not good. I do want to be storing them in the session as there are a number of them, and I think it's a bit, well, clunky. I would like to ideally (assuming you don't disagree), store these 'constants' (they aren't traditional constants, but they won't change for the duration of the deployment), in the application scope, so they need only be pulled back from the database once per deployment.
What I'm wondering is, what is the way to store these dropdown values in the application context at startup?
There is an interface by the name of ServletContextListener which provides a hook into the initialization of your servlet context. The ServletContext is your application's context, ie. its configuration.
One way to do what you are describing is to implement this interface and register the ServletContextListener either in web.xml or by annotating the class with #WebListener.
In the contextInitialized() method, you would pull the constants, set them up in any way you needed to and then put them in the ServletContext as attributes
#WebListener
public static class MyListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
int someConstant = 42;
context.setAttribute("myConstant", someConstant);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
...
}
}
You can then access these constants anywhere you have access to the ServletContext.
In a Servlet or Filter, you can get it from the ServletRequest
HttpServletRequest request = ....;
request.getServletContext();
You also have access to it from the init() methods of Servlet and Filter if you want to add more attributes there.

Resources