Springboot-- How to use #Profile with value having regular expression? - java

I have a class in my springboot maven project. I want to annotate Class with #Profile in a way that this class should not be configured for any profile ending with '-test'.
ConfigureData class is under /main.
Under my /test directory, I have couple of tests classes annotated with #ActiveProfiles as 'unit-test', 'integration-test', 'functional-test', 'system-test' etc.
I am trying something like below to ignore auto configuration of 'ConfigureData' class for all the profiles ending with '-test', but it is not working!
Is there a way to achieve this using some regular expression without specifying name of each and every test profile under #Profile?
#Configuration
#Profile("!.*-test")
public class ConfigureData {
}

I can't say for SPeL I know that it supports Regex with "matches" (special word), however, I think its too fragile, so I propose another way: Use Conditional with custom condition.
In fact #Profile introduced in Spring 3.x was rewritten in spring 4 to be implemented with a more flexible "Conditional approach".
Take a look at the source code of profile annotation:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(ProfileCondition.class)
public #interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
Note that Profile is implemented as "Conditional" and it has a custom condition that examines all the currently running profiles and decides whether condition matches or not.
Here is an implementation of Condition in Spring 4:
class ProfileCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
So you can create your own annotation like #Production annotate with your custom Condition that checks the profiles and if the're test - do not match or something.
Then put #Production on the configuration (class ConfigureData in the question) and you're done!

I don't think wild card is supported in profile expression. Multiple profiles can be mentioned within #Profile sticking to have only supported operators.
Spring official documentation says,
The following operators are supported in profile expressions:
! - A logical not of the profile
& - A logical and of the profiles
| - A logical or of the profiles
The complete detail can be found here,
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/Profiles.html#of-java.lang.String...-

Related

Creating and using annotation in java

I am reviewing open source spring projects. I am confused about the use of annotations around here. I want to ask to clarify this.
#Target(ElementType.METHOD)
#Retention(RUNTIME)
#Bean
public #interface Merge {
#AliasFor("targetRef")
String value() default "";
#AliasFor("value")
String targetRef() default "";
Placement placement() default Placement.APPEND;
int position() default 0;
Class<MergeBeanStatusProvider> statusProvider() default MergeBeanStatusProvider.class;
boolean early() default false;
}
An annotation has been created here named Merge. It has different parameters and default values.
#Configuration
public class LocalConfiguration {
#Merge(targetRef = "mergedList", early = true)
public List<String> blLocalMerge() {
return Arrays.asList("local-config1", "local-config2");
}
}
And this is usage of #Merge annotation in any class I choosed randomly.
When I examined the code, I could not find any class related to the implementation of Merge annotation. By the way, this problem I'm having isn't just about this annotation. Almost all the annotations I have examined are used without being implemented in any way.
I think I will understand the others if we start from this annotation.
What does this anotation do? What kind of message does it give to the place where it is used. How does the application understand what that annotation does in runtime without being implemented anywhere.
Thanks.
Annotations don't have implementations. They are processed by external classes or tools depending on the RetentionPolicy. In this case, the Merge annotation has Runtime retention so it will be available via reflection once the class is loaded. At runtime any interested party (in this case I assume the Spring Framework) can use getAnnotations on your LocalConfiguration class to detect the Merge annotation and take whatever action that needs to be taken. The possibilities are really up to the framework that defined the annotation. A lot of Spring injection works like this with annotations but they are also used by many other frameworks such as Hibernate, Jersey, etc. The main idea is that annotations act as markers on specific code points to be used by an external entity at a later point.

How to handle multiple fallback values in Spring Expression Language

Question is : how can I handle a chain of fallback values in Spring expression, allowing me to fallback on higher level configuration until I got a defined value ?
To explain a little, let's illustrate this with my own use case : I was intending to create a Spring application using the #Scheduled annotation to run some processes. Thing is I have many scheduled tasks and I would let the running frequency to be easily configurable for all of them, or only for a subset.
So I was looking for something like
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${ specific-task-cron ?: task-families-cron ?: default-cron }")
public void specificTask() {
doSomething();
}
}
Letting application configure either the frequency of all the Scheduled task at once by overriding the default-cron value, or only a subset of them by overriding the task family property, or finally, specifying on a task basis. Advantage of this approach is that it allows to play with multiple configuration levels as every scheduled task looks for the appropriate property starting from the most specific, and looking for a more generic until it got something defined, ultimately fall-backing on a default global value.
Unfortunately... this do not work. When evaluated, if the first element is not defined, then if fallback on the whole remaining. In this example, it would mean that if configuration misses a value for specific-task-cron, then the resolved value is : task-families-cron ?: default-cron which unfortunately is not what I'm looking for!
I found 2 way to handle it :
The first one, is purely based on Spring expression. Spring seems to re-evaluates the result of the Elvis operator if it also is a Spring expression. So my working configuration was :
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${ specific-task-cron ?: ${ task-families-cron ?: default-cron }}")
public void specificTask() {
doSomething();
}
}
Which in my real use case, with one more fallback, was not really convenient...
So I switched to a simpler solution that relies on configuration by defining a chain of values this way :
#application.yml
default-cron: '0 */5 0 0 0 0' #every 5 minutes
task-families-cron: ${default-cron}
specific-task-cron: ${task-families-cron}
Combined with
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${specific-task-cron}")
public void specificTask() {
doSomething();
}
}
This way any override of a property apply to hierarchical sub-levels, unless they got themselves overridden.
Both solutions seems to works, so at the end, yes Spring expression language handle multiple fallback. Whether it should be used, or is the configuration approach better readable... I let you decide.

Spring conditional annotations (NOT #Conditional)

Can you make Spring annotations conditional?
I mean for example having the following method:
#CachePut(value = "latestALLFXRatesCache", key = "#definition.key")
public AssetValueSnapshot refreshCacheLatestFXValue(DataSource definition) {
AssetValueSnapshot assetsnap = getLatestFXValueSnapshot(true,definition);
}
Can I make the #Cacheput annotation conditional (preferably with another spring annotation)?
For example:
if #Profile("XXX") or #Conditional(XXX)
#Cacheput
else
#Cacheable
public AssetValueSnapshot ...
Regardless whether this makes sense and knowing that #Profile and #Conditional are not made for this, can I do what I want with annotations or is there some other way in Spring?
Edit after comments:
I'm talking about a broader scope than just a specific annotation (cacheput in this case). I'm wondering if there is an annotation/other way that makes any annotation conditional; regardless of that annotations own conditional behavior.

can we use spring expressions (spel) in other annotations?

I want to be able to do this:
#Controller
#RequestMapping("/#{handlerMappingPaths.security}/*")
public class SecurityController {
etc
//for instance, to resuse the value as a base for the folder resolution
#Value("#{handlerMappingPaths.security}/")
public String RESOURCE_FOLDER;
#RequestMapping(value="/signin-again", method = RequestMethod.GET)
public String signinAgainHandler() {
return RESOURCE_FOLDER + "signin_again";
}
}
this doesn't appear to work now, am I missing something?
One way you can find out things like this is to have a look yourself. This is an example for eclipse, but it should work similarly for other IDEs:
First of all, make sure you have the sources of the spring libraries you are using. This is easiest if you use maven, using the maven-eclipse-plugin or using m2eclipse.
Then, in Eclipse select Navigate -> Open Type.... Enter the type you are looking for (something like RequestMa* should do for lazy typers like myself). Enter / OK. Now right-click the class name in the source file and select References -> Project. In the search view, all uses of this class or annotation will appear.
One of them is DefaultAnnotationHandlerMapping.determineUrlsForHandlerMethods(Class, boolean), where this code snippet will tell you that expression language is not evaluated:
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
RequestMapping mapping = AnnotationUtils.findAnnotation(
method, RequestMapping.class);
if (mapping != null) {
String[] mappedPatterns = mapping.value();
if (mappedPatterns.length > 0) {
for (String mappedPattern : mappedPatterns) {
// this is where Expression Language would be parsed
// but it isn't, as you can see
if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
mappedPattern = "/" + mappedPattern;
}
addUrlsForPath(urls, mappedPattern);
}
}
else if (hasTypeLevelMapping) {
urls.add(null);
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
Remember, it's called Open Source. There's no point in using Open Source Software if you don't try to understand what you are using.
Answering in 2020: with current Spring versions, SpEL expressions can be used in #RquestMappning annotations.
They are correctly parsed.
Inner details:
Spring's RequestMappingHandlerMapping calls embeddedValueResolver#resolveStringValue.
JavaDoc of EmbeddedValueResolver states the following:
StringValueResolver adapter for resolving placeholders and expressions
against a ConfigurableBeanFactory. Note that this adapter resolves
expressions as well, in contrast to the
ConfigurableBeanFactory.resolveEmbeddedValue method. The
BeanExpressionContext used is for the plain bean factory, with no
scope specified for any contextual objects to access.
Since: 4.3
This means both regular placeholders (e.g. ${my.property}) and SpEL expressions will be parsed.
Note that because regular placeholders are parsed first and SpEL expressions are parsed later, it's even possible to set the value of a property to a SpEL expression. Spring will first replace the placeholder with the property value (SpEL expression) and then parse the SpEL expression.
#Sean answered the question of whether spring supported this, but I also wanted to answer the question of just generally how not to duplicate configuration when using annotations. Turns out this is possible using static imports, as in:
import static com.test.util.RequestMappingConstants.SECURITY_CONTROLLER_PATH
#Controller
#RequestMapping("/" + SECURITY_CONTROLLER_PATH + "/*")
public class SecurityController {
etc
//for instance, to resuse the value as a base for the folder resolution
public String RESOURCE_FOLDER = SECURITY_CONTROLLER_PATH + "/";
#RequestMapping(value="/signin-again", method = RequestMethod.GET)
public String signinAgainHandler() {
return RESOURCE_FOLDER + "signin_again";
}
}

Require a default constructor in java?

Is there any way to require that a class have a default (no parameter) constructor, aside from using a reflection check like the following?
(the following would work, but it's hacky and reflection is slow)
boolean valid = false;
for(Constructor<?> c : TParse.class.getConstructors())
{
if(c.getParameterTypes().length == 0) {
valid = true;
break;
}
}
if(!valid)
throw new MissingDefaultConstructorException(...);
You can build an Annotation processor for that. Annotation Processors are compiler plugins that get run at compile time. Their errors show up as compiler errors, and may even halt the build.
Here is a sample code (I didn't run it though):
#SupportedAnnotationTypes("*") // needed to run on all classes being compiled
#SupportedSourceVersion(SourceVersion.RELEASE_6)
public class DefaultConstructor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (TypeElement type : ElementFilter.typesIn(roundEnv.getRootElements())) {
if (requiresDefaultConstructor(type))
checkForDefaultConstructor(type);
}
return false;
}
private void checkForDefaultConstructor(TypeElement type) {
for (ExecutableElement cons :
ElementFilter.constructorsIn(type.getEnclosedElements())) {
if (cons.getParameters().isEmpty())
return;
}
// Couldn't find any default constructor here
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR, "type is missing a default constructor",
type);
}
private boolean requiresDefaultConstructor(TypeElement type) {
// sample: require any JPA Entity to have a default constructor
return type.getAnnotation(Entity.class)) != null
|| type.getQualifiedName().toString().contains("POJO");
}
}
The annotation processor becomes even easier if you introduce an annotation (e.g. RequiresDefaultAnnotation).
Declaring the requirement of having a default qualifier
::I am also assuming that the OP asking for a mechanism that prevents accidental errors for developers, especially written by someone else.::
There has to be a mechanism to declare which classes require a default processor. Hopefully, you already have a criteria for that, whether it is a pattern in the name, pattern in the qualifier, a possible annotation, and/or a base type. In the sample I provided above, you can specify the criteria in the method requiresDefaultConstructor(). Here is a sample of how it can be done:
Based on a name pattern. TypeElement provide access to the fully qualified name and package name.
return type.getQualifiedName().toString().contains("POJO");
Based on an annotation present on the type declaration. For example, all Java Bean Entity classes should have a non-default constructors
return type.getAnnotation(Entity.class) != null;
Based on a abstract class or interface.
TypeElement basetype = processingEnv.getElements().getTypeElement("com.notnoop.mybase");
return processingEnv.getTypes().isSubtype(type.asType(), basetype.asType());
[Recommended Approach]: If you are using the basetype interface, I recommend mixing the annotation approach with the base type interface. You can declare an annotation, e.g. MyPlain, along with the meta annotation: #Inherited. Then you can annotate the base type with that annotation, then all subclasses would inherit the annotation as well. Then your method would just be
return type.getAnnotation(MyPlain.class) != null;
This is better because it's a bit more configurable, if the pattern is indeed based on type hierarchy, and you own the root class.
As mentioned earlier, just because it is called "annotation processing", it does mean that you have to use annotations! Which approach in the list you want to follow depends on your context. Basically, the point is that whatever logic you would want to configure in your deployment enforcement tools, that logic goes in requiresDefaultConstructor.
Classes the processor will run on
Annotation Processors invocation on any given class depends on SupportedAnnotationTypes. If the SupportedAnnotationTypes meta-annotation specifies a concrete annotation, then the processor will only run on those classes that contain such annotation.
If SupportedAnnotationTypes is "*" though, then the processor will be invoked on all classes, annotated or not! Check out the [Javadoc](http://java.sun.com/javase/6/docs/api/javax/annotation/processing/Processor.html#getSupportedAnnotationTypes()), which states:
Finally, "*" by itself represents the
set of all annotation types, including
the empty set. Note that a processor
should not claim "*" unless it is
actually processing all files;
claiming unnecessary annotations may
cause a performance slowdown in some
environments.
Please note how false is returned to ensure that the processor doesn't claim all annotations.
No. The above check can be easier rewritten as:
try {
MyClass.newInstance();
} catch (InstantiationException E) {
// no constructor
} catch (IllegalAccessException E) {
// constructor exists but is not accessible
?
You can employ PMD and Macker in order to guarantee architectural rules.
In partilar, Macker provokes compilation errors, breaking your build process when validations fail.
Macker extends some concepts made popular by PMD regarding validations of source code. A good example is when you'd like to guarantee that all classes from a package implements a certain interface.
So, if you are very paranoid (like me!) about verifying all possible architectural rules, Macker is really useful.
http://innig.net/macker/
Note: The website is not great. Colors will hurt your eyes... but the tools is very useful anyway.
Richard Gomes
http://www.jquantlib.org/

Categories