So the class I'm doing JSR-303 bean validation on has two fields with the same pattern constraint applied to each:
#Column(name="test_suite_revision")
#XmlElement(name="test_suite_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTestSuite;
#Column(name="test_revision")
#XmlElement(name="test_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTest;
IMPORTANT - this class is NOT a form-backing class in a classic Spring MVC webapp but an entity class that lives at the base of a web service. So the validation is occuring in the service.
Now the web client that consumes the web service is a Spring MVC and does have a form-backing bean which ties to a jsp with places to put error messages.
So suppose a user enters an incorrectly-formatted string into one of the two fields. I can trap for it with this pretty standard code snippet
Set<ConstraintViolation<TestCase>> violations = validator.validate( permit);
if( !violations.isEmpty()) {
logger.debug( "basic validation FAILED with " + violations.size() + " errors");
Iterator<ConstraintViolation<TestCase>> iter = violations.iterator();
while( iter.hasNext()) {
ConstraintViolation<TestCase> cv = iter.next();
logger.debug( "invalidValue:" + cv.getInvalidValue());
logger.debug( "message:" + cv.getMessage());
ConstraintDescriptor<?> cd = cv.getConstraintDescriptor();
Map<String, Object> mapp = cd.getAttributes();
for( String keey : mapp.keySet()) {
logger.debug("mapp key:" + keey + ":" + mapp.get(keey));
}
which writes out
basic validation FAILED with 1 errors
invalidValue:050607
message:value must be of the form xx-xx-xxxx
mapp key:message:value must be of the form xx-xx-xxxx
mapp key:payload:[Ljava.lang.Class;#1367702
mapp key:flags:[Ljavax.validation.constraints.Pattern$Flag;#bf5210
mapp key:groups:[Ljava.lang.Class;#a49be5
mapp key:regexp:\d\d-\d\d-\d\d\d\d
Here's the rub: How does one figure out WHICH field failed validation? I can't seem to find a way to extract the field name , "revisionTest" or "revisionTestSuite"
from the ConstraintViolation object nor the ConstraintDescritpor object.
the getValidationAppliesTo() method newly available in version 1.1.0.Final of javax.validation-api seems promising but so far this method throws an AbstractMethodError during runtime. Ugh.
TIA,
Still-learning Steve
See ConstraintViolation#getPropertyPath method:
/**
* #return the property path to the value from {#code rootBean}
*/
Path getPropertyPath();
Path.Node#getName will give you the property name. For field names in nested beans, you have walk the path.
Related
I have a requirement to read information that is available in the META/MANIFEST.MF file of Spring Boot MVC web application and use this info to perform some business logic. I'm using gradle to build the application as war file and deploying it into the external tomcat.
I have tried the following:
#Configuration
public class AppConfig
{
#Bean("manifest")
public java.util.jar.Manifest getManifest() throws IOException
{
InputStream inputFile = this.getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
Manifest manifestObj = new Manifest(inputFile);
return manifestObj;
}
}
AppService.java
#Service
public class AppService
{
#Autowired
#Qualifier("manifest")
private Manifest manifest;
#PostConstruct
public String init()
{
Attributes mainAttributes = manifest.getMainAttributes();
String buildNum = mainAttributes.getValue("Build-Number");
String customPropInfo= mainAttributes.getValue("customPropInfo");
String systemPrp1= buildNum + "_" + "SomeBusinessInfoLogic1";
String systemPrp2= customPropInfo+ "_" + "SomeBusinessInfoLogic2";
//Some Business Logic with these attributes systemPrp, systemPrp2
logger.info("System Props are updated");
}
}
I'm getting null for both buildNum and customPropInfo.
Note: I have tried creating the Manifest bean something like this which was created by me. As per the #M.Deinum suggestion I'm creating this new question here. I also tried the solutions here which didn't work for me.
#M.Deinum suggested to make use of Spring Boot's Actuator Info endpoint. But this endpoint is useful when we want to access the info outside of the application but my requirement is different as I need the data that is available in MANIFEST.MF file to perform some business operations within the application.
I get the following error when I tried this solution "/META-INF/MANIFEST.MF".
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'manifest' defined in class path resource [com/abc/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.jar.Manifest]: Factory method 'getManifest' threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "java.io.InputStream.read(byte[], int, int)" because "this.in" is null
Can someone please help me to read information from META/MANIFEST.MF of the Spring Boot MVC Web Application?.
UPDATE1: I get the following MainAttributes when I try to print MainAttributes. But the problem is when I try to deploy the war into external tomcat.
System.out.println("Manifest MainAttributes = " +manifestObj.getMainAttributes().keySet());
Output:
Manifest MainAttributes = [Manifest-Version, Implementation-Title, Automatic-Module-Name, Implementation-Version, Built-By, Spring-Boot-Jar-Type, Build-Jdk-Spec]
UPDATE2:
I have updated to AppService.java to print the info available in autowired Manifest object. Something like below:
#Configuration
public class AppConfig
{
#Bean("manifest")
public java.util.jar.Manifest getManifest() throws IOException
{
InputStream inputFile = new ClassPathResource("/META-INF/MANIFEST.MF").getInputStream();
Manifest manifestObj = new Manifest(inputFile);
System.out.println("Manifest Manifest-Version = " +manifestObj.getMainAttributes().getValue("Manifest-Version"));
System.out.println("Manifest KeySet = " +manifestObj.getMainAttributes().keySet());
return manifestObj;
}
}
#Service
public class AppService
{
#Autowired
#Qualifier("manifest")
private Manifest manifest;
#PostConstruct
public String init()
{
Attributes mainAttributes = manifest.getMainAttributes();
mainAttributes.forEach((k,v) -> {
System.out.println("AppService.init(): Key = "+k+", Value = "+v);
});
String buildNum = mainAttributes.getValue("Build-Number");
String customPropInfo= mainAttributes.getValue("customPropInfo");
String systemPrp1= buildNum + "_" + "SomeBusinessInfoLogic1";
String systemPrp2= customPropInfo+ "_" + "SomeBusinessInfoLogic2";
//Some Business Logic with these attributes systemPrp, systemPrp2
logger.info("System Props are updated");
}
}
I see the following output on the console:
AppService.init(): Key = Implementation-Title, Value = Apache Tomcat Bootstrap
AppService.init(): Key = Implementation-Version, Value = 9.0.12
AppService.init(): Key = Specification-Vendor, Value = Apache Software Foundation
AppService.init(): Key = Specification-Title, Value = Apache Tomcat Bootstrap
AppService.init(): Key = Class-Path, Value = commons-daemon.jar
AppService.init(): Key = Manifest-Version, Value = 1.0
AppService.init(): Key = Main-Class, Value = org.apache.catalina.startup.Bootstrap
AppService.init(): Key = Implementation-Vendor, Value = Apache Software Foundation
AppService.init(): Key = Ant-Version, Value = Apache Ant 1.9.9
AppService.init(): Key = X-Compile-Target-JDK, Value = 1.8
AppService.init(): Key = X-Compile-Source-JDK, Value = 1.8
AppService.init(): Key = Created-By, Value = some xyz
AppService.init(): Key = Specification-Version, Value = 9.0
So just by the above output, I think MANIFEST.MF is not application specific but is from commons-daemon.jar.
OK - the problem isn't that you "can't read data from META/MANIFEST.MF in Spring Boot MVC Web Application". Rather, the problem is that your code happens to be reading the WRONG MANIFEST.MF from some other, random .jar in the classpath.
One solution might be to use JarClassLoader.
Another solution, as M. Deinum suggested, might be to store the properties you wish to retrieve in application.properties (or some other "global" properties file) instead of MANIFEST.MF.
ALSO:
I assume you're probably using an IDE to develop your app (Eclipse, Netbeans, etc). If you haven't already, I would STRONGLY encourage you to familiarize yourself with your IDE's debugger: the ability to set breakpoints, display variables, single-step through method calls, etc.
I am trying to use some Java RHS to get the string value of dependent tokens using Stanford dependency parser in GATE, and add them as features of a new annotation.
I am having problems targeting just the 'dependencies' feature of the token, and getting the string value from the tokenID.
Using below specifying only 'depdencies' also throws a java null pointer error:
for(Annotation lookupAnn : tokens.inDocumentOrder())
{
FeatureMap lookupFeatures = lookupAnn.getFeatures();
token = lookupFeatures.get("dependencies").toString();
}
I can use below to get all the features of a token,
gate.Utils.inDocumentOrder
but it returns all features, including the dependent tokenID's; i.e:
dependencies = [nsubj(8390), dobj(8394)]
I would like to get just the dependent token's string value from these tokenID's.
Is there any way to access dependent token string value and add them as a feature to the annotation?
Many thanks for your help
Here is a working JAPE example. It only printns to the GATE's message window (std out), It doesn't create any new annotations with features you asked for. Please finish it yourself...
Stanford_CoreNLP plugin has to be loaded in GATE to make this JAPE file loadable. Otherwise you will get class not found exception for DependencyRelation class.
Imports: {
import gate.stanford.DependencyRelation;
}
Phase: GetTokenDepsPhase
Input: Token
Options: control = all
Rule: GetTokenDepsRule
(
{Token}
): token
-->
:token {
//note that tokenAnnots contains only a single annotation so the loop could be avoided...
for (Annotation token : tokenAnnots) {
Object deps = token.getFeatures().get("dependencies");
//sometimes the dependencies feature is missing - skip it
if (deps == null) continue;
//token.getFeatures().get("string") could be used instead of gate.Utils.stringFor(doc,token)...
System.out.println("Dependencies for token " + gate.Utils.stringFor(doc, token));
//the dependencies feature has to be typed to List<DependencyRelation>
List<DependencyRelation> typedDeps = (List<DependencyRelation>) deps;
for (DependencyRelation r : typedDeps) {
//use DependencyRelation.getTargetId() to get the id of the target token
//use inputAS.get(id) to get the annotation for its id
Annotation targetToken = inputAS.get(r.getTargetId());
//use DependencyRelation.getType() to get the dependency type
System.out.println(" " +r.getType()+ ": " +gate.Utils.stringFor(doc, targetToken));
}
}
}
I've built a REST Service with Spring Boot. In one request method I'm validating the incoming request vs. an Object that I have annotated for validation (#NotNull etc.) using Hibernate Validator. The code for the REST endpoint looks like this:
#GetMapping(value = "/parameter-dates")
public ResponseEntity getParameterDates(ParameterDateRequest parameterDateRequest) {
Set<ConstraintViolation<ParameterDateRequest>> inputErrors = validator.validate(parameterDateRequest);
if (!inputErrors.isEmpty()) {
objectValidationErrorMessages = new ArrayList<>();
for (ConstraintViolation<ParameterDateRequest> constraintViolation : inputErrors) {
objectValidationErrorMessage = new ObjectValidationErrorMessage();
log.info("Error for user: " + loggedInUser.getUserEmail() +
" field: " + constraintViolation.getPropertyPath() + " with value: " + parameterDate.getParameterDateUnadjusted() +
" has error: " + constraintViolation.getMessage());
objectValidationErrorMessage.setFieldWithError(constraintViolation.getPropertyPath().toString());
objectValidationErrorMessage.setErrorMessage(constraintViolation.getMessage());
objectValidationErrorMessages.add(objectValidationErrorMessage);
}
return (new ResponseEntity(objectValidationErrorMessages, headers, HttpStatus.BAD_REQUEST));
}
//The rest of the code that is used when there is not validation errors
I would like to move the creation of the validation error messages to a metod of it's own like this:
public List<ObjectValidationErrorMessage> getErrorMessages(Class<?> clazz, Object model) {
List<ObjectValidationErrorMessage> objectValidationErrorMessages = new ArrayList<>();
Set<ConstraintViolation<?>> inputErrors = validator.validate(model);
if (!inputErrors.isEmpty()) {
for (ConstraintViolation<?> constraintViolation : inputErrors) {
objectValidationErrorMessage = new ObjectValidationErrorMessage();
objectValidationErrorMessage.setFieldWithError(constraintViolation.getPropertyPath().toString());
objectValidationErrorMessage.setErrorMessage(constraintViolation.getMessage());
objectValidationErrorMessages.add(objectValidationErrorMessage);
}
}
return objectValidationErrorMessages;
}
With the code as it is now I get the following error Unknow class: validateClass for Set<ConstraintViolation<validatedClass>>. How do I pass a Class name, in this case ParameterDateRequest as an argument to a method?
Update:
I manged to pass the Class as Class<?> clazz. I also realised that I need to pass the Object as an Object but I get this error:
validate (T, Class<?>...) in Validator cannot be applied
to (java.lang.Object)
reason: Incompatible equality constraint: ? and T
You can generify your method:
public <T> List<ObjectValidationErrorMessage> getErrorMessages(Class<T> clazz, T model) {
This will ensure that your Class<?> token and your Object have matching types.
Then pass the arguments as such:
validator.validate(model, clazz)
Note I could be misreading the error (I couldn't find the javadocs for the validator), so it's also possible that validator has a class-level generic (e.g. Validator<MyType>). If that's the case, then the T/Object you pass to validator#validate must match the type of the Validator, but the second parameter seems to accept any classtype (Class<?>). You also wouldn't need to generify the method as well, you would just have to match the type for the validator.
So I have to create a filter that adds/formats date strings.
My implementation was to create a ContainerRequestFilter and perform formatting there; then add this custom filter through #NameBinding.
My problem seems to be that #NameBinding is ignored when used with #PreMatching, thus not being able to work since I also use reflection to extract properties from my filter/annotation.
So after performing formatting in filter, the idea is to use:
uriBuilder.replaceQueryParam(startDateQueryParamName, formattedString);
but even if I add a hardcoded value, the value is still the original.
Say I make a request: .../api/x?startDate=1234-01-01T00:00:00
And I hardcode in filter:
`uriBuilder.replaceQueryParam(startDateQueryParamName, "2020-05-05T00:00:00");`
I still get 1234-01-01T00:00:00 in resource method:
#GET
#Path("/t1")
#Produces(MediaType.TEXT_PLAIN)
#StartEndDateFilter(required = true)
public String testLocalDateTime(#QueryParam("startDate") LocalDateTime startDate, #QueryParam("endDate") LocalDateTime endDate, #Context UriInfo urinfo) {
MultivaluedMap<String, String> m = urinfo.getQueryParameters();
String d = startDate == null ? "nothin " : startDate.toString();
String e = endDate == null ? "nothin " : endDate.toString();
return String.format("start: %s \nend: %s", d, e);
}
So, I thought maybe using #PreMatching would help but, as I mentioned, this shows a warning:
Warning: #PreMatching provider, class com.api.DateRangeFilter, also
annotated with a name binding annotation. Name binding will be
ignored.
And on top of that, when I call requestContext.setRequestUri(uriBuilder.build()); I get the following error when I call the endpoint:
Warning: StandardWrapperValve[com.api.Ap]: Servlet.service() for
servlet com.api.Ap threw exception java.lang.IllegalStateException:
Method could be called only in pre-matching request filter. at
org.glassfish.jersey.server.ContainerRequest.setRequestUri(ContainerRequest.java:411)
at com.api.DateRangeFilter.filter(DateRangeFilter.java:153)
line 153 is:
requestContext.setRequestUri(uriBuilder.build());
This is a design question is the same problem in any language.
Guys as you do to map the controller to the domain model?
We have situations in general larger than ... consider the example objects .
situation.1 - We have a request that has all the parameters of the account ;
{ " id" : " 1 " , "name " : "test " , "some " : " xxx " } ............. and other fields .
situation.2 - can request that has to have a certain account parameters , for example in the case of an update;
{" id" , " 1" , "name " , " testUpdated "}
situation.3 - We have a request that has some parameters of the account , others have more like id as user together;
{ " id" : " 1 " , "user " : " xxx " , "service " : " yyy " } in which case each piece of the request will turn an object .
public class Account {
private Long id;
private String name ;
private String some ;
}
I see a few options ;
1 - Can I get AccountForm in the controller and set the properties for the Account object and others in CONTROLLER ;
+ ok for situation.1 situations 2, and situation.3
+ Separates the requisition of the object domain
- Pollutes the controller with code conversion
- Controller is full of setters .. if a higher class as a large object as a request is very confusing .
controller ( AccountForm from ) {
Account account = new Account ( )
account.setNome form.getNome = ();
account.setSome form.getSome = ();
Other outher = new Other ( ) ;
other.setSome ( form.getSome ( ) ) ;
}
2 - Can I get AccountRequest in the controller and have a method in itself as AccountRequest.getAccount ( ) to return a mapped model , in this case the mapping is at own Request object .
+ Separates the requisition of the object domain
+ Encapsulates the conversion in a place with easy access .
+ Meets situation.1 situation.2 and situation3 ;
- Request object has two responsibilities represent the request and map to a valid model .
controller ( AccountForm accountRequest ) {
Account account = accountRequest.getAccount ( ) ;
Outher outher accountRequest.getOther = ( )
}
3 - Can I get the controller Direct Account which had been filled with nulls .
+ Eliminate object request
- Serves only situation.1 situation.2 .
controller (Account account ) {
account.someMethod ();
}
4 - Outsource this mapping request parameters to another object mapper for request ..
+ Isolates logic mapping
- Until complexity for simpler cases are used as standard for all such a find by id .
- One more class for each request ;
In the case of API gets worse response has two further classes. speaking in terms of request for response .... AccountRequest, AccountRequestMapper, Account, AccountResponseMapper, AccountResponse ........
I'm doing more testing the Hybrid option 3 for simple cases (find ID or updates) .... with option 2 for example for more complex cases ...
What would be ideal / What is expressive and easy to maintain? Thank you.
Each of your ways listed in your question is technically "correct" :). Whether you choose one of them or use a different approach depends on the complexity of your use case.
Do read through the DDD bible to understand if you even need a Domain model.
Now let us assume that you do and also further assume that Account is an aggregate root, then you certainly do not want to be exposing your Domain Model to the external word.
You will probably use a DTO or such to collect enough information about the Account model and then delegate to a factory/builder who will build out the Account AR for you.
Again i would encourage you to read through the book and ascertain if you need the complexity.