Copy changed fields into an object in Java - java

I have a method like this:
public User getUpdatedUser(UserInfo userInfo, User user) throws ProvisioningException {
if (!userInfo.getUserExternalId().equals(user.getImmutableId()) || !userInfo.getAccountExternalId().equals(
getExternalAccountId(user.getAccountid())))
throw new ProvisioningException(Response.Status.BAD_REQUEST, ProvisioningErrorCodes.INVALID_INPUT);
if (user.getEmail() != userInfo.getEmail()) user.setEmail(userInfo.getEmail());
if (user.getFirstName() != userInfo.getFirstName()) user.setFirstName(userInfo.getFirstName());
if (user.getLastName() != userInfo.getLastName()) user.setLastName(userInfo.getLastName());
if (user.getPhoneNumber() != userInfo.getPhoneNumber()) user.setPhoneNumber(userInfo.getPhoneNumber());
if (user.getCompany() != userInfo.getCompany()) user.setCompany(userInfo.getEmail());
if (user.getJobTitle() != userInfo.getJobTitle()) user.setJobTitle(userInfo.getJobTitle());
if (user.getStatus() != ApiUtils.changeEnumClass(userInfo.getStatus(), DbConstants.UserStatus.class))
user.setStatus(ApiUtils.changeEnumClass(userInfo.getStatus(), DbConstants.UserStatus.class));
if (user.getAccountAdministratorInternalUse() != isAccountAdmin(userInfo.getRoles()))
user.setAccountAdministratorInternalUse(isAccountAdmin(userInfo.getRoles()));
if (user.getPodAdministratorInternalUse() != isPodAdmin(userInfo.getRoles()))
user.setPodAdministratorInternalUse(isPodAdmin(userInfo.getRoles()));
return user;
}
Basically, copying only those fields into user which are different. Is there a neater/cleaner way to do this in Java instead of all the if conditions?

Please, consider the use of JaVers.
The library will allow you mainly to compute diffs that you can apply latter in your objects.
You can take a different approach and use a mapper library, like Mapstruct.
After installing the required dependencies, define a Mapper interface, something like:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
#Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
void update(UserInfo userInfo, #MappingTarget User user);
}
And use it to update the properties in the target object:
UserMapper.INSTANCE.update(userInfo, user);
The library will take care of map every property in the destination object. You can tweak the mapping process as appropriate.
A lot of more elaborated, but if your information is JSON you can apply a merge patch operation over the destination object with the received JSON information. Please, review this excellent article, it is focused on Spring, but it will provide you a great background about the proposed solution.
Finally, you can follow a simpler approach and use Apache Commons BeanUtils and copyProperties between your objects.
Please, be aware that with the exception of the first solution based on JaVers, the rest of the proposed approaches will copy all properties from the source to the destination object, not only the different ones. Unless for the possible performance overhead or any unindicated side effect, this fact will make no difference in the result obtained.
Alternative approach, use it with responsability.
Manual mode (reflection).
//user is the User instance you wish to modify
Class<User> rfkClass = User.class;
Field field = rfkClass.getDeclaredField("name"); //private final? Who cares
field.setAccessible(true);
field.set(user, "TheDestroyer");
field = rfkClass.getDeclaredField("email");
field.setAccessible(true);
field.set(user, "chuck#norris.com");
//... your changes here
// return user; --> same instance, now modified internally
Regardless the variables were declared as final, private, whatever, reflection just doesn't care.

You could define an API by which both objects abide, and then use reflection to create a generic method.
interface User {
void setFoo(String foo);
String getFoo();
}
class UserImpl implements User { ... }
class UserInfo implements User { ... }
public class Mirrorer<T> {
private final String[] methodNames;
private final MethodHandle[] methodHandles;
public Mirrorer(Class<T> interfaceClass) {
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException("interfaceClass must be an interface.");
}
Method[] methods = interfaceClass.getDeclaredMethods();
int methodsCount = methods.length;
methodNames = new String[methodsCount];
methodHandles = new MethodHandle[methodsCount];
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
for (int i = 0; i < methodsCount; i++) {
Method method = methods[i];
methodNames[i] = method.getName();
methodHandles[i] = lookup.unreflect(method);
}
} catch (IllegalAccessException e) {
throw new AssertionError();
}
}
public void mirror(T from, T to) throws Throwable {
for (int i = 0; i < methodNames.length; i++) {
String methodName = methodNames[i];
if (methodName.startsWith("get")) {
MethodHandle methodHandle = methodHandles[i];
Object fromValue = methodHandle.invoke(from);
Object toValue = methodHandle.invoke(to);
if (!Objects.equals(fromValue, toValue)) {
MethodHandle setter = getSetter(methodName.substring(3), methodHandle.type().returnType());
setter.invoke(to, fromValue);
}
}
}
}
private MethodHandle getSetter(String field, Class<?> type) {
for (int i = 0; i < methodNames.length; i++) {
String methodName = methodNames[i];
if (methodName.equals("set" + field)) {
MethodHandle methodHandle = methodHandles[i];
MethodType methodType = methodHandle.type();
if (methodType.parameterCount() != 0 && methodType.parameterType(0).equals(type)) {
return methodHandle;
}
}
}
throw new AssertionError();
}
}
Mirrorer<User> mirrorer = new Mirrorer<>(User.class);
UserInfo userInfo = ...
UserImpl user = ...
mirrorer.mirror(userInfo, user);

Looks like you just copy all different fields from userInfo to user and you are able not to compare all the valuse, just set it all together.
public User getUpdatedUser(UserInfo userInfo, User user) throws ProvisioningException {
if (!userInfo.getUserExternalId().equals(user.getImmutableId())
|| !userInfo.getAccountExternalId().equals(getExternalAccountId(user.getAccountid())))
throw new ProvisioningException(Response.Status.BAD_REQUEST, ProvisioningErrorCodes.INVALID_INPUT);
user.setEmail(userInfo.getEmail());
user.setFirstName(userInfo.getFirstName());
user.setLastName(userInfo.getLastName());
user.setPhoneNumber(userInfo.getPhoneNumber());
user.setCompany(userInfo.getEmail());
user.setJobTitle(userInfo.getJobTitle());
user.setStatus(userInfo.getStatus());
user.setAccountAdministratorInternalUse(isAccountAdmin(userInfo.getRoles()));
user.setPodAdministratorInternalUse(isPodAdmin(userInfo.getRoles()));
return user;
}

Related

Choose which methods to run, with user input

I have a list of methods within my class. And then want to have input string array, where the user can choose which methods they want to run. We are running expensive insurance calculations. And have over say eg 20 methods. Is there a way to conduct this without do an if check on each? maybe with reflection or interface?
#Override
public void ProductTest(ProductData productData, String[] methodNames) {
public void methodA(ProductData productData){...};
public void methodB(ProductData productData){...};
public void methodC(ProductData productData){...};
public void methodD(ProductData productData){...};
public void methodE(ProductData productData){...};
}
I am willing to change the Array into a different ObjectType if needed, to execute properly. Using SpringBoot, has it has a library of utility classes.
Use a Map<String, Consumer<ProductData>>, not separate method handles. Main reason - reflection is slow and dangerous when given user "input"
Use map.get(input).accept(product) to call it.
https://docs.oracle.com/javase/8/docs/api/index.html?java/util/function/Consumer.html
Example
Map<String, Consumer<ProductData>> map = new HashMap<>();
map.put("print_it", System.out::println);
map.put("print_id", data -> System.out.println(data.id));
map.put("id_to_hex", data -> {
int id = data.getId();
System.out.printf("0x%x%n", id);
});
ProductData data = new ProductData(16);
map.get("print_it").accept(data);
map.get("print_id").accept(data);
map.get("id_to_hex").accept(data);
Outputs
ProductData(id=16)
16
0x10
If you are planning on chaining consumers using andThen, you'd be better having an Optional<ProductData>, and using a Function<ProductData, ProductData> with Optional.map()
One way to do it is via reflection. You can iterate over methods in the class object and look for ones to run by name. Here's some example code--this would print out a list of names the user could type in:
myObject.getClass().getDeclaredMethods().each((method)->System.out.println(method.getName()))
And this is how you would call it once the user had made a selection:
productTest.getDeclaredMethods().each((method)->
if(method.getName().equals(userSelectedName))
method.invoke(productTest, productData)
)
The ONLY advantage to this approach is that you don't have to maintain a second structure (Switch, Map, etc...) and add to it every time you add a new method. A personality quirk makes me unwilling to do that (If adding something one place forces you to update a second, you're doing it wrong), but this doesn't bother everyone as much as it bothers me.
This isn't dangerous or anything, if you don't have a method in the class it can't call it, but if you are relying on users "Typing", I'd suggest listing out the options and allowing a numeric selection--or using reflection to build a map like OneCricketeer's.
I've used this pattern to write a testing language and fixture to test set-top TV boxes, it was super simple to parse a group of strings, map some to methods and other to parameters and have a very flexible, easily extensible testing language.
The method object also has a "getAnnotation()" which can be used to allow more flexible matching in the future.
You can use method invocation.
For example, you can have two methods, first one will loop through your methodNames array and call the second method:
public void callPassedMethods(ProductData productData, String[] methodNames) {
for (String m : methodNames) {
callMethod(productData, m)
}
}
And the second method will actually find a method in your class that matches the string passed and invoke it:
public void callMethod(ProductData productData, String methodName) {
try {
ClassName yourObj = new ClassName(); // Class where your methods are
Method method = yourObj.getClass().getDeclaredMethod(methodName, ProductData.class);
method.invoke(yourObj, productData);
} catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// handle exceptions
}
}
Or, you can always use the good old switch statement:
for (String m : methodNames) {
switch (m) {
case "methodA":
methodA();
break;
case "methodB":
methodB();
break;
// ... continue with as many cases as you need
}
}
If you go with the reflection route, you don't really want to expose your method names to the end users. They might not be end user-friendly, and if they are, there is no reason for users to know this information and there might be methods, which are not supposed to be invoked by users. I would use custom annotations to build more flexible matching.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface UserChoice {
String userFriendlyOption();
int optionNumber();
}
optionNumber will be used for matching the method to invoke, userFriendlyOption is some user friendly text.
Annotate only the methods, supposed to be used by users.
#RequiredArgsConstructor
public class ProductData {
private final double data;
#UserChoice(userFriendlyOption = "see result for option a", optionNumber = 1)
public void methodA() {
System.out.println(data + 1);
}
#UserChoice(userFriendlyOption = "see result for option b", optionNumber = 2)
public void methodB() {
System.out.println(data + 2);
}
#UserChoice(userFriendlyOption = "see result for option c", optionNumber = 3)
public void methodC() {
System.out.println(data);
}
public void methodNotForUser() {
System.out.println("Should not be seen by users");
}
}
Like this methodNotForUser() can't be invoked by end users.
Simplified matcher might look like this.
#RequiredArgsConstructor
public class ProductTester {
private final ProductData data;
private Map<Integer, MethodData> map;
public void showOptions() {
if (this.map == null) {
this.map = new HashMap<>();
for (Method method : this.data.getClass().getMethods()) {
UserChoice userChoice = method.getAnnotation(UserChoice.class);
if (userChoice != null) {
String userRepresentation = userChoice.optionNumber() + " - " + userChoice.userFriendlyOption();
this.map.put(userChoice.optionNumber(), new MethodData(userRepresentation, method));
}
}
}
this.map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> System.out.println(entry.getValue().getUserRepresentation()));
}
public void showOptionResult(int choice) {
MethodData methodData = this.map.get(choice);
if (methodData == null) {
System.out.println("Invalid choice");
return;
}
System.out.println("Result");
try {
methodData.getMethod().invoke(this.data);
} catch (IllegalAccessException | InvocationTargetException ignore) {
//should not happen
}
}
}
MethodData is simple pojo with the sole purpose to not recalculate user representation.
#RequiredArgsConstructor
#Getter
public class MethodData {
private final String userRepresentation;
private final Method method;
}
Short main to illustrate the idea and play around:
public class Temp {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Write initial value");
double data = scanner.nextDouble();
ProductData myData = new ProductData(data);
ProductTester tester = new ProductTester(myData);
tester.showOptions();
System.out.println("Write option number");
int userChoice = scanner.nextInt();
tester.showOptionResult(userChoice);
}
}

Java Array of InnerClass throwing java.lang.NoSuchFieldError

I am trying to brushup java after a long time.
Any help is much appreciated.
For demonstration I have Animal Class that has an array of innerclass of Organs.
public class Animal
{
String nameOfAnimal;
Organs [] vitalOrgans = new Organs[3];
public Animal()
{
}
public String getNameOfAnimal() {
return nameOfAnimal;
}
public void setNameOfAnimal(String nameOfAnimal) {
this.nameOfAnimal = nameOfAnimal;
}
#Override
public String toString() {
return "Animal{" + "nameOfAnimal=" + nameOfAnimal + "}";
}
class Organs{
String nameOfOrgan;
public String getNameOfOrgan() {
return nameOfOrgan;
}
public void setNameOfOrgan(String nameOfOrgan) {
this.nameOfOrgan = nameOfOrgan;
}
#Override
public String toString() {
return "Organs{" + "nameOfOrgan=" + nameOfOrgan + '}';
}
}
}
Now in driver file when I make call there is no syntactical error but I get "Exception in thread "main" java.lang.NoSuchFieldError: vitalOrgans"
Animal mamal = new Animal();
mamal.setNameOfAnimal("Chimp");
mamal.vitalOrgans[0].setNameOfOrgan("Heart");
System.out.println(mamal.vitalOrgans[0].getNameOfOrgan());
What would be the way to make this (or similar idea) to work.
Thanks.
You would need to initialize the vitalOrgrans with new Organs(). Like:
public Animal() {
for (int i = 0; i < vitalOrgans.length; i++) {
vitalOrgans[i] = new Organs();
}
}
Because when you say :
Organs[] vitalOrgans = new Organs[3];
You are creating an array of 3 null Organs. Hence the null pointer exception, when accessing "vitalOrgans[i].".
Taking the relevant bit of code:
public class Animal
{
//...
Organs [] vitalOrgans = new Organs[3];
//...
}
Since your declaration of vitalOrgans was never given an access modifier (i.e. one of private, public, protected) it took on default access, which means only other classes in the same package can see it. Since your other block of code is not in the same package, it cannot see the field.
A minimally viable modification to just make it work would be to set the access to public:
public class Animal
{
//...
public Organs [] vitalOrgans = new Organs[3];
//...
}
While this works, it's not necessarily the best solution, as if you ever change how vitalOrgans is represented, or need to perform any validation, those edits would have to be done throughout the application. Thus, a better solution (and also, a major stylistic convention in Java for those exact reasons) is to make it (and all your fields, in fact) private and access via methods:
public class Animal {
private String nameOfAnimal;
private Organs[] vitalOrgans = new Organs[3];
//...
public Organs[] getVitalOrgans() {
return vitalOrgans;
}
//Alternative accessor that fetches only one organ.
public Organs getVitalOrgan(int index) {
if(index >= 0 && index < vitalOrgans.length)
return vitalOrgans[index];
else
return null;
}
public void setVitalOrgans(Organs[] vitalOrgans) {
this.vitalOrgans = vitalOrgans
}
//...
}
Your caller could then access Organs via either form of the get method (note, you probably want Organs to be public):
Animal.Organs futureMammalHeart = mamal.getVitalOrgan(0); //Animal.Organs due to Organs being an inner class.
if(futureMammalHeart != null) //Demonstration of null check. Safety first!
futureMammalHeart.setNameOfOrgan("Heart");
Animal.Organs[] mammalianVitalOrgans = mamal.getVitalOrgans();
if(mammalianVitalOrgans != null) //Just in case...
System.out.println(mamal.mammalianVitalOrgans[0].getNameOfOrgan());
Also, as Ari mentioned in his answer, don't forget to initialize the organs in your array, otherwise you will get a NullPointerException!

Is JRE 1.8 still JavaBean specs compliant about IndexedPropertyDescriptor?

This question seems awkward but we are facing a strange behaviour while retrieving the PropertyDescriptors of a javabean.
Here are the execution results on 1.6, 1.7 and 1.8 of a simple piece of code, compiled with 1.6 compliance.
Java 1.6 execution:
java.beans.PropertyDescriptor#4ddc1428 <- Not important
java.beans.IndexedPropertyDescriptor#7174807e <- Yes I have an indexed property
Java 1.7 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.IndexedPropertyDescriptor[name=values; indexedPropertyType=class java.lang.String; indexedReadMethod=public java.lang.String JavaBean.getValues(int)] <- Yes I have an indexed property
Java 1.8 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.PropertyDescriptor[name=values; propertyType=interface java.util.List; readMethod=public java.util.List JavaBean.getValues()] <- Ouch! This is not an indexed property anymore!
Why has it changed?
The javabean specs states about accessing a property with an index. It is not said as mandatory to use an array as the container of the indexed property. Am I wrong?
I read the specs and chapter 8.3.3 talks about Design Patterns for Indexed properties, not the strict rule.
How to make the previous behaviour coming back again without refactoring all the app ? < Old application, lot of code to modify, etc...
Thanks for the answers,
JavaBean class
import java.util.ArrayList;
import java.util.List;
public class JavaBean {
private List<String> values = new ArrayList<String>();
public String getValues(int index) {
return this.values.get(index);
}
public List<String> getValues() {
return this.values;
}
}
Main class
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Test {
public static void main(String[] args) throws IntrospectionException {
PropertyDescriptor[] descs =
Introspector.getBeanInfo(JavaBean.class).getPropertyDescriptors();
for (PropertyDescriptor pd : descs) {
System.out.println(pd);
}
}
}
From JavaBeans 1.01 specification, section 7.2 “Indexed properties”:
A component may also expose an indexed property as a single array value.
Section 8.3 is describing the design patterns which introspection recognizes, in the absence of explicit BeanInfo. Section 8.3.3 is saying that only array properties will trigger automatic recognition of indexed properties.
You're technically correct; it is not mandatory to use an array. But if you don't, the spec says you have to provide your own BeanInfo to expose the property as an indexed property.
So the answer to your question's title is: Yes, Java 1.8 is JavaBean specs compliant.
I'm not sure why List properties were ever supported. Maybe a future JavaBeans specification was going to support them which has since been withdrawn.
As to your final question: I think you'll have to create a BeanInfo class for each class with List properties. I expect you can create a general superclass to make it easier, something like:
public abstract class ListRecognizingBeanInfo
extends SimpleBeanInfo {
private final BeanDescriptor beanDesc;
private final PropertyDescriptor[] propDesc;
protected ListRecognizingBeanInfo(Class<?> beanClass)
throws IntrospectionException {
beanDesc = new BeanDescriptor(beanClass);
List<PropertyDescriptor> desc = new ArrayList<>();
for (Method method : beanClass.getMethods()) {
int modifiers = method.getModifiers();
Class<?> type = method.getReturnType();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers) &&
!type.equals(Void.TYPE) &&
method.getParameterCount() == 0) {
String name = method.getName();
String remainder;
if (name.startsWith("get")) {
remainder = name.substring(3);
} else if (name.startsWith("is") &&
type.equals(Boolean.TYPE)) {
remainder = name.substring(2);
} else {
continue;
}
if (remainder.isEmpty()) {
continue;
}
String propName = Introspector.decapitalize(remainder);
Method writeMethod = null;
Method possibleWriteMethod =
findMethod(beanClass, "set" + remainder, type);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(Void.TYPE)) {
writeMethod = possibleWriteMethod;
}
Class<?> componentType = null;
if (type.isArray()) {
componentType = type.getComponentType();
} else {
Type genType = method.getGenericReturnType();
if (genType instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) genType;
if (p.getRawType().equals(List.class)) {
Type[] argTypes = p.getActualTypeArguments();
if (argTypes[0] instanceof Class) {
componentType = (Class<?>) argTypes[0];
}
}
}
}
Method indexedReadMethod = null;
Method indexedWriteMethod = null;
if (componentType != null) {
Method possibleReadMethod =
findMethod(beanClass, name, Integer.TYPE);
Class<?> idxType = possibleReadMethod.getReturnType();
if (idxType.equals(componentType)) {
indexedReadMethod = possibleReadMethod;
}
if (writeMethod != null) {
possibleWriteMethod =
findMethod(beanClass, writeMethod.getName(),
Integer.TYPE, componentType);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(
Void.TYPE)) {
indexedWriteMethod = possibleWriteMethod;
}
}
}
if (indexedReadMethod != null) {
desc.add(new IndexedPropertyDescriptor(propName,
method, writeMethod,
indexedReadMethod, indexedWriteMethod));
} else {
desc.add(new PropertyDescriptor(propName,
method, writeMethod));
}
}
}
propDesc = desc.toArray(new PropertyDescriptor[0]);
}
private static Method findMethod(Class<?> cls,
String name,
Class<?>... paramTypes) {
try {
Method method = cls.getMethod(name, paramTypes);
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers)) {
return method;
}
} catch (NoSuchMethodException e) {
}
return null;
}
#Override
public BeanDescriptor getBeanDescriptor() {
return beanDesc;
}
#Override
public PropertyDescriptor[] getPropertyDescriptors() {
return propDesc;
}
}
I am facing the same issue. I am trying to save StartDate and End date as List from JSP but it is not saved and values are wiped out. In my project, there are start date and end date fields. I debugged BeanUtilsBean then I observed that fields do not have writeMethod. I have added one more setter method for each field in my class and it works.
java.beans.PropertyDescriptor[name=startDateStrings; propertyType=interface java.util.List; readMethod=public java.util.List com.webapp.tradingpartners.TradingPartnerNewForm.getStartDateStrings()]
This is a JavaBeans crapspec problem, which only allows void setters. If you want this-returning setters, then you can't have JavaBeans compatibility and there's nothing Lombok could do about it.
In theory, you could generate two setters, but then you'd have to call them differently and having two setters per field is simply too bad.
<cc:dateInput property='<%= "startDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
<cc:dateInput property='<%= "endDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
public List<String> getStartDateStrings() {
return startDateStrings;
}
public String getStartDateStrings(int index) {
return startDateStrings.get(index);
}
public void setStartDateStrings(int index, String value) {
startDateStrings.set(index, value);
}
public List<String> getEndDateStrings() {
return endDateStrings;
}
public String getEndDateStrings(int index) {
return endDateStrings.get(index);
}
public void setEndDateStrings(int index, String value) {
endDateStrings.set(index, value);

Spring SimpleJdbcCall default (optional) arguments

I am trying to invoke a stored procedure which has default (optional) arguments without passing them and it is not working. Essentially the same problem as described here.
My code:
SqlParameterSource in = new MapSqlParameterSource()
.addValue("ownname", "USER")
.addValue("tabname", cachedTableName)
.addValue("estimate_percent", 20)
.addValue("method_opt", "FOR ALL COLUMNS SIZE 1")
.addValue("degree", 0)
.addValue("granularity", "AUTO")
.addValue("cascade", Boolean.TRUE)
.addValue("no_invalidate", Boolean.FALSE)
.addValue("force", Boolean.FALSE);
And I get an exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Required input parameter 'PARTNAME' is missing
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209)
Where PARTNAME is an optional parameter according to this. Also confirmed by the fact that I can run this procedure w/o the PARTNAME argument manually.
Ater giving up on this question and just passing all the parameters, including optional ones I ran into its inability to pass boolean arguments, because boolean is not an SQL data type, only PL/SQL.
So my current solution is that JDBC is not suited for running stored procedures and this is how I'm working around it:
jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException{
CallableStatement cs = con.prepareCall("{call sys.dbms_stats.gather_table_stats(ownname=>user, tabname=>'" + cachedMetadataTableName + "', estimate_percent=>20, method_opt=>'FOR ALL COLUMNS SIZE 1', degree=>0, granularity=>'AUTO', cascade=>TRUE, no_invalidate=>FALSE, force=>FALSE) }");
return cs;
}
},
new CallableStatementCallback() {
public Object doInCallableStatement(CallableStatement cs) throws SQLException{
cs.execute();
return null; // Whatever is returned here is returned from the jdbcTemplate.execute method
}
}
);
Came up with a decent solution to this today, that copes with non-null defaults, and does not use fruity reflection techniques. It works by creating the metadata context for the function externally to retrieve all the parameter types and so forth, then constructing the SimpleJdbcCall manually from that.
First, create a CallMetaDataContext for the function:
CallMetaDataContext context = new CallMetaDataContext();
context.setFunction(true);
context.setSchemaName(schemaName);
context.setProcedureName(functionName);
context.initializeMetaData(jdbcTemplate.getDataSource());
context.processParameters(Collections.emptyList());
Next, create the SimpleJdbcCall, but force it to not do its own metadata lookup:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate);
// This forces the call object to skip metadata lookup, which is the part that forces all parameters
simpleJdbcCall.setAccessCallParameterMetaData(false);
// Now go back to our previously created context and pull the parameters we need from it
simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(0));
for (int i = 0; i < params.length; ++i) {
simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(i));
}
// Call the function and retrieve the result
Map<String, Object> resultsMap = simpleJdbcCall
.withSchemaName(schemaName)
.withFunctionName(functionName)
.execute(params);
Object returnValue = resultsMap.get(context.getScalarOutParameterName());
I found solution for my case with SimpleJdbcCall and Spring 5.2.1, Java 8, Oracle 12.
You need to:
Use .withoutProcedureColumnMetaDataAccess()
Use .withNamedBinding()
Declare parameters, you know about in .declareParameters() call. Procedure will be called only with parameters, declared in this method. Default parameters, you dont want to set, arent writing here.
Example call is below
final String dataParamName = "P_DATA";
final String ageParamName = "P_AGE";
final String genderParamName = "P_GENDER";
final String acceptedParamName = "P_ACCEPTED";
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(getJdbcTemplate())
.withCatalogName("PKG_USER")
.withProcedureName("USER_CHECK")
.withoutProcedureColumnMetaDataAccess()
.withNamedBinding()
.declareParameters(
new SqlParameter(dataParamName, OracleTypes.VARCHAR),
new SqlParameter(ageParamName, OracleTypes.NUMBER),
new SqlParameter(genderParamName, OracleTypes.VARCHAR),
new SqlOutParameter(acceptedParamName, OracleTypes.NUMBER)
);
SqlParameterSource parameterSource = new MapSqlParameterSource()
.addValue(dataParamName, data)
.addValue(ageParamName, age)
.addValue(genderParamName, gender);
Map<String, Object> out = simpleJdbcCall.execute(parameterSource);
Here is a different approach that I have taken. I added the ability for the user to set the number of parameters they will be providing on the call. These will be the first n number of positional parameters. Any remaining parameters available in the stored-proc, will have to be set via the database's default value handling. This allows new parameters to be added to the end of the list with default values, or to be null-able, without breaking code that does not know to provide a value.
I sub-classed SimpleJdbcCall and added the methods to set the "maxParamCount". I also used a bit a evil reflection to set my sub-classed version of CallMetaDataContext.
public class MySimpleJdbcCall extends SimpleJdbcCall
{
private final MyCallMetaDataContext callMetaDataContext = new MyCallMetaDataContext();
public MySimpleJdbcCall(DataSource dataSource)
{
this(new JdbcTemplate(dataSource));
}
public MySimpleJdbcCall(JdbcTemplate jdbcTemplate)
{
super(jdbcTemplate);
try
{
// Access private field
Field callMetaDataContextField = AbstractJdbcCall.class.getDeclaredField("callMetaDataContext");
callMetaDataContextField.setAccessible(true);
// Make it non-final
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(callMetaDataContextField, callMetaDataContextField.getModifiers() & ~Modifier.FINAL);
// Set field
callMetaDataContextField.set(this, this.callMetaDataContext);
}
catch (NoSuchFieldException | IllegalAccessException ex)
{
throw new RuntimeException("Exception thrown overriding AbstractJdbcCall.callMetaDataContext field", ex);
}
}
public MySimpleJdbcCall withMaxParamCount(int maxInParamCount)
{
setMaxParamCount(maxInParamCount);
return this;
}
public int getMaxParamCount()
{
return this.callMetaDataContext.getMaxParamCount();
}
public void setMaxParamCount(int maxInParamCount)
{
this.callMetaDataContext.setMaxParamCount(maxInParamCount);
}
}
In my CallMetaDataContext sub-class, I store the maxInParamCount, and use it to trim the list of parameters known to exist in the stored-proc.
public class MyCallMetaDataContext extends CallMetaDataContext
{
private int maxParamCount = Integer.MAX_VALUE;
public int getMaxParamCount()
{
return maxParamCount;
}
public void setMaxParamCount(int maxInParamCount)
{
this.maxParamCount = maxInParamCount;
}
#Override
protected List<SqlParameter> reconcileParameters(List<SqlParameter> parameters)
{
List<SqlParameter> limittedParams = new ArrayList<>();
int paramCount = 0;
for(SqlParameter param : super.reconcileParameters(parameters))
{
if (!param.isResultsParameter())
{
paramCount++;
if (paramCount > this.maxParamCount)
continue;
}
limittedParams.add(param);
}
return limittedParams;
}
}
Use is basically the same except for seeting the max parameter count.
SimpleJdbcCall call = new MySimpleJdbcCall(jdbcTemplate)
.withMaxParamCount(3)
.withProcedureName("MayProc");
SMALL RANT: It's funny that Spring is well know for its IOC container. But, within its utility classes, I have to resort to reflection to provide an alternate implementation of a dependent class.
Was also struggling with the problem, and didn't want to deal with strings.
There could be more interesting solution, if we get default values from meta data, which spring doesn't care about in default implementation, but I simply put nulls there.
The solution came like the following:
Overridden simpleJdbcCall
private class JdbcCallWithDefaultArgs extends SimpleJdbcCall {
CallableStatementCreatorFactory callableStatementFactory;
public JdbcCallWithDefaultArgs(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
#Override
protected CallableStatementCreatorFactory getCallableStatementFactory() {
return callableStatementFactory;
}
#Override
protected void onCompileInternal() {
callableStatementFactory =
new CallableStatementCreatorWithDefaultArgsFactory(getCallString(), this.getCallParameters());
callableStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
}
#Override
public Map<String, Object> execute(SqlParameterSource parameterSource) {
((CallableStatementCreatorWithDefaultArgsFactory)callableStatementFactory).cleanupParameters(parameterSource);
return super.doExecute(parameterSource);
}
}
And overriden CallableStatementCreatorFactory
public class CallableStatementCreatorWithDefaultArgsFactory extends CallableStatementCreatorFactory {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<SqlParameter> declaredParameters;
public CallableStatementCreatorWithDefaultArgsFactory(String callString, List<SqlParameter> declaredParameters) {
super(callString, declaredParameters);
this.declaredParameters = declaredParameters;
}
protected void cleanupParameters(SqlParameterSource sqlParameterSource) {
MapSqlParameterSource mapSqlParameterSource = (MapSqlParameterSource) sqlParameterSource;
Iterator<SqlParameter> declaredParameterIterator = declaredParameters.iterator();
Set<String> parameterNameSet = mapSqlParameterSource.getValues().keySet();
while (declaredParameterIterator.hasNext()) {
SqlParameter parameter = declaredParameterIterator.next();
if (!(parameter instanceof SqlOutParameter) &&
(!mapContainsParameterIgnoreCase(parameter.getName(), parameterNameSet))) {
logger.warn("Missing value parameter "+parameter.getName() + " will be replaced by null!");
mapSqlParameterSource.addValue(parameter.getName(), null);
}
}
}
private boolean mapContainsParameterIgnoreCase(String parameterName, Set<String> parameterNameSet) {
String lowerParameterName = parameterName.toLowerCase();
for (String parameter : parameterNameSet) {
if (parameter.toLowerCase().equals(lowerParameterName)) {
return true;
}
}
return false;
}
#Override
public void addParameter(SqlParameter param) {
this.declaredParameters.add(param);
}
I use this util method:
public <T> void setOptionalParameter(MapSqlParameterSource parameters, String name, T value) {
if (value == null)
parameters.addValue(name, value, Types.NULL);
else
parameters.addValue(name, value);
}

Is it possible to set multiple messages using oval AbstractAnnotationCheck?

I am using the Oval validation framework to validate fields that HTML fields cannot hold malicious javascript code. For the malicious code detection, I am using an external framework that returns me a list of errors that I would like to use as error messages on the field. The problem I am running into is that I can only setMessage in the check implementation, while I would rather do something like setMessages(List). So while I am currently just joining the errors with a comma, I would rather pass them back up as a list.
Annotation
#Target({ ElementType.METHOD, ElementType.FIELD})
#Retention( RetentionPolicy.RUNTIME)
#Constraint(checkWith = HtmlFieldValidator.class)
public #interface HtmlField {
String message() default "HTML could not be validated";
}
Check
public class HtmlFieldValidator extends AbstractAnnotationCheck<HtmlDefaultValue> {
public boolean isSatisfied( Object o, Object o1, OValContext oValContext, Validator validator ) throws OValException {
if (o1 == null) {
return true;
} else {
CleanResults cleanResults = UIowaAntiSamy.cleanHtml((String) o1);
if (cleanResults.getErrorMessages().size() > 0) {
String errors = StringUtils.join(cleanResults.getErrorMessages(), ", ");
this.setMessage(errors);
return false;
} else {
return true;
}
}
}
}
Model class
class Foo {
#HtmlField
public String bar;
}
Controller code
Validator validator = new Validator(); // use the OVal validator
Foo foo = new Foo();
foo.bar = "<script>hack()</script>";
List<ConstraintViolation> violations = validator.validate(bo);
if (violations.size() > 0) {
// inform the user that I cannot accept the string because
// it contains invalid html, using error messages from OVal
}
If setMessage(String message) is a method created by a superclass, you can override it and once it receives the data, simply split the string into a list and call a second function in which you would actually place your code. On a side note, I would also recommend changing the separating string to something more unique as the error message itself could include a comma.
Your question doesn't really make much sense though. If you are "passing them back up" to a method implemented in a superclass, then this voids the entire point of your question as the superclass will be handling the data.
I am going to assume the setError methods is a simple setter that sets a String variable to store an error message that you plan to access after checking the data. Since you want to have the data in your preferred type, just create a new array of strings in your class and ignore the superclass. You can even use both if you so desire.
public class HtmlFieldValidator extends AbstractAnnotationCheck<HtmlDefaultValue> {
public String[] errorMessages = null;
public void setErrorMessages(String[] s) {
this.errorMessages = s;
}
public boolean isSatisfied( Object o, Object o1, OValContext oValContext, Validator validator ) throws OValException {
if (o1 == null) {
return true;
} else {
CleanResults cleanResults = UIowaAntiSamy.cleanHtml((String) o1);
if (cleanResults.getErrorMessages().size() > 0) {
//String errors = StringUtils.join(cleanResults.getErrorMessages(), ", ");
//this.setMessage(errors);
this.setErrorMessages(cleanResults.getErrorMessages());
return false;
} else {
return true;
}
}
}
}
Elsewhere:
HtmlFieldValidator<DefaultValue> hfv = new HtmlFieldValidator<DefaultValue>();
boolean satisfied = hfv.isSatisfied(params);
if (!satisfied) {
String[] errorMessages = hfv.errorMessages;
//instead of using their error message
satisfy(errorMessages);//or whatever you want to do
}
EDIT:
After you updated your code I see what you mean. While I think this is sort of overdoing it and it would be much easier to just convert the string into an array later, you might be able to do it by creating a new class that extends Validator its setMessage method. In the method, you would call super.setMethod as well as splitting and storing the string as an array in its class.
class ValidatorWithArray extends Validator {
public String[] errors;
public final static String SPLIT_REGEX = ";&spLit;";// Something unique so you wont accidentally have it in the error
public void setMessage(String error) {
super.setMessage(error);
this.errors = String.split(error, SPLIT_REGEX);
}
}
In HtmlFieldValidator:
public boolean isSatisfied( Object o, Object o1, OValContext oValContext, Validator validator ) throws OValException {
if (o1 == null) {
return true;
} else {
CleanResults cleanResults = UIowaAntiSamy.cleanHtml((String) o1);
if (cleanResults.getErrorMessages().size() > 0) {
String errors = StringUtils.join(cleanResults.getErrorMessages(), ValidatorWithArray.SPLIT_REGEX);
this.setMessage(errors);
return false;
} else {
return true;
}
}
}
And now just use ValidatorWithArray instead of Validator
The situation in which I want to achieve this was different from yours, however what I found was best in my case was to create an annotation for each error (rather than having one that would return multiple errors). I guess it depends on how many errors you are likely to be producing in my case it was only two or three.
This method makes also makes your code really easy to reuse as you can just add the annotations wherenever you need them and combine them at will.

Categories

Resources