Spring data rest. How to take repository class through request URI - spring-data-rest

Can anyone help me please.
I have a few entities and repositories inside my app which is based on spring data rest.
Right now I wrote my own HandlerInterceptor implementation to do preHandling each request and I have to know which repository will use for each http request.
Ofc I can make separate Interceptor for each repository but this solution is not flexible Any ideas?
Thx for advance

Repositories are resolved in spring data-rest using the Repositories class.
The repositories class exposes a helper method (getRepositoryFor(Class<?>)) for finding the repository for a given class.
You can use the following snippet for finding a repository for a given class in your interceptor:
Repositories repositories = new Repositories(appContext);
repositories.getRepositoryFor(entityObject.getClass());
A more elegant solution would be to take advantage of the built-in spring-data-rest repository lookup implementation with a custom controller(RootResourceInformationHandlerMethodArgumentResolver)
For this you just need to add a RootResourceInformation parameter to a RepositoryRestController endpoint method.
#RepositoryRestController
#RequestMapping("/customName")
public class RepositoryExportController {
#Autowired
private ApplicationContext appContext;
#RequestMapping(method = RequestMethod.GET, value = "{repository}",
produces = MediaTypes.HAL_JSON_VALUE)
#ResponseBody
public Resources<Resource<?>> export(RootResourceInformation resourceInformation, ...) {
Repositories repositories = new Repositories(appContext);
CrudRepository repo=(CrudRepository)repositories.getRepositoryFor(resourceInformation.getDomainType());
repo.findAll();
...other logic....
}

Related

Registering a service that has a dependency injection

I'd like to have the following structure
A Repository has DbContext injecting into it.
A Controller has Repository injecting into it.
So a controller uses the repo and the repo already has context in it.
So I register the context in startup.cs
services.AddDbContext<DbContext> x => x.UseSqlServer(Configuration.GetValue<string>("AppSettings:DBCS")));
Register the repo right below the above line
services.AddScoped(x => new DB.Tasker.LogEntryRepo());
and then use the DIed repo inside the controller
private LogEntryRepo _repo;
public LogsController(LogEntryRepo repo)
{
_repo = repo;
}
Problem is compiler complains quite predictably complains about this line services.AddScoped(x => new DB.Tasker.LogEntryRepo()); as the repo obviously doesn't have a parameterless controller.
What's the proper way to accomplish what I am trying to accomplish?
You register them in Startup.cs
services.AddScoped<IMyRepository, MyRepository>();
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetValue<string>("AppSettings:DBCS")));
DI the DbContext into your Repository
public MyRepository(MyDbContext myDbContext)
{
_dbContext = myDbContext;
}
And DI the Repository into your Controller. Notice we are using IMyRepository, not MyRepository. We tell it which repository to DI when we register it in Startup.cs. Be sure that your repository implements your interface.
public MyController(IMyRepository myRepository)
{
_repository = myRepository;
}
Then in your controller you can do things like
_repository.DoSomeDatabaseStuff(param1, param2);
If you don't take advantage of the Interface, then you're ignoring the power of DI. This allows you to also do things like create a UnitTest where you mock a repository.
services.AddScoped<IMyRepository, MyMockRepository>();
In MyMockRepository, you may fake the data, or ensure your database connections and queries work, but simply fail to commit or automatically rollback changes.

How to write global filter for mobilefirst 8.0 java adapter

I need a global filter on mobilefirst 8.0 java adapter. Am tring to write ContainerRequestFilter filter. But i need some mobilefirst data in there. ConfigurationApi,AdaptersAPI. How can i get it in this context ? Or there have other way to call some code with all java adapter methods ?
You can write a ContainerRequestFilter and use it with an adapter. All you need to do is add to the getClasses() method in the adapter application class (unless it's in the same package as the application class, in this case it will happen automatically).
You can use the #Context annotation in filters to inject any MFP API you need, just like in your resource classes.
Here is a working example:
public class MyRequestFilter implements ContainerRequestFilter {
#Context
ConfigurationAPI configApi;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
doStuff();
}
}
Thanks all! Question was solved. Helped this page
https://jax-rs-spec.java.net/nonav/2.0-rev-a/apidocs/index.html about #NameBinding annotation.
and additional #Produce annotation on Filter class.

Swagger overrides Path-Annotations

I just got swagger to produces a valid swagger.json.
I configured swagger by using the Application-config method.
However, as soon as I override the getClasses-Method to add the swagger resouces, my JAX-RS Path-annotated classes stop working.
The method looks like this
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(io.swagger.jaxrs.listing.ApiListingResource.class);
resources.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
return resources;
}
and invoking super.getClasses() returns am empty set.
I got too many resources in my project, which I would not like to add manually.
Is there any way swagger does not mess with my previous configuration?
Thank you!
You can use a javax.ws.rs.core.Feature. Just register the classes through the callback's FeatureContext. Annotating the feature with #Provider will have it registered through the scanning.
#Provider
public class SwaggerFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(ApiListingResource.class);
context.register(SwaggerSerializers.class);
return true;
}
}
But note that if the application is already registering the resources and providers by class-path scanning, I imagine it should also pick up the Swagger classes, as they are annotated with #Path[1] and #Provider[2]. Those are the annotations the class-path scan looks for.
I haven't tried it myself (I stopped using class-path scanning[3]), but have you tried just not registering them at all? In theory the class-path scan should pick it up.
1. io.swagger.jaxrs.listing.ApiListingResource
2. io.swagger.jaxrs.listing.SwaggerSerializers
3. When to Use JAX-RS Class-path Scanning Mechanism

How to add Custom Interceptor in Spring data rest (spring-data-rest-webmvc 2.3.0)

I am working on the spring data rest services & facing some issue in the custom interceptors. Earlier I used spring-data-rest-webmvc 2.2.0 & added interceptor in following way.
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new MyInterceptor() });
return mapping;
}
It worked perfectly fine for me. But when i upgraded to spring-data-rest-webmvc 2.3.0 version, I noticed that handlerMapping is hidden behind DelegatingHandlerMapping. Hence I tried to add interceptor in following way.
In one of my config class I have extended RepositoryRestMvcConfiguration class & override its method.
public class AppConfig extends RepositoryRestMvcConfiguration {
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping()
{
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(super.resourceMappings(), super.config());
repositoryMapping.setInterceptors(new Object[] { new MyInterceptor()});
repositoryMapping.setJpaHelper(super.jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(super.config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
But after adding this some of my repository operations (findAll() operation on repository) start failing. If I removed this interceptors those operations worked fine. (In this interceptor I am just authenticate the user.)
Hence I am unable to understand problem here. Am I adding the interceptor in wrong way? Is there any other way to add the interceptor?
You should not use repositoryMapping.setInterceptors() - it destoys the internal interceptors Spring placed there, and that's probably the reason some methods stopped working.
I suggest you override jpaHelper() method and put your interceptors into the JpaHelper object in RepositoryRestMvcConfiguration. Spring will should them to the global interceptor list.
But, again, if all you need is authentication, why not use a Spring Security filter?
EDIT: the solution above works only for RepositoryRestHandlerMapping, not for BasePathAwareHandlerMapping.
I suggest you declare a custom MappedInterceptor bean somewhere:
#Bean
public MappedInterceptor myMappedInterceptor() {
return new MappedInterceptor(new String[]{"/**"}, new MyInterceptor());
}
From my understanding of the source code Spring should automatically add this interceptor to all request handlers.

Annotation JCacheResult is not working in Infinispan and Glassfish 3.1.1

I am trying to integrate JCache from Infinispan into my existing EJB project.
I have added Infinispan 5.0.1 CDI and Core packages to Maven pom.
Added Infinispan Interceptors in beans.xml and able to use the CacheResult annotation.
I am deploying the app in Glassfish 3.1.1. I have checked the Weld jar version, which is
Module : org.jboss.weld.osgi-bundle:1.1.1.Final
In the runtime, the CacheResult Method interceptor is not caching the method result and its always called.
My code looks like this,
public void cacheTest() {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
EmbeddedCacheManager manager = createCacheConfig();
Set<String> cacheList = manager.getCacheNames(); // new
// DefaultCacheManager().getCacheNames();
for (String cache : cacheList) {
System.out.println("Cache name " + cache);
}
defaultCache = manager.getCache("test-cache");
defaultCache.put("aa", "AA");
String user = "User";
greet(user);
Set<String> keys = defaultCache.keySet();
for (String key : keys) {
System.out.println("Key is -" + key + "Value is -"
+ defaultCache.get(key));
}
}
#CacheResult(cacheName = "test-cache")
public String greet(#CacheKeyParam String user) {
user += "Hello";
return user;
}
public EmbeddedCacheManager createCacheConfig() {
EmbeddedCacheManager manager = new DefaultCacheManager();
Configuration conf = new Configuration();
conf.fluent().eviction().strategy(EvictionStrategy.FIFO).maxEntries(10)
.expiration().maxIdle(1200000L).build();
conf.fluent().clustering().sync();
manager.start();
manager.defineConfiguration("test-cache", conf);
return manager;
}
greet() method gets called but it will never add the method result to the test-cache. I feel am I missing some configuration or...I dont know. Please help me on this.
when I Inject the classes, they wont get constructed and they are null. The code is like this,
#Inject
private static org.infinispan.Cache<String, String> defaultCache;
#Inject
private static EmbeddedCacheManager defaultCacheManager;
These gets executed without any error, but they wont get initialized.
I have no clue...But I am able to inject other EJBs with in this class easily. By the way I am trying to add Jcache functionality in one of EJBs.
I would appreciate your help...
Thanks...
Raj S
Your greet method is in a CDI bean or in an EJB, right?
The cache defined in JCache annotations is looked up in the cache manager provided by Infinispan CDI. This cache manager contains the cache configured with CDI (for more information, see https://docs.jboss.org/author/display/ISPN/CDI+Support). In your example the test-cache configuration will have no effect.
Another thing, if your cacheTest and greet methods are in the same class the greet method cannot be intercepted. If that's not the case maybe you're hitting GLASSFISH-17184.
For the Cache and EmbeddedCacheManager injections the problem is that you're doing a static injection, not supported by CDI. From CDI (JSR-299) specification
An injected field is a non-static, non-final field of a bean class, or of any Java EE component class supporting injection.
If your method result is not cached, I think it's because the CacheResultInterceptor is not called. I've just made the test with the Infinispan CDI quickstart. If the interceptors are in a lib they are not enabled. I think it's a bug in Glassfish.
Btw, you can see an example of code in the Infinispan CDI quickstart here.
Hope this help!