Evaluate pathparam arguments in Jersey using AspectJ - java

I have several APIs which retain a parameter "feature" from the url (path param). To avoid retrieving it in each method endpoint (eg.)
#GET
public void findAll(#PathParam("feature") String feature);
am trying to implement AOP using AspectJ.
Following is the implementation of the Aspect
#Aspect
public class FeatureAOP {
#Pointcut("execution(* x.y.z.rest.ModifiersFacadeWrapper.*(..)) && args(feature)")
public void pointCut(String feature) {
}
#Before("x.y.z.rest.aop.FeatureAOP.pointCut(feature)")
public void parseParams(JoinPoint jp, String feature) {
Object[] x = jp.getArgs();
System.out.println("Feature: " + feature);
}
}
The above method gives me the value of "feature" in the Aspect class but if I change the method findAll to following signature, it doesn't works.
#GET
public void findAll();
What I understand is the control is transferred to the Aspect after the parameters are resolved and removing it from the method definition is failing it.
Doing so, thus takes me to the same point where I have to define all method endpoints with the parameter in its signature. I would like to know if there is a way I can get the PathParams in the Aspect class without having to define my methods with the designated parameters.

I think you could probably do it by putting the resolved params in a globally accessible data structure (e.g. a Singleton having some sort of Map or Set), but
I wouldn't recommend that kind of approach. I don't know why you don't like having all the params in your method signatures, but that is the intended way of declaring rest services, e.g.
#GET
#Path("{feature}")
#Produces("text/plain")
public String getFeature(#PathParam("feature") String feature) {
return feature;
}
This way you don't have to write any code for retrieving the params, the rest library you are using (be it Jersey or a different one) will just do everything for you.

Related

exclusion of some fields/parameters from logging via Spring AOP

In my spring project I have such an aspect class for logging
#Aspect
#Component
public class BaseLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(BaseLoggingAspect.class);
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface NonLoggingField {
}
#Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
#Pointcut("within(img.imaginary.service.*)")
private void inServices() {
}
#Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
#Before("allPublicMethods() && inServices() || inDao()")
public void logBeforeCall(JoinPoint joinPoint) {
if (logger.isDebugEnabled()) {
logger.debug("begin method {} in {} class with arguments: {}", joinPoint.getSignature().getName(),
joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getArgs());
}
}
}
this aspect simply catches all the public methods of the service and dao layers and outputs to the log at the beginning of execution the name of the method, the name of the class, and the masi of the values of the arguments of the method
in this aspect, I created a NonLoggingField annotation that I want to apply to some fields of classes of those objects that can be passed to the parameters of these logged methods, for example this:
public class User {
#NonLoggingField
public String userEmail;
public name;
public User(String userEmail, String name) {
this.userEmail = userEmail;
this.name= name;
}
public String tiString() {
return String.format("user name: %s and his email: %s", name, userEmail);
}
}
the fact is that such objects will be written to the log through its toString method, but it is necessary that the email somehow does not get into the log using the notLoggingField annotation, while there are thoughts in my head to do through reflection, but there is no clarity how to do this without over difficult code using reflection, especially considering that objects may have objects of other types inside, which may have the same fields with annotations or collections with objects with such fields. perhaps the AspectJ library can help, but I can't find such mechanisms in it. Please help me come up with something
During runtime, a method parameter is just a value. The JVM does not know at this point if the caller called the method using constants, literals, fields or results of other method calls. That kind of information, you only see in the source code. In byte code, whatever dereferencing operation or computation necessary to determine the parameter's value (or a reference to the corresponding object) is done before calling the method. So there is no connection to the field annotation.
Would annotating method parameters be an alternative for you?
If your requirement is very specific, e.g. intercept field accesses from toString methods and return dummy values instead, if the field is annotated, that would be possible. But this would not be fool-proof. Imagine for example that toString calls a getter method instead of directly accessing the field or that a method other than toString logs the field. You do not always want to falisfy the field value on read access, because other parts of the application might rely on it working correctly. Not every toString call is made in order to log something.
I think you should solve the problem in another way, e.g. by applying filter rules for the logging tool you use. Or if you really want solve it at the application level, you could create an interface like
public interface PrivacyLogger {
String toStringSensitive();
}
and make each class containing sensitive information implement that interface. The logging aspect could then for each printed object determine if it is instanceof toStringSensitive(). If so, it would log the result of toStringSensitive() instead of toString(), i.e. in the simplest case something like
Object toBeLogged = whatever();
logger.log(
toBeLogged instanceof PrivacyLogger
? ((PrivacyLogger) toBeLogged).toStringSensitive()
: toBeLogged
);
Of course, you need to iterate over getArgs() and determine the correct log string for each object. Probably, you want to write a utility method doing that for the whole parameters array.
Moreover, in a complex class, the toStringSensitive() implementation should of course also check if its own fields are PrivacyLogger instances and in that case fold the values of their resapctive toStringSensitive() methods into itw own, so that it works recursively.
I am sorry I have no better news for you, but privacy is something which needs too be built into an application from the ground. There is no simple, fool-proof way to do that with one little aspect. The aspect can utilise the existing application infrastructure and avoid scattering and tangling, but it cannot decide on its own what needs to be prohibited from getting logged and what not.

Spring REST - Is there a way to override the character Spring uses to separate a query parameter into a list of values?

I'm writing a REST API using Spring and have certain clients to the service that cannot or will not change how they call my service.
Normally when sending a query param with a list of values you would just comma delimit the parameter and Spring will do the rest
curl http://host.com/api/endpoint?listParam=1,2,3
And the controller
#GetMapping("/api/endpoint")
public ResponseEntity endpoint(#RequestParam("listParam" List<String> listParam){
// Here, listParam is populated with 1,2,3
}
Unfortunately my clients are going to be passing lists with the bar | delimiter and it simply isn't possible to get them to change that.
Example: curl http://host.com/api/endpoint?listParam=1%7C2%7C3%7C
I would still like to use Spring to break these calls out into lists so I don't have to clutter my code with manual String.split() calls.
What I've already tried:
I found the #InitBinder annotation and wrote the following
#InitBinder
public void initBinder(WebDataBinder dataBinder){
dataBinder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("|"));
}
However, this code doesn't seem to ever be called (watching with breakpoints) and requests using the bar as the delimiter fail with a 400 BAD REQUEST.
Any suggestions would be much appreciated, thanks!
404 is coming due to URL encoding issue.
You need to encode | then it will work, but it will create another problem, params would not be split.
To work around this you need to create a custom conversion that can convert String to Collection. For the custom conversion, you can check the StringToCollectionConverter class. Once you have custom conversion then you can register that service, in any of the configuration classes add following function
#Autowired
void conversionService(GenericConversionService genericConversionService) {
genericConversionService.addConverter(myStringToCollectionConvert());
}
#Bean
public MyStringToCollectionConvert myStringToCollectionConvert() {
return new MyStringToCollectionConvert();
}
In this MyStringToCollectionConvert is class that will parse String and converts to a collection of Strings.
I've accepted Sonus21's answer since his suggestion allowed me to hunt down an example that worked, but my solution was not exactly his.
The class StringToCollectionConverter did in fact exist for me, but it wasn't accessible and I couldn't use it in any way. However, in looking at the interface it implemented (ConditionalGenericConverter) and searching for more examples with Spring converters I eventually settled on the following solution.
The listParam in my question actually refers to a set of Enum values. The first thing I did was rewrite my controller to actually use the Enum values instead of raw Integers.
#GetMapping("/api/endpoint")
public ResponseEntity endpoint(#RequestParam("listParam" List<EnumClass> listParam){
// ...
}
Next, I wrote a Spring Custom Converter (Baeldung Doc)
public class CustomStringToEnumClassListConverter implements Converter<String, List<EnumClass>> {
#Override
public List<EnumClass> convert(String str) {
return Stream.of(
str.split("\\|")) // Here is where we manually delimit the incoming string with bars instead of commas
.map(i -> EnumClass.intToValue(Integer.parseInt(i))) // intToValue is a method I wrote to get the actual Enum for a given int
.collect(Collectors.toList());
}
}
Finally, I wrote a Config Bean and registered this Custom Converter with Spring:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry){
registry.addConverter(new CustomStringToEnumClassListConverter());
}
}
Once all of this was done, Spring automatically populated the listParam list with EnumClass objects.

Apply Spring JPA Specification to multiple repositories and queries

I have the following situation:
My project contains multiple entities, each one with its respective controller, service and JPA repository. All of these entities are associated with a specific company by a "companyUuid" property.
Every incoming request in my controllers will have a "user" header, which will give me the details of the User making that request, including which company he is associated with.
I need to retrieve the company associated with the user from the header and filter every subsequent query by this company, which would be essentially like adding WHERE companyUuid = ... to each query.
What I did as a solution was a generic function for creating the Specification object:
public class CompanySpecification {
public static <T> Specification<T> fromCompany(String companyUuid) {
return (e, cq, cb) -> cb.equal(e.get("companyUuid"), companyUuid);
}}
Implemented repository as follows:
public interface ExampleRepository extends JpaRepository<Example, Long>, JpaSpecificationExecutor<Example> { }
Changed the "find" calls to include the specification:
exampleRepository.findAll(CompanySpecification.fromCompany(companyUuid), pageRequest);
Of course, this requires adding #RequestHeader to the controller functions to get the user in the header.
Although this solution works absolutely fine, it would require a lot of copy-pasting and code repetition to get it done for all routes of my #RestControllers.
Therefore, the question is: how can I do this in an elegant and clean way for all my controllers?
I have researched this quite a bit now and I came across the following conclusions:
Spring JPA and Hibernate don't seem to provide a way of dynamically using a Specification to restrict all queries (reference: Automatically Add criteria on each Spring Jpa Repository call)
Spring MVC HandlerInterceptor would maybe help for getting the User out of the header in each request, but it doesn't seem to fit overall since I don't use views in this project (it's just a back-end) and it can't do anything about my repository queries
Spring AOP seemed like a great option to me and I gave it a go. My intention was to keep all repository calls as they were, and add the Specification to the repository call. I created the following #Aspect:
#Aspect
#Component
public class UserAspect {
#Autowired(required=true)
private HttpServletRequest request;
private String user;
#Around("execution(* com.example.repository.*Repository.*(..))")
public Object filterQueriesByCompany(ProceedingJoinPoint jp) throws Throwable {
Object[] arguments = jp.getArgs();
Signature signature = jp.getSignature();
List<Object> newArgs = new ArrayList<>();
newArgs.add(CompanySpecification.fromCompany(user));
return jp.proceed(newArgs.toArray());
}
#Before("execution(* com.example.controller.*Controller.*(..))")
public void getUser() {
user = request.getHeader("user");
}
}
This would have worked perfectly, since it would require almost no modifications at all to controllers, services and repositories. Although, I had a problem with the function signature. Since I am calling findAll(Pageable p) in my Service, the signature of the function is already defined in my advice, and I can't change to the alternative version findAll(Specification sp, Pageagle p) from inside the advice.
What do you think would be the best approach in this situation?
Here is an idea:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
#Aspect
public class UserAspect {
#Around("execution(* com.example.repository.*Repository.findAll())")
public Object filterQueriesByCompany(ProceedingJoinPoint jp) throws Throwable {
Object target = jp.getThis();
Method method = target.getClass().getMethod("findAll", Specification.class);
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return method.invoke(target, CompanySpecification.fromCompany(request.getHeader("user")));
}
}
The above aspect intercepts the findAll() methods from repository and, instead of proceeding the call it replaces with another call to findAll(Specification) method. Notice how I get the HttpServletRequest instance.
Of course, it's a starting point not an out of the box solution.
I am not a Spring or Java EE user, but I can help you with the aspect part. I googled a bit, too, because your code snippets without imports and package names are a bit incoherent, so I cannot just copy, paste and run them. Judging from the JavaDocs for JpaRepository and JpaSpecificationExecutor, both of which you extend in your ExampleRepository, you are trying to intercept
Page<T> PagingAndSortingRepository.findAll(Pageable pageable)
(inherited by JpaRepository) and call
List<T> JpaSpecificationExecutor.findAll(Specification<T> spec, Pageable pageable)
instead, right?
So in theory we can use this knowledge in our pointcut and advice in order to be more type-safe and avoid ugly reflection tricks. The only problem here is that the intercepted call returns Page<T> while the method you want to call instead returns List<T>. The calling method surely expects the former and not the latter, unless you always use Iterable<T> which is a super-interface for both interfaces in question. Or maybe you just ignore the return value? Without you answering that question or showing how you modified your code to do this, it will be difficult to really answer your question.
So let us just assume that the returned result is either ignored or handled as Iterable. Then your pointcut/advice pair looks like this:
#Around("execution(* findAll(*)) && args(pageable) && target(exampleRepository)")
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, ExampleRepository exampleRepository) throws Throwable {
return exampleRepository.findAll(CompanySpecification.fromCompany(user), pageable);
}
I tested it, it works. I also think it is a bit more elegant, type-safe and readable than what you tried or what was proposed by Eugen.
P.S.: Another option is to convert the list into a corresponding page manually before returning it from the aspect advice if the calling code indeed expects a page object to be returned.
Update due to follow-up question:
Eugen wrote:
For another entity, let's say Foo, the repository would be public interface FooRepository extends JpaRepository<Foo, Long>, JpaSpecificationExecutor<Foo> { }
Well, then let us just generalise the pointcut and assume that it should always target classes which extend both interfaces in question:
#Around(
"execution(* findAll(*)) && " +
"args(pageable) && " +
"target(jpaRepository) && " +
//"within(org.springframework.data.jpa.repository.JpaRepository+) && " +
"within(org.springframework.data.jpa.repository.JpaSpecificationExecutor+)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaRepository jpaRepository) throws Throwable {
return ((JpaSpecificationExecutor) jpaRepository)
.findAll(CompanySpecification.fromCompany(user), pageable);
}
The part of the pointcut I commented out is optional because I am narrowing down to JpaRepository method calls already via target() parameter binding using the advice signature. The second within() should be used, however, in order to make sure the intercepted class actually also extends the second interface so we can cast and execute the other method instead without any problems.
Update 2:
As Eugen said, you can also get rid of the cast if you bind the target object to the type JpaSpecificationExecutor - but only if you do not need the JpaRepository in your advice code before that. Otherwise you would have to cast the other way. Here it seems it is not really needed, so his idea makes the solution a little more lean and expressive, indeed. Thanks for the contribution. :-)
#Around(
"target(jpaSpecificationExecutor) && " +
"execution(* findAll(*)) && " +
"args(pageable) && " +
"within(org.springframework.data.jpa.repository.JpaRepository+)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
Or alternatively, if you do not want to merge execution() with within() (a matter of taste):
#Around(
"target(jpaSpecificationExecutor) && " +
"execution(* org.springframework.data.jpa.repository.JpaRepository+.findAll(*)) && " +
"args(pageable)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
Less type-safe, but also an option if you believe that the are no other classes with * findAll(Pageable) signature:
#Around("target(jpaSpecificationExecutor) && execution(* findAll(*)) && args(pageable)")
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
You might notice that this suspiciously looks like my original solution for one specific sub-interface, and you are right. I recommend to be a little more strict, though, and not use the last option, even though it works in my test case and you would probably be fine with it.
Finally, I think we have covered most bases by now.

Jersey #Path for plural/single REST nouns in same class

I have a class this is annotated with #Path like so:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit"))
{
//This class returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id)
{
//This class returns a single widget by id
//...}
When I fire up a test client the localhost/widgets maps as expected, but when the getWidgetById method is mapped to localhost/widgets/widget/{id}. This is not what I want - I would like to have localhost/widgets and localhost/widget/{id}
I have tried omitting the #Path annotation at the class level, but that prevents Jersey from recognizing this class as a REST Resource (I tried both the ScanningResourceConfig and the ClassNameResourceConfig - both failed to load the class as a resource unless there was a #Path at the class level).
I guess a (ugly) workaround would be to split the methods between classes a WidgetResource class and a WidgetsResource class. I think this is a terrible solution since both of these methods share resources in the same class, but I really need the REST-ful localhost/widget (for a single entity) and localhost/widgets (for plural).
Am I missing something - is there some way for me to have Jersey pick up the class as a Resource class if I just #Path annotate the methods (I couldn't get it to work), if not can I force absolute mapping (#Path(/widget/{id})) or some relative mapping (#Path(../widget/id) - neither of those work in reality - just an analogy of what I'm after. Thanks!
This part is about what you need:
Personally, I find your mapping strange and confusing. Just keep it like this:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
// it's also possible to limit the number returned by
// using a query parameter. You could easily implement
// pagination by adding further query parameters like
// 'offset', 'sortOrder', etc.
//...
}
#GET
#Path("{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}
It seems natural to append the path to a collection with an ID to fetch an object from the collection. There's really no need to make it widgets/widget/{id}. The widget part is obvious and unnecessary.
Here's a really neat tutorial on RESTful APIs: "Teach a dog to REST" by apigee I think it's a really good video. The authors make a couple of good points. And here's a link to a longer version of the same presentation
This part is about what you want:
If you really want to keep the plural/singular dualism (which I really don't recomment), you can annotate your code like this:
But it's really ugly
#Path("/")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
#Path("widgets")
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}
My suggestion is to have your paths be:
"widgets" and "widgets/id/{id}". Or if you knew you were never going to query by anything other than id, your second one could simply be "widgets/{id}".
I would not switch between plural and singular in your path. Since you accessing the same type of resource for both, your root should be the same. The second form just specifies it more -- a vectoring-based approach for getting more specific.

Path segment sequence to vararg array in JAX-RS / Jersey?

JAX-RS/Jersey allows URL path elements to be converted to Java method arguments using #PathParam annotations.
Is there a way to convert an unknown number of path elements into arguments to a vararg Java method? I. e. /foo/bar/x/y/z should go to method: foo(#PathParam(...) String [] params) { ... } where params[0] is x, params[1] is y and params[2] is z
Can I do this in Jersey/JAX-RS or some convenient way?
Not sure if this is exactly what you were looking for but you could do something like this.
#Path("/foo/bar/{other: .*}
public Response foo(#PathParam("other") VariableStrings vstrings) {
String[] splitPath = vstrings.getSplitPath();
...
}
Where VariableStrings is a class that you define.
public class VariableStrings {
private String[] splitPath;
public VariableStrings(String unparsedPath) {
splitPath = unparsedPath.split("/");
}
}
Note, I haven't checked this code, as it's only intended to give you an idea.
This works because VariableStrings can be injected due to their constructor
which only takes a String.
Check out the docs.
Finally, as an alternative to using the #PathParam annotation to inject a VariableString
you could instead wrap this logic into your own custom Jersey Provider. This provider would inject a "VariableStrings" more or less the same manner as above, but it might look a bit cleaner. No need for a PathParam annotation.
Coda Hale gives a good overview.

Categories