Background: I asked another question (here: Performance degradation after moving to jersey 2) about jersey performance. I also opened an issue in jersey Jira. Apparently, there is a known performance problem with sub-resources in jersey 2.
My data model based on entity A, with several sub-entities. However, the ID of the sub entities is unique, and the server allows to access them directly. For example,
/server/As/{a_id}/Bs/{b_id} == /server/Bs/{b_id}
We're using sub-resources for that, so the same resource (B) is both a spring component and a member of A resource. All the resources are spring beans, so both Bs are the same instance.
Now I'm trying to workaround this problem by not using sub-resources at all. I found out that #Path does not support multiple paths. Is there any idea how to solve it?
I tried the following, does it make sense? Can you offer alternatives? I'm asking because I'll have to do the same trick many times (for entities C, D, E etc. and maybe additional level of resources)
First, I removed B reference from A resource class, then:
public abstract class AbstractB {
#GET
#Path({b_id})
#Produce(MediaType.APPLICATION_JSON)
public Response getB(#PathParam("b_id") String bId) {
...
}
...
}
#Component
#Path ("As/{a_id}/Bs")
public class B extends AbstractB{/* empty */}
#Component
#Path ("Bs")
public class AsB extends AbstractB{/* empty */}
Edit: (by peeskillet) - Link to issue
Related
I have an application with 2 entities : A and B.
Each of these entities contain a "Workspace" :
#Entity
class A {
private Workspace workspace;
}
#Entity
class B {
private Workspace workspace;
}
For each request that comes in like a GET <api>/resourceA or a POST <api>/resourceB/{id}/createSomething, we need to check that the caller (that is logged in through Keycloak and has a JWT) has access to the Workspace that A is part of (or B, in the latter case). It's all "dynamic" as it's based on the ID of the specific object A.
So, for each endpoint (or each Resource, might be all that's needed), we need to call a method that is specific to that endpoint/Resource. We cannot do this with just "Roles", because we need to have that specific check on the IDs, like :
A.getWorkspace().isAccessibleBy(theCallingUser)
I know Spring Security has specific methods like "hasRole()" and you can call specific code in the #PreAuthorize annotations, but I haven't seen the same in Quarkus.
i have a little kont in my brain about structuring our code. We have a REST Backend based on SpringBoot. To handle requests regarding to security checks we use HandlerInterceptors. In some specific cases we need a specific interceptor and not our default one. The default one is registered in a 3rd party lib that no one can forget it. But i want all coders to think about this specific interceptor.
Actually, i just said it to them to achieve this.
Here's my question: Is there an option to create required (or necessary) interfaces which must be implemented? This would be a way to provide our security code by lib and to have the security that every coder implemented our specific interface (also if he just does nothing with it).
pseudo code:
public interface thinkForIt(){
Object SecBean specificSecBean;
public void methodToThinkOn();
}
public SecImpl implements thinkForIt(){
#Override
public void methodToThinkOn(){
return null; // i thought about it but i do not need to do anyting!
}
If the interface thinkForIt would have any annotations like #required, users could get warning or error if they did not implement it...
Looking for a solution and thanks for your comments in advance!
Your overall design is questionable; you are reinventing security code, which is always a red flag. Use Spring Security instead.
However, there's a simple way to ensure that "some bean of type Foo" has been registered with the context:
#Component
#RequiredArgsConstructor
public class ContextConfigurationVerifier {
final Foo required;
}
I'd like to learn if there are some rules / conditions that a Spring component is wrapped (proxied) by CGLIB. For example, take this case:
#Component
public class TestComponent {
}
#Service
//#Transactional(rollbackFor = Throwable.class)
public class ProcessComponent {
#Autowired
private TestComponent testComponent;
public void doSomething(int key) {
// try to debug "testComponent" instance here ...
}
}
If we let it like this and debug the testComponent field inside the method, then we'll see that it's not wrapped by CGLIB.
Now if we uncomment the #Transactional annotation and debug, we'll find that the instance is wrapped: it's of type ProcessComponent$$EnhancerByCGLIB$$14456 or something like that. It's clearly because Spring needs to create a proxy class to handle the transaction support.
But I'm wondering, is there any way that we can detect how and when does this wrapping happen ? For example, some specific locations in Spring's source code to debug into to find more information; or some documentations on the rules of how they decide to create a proxy.
For your information, I need to know about this because I'm facing a situation where some component (not #Transactional, above example is just for demonstrating purpose) in my application suddenly becomes proxied (I found a revision a bit in the past where it is not). The most important issue is that this'll affect such components that also contain public final methods and another issue (also of importance) is that there must have been some unexpected changes in the design / structure of classes. For these kind of issues, of course we must try to find out what happened / who did the change that led to this etc...
One note is that we have just upgraded our application from Spring Boot 2.1.0RELEASE to 2.1.10RELEASE. And checking the code revision by revision up till now is not feasible, because there have been quite a lot of commits.
Any kind of help would be appreciated, thanks in advance.
You could debug into org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(Class, String, TargetSource).
If any advisor is found, the bean will be proxied.
If you use a #Lookup method injection it will also proxy the component class.
I have quite some JpaRepository extended Repository interfaces due to the design of the database.
In order to construct a simple object i.e Person I have to make method calls to about 4 - 5 repositories just because the data is spread like that throughout the database. Something like this (pardon for pseudocode):
#Service
public class PersonConstructService {
public PersonConstructService(Repository repository,
RepositoryTwo repositoryTwo,
RepositoryThree repositoryThree) {
public Person constructPerson() {
person
.add(GetDataFromRepositoryOne())
.add(GetDataFromRepositoryTwo())
.add(GetDataFromRepositoryThree());
return person;
}
private SomeDataTypeReturnedOne GetDataFromRepositoryOne() {
repository.doSomething();
}
private SomeDataTypeReturnedTwo GetDataFromRepositoryTwo() {
repositoryTwo.doSomething();
}
private SomeDataTypeReturnedThree GetDataFromRepositoryThree() {
repositoryThree.doSomething();
}
}
}
PersonConstructService class uses all these interfaces just to construct a simple Person object. I am calling these repositories from different methods inside the PersonConstructService class. I have thought about spreading this class into multiple classes, but I do not think this is correct.
Instead I would like to use a repositoryService which would include all the repositories listed necessary for creation of a Person object. Is that a good approach? Is it possible in Spring?
The reason I am asking is that sometimes the count of injected Services into a class is about 7-8. This is definitely not good.
I do not think you can / shoudl create a meta-repository like abstraction. Repositories have a well defined meaning, conceptually, they are CRUD services (and a bit more sometimes :-)) for your Hibernate/JPA/Datastore entities. And I guess this is enough for them. Anything more is confusing.
Now what I would propose is a "smart" way of building your "Person" objects that is automa(g)tically aware of any new services that contribute to the meaning of the Person object.
The crux of it would be that :
you could have your Repositories implement a given Interface, say PersonDataProvider, which would have a method, say public PersonPart contributeDataToPersonBuidler(PersonBuilder).
You would make your #Service implement Spring's BeanFactoryPostProcessor interface, allowing you to inspect the container for all such PersonDataProvider instances, and inject them to your service (see accepted answer at How to collect and inject all beans of a given type in Spring XML configuration)
Your #Service implementation would then be to ask all the PersonDataProviders in turn to ask them to contribute their data.
I could expand a bit, but this seems to me like the way to go.
One could argue that this is not clean (it makes your Repositories aware of "something" that happens at the service layer, and they should not have to), and one could work around that, but it's simpler to expose the gist of the solution that way.
EDIT : since this post was first written, I came aware that Spring can auto-detect and inject all beans of a certain type, without the need of PostProcessors. See the accepted answer here : Autowire reference beans into list by type
I see it as a quite reasonable and practical data aggregation on Service layer.
It's perfectly achievable in Spring. If you have access to repositories code you can name them all like:
#Repository("repoOne")
public class RepositoryOne {
#Repository("repoTwo")
public class RepositoryTwo {
And inject them into the aggregation service as necessary:
#Service
public class MultipleRepoService {
#Autowired
#Qualifier("repoOne")
private RepositoryOne repositoryOne;
#Autowired
#Qualifier("repoTwo")
private RepositoryTwo repositoryTwo;
public void doMultipleBusiness() {
repositoryOne.one();
repositoryTwo.two();
}
}
In fact, you even don't need to name and Qualify them if they are different classes, but if they are in hierarchy or have the same interface...
Also, you can inject directly to constructing method if autowiring is not a case:
public void construct(#Qualifier("repoOne")RepositoryOne repoOne,
#Qualifier("repoTwo")RepositoryTwo repoTwo) {
repoOne.one();
repoTwo.two();
}
I have a REST service with version v1 running fine in production. Now I need to make version v2 url as the response format is changed so we don't want to affect our current customers who are using v1 url. We will be returning some other object back with version v2 url instead of using ClientResponse object.
Below is my current design in which version is provided in #Path annotation. This was done by somebody else who left our team.
#Component
#Scope("request")
#Path("/abc/hello/v1")
public class ClientService {
// ... some variables
#GET
#Path("/json/line")
#Produces(MediaType.APPLICATION_JSON)
public ClientResponse getLineData(#Context UriInfo uriInfo) {
}
}
What is the best way to design version v2 url here? Should I just make a new class and have #Path as #Path("/abc/hello/v2") like this and copy paste everything in it? Or should I create some abstract class and have ClientServiceV1 extend that abstract class and then have ClientServiceV2 extend that abstract class as well? How should I proceed?
My strategy for versioning REST API is to not let JAX-RS runtime automatically determine what REST resources to load and instead explicitly state them in the java.ws.rs.Application implementation.
My java.ws.rs.Application implementation is where I do the versioning and I state it there the base API URI
#javax.ws.rs.ApplicationPath("v1")
public class MyAppV1 extends java.ws.rs.Application {
Set<Class<?>> getClasses() {
return new java.util.HashSet<>(java.util.Arrays.asList(
ClientService.class,
OtherService.class));
}
}
And then create another one for "v2" where I start adding my components there.
The intent of it is I can have multiple versions present and I can deprecate the old ones and eventually remove them as needed. It also allows me to reuse the existing services.
However, if your existing services are suffixed with "v1" then you may want to either duplicate the code or make it point to the new version depending on your needs.