I want to declare a warning on all fields Annotated with #org.jboss.weld.context.ejb.Ejb in AspectJ.
But I do not find a way how to select that field.
I guess the aspect should be something like that:
public aspect WrongEjbAnnotationWarningAspect {
declare warning :
within(com.queomedia..*) &&
??? (#org.jboss.weld.context.ejb.Ejb)
: "WrongEjbAnnotationErrorAspect: use javax.ejb.EJB instead of weld Ejb!";
}
Or is it impossible to declare warnings on fields at all?
The only field pointcuts I see are for get and set. This makes sense because aspects are primarily about executing code. Declaring compiler warnings is sortof a nice side benefit. If we just talk about a field, independent of the use of that field, when would the pointcut be hit? I think you should be able to do what you want with the Annotation Processing Tool instead of AspectJ. Here is a first stab at it, mostly copied from the example on the tool's web page linked above.
public class EmitWarningsForEjbAnnotations implements AnnotationProcessorFactory {
// Process any set of annotations
private static final Collection<String> supportedAnnotations
= unmodifiableCollection(Arrays.asList("*"));
// No supported options
private static final Collection<String> supportedOptions = emptySet();
public Collection<String> supportedAnnotationTypes() {
return supportedAnnotations;
}
public Collection<String> supportedOptions() {
return supportedOptions;
}
public AnnotationProcessor getProcessorFor(
Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
return new EjbAnnotationProcessor(env);
}
private static class EjbAnnotationProcessor implements AnnotationProcessor {
private final AnnotationProcessorEnvironment env;
EjbAnnotationProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
public void process() {
for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations())
typeDecl.accept(new ListClassVisitor());
}
private static class ListClassVisitor extends SimpleDeclarationVisitor {
public void visitClassDeclaration(ClassDeclaration d) {
for (FieldDeclaration fd : d.getFields()) {
fd.getAnnotation(org.jboss.weld.context.ejb.Ejb.class);
}
}
}
}
}
Sort of agree with #JohnWatts, but also feel that get() will work for you:
declare warning :
within(com.queomedia..*) &&
get(#org.jboss.weld.context.ejb.Ejb * *.*)
: "WrongEjbAnnotationErrorAspect: use javax.ejb.EJB instead of weld Ejb!";
This will show a warning at any code that tries to use the fields annotated with #org.jboss.weld.context.ejb.Ejb and not the field itself, but should suffice as a compile time warning?
Related
I am currently working on a AWS Lambda using Java 11. It is requiring me for an implementation of the handler to have an empty constructor. My handler looks like this
public class ApiKeyHandler {
private final SecretsManagerClient secretsManagerClient;
public ApiKeyHandler() {
secretsManagerClient = DependencyFactory.secretsManagerClient();
}
public void handleRequest(Object event, Context context) {
//Other codes here
secretsManagerClient.getSecret(/../);
}
}
And Dependency Factory class
public class DependencyFactory {
private DependencyFactory() {}
/**
* #return an instance of SecretsManagerClient
*/
public static SecretsManagerClient secretsManagerClient() {
return SecretsManagerClient.builder()
.region(/**/)
.build();
}
}
Now, when I am trying to write unit test for this I cant mock objects in constructor. Is there a way I can mock it?
I tried
#Mock SecretsManagerClient secretsManagerClient;
#InjectMocks ApiKeyHandler handler;
but no luck. Thank you
It looks like you have a couple of options:
You can add another constructor with the parameters to inject. This is easy and clean from the test perspective, but after all you'll have the code in production (this constructor in this case) that is used only for tests.
In generally I don't advocate this approach, although I understand that there are technology limitations here.
You can Mock the Dependency Factory. Since the call is static, you might end up using PowerMock / PowerMockito that can actually mock static calls. This is something that can turn to be really painful to maintain, in general this approach is discouraged these days.
You can Rewrite the DependencyFactory so that it could be configured with some kind of mock implementation (that will allow to specify mock dependencies):
public interface DependencyFactoryMode {
SecretsManagerClient secretsManagerClient();
}
public class RealDependencyFactoryMode implements DependencyFactoryMode {
public SecretsManagerClient secretsManagerClient() {
return SecretsManagerClient.builder()
.region(/**/)
.build();
}
}
// in src/test/java - test code in short
public class DependencyFactoryTestMode implements DependencyFactoryMode {
private SecretsManagerClient smc = Mockito.mock(SecretsManagerClient.class);
public SecretsManagerClient secretsManagerClient() {
return smc;
}
// this will be used in tests
public SecretsManagerClient getSmcMock() {return smc;}
}
public class DependencyFactory {
private static DependencyFactoryMode mode;
static {
// depending on the configuration, external properties or whatever
// initialize in production mode or test mode
// of course this is the most "primitive" implementation you can probably
// do better
if(isTest) {
mode = new TestDependencyFactoryTestMode();
} else {
// this is a default behavior
mode = new RealDependencyFactoryMode();
}
}
private DependencyFactory() {}
public static DependencyFactoryMode getMode() {
return mode;
}
public static SecretsManagerClient secretsManagerClient() {
return mode.secretsManagerClient();
}
}
With this approach you'll have to pre-configure the dependency factory so that while running in the test it will "know" that it should run in the test mode.
public class Test {
#Test
public void test() {
// DependencyFactoryMode will be used in the test mode
DependecyFactoryMode testMode = DependencyFactory.getMode();
var smc = testMode.secretsManagerClient();
Mockito.when(smc.foo()).thenReturn(...);
}
}
Now this approach suffers from the same drawback as "1" but at least you have a code "only for tests" only in the factory, rather than in all lambda functions (I assume you have many of them, otherwise probably the first approach will be the least of all evils).
Another possible drawback is that the same instance of DependencyFactory (with the shared static mocking mode) will be shared between the tests, so you might end up "reseting" all the relevant mocks after the test.
Again, these all are complications because in the form that you've presented there is no way to provide a dependency injection in constructor because of the technology limitation.
Add a second constructor that accepts parameters:
public ApiKeyHandler(SecretsManagerClient client) {
secretsManagerClient = client;
}
public ApiKeyHandler() {
this(DependencyFactory.secretsManagerClient());
}
I need to ensure that, in specific classes (e.g. all classes extending some other class), fields annotated with e.g. #Deprecated are also annotated with #ThisOtherAnnotationMustBeHere.
#Deprecated
#ThisOtherAnnotationMustBeHere // this must be present if #Deprecated is also present; otherwise build should fail
private String field;
I need in general something to check for the presence of annotations.
I guess I could write a JUnit test for this using reflection, but I was wondering if there was a Maven solution to this.
Following #khmarbaise suggestion (thanks!) I've used archunit.org to write a unit test for this. In my case I needed to verify that join fields in JPA entities were annotated with a specific custom JsonAdapter
class CodeChecksTest {
#ArchTest
public static final ArchRule persistenceIdAnnotationRule = fields().that()
.areDeclaredInClassesThat().areAnnotatedWith(Entity.class).and()
.areAnnotatedWith(OneToOne.class).or()
.areAnnotatedWith(OneToMany.class).or()
.areAnnotatedWith(ManyToOne.class).or()
.areAnnotatedWith(ManyToMany.class)
.should(beAnnotatedForMyCustomAdapter());
private static ArchCondition<? super JavaField> beAnnotatedForMyCustomAdapter() {
return new ArchCondition<JavaField>("annotated with #JsonAdapter(MyCustomAdapter.class)") {
#Override
public void check(JavaField item, ConditionEvents events) {
final Optional<JsonAdapter> annotation = item.tryGetAnnotationOfType(JsonAdapter.class);
final boolean satisfied = annotation.isPresent() && annotation.get().value() == MyCustomAdapter.class;
// createMessage is a utility method
String message = createMessage(item,
(satisfied ? "is " : "is not ") + getDescription());
events.add(new SimpleConditionEvent(item, satisfied, message));
}
};
}
}
I have an #Audit annotation, it has many optional attributes, I need to enforce the use of one boolean attribute useAccount = true for certain packages.
I am trying to use archunit to accomplish this validation, that way whenever a developer commits code that breaks the rule the CI will break and inform the team.
This would break the build:
#Audit
public myMethod(...) {
...
}
This is the right way:
#Audit(useAccount = true)
public myMethod(...) {
...
}
The problem is that Archunit doesn't currently support asserting over methods. I was expecting to do something like:
methods().that().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)
Then my custom condition attributeCheckCondition would take care of looking into the attribute value.
Is there a way of retrieving methods as we retrieve classes? Without having to write a more complicated predicate and condition?
Update
Since ArchUnit 0.10.0 it is possible to create rules for members.
methods().that()
.areDeclaredInClassesThat()
.resideInAnyPackage("..controllers..", "..service..")
.and()
.areAnnotatedWith(Audit.class)
.should(attributeCheckCondition)
See also Composing Member Rules in the User Guide.
Original Answer
Since there are currently no basic rule definitions available for methods, an intermediate step is necessary. ArchUnit has a ClassesTransformer to transform JavaClasses into a collection of other types.
ClassesTransformer<JavaMethod> methods = new AbstractClassesTransformer<JavaMethod>("methods") {
#Override
public Iterable<JavaMethod> doTransform(JavaClasses javaClasses) {
Set<JavaMethod> allMethods = new HashSet<>();
for (JavaClass javaClass : javaClasses) {
allMethods.addAll(javaClass.getMethods());
}
return allMethods;
}
};
This ClassesTransformer can then be used as a base for custom rule definitions.
ArchRule rule = ArchRuleDefinition.all(methods)
.that(owner(resideInAnyPackage("..controllers..", "..service..")))
.and(annotatedWith(Audit.class))
.should(haveAttributeValue());
rule.check(javaClasses);
See also Rules with Custom Concepts in the User Guide and this issue.
I found a way of doing it with custom predicate and condition over classes, when I did that I was not aware of Roland's response which seems to be better, as it provides a way to express the rule assertion from the methods perspective which is why I was asking for.
However I wanted to post the solution here so it can be useful for others.
DescribedPredicate<JavaClass> HAVE_A_METHOD_ANNOTATED_WITH_AUDIT =
new DescribedPredicate<JavaClass>("have a method annotated with #Audit")
{
#Override
public boolean apply(JavaClass input)
{
return input.getMethods().stream().anyMatch(method -> method.isAnnotatedWith(Audit.class));
}
};
ArchCondition<JavaClass> ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE =
new ArchCondition<JavaClass>("only set useAccount attribute to true")
{
#Override
public void check(JavaClass item, ConditionEvents events)
{
item.getMethods().stream().filter(method ->
method.isAnnotatedWith(Audit.class) && !method.getAnnotationOfType(Audit.class)
.useAccount()
)
.forEach(method -> {
String message = String.format(
"Method %s is annotated with #Audit but useAccount is not set to true",
method.getFullName());
events.add(SimpleConditionEvent.violated(method, message));
});
}
};
Then the rule is expressed as:
ArchRule ANNOTATION_RULE = classes()
.that()
.resideInAnyPackage("..controller..", "..service..")
.and(HAVE_A_METHOD_ANNOTATED_WITH_AUDIT)
.should(ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE);
Here is another custom example in addition to #raspacorp (who inspired me!).
To check #Secured(ROLE) method annotation, I've implemented the following rule:
public static class SecuredByRoleArchCondition extends ArchCondition<JavaMethod> {
private final String[] expectedRoles;
public SecuredByRoleArchCondition(String[] expectedRoles) {
super(String.format("accessed by #Secured methods with roles %s", Arrays.toString(expectedRoles)));
this.expectedRoles = expectedRoles;
}
public static SecuredByRoleArchCondition haveSecuredAnnotationWithRoles(String... expectedRoles) {
return new SecuredByRoleArchCondition(expectedRoles);
}
#Override
public void check(JavaMethod javaMethod, ConditionEvents events) {
if (!javaMethod.isAnnotatedWith(Secured.class)) {
String message = String.format("Method %s annotation #Secured(%s) is missing",
javaMethod.getFullName(), Arrays.toString(expectedRoles));
events.add(SimpleConditionEvent.violated(javaMethod, message));
return;
}
String[] annotationRoleValues = javaMethod.getAnnotationOfType(Secured.class).value();
if (!Arrays.equals(annotationRoleValues, expectedRoles)) {
String message = String.format("Method %s #Secured with %s has wrong roles, expected %s instead",
javaMethod.getFullName(), Arrays.toString(annotationRoleValues), Arrays.toString(expectedRoles));
events.add(SimpleConditionEvent.violated(javaMethod, message));
}
}
}
Here is a sample usage of this archCondition:
#ArchTest
static ArchRule admin_actions_with_post_mapping_should_be_secured_by_ADMIN_WRITE_role =
methods()
.that().areDeclaredInClassesThat().resideInAnyPackage(ADMIN_PACKAGES)
.and().areAnnotatedWith(PostMapping.class)
.should(haveSecuredAnnotationWithRoles("ADMIN_WRITE"));
I would like to provide elegant mechanism to skip chosen tests when value of some environmental variable is not admissible. I chose adding my own annotation #RunCondition to define which value are allowed for particular tests. Then I created my own listener for TestNG that marks tests as disabled when value of environmental variable is not within admissible scope defined in annotation parameters.
My code looks as follows:
public class ExampleTest {
private int envVar;
#BeforeClass
public void setUp() {
//set up of some environmental variables which depends on external source
StaticContext.setVar(getValueFromOuterSpace());
}
#RunCondition(envVar=2)
#Test
public void testFoo(){
}
}
public class SkipTestTransformer implements IAnnotationTransformer {
#Override
public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
RunCondition annotation = method.getAnnotation(RunCondition.class);
int[] admissibleValues = annotation.envVar();
for (int val : admissibleValues) {
if (StaticContext.getVar() == val) {
return; // if environmental variable matches one of admissible values then do not skip
}
}
iTestAnnotation.setEnabled(false);
}
}
public #interface RunCondition {
int[] envVar();
}
My code works great, but there is a small problem that transform method is invoked before the setUp which is the #BeforeClass function. Is there any other possibility to run Transformer after all initialization of test? I consider such solution elegant and clear and I don't want any ugly if clauses to reach my goal...
I'm using Java 7 and TestNG v5.11.
Try to implement IMethodInterceptor (An instance of this class will be invoked right before TestNG starts invoking test methods.) instead of annotation transformer. It will allow to manage list of tests which will be executed. It also allows to work with your tests annotations. The restriction is that test methods having dependencies will not be passed to intercept method.
There is a better concept directly supported by the testing frameworks called assumptions. You should not disable the test, but rather skip the execution:
in JUnit you can use assumeThat(boolean) family of methods
in TestNG you can throw SkipException
In that case the method will not disappear, it will be marked as skipped.
You can check your own annotation in a setup method (#BeforeMethod) and throw a SkipException to skip this test.
public class ExampleTest {
private int envVar;
#BeforeClass
public void setUp() {
//set up of some environmental variables which depends on external source
StaticContext.setVar(2);
}
#BeforeMethod
public void checkRunCondition(Method method) {
RunCondition annotation = method.getAnnotation(RunCondition.class);
if (annotation != null) {
int[] admissibleValues = annotation.envVar();
for (int val : admissibleValues) {
if (StaticContext.getVar() == val) {
// if environmental variable matches one of admissible values then do not skip
throw new SkipException("skip because of RunCondition");
}
}
}
}
#RunCondition(envVar = 2)
#Test
public void testFoo() {
}
#Retention(RetentionPolicy.RUNTIME)
public #interface RunCondition {
int[] envVar();
}
}
The following is an approximation of the problem I'm facing.
Think we have a password validator with some rules.
public interface RuleChecker{
//Checks for a password strenght, returns 10
//for strong or 0 for soft password.
int check(String pass);
}
And then we have several implementations, our service will only accept the password if it is over 8 score.
public class NoCheck implements RuleChecker {
public int check(String pass){return 10;}
}
public class LengthCheck implements RuleChecker{
...
}
public class AlphanumericCheck implements RuleChecker{
...
}
public class AlphaAndLenghtCheckAdapter implements RuleChecker{
...
}
But for testing purposes, we want to implement a webservice within the application where we can "admin" those rules, and select which ones to have.
public class PasswordCheckService{
private RuleChecker checker;
#Inject
public PasswordCheckService(final RuleChecker checker){
this.checker = checker;
}
public boolean checkPassword(String password){
return checker.check(password) > 8;
}
}
So, is there any way in Guice, to change at runtime, the injection a service has?
Example:
We started the application and by default LengthCheck is selected and injected on the application, at the website we select the NoCheck checkbox and save options, which is stored into the database, can I configure Guice to automatically change the bean the service had injected before? so from now and on there will be no checks on new passwords?
--
As for now, I have found those topics
Google Guice and varying injections at runtime
But i dont know if that kind of providers fits my problem.
Guice runtime dependency parameters reinjection
That nice question is talking something similar, but not what I'm looking form.
guice: runtime injection/binding at command line
This is the closest to my problem but he only does on starting "runtime" and does not change it over the time.
Any helps?
Thank you!
Using the tip of the first comment I implemented this POC but still does not works, if you change select another button the service bean is not updated.
https://bitbucket.org/ramonboza/guicedynamicconfig
Create a provider for each field type (login, password, birth date...), with a parameter to change the implementation to return.
public class MyModule extends AbstractModule {
public void configure() {
bind(RuleChecker.class).annotatedWith(named("password")).toProvider(PasswordRuleCheckerProvider.class);
bind(RuleChecker.class).annotatedWith(named("login")).toProvider(LoginRuleCheckerProvider.class);
}
}
public static class PasswordRuleCheckerProvider implements Provider<RuleChecker> {
private static CheckType type = CheckType.ALPHANUMERIC;
// static type setter.
public RuleChecker get() {
// it would even be better if you could use singletons here.
switch(type) {
case LENGTH:
return new LengthCheck();
case ALPHANUMERIC:
return new AlphanumericCheck();
case ALPHALENGTH:
return new AlphaAndLenghtCheckAdapter();
case NONE:
default:
return NoCheck();
}
}
}
// Almost same provider for your LoginRuleCheckerProvider. You could do something generic.
In your admin section you change "type" value, so your rules will change. It can affect a limited set of fields, thanks to the annotations. For instance : PasswordRuleCheckerProvider.setType(CheckType.LENGTH);. Will only affect fields with #Named('password').
You have to declare your fields and services like this :
public abstract class DynamicService {
protected void updateService() {
// Reinject with the new implementations the members.
App.getInjector().injectMembers(this);
}
}
public class PasswordCheckService extends DynamicService {
#Inject
#Named("password")
private RuleChecker passwordChecker;
public void changePasswordCheckType(CheckType type) {
PasswordRuleCheckerProvider.setType(type);
// Reinject, so you have your new implementation.
updateService();
}
// [...]
}