Variables can not be used inside bindings - java

I have a validation function that return which part is failing. For example -
public class DateValidator{
public String validateDate(startDate, endDate){
try{
LocalDate.parse(startDate, formatter);
LocalDate.parse(endDate, formatter);
} catch(DateTimeParseException e) {
return "INVALID_DATE_FORMAT";
}
if (startDate.isAfter(endDate)) {
return "INVALID_START_AND_END_DATES";
}
..... so on
}
}
I want to use the this in Drools for Validation as
rule
"ValidateDate"
when
$error: Error();
$request: Request();
DateValidator( $dateValidation: validateDate($request.getStartDate(), $request.getEndDate()) != null);
then
$error.getBadRequest($dateValidation);
end;
I want to use the return type of the variable as not null means the validation did not passed. But I am getting below exception for the DRL file -
text=Variables can not be used inside bindings.
Variable [$request] is being used in binding
'validateDate($request.getStartDate(), $request.getEndDate())']

Try to execute the function call as part of a from Conditional Element:
rule
"ValidateDate"
when
$error: Error();
$request: Request(
$startDate: startDate,
$endDate: endDate
);
$dv: DateValidator()
$msg: String() from $dv.validateDate($startDate, $endDate)
then
$error.getBadRequest($msg);
end
Hope it helps,

Related

RxJava fromCallable() and defer() difference in behaviour

I have implement a validate function, sample code are as below:
public class TestObject {
LocalDateTime d1;
LocalDateTime d2;
//getters, setters
}
public class ErrorResult {
String errorMsg;
public ErrorResult(String errorMsg){
this.errorMsg = errorMsg;
}
}
public Observable<ErrorResult> validate(TestObject testObject){
// implementation section
}
For the implementation section, I have tried to use Observable.fromCallable() and Observable.defer() functions to do the same job, for example:
// for implementation using Observable.fromCallable
return Observable.fromCallable(() -> {
LocalDateTime d1 = testObject.getD1();
LocalDateTime d2 = testObject.getD2();
if(d1.isBefore(d2))
return Observable.just(new ErrorResult("Error d1 is before d2"));
return Observable.empty();
});
// for implementation using Observable.defer
return Observable.defer(() -> {
LocalDateTime d1 = testObject.getD1();
LocalDateTime d2 = testObject.getD2();
if(d1.isBefore(d2))
return Observable.just(new ErrorResult("Error d1 is before d2"));
return Observable.empty();
});
While the implementation of the logic within the anonymous functions is the same,
the implementation using Observable.fromCallable throw a compilation error in IDE (Intellij), which stated "no instance(s) of type variable(s) T exist so that Observable conforms to ErrorResult". It was caused by the return of Observable.empty() as the fallback return in case no error exists
the implementation using Observable.defer compile successfully without error
May I know what is the difference between Observable.fromCallable() and Observable.defer() in this case?
Thanks a lot for the help
fromCallable is not designed to return an Observable, while defer is. So in your example with fromCallable, you're ending up with a Observable<Observable<ErrorResult>>. You actually can't get your desired behavior with fromCallable, because you can't use it to get an empty Observable (returning null from the Callable results in a NullPointerException).

Date as #QueryParam in JAX-RS RestEasy [duplicate]

I have a service defined as follows.
public String getData(#QueryParam("date") Date date)
I'm trying to pass a java.util.Date to it from my client (which is jaxrs:client of CXF, not a generic HTTP client or browser).
My service receives the date as Thu Mar 01 22:33:10 IST 2012 in the HTTP URL. Since CXF won't be able to create a Date object using this String, my client receives a 404 error.
I tried using a ParameterHandler on the service side, but I still can't parse it successfully because I'm not expecting the date in any specific format.
As per this post, passing a Date is supposed to work out of the box, but I can't seem to get the basic case working. Am I required to do anything in order to successfully pass a Date object from my client to service? Appreciate any help.
Thanks
The problem is that JAX-RS dictates that parameter unbundling be done in one of two ways:
The parameter bean has a public constructor that accepts a String
The parameter bean has a static valueOf(String) method.
In your case, the Date is being unbundled via its Date(String) constructor, which cannot handle the input format your client is sending. You have a couple options available to remedy this:
Option 1
Get your client to change the format of the date before they send it. This is the ideal, but probably the hardest to accomplish!
Option 2
Handle the crazy date format. The options for this are:
Change your method signature to accept a string. Attempt to construct a Date object out of that and if that fails, use your own custom SimpleDateFormat class to parse it.
static final DateFormat CRAZY_FORMAT = new SimpleDateFormat("");
public String getData(#QueryParam("date") String dateString) {
final Date date;
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
Define your own parameter class that does the logic mentioned above. Give it a string constructor or static valueOf(String) method that invokes the logic. And an additional method to get the Date when all is said and done.
public class DateParameter implements Serializable {
public static DateParameter valueOf(String dateString) {
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
private Date date;
// Constructor, Getters, Setters
}
public String getData(#QueryParam("date") DateParameter dateParam) {
final Date date = dateParam.getDate();
}
Or finally, you can register a parameter handler for dates. Where its logic is simply the same as mentioned for the other options above. Note that you need to be using at least CXF 2.5.3 in order to have your parameter handler evaluated before it tries the default unbundling logic.
public class DateHandler implements ParameterHandler<Date> {
public Map fromString(String s) {
final Date date;
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
}
Percepiton's answer was very useful, but ParameterHandler has been deprecated in Apache-cxf 3.0, see the Apache-cxf 3.0 Migration Guide:
CXF JAX-RS ParameterHandler has been dropped, please use JAX-RS 2.0 ParamConverterProvider.
So I add an example with the ParamConverterProvider :
public class DateParameterConverterProvider implements ParamConverterProvider {
#Override
public <T> ParamConverter<T> getConverter(Class<T> type, Type type1, Annotation[] antns) {
if (Date.class.equals(type)) {
#SuppressWarnings("unchecked")
ParamConverter<T> paramConverter = (ParamConverter<T>) new DateParameterConverter();
return paramConverter;
}
return null;
}
}
public class DateParameterConverter implements ParamConverter<Date> {
public static final String format = "yyyy-MM-dd"; // set the format to whatever you need
#Override
public Date fromString(String string) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
return simpleDateFormat.parse(string);
} catch (ParseException ex) {
throw new WebApplicationException(ex);
}
}
#Override
public String toString(Date t) {
return new SimpleDateFormat(format).format(t);
}
}
The #SuppressWarnings is required to suppress an "unchecked or unsafe operations" warning during compilation. See How do I address unchecked cast warnings for more details.
The ParamConverterProvider can be registred as provider. Here is how I did it:
<jaxrs:server id="myService" address="/rest">
<jaxrs:serviceBeans>
...
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="dateParameterConverterProvider" />
</jaxrs:providers>
</jaxrs:server>
<bean id="dateParameterConverterProvider" class="myPackage.DateParameterConverterProvider"/>
See Apache-cxf JAX-RS : Services Configuration for more information.
Using a custom DateParam class seems the safest option. You can then base your method signatures on that and implement the ugly conversion logic inside the valueOf() method or the class constructor. It is also more self-documenting than using plain strings
As #Perception suggests in option two, you can handle the date. But you should use following:
private Date getDateFromString(String dateString) {
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = df.parse(dateString);
return date;
} catch (ParseException e) {
//WebApplicationException ...("Date format should be yyyy-MM-dd'T'HH:mm:ss", Status.BAD_REQUEST);
}
}
You call it from within the resource as
Date date = getDateFromString(dateString);//dateString is query param.

Jackson does not use setter

I know this kind of question was raised in the past but not exactly the same issue so i found the right to ask this question.
I'm using JERSEY together with JACKSON for REST web service (JAVA 1.8_011 + Tomcat v7.0 + windows 7 + JERSEY-common 2.23.2 + JACKSON 2.8.2)
One of my POJO field has the following setter:
public void setEndDate(LocalDateTime endDate) {
if (this.startDate != null && this.startDate.isAfter(endDate))
{
throw new IllegalArgumentException("Start date must to be before End date");
}
this.endDate = endDate;
}
my web service is the following:
#PUT
#Path("/updateCoupon")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String updateCoupon(Coupon coupon) {
try
{
//Coupon tmpCoupon = new Coupon(coupon);
System.out.println("*" + coupon.getEndDate().toString() + "*");
getFacade().updateCoupon(coupon);
return "ok";
}
catch (FacadeException | IllegalArgumentException e)
{
return e.getMessage();
}
}
JSON:
{
"startDate":"2016-11-04T00:00",
"endDate":"2016-11-09T00:00",
"amount":7,
"id":143,
"image":"390_290_5cc10a4d-9a3f-4cfc-8.jpg",
"message":"gfd",
"price":3.34,
"title":"n37",
"type":"HEALTH"
}
After debugging and tests the problem is that the JSON does not use my setter to transform from JSON to the POJO (it happens in more setters so the setter it self is not the issue)
Thanks
Your current code for Coupon is dependent on the order that the setters are invoked. If setEndDate is invoked before setStartDate, the validation in setEndDate can't actually use the startDate field.
To fix the problem, you could:
remove setters from your bean and convert to initializing with a constructor that performs validation logic
use a static factory method and label it with #JsonCreator, so that Jackson will use that instead of the constructor
some combination of the two things above
switch to some kind of bean object creator which lets you author a check method to be run after all setters have been invoked (essentially an automatic version of the second option), such as Immutables, or FreeBuilder

Share same method precondition logic between controllers and services?

I have a Service and a Controller .
Each method in the service its preconditions , for example :
public void doSomething(Parameter para1 , Parameter para2 ...) {
if ( something wrong ) {
throw new RuntimeException1();
}
if ( another thing wrong ) {
throw new RuntimeException2();
}
// continue do something
}
And in the Controller layer , there are two methods , one is showForm() which displays form for user to input ; another is doApplyForm() which accepts form and calls the underlaying Service.doSomething().
The following is the pseudo code (I eliminated some BindingResult , attr.addFlashAttribute codes ) :
#Injected Service service;
public String showForm() {
if ( something wrong ) {
throw new RuntimeException1();
}
if ( another thing wrong ) {
throw new RuntimeException2();
}
return "showForm";
}
public String doApplyForm(#Validated Form form) {
try {
service.doSomething(para1 , para2 ...);
return "redirect:/";
} catch (Exception e) {
// error handling
return "redirect:/error";
}
}
It works well , but I am not satisfied. There're bad smells within.
The problem is in the showForm() , which shares identical preconditions with Controller.doSomething() .
If Service.doSomething() add another preconditions in the future , Controller.showForm() has to do correspondent changes.
I wonder if there're any design pattern or frameworks to eliminate such bad smell ?
Java8's functional solutions are welcome.
Thanks.
You can define a util class called Preconditions and move all your validation logic there. It's a common pattern and there are a number of frameworks that make use of it. For example, Guava: Preconditions docs.
At least like this your if (condition) throw new exception will be capsulated and easier to manage.
Introduce a parameter object for the service request and put the validation logic into the request object. E.g.
public class DoSomethingRequest {
private Parameter param1;
private Parameter param2;
public void validate(){
if ( something wrong ) {
throw new RuntimeException1();
}
if ( another thing wrong ) {
throw new RuntimeException2();
}
}
}
Your service will be more easy
public void doSomething(DoSomethingRequest request) {
request.validate();
}
so the controller
public String showForm() {
DoSomethingRequest request = ... // obtained somehow
request.validate();
// ...
return "showForm";
}
This encapsulates the service method's preconditions in an object.

Calling a method with a variable name - possible?

I have a function for a Selenium Test that looks like this.
public static WebElement getElmObject (String locinfo, String loctype) {
try{
return driver.findElement(By.loctype(locinfo));
} catch (Throwable t){
return null;
}
The function is supposed to take in the info string and the type (the name of the method to call in the BY class - like xpath, cssselector, tagname etc.) How do I get Java to evaluate the value of "loctype"?
I come from a ColdFusion background and this is easy to do with CF but I am having a hard time trying to do this in Java. I just get a "cannot resolve method" issue and it won't compile. Is it even possible to do?
You can do this using Reflection.
public static WebElement getElmObject(String locinfo, String loctype) {
try {
Method method = By.class.getMethod(loctype, String.class);
By by = (By) method.invoke(By.class, locinfo);
return driver.findElement(by);
} catch (Throwable t) {
return null;
}
}
However I find this strange and I would recommend using different methods (getElmObjectById, getElmObjectByCss, etc.) or to use an enum (ID, CSS, XPATH, etc.) as parameter instead of the method name. Using the method name as parameter, it makes your caller dependent of the Selenium implementation. If they change the name of a method, your code will not work anymore and you will even not notice this at compile time!
we can also do it with enum like this
other than creating seperate methods for each and every locator like getElmObjectById as LaurentG said we can also achieve it as shown below
public enum avilableLocators
{
CLASS_NAME, CSS_SELECTOR, XPATH
}
and have a method with switch case or if-else if which will have a return type of By
public By locinfo(String locinfo)
{
String locatorValue=null;
switch (locType(locinfo))
{
case XPATH:
locatorValue=locinfo.split(",")[1]/*assuming that you are passing locinfo,locvalue*/
return By.xpath(locator);
}
}
public final avilableLocators locType(String loctype) {
if (loctype.contains("xpath"))
{
return avilableLocators.XPATH;
}
}
so the final usage can be like this
String locDetails="xpath,//*[#id='ComScorePingFile']"
locinfo(locDetails);

Categories