ui binder pattern: initializing variables using generators - java

I am trying to create a GWT generator that does the following:
public class MyPool {
#InitializeThisVariable
Element1 el1;
#InitializeThisVariable
Element2 el2;
private static final ChildPool childPool = GWT
.create(ChildPool.class);
interface ChildPool extends Pool<MyPool>{}
public MyPool(){
}
}
I want the generator to initialize the annotated fields. After doing some research, I have found out that the only way to do this is to use the pattern used by ui-binder as above (I do not want to use Annotations Processors).
However I get the following error when compiling:
[ERROR] Line 16: Rebind result 'ChildPool' must be a class
Help would be much appreciated.

Your generator needs to return name if the generated class. Either that or you forgot the <generate-with> in your module.
Also, your code doesn't make use of the generated Pool instance.
Note however that generators are being deprecated in GWT 2.8, and you should really use other kind of code generators (be it annotation processors or something else). You shouldn't start writing new generators nowadays.

Related

Using Lombok's SuperBuilder with Hibernate Validator (jakarta.validation.x) annotation on a container type leads to "Type mismatch"

I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.

How to dynamic generate Enum type from mysql in Java spring boot application?

In my project,we want manage all REST APIs errorcodes in a Enum type,like the following code:
package com.example.util
public enum ErrorType{
SUCCESS("0000", "success")
PARAMS_EMPTY("3001", "params cannot be empty")
}
The problem we encounter is if we put the class into util package,everytime we add a new error type in business spring boot app,we'll need to modify,publish and recompile the app and util project.That would be hard to maintance the util package.Basically,we prefer to maintance a relatively stable utility package.
So we are considering if there is a way that we can generate Enum type dynamiclly,we can comfigure the error information in mysql in advance,then we can load them into enum type in application boot procedure.
I'm not sure is this a good idea to dynamic generate enum type in Java,or if there is a better solution for this problem.
You can't add or remove values from an enum. Enums are complete static enumerations.
If you need to handle variable values you need to work with a standard class.
For example you can have something like the following:
public Error {
public static Error getByName(String name) {
....
}
public static Error getByCode(int code) {
....
}
}
and use it as follow:
Error myError404 = Error.getByCode(404);
Obviously this code gives you a lot of flexibility, but you cannot know in advance if a particular error code exists or not. And you can't use ide facilities related to enums.
Generating an enum would not be so useful I think, since part of the power of enums is that you can use them statically in your code - so then you have to update your code anyway.
How about adding a an exception handler to your util library that can be populated with error codes / description mappings from the database that can then translate errors/exceptions to response codes / error messages for your API? (just guessing you have an api here :-) )
Thanks for your reply,we finally decide to give up this idea.Dynamic generate an enum would not help,indeed it will add more complexity to our project,it's not a common use of enum.
Instead of this,we predefine our main error type likes user_error,system_error and db_error,etc.The specific error information will be processed in the business service.

Problems searching for annotated classes using Reflections API

I'm having a very difficult time using the Reflections API to find classes that are annotated with a custom annotation at runtime. The ultimate goal is to find all classes in the project that are annotated with my custom #Job annotation, collect them, and allow each of them to be run from one location without adding each one to the page manually. However, I'm finding it extremely difficult to get the initial search to work correctly, so I cannot move on with my project.
My current approach is to use:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("jobs"))
.setScanners(new TypeAnnotationsScanner())
.filterInputsBy(new FilterBuilder().includePackage("jobs")));
Set<Class<?>> jobs = reflections.getTypesAnnotatedWith(Job.class);
where "jobs" is the package containing all of the job classes that I am searching for, which will be annotated with the custom #Job annotation. "jobs" is a base package in my project, but the overall url on my machine looks something like ".../(project)/app/jobs". This setup results in one url being searched, which is ".../(project)/app/" with the additional filter "+jobs.*" in the configuration object. This seems like it is working correctly, but clearly something is wrong because I do not get any classes in the set.
If it matters, the annotation is coded as:
package jobs;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Job {
String description();
}
The annotation class is located within the same "jobs" package as the job classes I am searching for. An example of a job definition with the annotation included is:
package jobs;
#Job(description = "Description of what the job will do")
public class ExampleJob extends MasterJob {...}
I cannot find what I need to change in order to get this search to function as intended. Thanks for the help, and please let me know if I can clarify anything further.
EDIT: I believe the problem is associated with how the Play Framework loads its classes. Fortunately, the framework provides its own annotation search function, which I used instead. According to a comment, the code I have listed here will work, given that you have all the dependencies to run it. Feel free to use it as a template and let me know if it works for you as well.

IClassFile annotations

How do you check for Annotations when using IClassFile in Eclipse?
This doesnt seem to work classFile.getClass().isAnnotationPresent? Any help is appreciated.
The problem with using
for (final IClassFile classFile : classFiles) {
IAnnotation[] annotations = classFile.getType().getAnnotations();
Is that I have to get All the Packages, then get the Class Files in that package then get the Annotations. It will require 3 loops. Is there a way to minimize this?
I would say that the easiest way for you to find annotations is through a triple loop, but it might be slightly faster (assuming you are looking for a specific annotation) to use a 'SearchEngineinstead. Take a look at the source code for theorg.eclipse.jdt.internal.junit.launcher.JUnit4TestFinder` class. It looks for (source) classes annotated with #Test or #RunWith, which is similar to what you want to do, but for binary classes.
You would do something like this:
IJavaElement[] allPackagesToSearch = ...
SearchRequestor requestor = <implement the SearchRequestor abstract class and store all matches>
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(binaryPackages, IJavaSearchScope.APPLICATION_LIBRARIES);
int matchRule= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
SearchPattern runWithPattern= SearchPattern.createPattern("com.foo.MyAnnotation", IJavaSearchConstants.ANNOTATION_TYPE, IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE, matchRule);
SearchParticipant[] searchParticipants= new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
new SearchEngine().search(annotationsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 2));
It's a bit of a mouthful, and to figure out how this works, I'd recommend reading the JavaDoc for SearchEngine, SearchPattern, and SearchRequestor.
If you want to find all annotations, then change the match rule, and instead of "com.foo.MyAnnotation", use "*".

How-to dynamically fill a annotation

Sadly, I forgot to take the code from work with me today. But maybe this little example will clarify things.
I use hibernate to map a bean to a table.
Example:
import javax.persistence.column;
….
String columnameA;
….
#Column(name="columnameA")
public String getColumname(){
return columnameA
}
….
I do not want to hardcode the columnname (“columnameA”) in my sourcecode, because I need to switch the columname without building the entire project.
I wanted to use something like:
#Column(name=getColumnName())
This does not work. The idea is, to to write the columnname somewhere in the jndi tree and use it at startup. So i only need to restart the application to change the columnname.
The only way around this problem – which I can think of – is to write my own annotation, which extends the hibernate class. Is there a simpler way of doing this?
You can't achieve this with annotations, but a solution to your specific problem is to implement a custom NamingStrategy:
public class NamingStrategyWrapper implements NamingStrategy {
private NamingStrategy target;
public NamingStrategyWrapper(NamingStrategy target) {
this.target = target;
}
public String columnName(String arg0) {
if ("columnameA".equals(arg0)) return getColumnName();
else return target.columnName(arg0);
}
...
}
-
AnnotationConfiguration cfg = new AnnotationConfiguration();
cfg.setNamingStrategy(new NamingStrategyWrapper(cfg.getNamingStrategy()));
factory = cfg.configure().buildSessionFactory();
The only values you can assign to attributes are constant values, specified by hand, or stored in public static final variables.
Annotations do not define behavior, but only meta-informations about class, methods and the likes. You can specify behavior in annotation processors, that read your annotations and generate new source code or other files.
Writing an annotation processo is beyond my knowledge, but you could find other information in the Annotations Processing Tool guide by Sun.

Categories