java-compiler throws error while compiling code inside a fat jar - java

I have a spring boot application generating code dynamically at runtime and then compiling when the application is run. When App is run in IntelliJ the generated code is compiled without any errors but when I create a fat jar and generate and compile the code I get a package missing error and few other errors
error: package javax.validation.constraints does not exist
import javax.validation.constraints.Size;
error: package javax.validation.constraints does not exist
import javax.validation.constraints.Size;
this is how I am compiling the code
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);
Iterable fileObjects = sjfm.getJavaFileObjects(entityFiles.toArray(new Path[] {}));
List<String> optionList = new ArrayList<String>();
JavaCompiler.CompilationTask task =
compiler.getTask(null, null, null, optionList, null, fileObjects);
task.call();
sjfm.close();
Here is how generated code looks like
package com.abc.def.entities;
import java.io.Serializable;
import java.lang.String;
import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
#Table(
name = "TABLE123"
)
public class EntityV1 implements Serializable {
#Size(
min = 1,
max = 16
)
private String anumber;
#Size(
min = 15,
max = 16
)
#Id
private String cnumber;
#NotNull
private LocalDateTime acdt
#Size(
min = 1,
max = 16
)
private String cu_number;
#Size(
min = 1,
max = 16
)
}
I extracted the fat jar to see if the dependencies are in the jar and they are there,
Seems like the compiler is not able to recognise any annotation. Can anyone help?

Related

How do I create a nested JSON using Jackson?

I am trying to add the BusinessInformation data in my existing JSON.
{
"Original": "Web Form",
"SubmitterNetworkName": "null",
"SourceName": "Contact Request Form",
"SourceKind": "Web Form",
** "BusinessInformation": {
"BusinessContactName": null,
“AccountNumber”:null,
},**
"EmployeeName": null,
"EmployeeDOB": null,
}
So I have a Spring Batch app that exports data from a database to another DB and during that run some JSON needs to be created mapping multiple columns. I am using lombok and jackson mainly for the creation of JSON.
The model sample that I have. I also tried creating BusinessInformation class.
package model
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#JsonIgnoreProperties(ignoreUnknown = true)
public class ColumnNameForTheDBImImportingTo implements Serializable {
#JsonProperty("Origin")
private String Origin;
and so on..
}
And then I have the Service for it.
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import twc.batch.extcbk.model.*;
import java.io.IOException;
#Component
#Slf4j
public class ColumnNameForTheDBImImportingToService {
private ColumnNameForTheDBImImportingTo columnNameForTheDBImImportingTo (some codes) {
ColumnNameForTheDBImImportingTo columnNameForTheDBImImportingTo = ColumnNameForTheDBImImportingTo .builder().build();
columnNameForTheDBImImportingTo.setOrigin("Web Form");
}
**Then I have a method for the object mapper.**
private String getColumnNameForTheDBImImportingTo (ColumnNameForTheDBImImportingTo columnNameForTheDBImImportingTo ) {
String columnNameForTheDBImImportingToStr = null;
try {
ObjectMapper mapper = new ObjectMapper();
columnNameForTheDBImImportingToStr = mapper.writeValueAsString(columnNameForTheDBImImportingTo );
log.debug("columnNameForTheDBImImportingToStr {}", columnNameForTheDBImImportingToStr );
} catch (IOException e) {
log.error("getColumnNameForTheDBImImportingTo " + e.getMessage());
}
return columnNameForTheDBImImportingToStr ;
}
**Then another class builds the db columns and sets the values.**
I've tried following the Baeldung but I don't quite understand it.
I tried creating a new class for the business information and i was thinking of inserting it in the ColumnNameForTheDBImImportingTo.
Please refer me to any useful information I can follow for this problem.
Thanks!
Data transformation is a typical use case for an item processor.
The way I would implement this with Spring Batch is configure a chunk-oriented step defined as follows:
A JsonItemReader to read the input file
An item processor that adds the business information
A JsonItemWriter to write the output file

"annotation value must be an annotation " - for #Builder Annotation

I am trying to use the #Builder annotation on my class and I get this compilation error:
**error: annotation value must be an annotation**
C:\Sample\Work\SpringBoot-Version\SpringBoot-3.0\spring-base-dependencies\spring-base-api\src\main\java\com\sample\cloudnative\base\api\BaseBodyError.java:19: error: annotation value must be an annotation #Builder ^** 1 error FAILURE: Build failed with an exception.
* What went wrong: Execution failed for task ':compileJava'.
> Compilation failed; see the compiler error output for details.
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
I am using Lombok 1.18.24, Spring Boot 3.0.2, and Java 17.
This was an Exiting Code working for Java version 11 with spring Boot 2.7 . I just updated the Java Version to 17 and updated the Boot Version to 3.0.2 and just ran gradlew clean build. I am getting this error .
The Java class I am compiling
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ford.cloudnative.base.api.annotation.ApiProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ApiModel(description = "Base Body Error")
public class BaseBodyError {
#Singular
#Size(max = 8192)
#ApiModelProperty(value = "List of data errors")
List<#NotNull BaseBodyDataError> dataErrors;
#Singular
#ApiModelProperty(value = "Error field attributes")
Map<String, Object> attributes;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ApiModel(description = "Base Body Data Error")
public static class BaseBodyDataError {
#Size(max = 1024, message = "code length is limited to 1024 characters")
#Pattern(regexp = "^.{0,1024}$")
#ApiModelProperty(example = "Unauthorized", value = "Error Code")
String code;
}
}

Why is JsonDeserialize not able to contruct classes using Lombok SuperBuilder

I am trying to create an object model that represents a hierarchy of nested device locations. For example a 'deck' contains a 'slide tray' which contains one or more 'slides'. I want to be able to read in a json file that contains the hierarchy/configuration of the system. I want to use Lombok builders in my classes so I can safely generate the json files in code when I need to. The more common use case is to read in the json file to create the pojo's on application startup. Generating the json files with the builder works great. However, I have not been to de-serialize the file back into pojo's.
Here is the error I am getting:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `my.org.Deck$DeckBuilder` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"type":"Deck","locNumber":1,
The top level super-class is this:
package my.org;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Getter;
import lombok.Singular;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import java.awt.geom.Point2D;
import java.util.List;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Deck.class, name = "Deck"),
#JsonSubTypes.Type(value = SlideTray.class, name = "SlideTray"),
#JsonSubTypes.Type(value = Slide.class, name = "Slide"),
#JsonSubTypes.Type(value = NullLoc.class, name = "null"),
})
#SuperBuilder
#Getter
#Accessors(fluent = true, chain = true)
#JsonDeserialize(builder = BaseLocationType.BaseLocationTypeBuilder.class)
public class BaseLocationType<T extends BaseLocationType> {
#JsonProperty("locNumber")
private int locNumber;
#JsonProperty("posRelativeToParent")
private Point2D.Double positionRelativeToParent;
#Singular
#JsonProperty("childLocs")
private List<T> childLocs;
}
The Deck sub-class:
package my.org;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
#SuperBuilder
#Getter
#EqualsAndHashCode(callSuper = true)
#Accessors(fluent = true, chain = true)
#JsonDeserialize(builder = Deck.DeckBuilder.class)
public class Deck extends BaseLocationType<SlideTray> {
private String deckField1;
private String deckField2;
}
The SlideTray sub-class:
package my.org;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
#SuperBuilder
#Getter
#EqualsAndHashCode(callSuper = true)
#Accessors(fluent = true, chain = true)
#JsonDeserialize(builder = SlideTray.SlideTrayBuilder.class)
public class SlideTray extends BaseLocationType<Slide> {
private String slideTrayField1;
}
The Slide sub-class:
package my.org;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
#SuperBuilder
#Getter
#EqualsAndHashCode(callSuper = true)
#Accessors(fluent = true, chain = true)
#JsonDeserialize(builder = Slide.SlideBuilder.class)
public class Slide extends BaseLocationType<NullLoc> {
private String slideField1;
}
NullLoc:
package my.org;
import lombok.experimental.SuperBuilder;
#SuperBuilder
public class NullLoc extends BaseLocationType<NullLoc> {
// no fields or builder, etc
}
Test Code - fails with the above exception on mapper.readValue():
// create 1 deck with 1 slideTray that has 2 slides
Deck.DeckBuilder<?, ?> deckBuilder = Deck.builder()
.locNumber(1)
.positionRelativeToParent(new Point2D.Double(1.0, 1.0))
.deckField1("deck f1 data")
.deckField2("deck f2 data")
.childLoc(SlideTray.builder()
.locNumber(2)
.positionRelativeToParent(new Point2D.Double(2.0, 2.0))
.slideTrayField1("slide tray f1 data")
.childLoc(Slide.builder()
.locNumber(3)
.positionRelativeToParent(new Point2D.Double(3.0, 3.0))
.slideField1("child1-slide f1 data")
.build())
.childLoc(Slide.builder()
.locNumber(4)
.positionRelativeToParent(new Point2D.Double(4.0, 4.0))
.slideField1("child2-slide f1 data")
.build()).build());
Deck deckPojo = deckBuilder.build();
// serialize the pojo's
String json = new ObjectMapper().writeValueAsString(deckPojo);
// de-serialize the json back into the pojo's
ObjectMapper mapper = new ObjectMapper();
Deck deckPojoDeserialized = mapper.readValue(json, Deck.class);
The json that is generated:
{
"type": "Deck",
"locNumber": 1,
"posRelativeToParent": {
"x": 1.0,
"y": 1.0
},
"childLocs": [
{
"type": "SlideTray",
"locNumber": 2,
"posRelativeToParent": {
"x": 2.0,
"y": 2.0
},
"childLocs": [
{
"type": "Slide",
"locNumber": 3,
"posRelativeToParent": {
"x": 3.0,
"y": 3.0
},
"childLocs": []
},
{
"type": "Slide",
"locNumber": 4,
"posRelativeToParent": {
"x": 4.0,
"y": 4.0
},
"childLocs": []
}
]
}
]
}
note: I'm not seeing a option here in stackoverflow to upload the demo-project zip file... but can figure out a way to share that if needed.
Thanks!
I think the root problem is related to the #JsonDeserialize annotation builder values defined across the three primary sub-classes, because they appear to be abstract class references. Which would also explain the error message you're receiving.
From the Lombok #SuperBuilder documentation ref:
To ensure type-safety, #SuperBuilder generates two inner builder classes for each annotated class, one abstract and one concrete class named FoobarBuilder and FoobarBuilderImpl (where Foobar is the name of the annotated class).
I believe updating the following #JsonDeserialize annotation builder values will help resolve the issue:
In the Deck sub-class:
#JsonDeserialize(builder = Deck.DeckBuilderImpl.class)
In the SlideTray sub-class:
#JsonDeserialize(builder = SlideTray.SlideTrayBuilderImpl.class)
In the Slide sub-class:
#JsonDeserialize(builder = Slide.SlideBuilderImpl.class)
Additional note with respect to BuilderImpl manual updates:
The #SuperBuilder documentationref includes the following supporting information relative to this topic:
Customizing the code generated by #SuperBuilder is limited to adding new methods or annotations to the builder classes, and providing custom implementations of the 'set', builder(), and build() methods. You have to make sure that the builder class declaration headers match those that would have been generated by lombok. Due to the heavy generics usage, we strongly advice to copy the builder class definition header from the uncustomized delomboked code.

Different validation annotation rule for same Data Access Object

How to customize the validation annotation based on some criteria using javax validation with Hibernate validation implementation.
Sample code:
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import org.springframework.stereotype.Component;
import com.kp.mechanic.workshop.validator.beans.GeoLocation;
import com.kp.mechanic.workshop.validator.beans.Practioner;
import com.kp.mechanic.workshop.validator.beans.PractionerAddress;
#Component("validator")
public class ValidatorService {
private Validator validator;
private ValidatorFactory factory;
public void execute() {
System.out.println(" Validation framework starts");
try {
// < DAO call to get GEO Location is AUS >
//Construct Geo Location:
GeoLocation geoLocation= new GeoLocation();
geoLocation.setStrtAddrLine1("walker street ");
geoLocation.setOptionalAddrLine2("bonitoa road");
geoLocation.setZipCD("SY");
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<Object>> resultSet= validator.validate(geoLocation);
for (ConstraintViolation<Object> object : resultSet) {
System.out.println(object.getPropertyPath() + ": " + object.getMessage());
}
}
catch(Exception e) {
System.out.println("Message "+e);
e.printStackTrace();
}
}
}
Refer the given below POJO using Lombok.
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
public class GeoLocation {
//City name
#NotNull(message="warn:Missing data")
#Size(min =3 , max =50, message = "warn:Invalid length")
#Pattern(regexp = "^a-zA-Z",message = "warn:Invalid characters found in text",flags=Pattern.Flag.CASE_INSENSITIVE)
private String cityNM;
//State Code
#NotNull(message="warn:Missing data")
#Size(min =2 , max =2, message = "warn:Invalid Length")
#Pattern(regexp = "^[a-zA-Z]",message = "warn:Invalid characters found in text",flags=Pattern.Flag.CASE_INSENSITIVE)
private String stateCD;
//zip code
#NotNull(message="warn:Missing data")
#Size(min =5 , max =9, message = "warn:Invalid Length")
#Pattern(regexp = "^[0-9]",message = "warn:Invalid characters found in text")
private String zipCD;
}
Using given above pom entries .
The above code is working fine for the given below validation rules for GEO Location is "AUS".
> CityName : not null , minimum 3 and maximum 50 characters, only alphabets.
> State Code : not null , maximum 2 , only alphabets.
> Zip Code : not null , minimum 5 and maximum 9, only digits.
Where as for "IND" , i would like to change the given below validation rules as such.
> CityName : not null , minimum 10 and maximum 15 characters, only alphabets
> State Code : not null , maximum 6, only alphabets.
> Zip Code : not null , maximum 10, only digits
Can you give any suggestion to change the validation rules based on the geo location type is IND?
This is a kind of Custom Annotation, is there any better approach to reuse the annotation without writing java logic in custom annotation class ?
Custom Annotation for cross fields
First of all appreciate for a nice question.
In above scenario I would go for custom annotation based validator as shown below.
package com.example.demo;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Constraint(validatedBy = StateCodeValidator.class)
#Documented
#Target({METHOD, FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface StateValidate {
String message() default "Invalid State";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.example.demo;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
public class StateValidator implements ConstraintValidator<StateValidate, String>{
#Autowired
private HttpServletRequest httpServletRequest;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//TODO you can check code size as per requirement
}
}
}
You need to annotate #StateValidate at any field you looking for
This will be more controlled approach and if needed i18 can be used to
manage to support multiple countries.
I got reference from given below links.
stackover flow .
Programmatic constraint definition
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure();
ConstraintMapping constraintMapping = configuration.createConstraintMapping();
if(geoLocation.getType().equals("AUS")) {
constraintMapping
.type(GeoLocation.class)
.property("cityName", ElementType.FIELD)
.constraint(new NotNullDef().message("cityName should not empty"))
.constraint(new SizeDef().min(3).message("Minimum 3 charater"))
.constraint(new SizeDef().max(50).message("Maximum 50 character"));
}else if(geoLocation.getType().equals("IND")) {
constraintMapping
.type(GeoLocation.class)
.property("cityName", ElementType.FIELD)
.constraint(new NotNullDef().message("cityName should not empty"))
.constraint(new SizeDef().min(10).message("Minimum 10 charater"))
.constraint(new SizeDef().max(15).message("Maximum 15 character"));
}
validator=configuration.addMapping(constraintMapping).buildValidatorFactory().getValidator();
resultSet= validator.validate(geoLocation);
for (ConstraintViolation<Object> object : resultSet) {
System.out.println(object.getPropertyPath() + ": " + object.getMessage());
}

JAXB namespace unmarshal changes between Java 8u45 and 8u111

I have looked through the jaxb and java changelogs between these two versions and have come up empty handed.
In 8u45 this code works fine,
package-info.java
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://www.example.org",
elementFormDefault = XmlNsForm.QUALIFIED
) package com.rezometry.fw.net.soap;
import javax.xml.bind.annotation.XmlNsForm;
Foo.java
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType( XmlAccessType.FIELD)
#XmlRootElement(name = "foo")
public class Foo {
#XmlElement( name = "bar", required = true )
public String bar;
}
Test.java
import javax.xml.transform.stream.StreamSource;
import javax.xml.bind.JAXB;
import java.io.StringReader;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
#Test public void testJaxB(){
Foo sample = JAXB.unmarshal( new StreamSource( new StringReader( "<foo><bar>bar1</bar></foo>" ) ), Foo.class );
assertThat( sample.bar, is("bar1") );
}
Yet in 8u111 sample.bar is null. I have read the various other answers regarding missing namespaces in the xml document, but I want to make sure going forward I am not patching over a bug that might be changed later.
Im am seeing this happen in intellij and the only difference in the set up is what version of java I am using. All other libraries are identical.

Categories

Resources