Can I use an annotation to modify a annotated property? - java

Given
class MyPOJO {
#XssSanitized()
String name
}
#Retention(RetentionPolicy.RUNTIME)
#interface XssSanitized {
}
import org.aspectj.lang.annotation.Aspect
#Aspect
#Slf4j
#Component
class XssSanitizedAspect {
#Around("#within(com.es.phoenix.services.security.XssSanitized))")
void sanitize(){
//capture the value of the annotated String
//make an arbritary change to the string
//return modified string or otherwise overwrite the property
log.info("XssSanitizedAspect is working!!!!")
}
}
Is it possible to modify the property myPOJOObject.name inside of XssSanitizedAspect?
In reality, I need a way to XSS sanitize String properties. I tried creating a custom Jackson Deserializer. It worked, but I cannot inject a feature toggle (without a workaround that breaks all the contracts with the UI) so that solution failed me.
I've gone through numerous examples online. I have not found an example where the annotated property has it's value directly modified in such a way.

Related

Springboot Get Api Endpoint returns 404 not found

I'm trying to create a Springboot Get API endpoint but I get a 404 not found error' here is my code
profile.java
#Getter
#Setter
public class Profile {
private String slackUsername;
private Boolean backend;
private Integer age;
private String bio;
ProfileController
#RestController
public class ProfileController {
#Autowired
private Profile profile;
#GetMapping(path = "/profile")
private ResponseEntity<String> userInfo(){
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return ResponseEntity.ok(profile.toString());
}
Main
#SpringBootApplication
#ComponentScan("com/ajavacode/HNGBackendStage1/api.profile")
public class HngBackendStage1Application {
public static void main(String[] args) {
SpringApplication.run(HngBackendStage1Application.class, args);
}
}
porm.xml
Please fix the value in #ComponentScan annotation like this and try again.
#SpringBootApplication
#ComponentScan(basePackages = "com.ajavacode.HNGBackendStage1")
public class HngBackendStage1Application {
public static void main(String[] args) {
SpringApplication.run(HngBackendStage1Application.class, args);
}
}
Also there is an issue in ProfileController class, you are auto wiring Profile class inside it. Profile class is not a bean so this is incorrect and userInfo() method is private, it should be public. Here is the fixed version.
#RestController
public class ProfileController {
#GetMapping(path = "/profile")
public ResponseEntity<String> userInfo(){
Profile profile=new Profile();
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return ResponseEntity.ok(profile.toString());
}
You have to check some items in your code;
Make sure the endpoint you are sending request , is correct .
Add #RequestMapping("Profile") to the controller to avoid repeated endpoint and reduce the ambiguity
Make sure your pom is correct
Looking at your code, there are a few things to mention.
First of all, your package structure looks good (besides the fact that I, personally, would keep everything lowercase).
With the package structure that you have in place, you actually don't need any #ComponentScan annotation at all. The annotation #SpringBootApplication at your main class by default scans for components with the package of that class as the base package. So you only need to set something if you want to explicitly scan for components in some other package, e.g., either at a higher level or if you want to skip packages in the hierarchy.
Next thing is the controller. Question here is: What do you actually want to achieve?
I assume that you want to build an application that provides a GET /profile endpoint that returns a response object like the example below:
{
"slackUsername": "alice",
"backend": false,
"age": 42,
"bio": "I'm just an example"
}
If my understanding is correct, there is at least one thing that is a bit odd: Currently, you defined a controller that would return the String representation of the Profile object. That isn't necessarily something as shown in the example above. If you do not override the toString() method, the result would be something like com.ajavacode.HNGBackendStage1.api.Profile#6d06d69c (see this Baeldung article for instance). And even if you use Lombok's #Data or #ToString annotations, the result will not be a JSON or XML representation but something that is suitable for logging, for instance.
Spring will already take care of the serialization into JSON (or XML) format. In your controller you can just return the Profile object or, alternatively, a ResponseEntity<Profile>:
#GetMapping(path = "/profile")
public Profile userInfo(){
Profile profile=new Profile();
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return profile;
}
The above example would create a response with the profile as the response body and HTTP status code 200 OK. In case you use ResponseEntity, you could also adjust the HTTP status code but in your case that probably is not necessary (yet).
Autowiring the Profile class also is not correct, as already mentioned. You only need to autowire classes beans, i.e., classes that are annotated with #Component, #Service, or #Repository. The class you "autowired" is just a POJO class representing some "data object", nothing that provides any kind of business logic.

SpringBoot: how to enable a #Configuration Class only if the corresponding #EnablerAnnotation has been used on a class?

How can I enable a #Configuration class only if the corresponding #EnableCustomConfiguration annotation has been used?
To make it clear I am trying to recreate the behaviour of common SpringBoot annotations, like e.g. #EnableEurekaClient, #EnableWebSecurity and so on.
This is my enabler:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface EnableMultitenancy {}
Here my configuration properties:
#Getter
#Setter
#Validated
#ConfigurationProperties("multitenancy")
public class MultitenancyProperties {
#NotEmpty(message = "You must provide at least one tenant")
private List<Tenant> tenants;
}
and this is my Configuration class:
#Configuration
#Conditional("On EnableMultitenancy used")
#EnableConfigurationProperties(MultitenancyProperties.class)
public class MultitenancyConfiguration{
#Bean
public MyFirstBean first(MultitenancyProperties properties){
return new MyFirstBean(properties);
}
#Bean
public MySecondBean second(MultitenancyProperties properties){
return new MySecondBean(properties);
}
}
How can I write such a condition, e.g. the annotation has been used on a class/component?
org.springframework.context.annotation.ConditionContext interface shows theability of #Conditional.
ConditionContext contains five methods:
getRegistry
getBeanFactory
getEnvironment
getResourceLoader
getClassLoader
So it seems your cannot directly depend on annotation, eg: #EnableCaching. But you can depend on the bean related to them, eg: ProxyCachingConfiguration for EnableCaching.
But i think is not a good design. Usually we will declare our dependency, this makes component sperately, also make things easy. When user want to use our component, they don't need to add all of depended annotation, eg: #EnableEureka,#EnableWebSecurity, just # MultitenancyConfiguration.
In your MultitenancyConfiguration class, you just need to replace the current #Conditional annotation with #ConditionalOnClass(EnableMulitenancy.class).
Update: Sorry, I misunderstood. Why don't you just import the MultitenancyConfiguration class by using #Import(MultitenancyConfiguration.class) in your EnableMultitenancy class? This will guarantee that your MultitenancyConfiguration class is enabled whenever #EnableMultitenancy is used.

Spring - Dynamically injecting based on qualifier value

In Weld, I can do the following to get dynamically inject config values from some source:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER })
public static #interface ConfigValue {
#Nonbinding
String value();
}
#Produces
#Dependent
#ConfigValue("")
public String stringValue(InjectionPoint ip) {
ConfigValue configValue = ip.getAnnotated().getAnnotation(ConfigValue.class);
return myConfigMap.get(configValue.value());
}
The equivalent Spring, however, matches based on the value of the #ConfigValue annotation.
I would like Spring to call a method to allow me to inject custom values for all fields annotated with #ConfigValue.
I'm aware of this: http://joshlong.com/jl/blogPost/supporting_your_own_field_or_method_injection_annotation_processors_in_spring.html
However that's a very complex solution for a seemingly simple problem. I'm wondering if there's a simpler solution...
Spring has InjectionPoint class. The catch is that your #ConfigValue annotation should not have Qualifier annotation. Then you can have a single method to produces various values.
However, if you choose to have #Qualifier on your #ConfigValue annotation, then you will have to have multiple producing methods one for each value of #ConfigValue. I dont seem to have come across anything equivalent to #Nonbinding though.
I have tested this for user object, not String.
PS: another approach is to have one "defining" annotation and one "non-defining" annotation on the injection point to serve your need.

Passing annotation properties to meta-annotations

Say I have an annotation with a property:
#Named(name = "Steve")
private Person person
and I want to create a compound annotation with several meta-annotations, including the one that takes a property
#Named
#AnotherAnnotation
#YetAnotherAnnotation
public #interface CompoundAnnotation {
...
}
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
Eg, something like this:
#CompoundAnnotation(name = "Bob")
private Person person;
that is equivalent to, but much more convenient than
#Named(name = "Bob")
#AnotherAnnotation
#YetAnotherAnnotation
private Person person;
Thanks!
PS apologies for my poor choice of an example annotation - I didn't have the javax.inject.#Named annotation in mind, just some arbitrary annotation that has properties.
Thank you everyone for your answers/comments.
It definitely seems to be the case that this is not possible. However, it just happens that there is a simple work-around for my case-in-point, which I will share in case it helps anyone:
I am working with Spring and want to create my own Annotations that have #Component as a meta-annotation, thus being autodetected by component scanning. However, I also wanted to be able to set the BeanName property (corresponding to the value property in #Component) so I could have custom bean names.
Well it turns out that the thoughtful guys at Spring made it possible to do just that - the AnnotationBeanNameGenerator will take the 'value' property of whatever annotation it is passed and use that as the bean name (and of course, by default, it will only get passed annotations that are #Component or have #Component as a meta-annotation). In retrospect this should have been obvious to me from the start - this is how existing annotations with #Component as a meta-annotation, such as #Service and #Registry, can provide bean names.
Hope that is useful to someone. I still think it's a shame that this is not possible more generally though!
It is a few years later now, and since you are using Spring, what you are asking for is sort of possible now using the #AliasFor annotation.
For example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#SpringApplicationConfiguration
#ActiveProfiles("test")
public #interface SpringContextTest {
#AliasFor(annotation = SpringApplicationConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
#AliasFor("value")
Class<?>[] classes() default {};
}
Now you can annotate your test with #SpringContextTest(MyConfig.class), and the amazing thing is that it actually works the way you would expect.
N.B. When you need to programmatically get the attribute values, the Spring automagical aliasing works only when you use AnnotatedElementUtils instead of AnnotationUtils, as the documentation says:
AnnotatedElementUtils defines the public API for Spring's meta-annotation programming model with support for annotation attribute overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils instead.
Example:
final Named namedAnnotation = AnnotatedElementUtils.findMergedAnnotation(Person.class, Named.class);
final String name = namedAnnotation.name();
assertEquals("Steve", name);
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
I think the simple answer is "no". There is no way to ask Person what annotations it has on it and get #Named for example.
The more complex answer is that you can chain annotations but you would have to investigate these annotations via reflection. For example, the following works:
#Bar
public class Foo {
public static void main(String[] args) {
Annotation[] fooAnnotations = Foo.class.getAnnotations();
assertEquals(1, fooAnnotations.length);
for (Annotation annotation : fooAnnotations) {
Annotation[] annotations =
annotation.annotationType().getAnnotations();
assertEquals(2, annotations.length);
assertEquals(Baz.class, annotations[0].annotationType());
}
}
#Baz
#Retention(RetentionPolicy.RUNTIME)
public #interface Bar {
}
#Retention(RetentionPolicy.RUNTIME)
public #interface Baz {
}
}
However the following statement will return null:
// this always returns null
Baz baz = Foo.class.getAnnotation(Baz.class)
This means that any 3rd party class that is looking for the #Baz annotation won't see it.

Java: Annotated Annotations (and passing values)

I have a annotation that does include several other annotations, pretty much like this one here:
#Component // Spring Component
#Interface OsgiService { boolean isFactory() }
meaning that all classes annotated with #OsgiService shall automatically also be annotated as #Component. Which works fine.
Now however, I'd like to add another annotation, that has a parameter which is dependent of the isFactory parameter of #OsgiService.
#Component // Spring Component
#Scope(isFactory() ? "prototype" : "singleton")
#Interface OsgiService { boolean isFactory() }
Which does not work. However, as isFactory property of an annotation requires to be a static value, shouldn't it be possible to have something like this?
I don't think this is possible.
You can create two annotations: #OsgiService and #OsgiServiceFactory

Categories

Resources