Is it possible to add message parameter for constraint violation template message? - hibernate-validator

I have my custom validator and I want to add some error messages for it.
So I have next code:
#Override
public boolean isValid(final String label,
final ConstraintValidatorContext constraintValidatorContext) {
constraintValidatorContext.disableDefaultConstraintViolation();
if(label.length() > MAX_LENGTH) {
constraintValidatorContext
.buildConstraintViolationWithTemplate("{error.maxLength}")
.addConstraintViolation();
return false;
}
...
}
My message looks like error.maxLength=You have exceeded max length of {0}, so it has parameter of maxLength.
It it possible to add it when building constraint violation?

Yes, you can.
You have to unwrap the ConstraintValidatorContext to an HibernateConstraintValidatorContext with:
HibernateConstraintValidatorContext hibernateConstraintValidatorContext = constraintValidatorContext.unwrap( HibernateConstraintValidatorContext.class );
Then, you can either use:
hibernateConstraintValidatorContext.addMessageParameter("name", value);
(and yours look like a message parameter ({variable}) so it's probably what you have to use - note you need HV 5.4.1.Final to have this method)
or
hibernateConstraintValidatorContext.addExpressionVariable("name", value);
if it's an expression variable (so using Expression Language and something like ${variable})

Related

Around Advice not working when dependent on response from other REST service

I am working with Spring AOP to define a common fallback method instead of duplicating code.
I used #Around as I have to return the object from Aspect.I am trying to decide #Around advice depending on the response returned,but not able to do so.
Here is my controller:
#RequestMapping(value = "/add/employee", method = RequestMethod.GET)
public EmployeeResponse addEmployee(#RequestParam("name") String name, #RequestParam("empId") String empId) {
EmployeeResponse employeeResponse=employeeService.createEmployee(name, empId);
return employeeResponse;
}
createEmployee in the service class is used to call another endpoint to insert some data.I want to decide my advice based on the employeeResponse but not able to do so.
I tried #AfterReturning also,but I can't return the object if I use that.
Below is my aspect class:
#Around(value = "execution(* com.test.service.EmployeeService.*(..)) and args(name,empId)")
public Object getAllAdvice2(ProceedingJoinPoint pjp, String name,String empId) throws Throwable {
System.out.println("Inside Aspect");
Object[] arguments = pjp.getArgs();
if (!checkForPath()) {
return pjp.proceed();
}
System.out.println("Call Second path please!!");
return arguments;
}
private boolean checkForPath() {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getResponse();
return response.getStatus()==501?true:false;
}
}
I did use HttpServletResponse and RequestContextHolder to get the context but seems it will take the present context i.e. "/add/employee".
How can I return the actual status from the checkForPath () (since I don't need to call pjp.proceed for every status code returned) so that I can execute the line System.out.println("Call Second path please!!"); depending on my error code.
Can anyone pls suggest where it is going wrong?
Your aspect code is quite chaotic and does not make much sense:
You are trying to check for a response before calling proceed(), as R.G said. Use something like EmployeeResponse response = (EmployeeResponse) proceed() instead, inspect the response and then decide what to do next.
You already bind the method parameters to name and empId, there is no need to use pjp.getArgs().
return arguments does not make sense because you ought to return an EmployeeResponse object (either the original result or another one), not the array of method arguments.

Custom ValidationAttribute: How to check for duplicate value, ignoring the object being edited

I'm trying to write a custom ValidationAttribute that verifies no records already exist with the same value.
The problem is that if the user is editing an existing record, then my code will find a record that matches the same value. It will find the one that is being edited if the user has not changed that value.
So I thought I could compare the value to the value in ValidationContext.ObjectInstance to detect when it hasn't changed, something like this:
public class UrlTagValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string tag = value as string;
if (string.IsNullOrWhiteSpace(tag))
return new ValidationResult("URL Tag is required.");
// We allow a valid that is equal to the original value
if (context.ObjectInstance is TrainerModel model && model.Tag == tag)
return ValidationResult.Success;
// Cannot be an existing tag
using (var dbContext = new OnBoard101Entities())
{
if (!dbContext.TrainerDetails.Any(td => td.Tag == tag))
return ValidationResult.Success;
}
return new ValidationResult("This URL Tag is not available. Please enter a different one.");
}
}
But this doesn't work. I find that the value in ValidationContext.ObjectInstance often matches the value entered even when I'm creating a new record.
I'm having a hard time finding good and current documentation on the current usage of ValidationContext. Can someone suggest a way to check if any records exist that match the entered value BUT allowing it when a record is being edited and the value of this field has not changed?
The item which is currently being edited most likely has some kind of property to identify it (look it up in the database). Therefore, you need to get that property so you can exclude that when you are searching the database for duplicate tags. Here is how to do that in your custom validation class. I am making the assumption the identifier is named TrainerId:
public class UrlTagValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string tag = value as string;
if(string.IsNullOrWhiteSpace(tag))
return new ValidationResult("URL Tag is required.");
var currentTrainer = validationContext.ObjectInstance
as TrainerModel;
if (currentTrainer == null)
{
// What do you want to do? You cannot always return an error
// because another type could be using this custom validation.
// Or you can return an error. Depends on your requirements and
// and usage.
}
using(var dbContext = new OnBoard101Entities())
{
if(dbContext.TrainerDetails.Any(td => td.Tag == tag && td.TrainerId !=
currentTrainer.TrainerId))
{
return new ValidationResult("This URL Tag is not available. Please enter a different one.");
}
}
return ValidationResult.Success;
}
}

Spring MVC: get RequestMapping value in the method

In my app, for certain reasons I would like to be able to get the value of the #RequestMapping in the corresponding methods, however, I can't find a way to do that. Here are more details about what I want:
Suppose, I have a method like this:
#RequestMapping(value = "/hello")
#ResponseBody
public void helloMethod(AtmosphereResource atmosphereResource) {
...
}
I would like to be able to get the mapping "/hello" within the method. I know, I can use placeholders in the mapping in order to get their values when the actual requests come, but I need a finite set of processable requests, and I don't want a chain of ifs or switches in my method.
Is it at all possible?
This would effectively be the same, wouldn't it?
private final static String MAPPING = "/hello";
#RequestMapping(value = MAPPING)
#ResponseBody
public void helloMethod(AtmosphereResource atmosphereResource) {
// MAPPING accessible as it's stored in instance variable
}
But to answer the original question: I wouldn't be surprised if there wasn't a direct way to access that, it's difficult to think of valid reasons to access this information in controller code (IMO one of biggest benefits of annotated controllers is that you can completely forget about the underlying web layer and implement them with plain, servlet-unaware methods)
You can get get this method's annotation #RequestMapping as you would get any other annotation:
#RequestMapping("foo")
public void fooMethod() {
System.out.printf("mapping=" + getMapping("fooMethod"));
}
private String getMapping(String methodName) {
Method methods[] = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName() == methodName) {
String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
if (mapping.length > 0) {
return mapping[mapping.length - 1];
}
}
}
return null;
}
Here I pass method's name explicitly. See a discussion o how to obtain current method name, if absolutely necessary here: Getting the name of the current executing method

WF4 - Composite custom activity throw an strange exception when using OutArgument

I'm trying to use a composite custom activity that apply a regular expression and return a boolean if it match.
The pattern is something encoded in design time.
The source text is coming from an activity. This activity is also specified in design time (I've made a activity designer that allow to drop an activity as source)
But I also need to return what sub string match the expression, so I added an OutArgument to retrieve the string that match, and the string captured.
Here is the code:
public class RegularExpression : NativeActivity<bool>
{
[RequiredArgument]
public string Pattern { get; set; }
public OutArgument<string> Captured { get; set; }
[RequiredArgument]
public Activity<string> RetrieveTextActivity { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
}
protected override void Execute(NativeActivityContext context)
{
if (this.RetrieveTextActivity != null)
context.ScheduleActivity<string>(this.RetrieveTextActivity, this.onRetrieveComplete);
}
private void onRetrieveComplete(NativeActivityContext context, ActivityInstance completedInstance, string result)
{
var regexp = new Regex(this.Pattern);
var match = regexp.Match(result);
this.Result.Set(context, match.Success);
if (this.Captured != null)
this.Captured.Set(context, match.Value);
}
}
If I execute this activity without binding a variable to the Captured argument, it works as expected (the Result is correctly set).
But if I use the designer to add a variable, then I bind the variable to the Captured argument this error popup:
The argument of type 'System.String' cannot be used. Make sure that
it is declared on an activity.
The exception is thrown when executing this line:
this.Captured.Set(context, match.Value);
Does someone have an idea why I can't set the argument ?
I also read that I shouldn't test that Captured is null, the runtime should automatically set a default value. But If I don't test, I've a NullReference when I don't bind a variable to the argument...
EDIT:
I want to add more information about the workflow itself. I've read in another topic that it may be VS. Here I just want to specify that I'm using a rehosted designer to create the workflow (and not VS). The workflow is then saved as XML in a database.
When I need to start a new workflow, I read the database, use XamlService.Load and Run the created workflow.
Does the error go away if you declare the argument in CacheMetadata?
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
RuntimeArgument argument = new RuntimeArgument("Captured", typeof(string), ArgumentDirection.Out);
metadata.Bind(this.Captured, argument);
metadata.AddArgument(argument);
}
EDIT: I was too quick. The above code should now compile and hopefully fix your problem.
My problem went away when I just called the base.CachMetadata(metadata) after my adds. Try:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
base.CacheMetadata(metadata);
}
You want to do it after your adds, because you want the base class to know what you've added when you call it. I think the base class uses reflection to do Damir Arh's answer for you automatically. This way you don't have to add or modify all that code every time you add or modify your properties. If you had a lot of properties it would become a pain real fast.

ASP.NET: displaying notification on another page, after succesfully saved data to database

I searched the web but haven't found a real good answer for this question..
Let's say I have a form, on AddToList.aspx, and i want that after you hit send, it will direct you back to List.aspx, with a message "The Item was added to list" in a message box div.
do i need to send List.aspx?msg=my message, or is there another good way of doing it?
EDIT:
so i made this helper class:
public class MessageHelper : System.Web.UI.MasterPage
{
public void SetMessage(String message)
{
Session["Message"] = message;
}
public string GetMessage()
{
if (String.IsNullOrEmpty(Session["Message"]))
{
String temp = Session["Message"];
Session["Message"] = "";
return temp;
}
else
{
return "";
}
}
}
and got this error:
Error 32 The best overloaded method match for 'string.IsNullOrEmpty(string)' has some invalid arguments
Error 33 Argument '1': cannot convert from 'object' to 'string'
Error 34 Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a cast?)
You need to convert to string. Session parameters are stored as objects.
It may also be userful to implement this as a extension method. This way it will be available on all page types (Master and UI)
public static class MessageHelper
{
public static void SetMessage(this Page page, String message)
{
Session["Message"] = message;
}
public static string GetMessage(this Page page)
{
var messageText = Session["Message"] as string;
if (!String.IsNullOrEmpty(messageText ))
{
Session["Message"] = "";
return messageText;
}
return "";
}
}
You could certainly use the query string to pass data to your List.aspx page, but be careful passing text that you're planning on writing out in the HTML - you'll need to protect against XSS attacks.
There are several other ways to do this. Chances are, you're going to have several places in your application where you want to redirect the user to another page, but also display a message that has something to do with what they did on the previous page (saved an item, deleted an item, etc.). It would be better to come up with more of a global scheme for this rather than a one-off just for this particular instance.
One idea is to use the Session for storing a message, then do your redirect.
Session("Message") = "Item was added to list."
Response.Redirect("List.aspx")
Then, on your pages (or a Master Page, perhaps), you check Session("Message") and if it's got something, you show that message to the user, then clear that variable.
If Session("Message") IsNot Nothing Then
Response.Write(CType(Session("Message"), String)) 'or set a label text, or show a pop up div, or whatever'
Session("Message") = Nothing
End If
If you use this approach, I recommend you write a helper class, and just use that to manage your messaging:
MessageHelper.SetMessage("Item added to list.")
and
MessageHelper.GetMessage()
would be the methods you would need.
I believe you could do it by setting the PostBackUrl of the button used to save the data to "List.aspx". Maybe set a variable to true/false on AddToList.aspx and then access it from List.aspx?
Not sure if it's better but it's an option.
I can't comment yet or I would have just commented this to your post. You need to cast your session variable like this: (string)Session["Message"]. So, code would look like this:
public class MessageHelper : System.Web.UI.MasterPage
{
public void SetMessage(String message)
{
Session["Message"] = message;
}
public string GetMessage()
{
if (String.IsNullOrEmpty((string)Session["Message"]))
{
String temp = (string)Session["Message"];
Session["Message"] = "";
return temp;
}
else
{
return "";
}
}
}
Actually there's a better way of writing that class: make it one property instead of two methods. It would look like this: (I also fixed your logic; GetMessage was always returning blank)
public class MessageHelper : System.Web.UI.MasterPage
{
public MessageHelper()
{
}
public string Message
{
set { Session["Message"] = value; }
get
{
if (String.IsNullOrEmpty((string)Session["Message"]))
{
Session["Message"] = "";
}
return (string)Session["Message"];
}
}
}
In your two respective files, you would set and get it like so:
//in AddToList.aspx
MessageHelper msg = new MessageHelper();
msg.Message = "The Item was added to your list.";
//and in List.aspx, assigned to an arbitrary Label named myMessageLabel
MessageHelper msg = new MessageHelper();
myMessageLabel.Text = msg.Message;

Resources