How to specify object custom serialization in ORMLite? - java

I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.
So I'm using the following code:
class ParentClass {
#DatabaseField(persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
where persister class a kind of:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();
public static MyFieldClassPersister getSingleton() {
return singleTon;
}
protected MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[0]);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
return jsonStringToObject(defaultStr);
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
String string = results.getString(columnPos);
return jsonStringToObject(string);
}
private static MyFieldClass jsonStringToObject(String string) {
// json to object conversion logic
}
}
Here are two issues I've met:
I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?
If I mark my custom field type as String type:
class ParentClass {
#DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
then ormlite crashes when saving object with the following message:
java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
field FieldType:name=myField,class=ParentClass is not valid for type
com.j256.ormlite.field.types.StringType#272ed83b, maybe should be
class java.lang.String
It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.

So basically your persister should be implemented in the next way:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();
private MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
}
public static MyFieldClassPersister getSingleton() {
return INSTANCE;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
MyFieldClass myFieldClass = (MyFieldClass) javaObject;
return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
}
private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
// logic here
}
private MyFieldClass getMyFieldClassFromJson(String json) {
// logic here
}
}
You should register it in onCreate method of your OrmLiteSqliteOpenHelper class
#Override
public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
try {
//...
DataPersisterManager
.registerDataPersisters(MyFieldClassPersister.getSingleton());
} catch (SQLException e) {
// log exception
}
}
And then you can use it in your model like this:
#DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;

Don't register the persister adapter in the onCreate() method. This method only gets called when your database is first created. You should add this somewhere else, like your constructor or onOpen() method.

Related

JAVA How can i get a method to accept a parent class and all of it's extended classes?

I apologize if this has been answered before but either i don't know the correct verbiage or my google fu is bad.
I have a TestModel class which has the getters and setters for all the tests I use. Then I have a AdditionalTestModel class that extends the TestModel with additional getters and setters for that specific type of tests.
Now I have BuildTest Class that i want to be able to pass TestModel and any extended classes of TestModel.
public static Class<?> buildTest(Class<?> test, Class<?> template)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class<?> testClass = test.getClass();
Method[] testMethods = testClass.getMethods();
for (Method method : testMethods) {
String name = method.getName();
if (name.startsWith("get")) {
String testMethodType = method.getReturnType().getTypeName();
// additional code removed//
}
}
If instead of Class<?> i was using TestModel it would work for any test that i pass of Class type TestModel. But i want to be able to pass the extended class to this method as well without having to write a method for each extended class. Any recommendations?
Adding information on the models in case it matters.
public class TestModel {
private String testDescription;
private String testName;
private String apiPath;
private String method;
private String expectedTest;
private Map<String, String> header = new HashMap<>();
private Object body;
private String expectedResult;
private String testCaseId;
private String testUUID;
private List testTypes;
public String getTestDescription() {
return testDescription;
}
public void setTestDescription(String testDescription) {
this.testDescription = testDescription;
}
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
public String getAPIPath() {
return apiPath;
}
public void setAPIPath(String apiPath) {
this.apiPath = apiPath;
}
public String getExpectedTest() {
return expectedTest;
}
public void setExpectedTest(String testName) {
this.expectedTest = testName;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
public String getExpectedResult() {
return expectedResult;
}
public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}
public String getTestCaseId() {
return testCaseId;
}
public void setTestCaseId(String testCaseId) {
this.testCaseId = testCaseId;
}
public String getTestUUID() {
return testUUID;
}
public void setTestUUID(String testUUID) {
this.testUUID = testUUID;
}
public List getTestTypes() {
return testTypes;
}
public void setTestTypes(List testTypes) {
this.testTypes = testTypes;
}
}
public class AdditionalTestModel extends TestModel {
#Override public Object getBody() {
return super.getBody();
}
}
Edit: per a request adding the call information here:
#Test(dataProvider = "Default", threadPoolSize = THREADS, timeOut = API_TIME_OUT)
#Description("")
public void sampleTest(AdditionalTestModel testFromDataProvider) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
testSetup(testFromDataProvider);
AdditionalTestModel test = BuildTest.buildTest(testFromDataProvider, template);
Response response = RestAPI.call(test, testEnvironment);
if (null != response) {
ValidateAPIResponse.validateTestModel(test, response);
} else {
Assert.fail("Response is null, probably a bad method.");
}
}
Where testFromDataProvider is passed from a TestNg data provider.
Now LppEdd below already pointed out i could only assign the base class using generics so working on trying it his way, just have not gotten a chance to change things up yet.
Edit: Also realize now my question was bad. Thanks LppEdd. I should have asked How can I get a method to accept an instance of a class and an instance of any extended class
You are close, you just need to use the extends modifier.
If the class passed in as the test and template parameter should be the same exact class type, you can do:
public static <T extends TestModel> Class<T> buildTest(Class<T> test, Class<T> template) { ... }
Otherwise you can do
public static Class<? extends extends TestModel> buildTest(Class<? extends TestModel> test, Class<? extends String> extends TestModel) { ... }
Which will allow different types to be returned and passed in to each parameter.
You can read up on Java generics and wilcards starting here: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
Your buildTest method must accept a TestModel class.
You might be looking for something like
public static TestModel buildTest(
final TestModel test,
final TestModel template) {
final Class<? extends TestModel> testClass = test.getClass();
final Method[] testMethods = testClass.getMethods();
for (final Method method : testMethods) {
final String name = method.getName();
if (name.startsWith("get")) {
final String testMethodType = method.getReturnType().getTypeName();
// additional code removed
}
}
// Maybe
return yourNewInstance; // yourNewInstance is a TestModel, or any class extending it
}
The template argument seems unused here (clarify).
What's the wanted return type? (clarify)
Usage example
final TestModel value1 = buildTest(new TestModel(), ...);
final TestModel value2 = buildTest(new AdditionalTestModel(), ...);
This looks to be exactly the same problem as must be solved by test frameworks. For example, see junit (https://junit.org/junit5/).
The core problem is how to obtain the collection of test methods of a class.
A direct solution would be to have the test class be required to answer its test methods, say, Collection<Function<Void, Void>> getTests(); This has several problems, one being that sub-classes must explicitly list their test methods, two being that sub-classes must be careful to add in the test methods from their super-class, and third, this really fits more as static behavior, which would try to shift java instance typing to the class layer, which just isn't supported by java.
An indirect solution would be to require that test methods satisfy a particular pattern (for example, must start with "test" and have no parameters), and use reflection to discover the methods. Or, use an annotation (say, #Test, which is what junit does) to mark out test methods, and again use the java reflection API to discover methods with the marker.

Use of Generics during validation

I am using apache CXF.
The following API is used to post a Contact.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ResponseResult create(#Context HttpHeaders httpHeaders, #Context Request request, #Context UriInfo uriInfo,
UserContact contact) throws MDMException;
Here the UserContact class contains the contact information about a user which is passed as JSON in the body.
I need to do some business validations on this UserContact object. But I do not like to have too much validation code packed in a single class.
I would like to do something like the following. But I am facing issue with Generics.
interface Rule<S>
{
void applyRule(S s)throws Exception;
}
interface Validatable
{
void validate() throws Exception;
}
public class MyValidator
{
private HashMap<? extends Rule ,?> map = new HashMap<>();
public void validate() throws Exception
{
for(Rule rule : map.keySet())
{
rule.applyRule(map.get(rule));
}
}
public <S> void addRule(Rule<S> rule, S data)
{
this.map.put(rule, data);
}
}
class EMailValidationRule implements Rule<String>
{
private static final Pattern emailPattern = Pattern.compile("email-regex");
public void applyRule(String s) throws Exception
{
if(!emailPattern.matcher(s).matches())
throw new Exception("Not a valid EMail");
}
}
So the UserContact has to do the following for its validation purposes. This keeps the code compact (IMO).
class UserContact implements Validatable
{
// some
// code
// related to User Contact
public void validate() throws Exception
{
MyValidator validator = new MyValidator();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "+1234567890");
validator.validate();
}
}
I keep getting error like :
The method put(capture#5-of ? extends Rule, capture#6-of ?) in the type HashMap is not applicable
for the arguments (Rule, S)
Also is the above design good for doing validations?
The problem is that, although your encapsulation ensures it, the compiler can not be sure that the retrieved Rule<...> has a type argument of the same type as the retrieved data.
There is also the problem of not being able to insert a Rule<T> with data of a subtype of T. If you have Rule<S> rule, S data the types have to be an exact match. While a Rule<S> could handle a subtype of S just fine.
While MyValidator is a cool little class, I can't really see the point in having it. Especially because you create a new one every time you call validate. It would also be hard to cache because the rules are static (the same for every instance of the class) and the data comes from individual instances (I'd assume).
You could also just do this:
class UserContact implements Validatable
{
// some
// code
// related to User Contact
// 1 rule instance for the entire class, not a new one per call to 'validate'
private static EMailValidationRule emailRule = new EmailValidationRule();
private static PhoneValidationRule phoneRule = new PhoneValidationRule();
public void validate() throws Exception
{
emailRule.applyRule("developer#stackoverflow.com");
phoneRule.applyRule("+1234567890");
}
}
Never the less, here is a working version of MyValidator:
class MyValidator {
private Map<Rule<?>, RuleNode<?>> map = new HashMap<>();
public void validate() throws Exception {
for(RuleNode<?> node : map.values())
node.apply();
}
public <T, D extends T> void addRule(Rule<T> rule, D data) {
#SuppressWarnings("unchecked") // unchecked, but safe due to encapsulation
RuleNode<T> r = (RuleNode<T>) map.get(rule);
if(r == null) {
r = new RuleNode<T>(rule);
map.put(rule, r);
}
r.add(data);
}
private static class RuleNode<T> { // Maintains that the rule and data are compatible
private final Rule<T> rule;
private final List<T> list = new ArrayList<>();
public RuleNode(Rule<T> rule) {
this.rule = rule;
}
public void add(T data) {
list.add(data);
}
public void apply() throws Exception {
for(T data : list)
rule.applyRule(data);
}
}
}
You just need to make a generic Version of the MyValidator Class
A generic class is defined with thss format:
class name<T1, T2, ..., Tn> { /* ... */ }
Defining the class using generics you will specify the types you want to use in your class, in your case <R extends Rule<S> ,S>
class MyValidator<R extends Rule<S> ,S>{
private HashMap<R ,S> map = new HashMap<>();
public void validate() throws Exception{
for(Rule<S> rule : map.keySet()){
rule.applyRule(map.get(rule));
}
}
public void addRule(R rule, S data){
this.map.put(rule, data);
}
}
Once done you just have to build a MyValidator of the desired type :
MyValidator<Rule<String>, String> validator = new MyValidator<>();
And finally add the rules matching the types of the validator :
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
So for example addind a phone validator your UserContact will looks like :
class PhoneValidationRule implements Rule<String>{
private static final Pattern phonePattern = Pattern.compile("phone-regex");
public void applyRule(String s) throws Exception{
if(!phonePattern.matcher(s).matches())
throw new Exception("Not a valid phone");
}
}
class UserContact implements Validatable{
public void validate() throws Exception{
MyValidator<Rule<String>, String> validator = new MyValidator<>();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "developer#stackoverflow.com");
validator.validate();
}
}

Use reflection to create classes at runtime

I have to create a list of objects, which are configured according to the name of some classes received as input.
For each object I have to call a method, which add an operation that is created dynamically.
However I don't know exactly ho to resolve the problem.
Please see an example below.
String className; // this is an input parameter
final Class<?> classType = Class.forName(className);
// here I would like to use classType instead of "?" but it gives me an error.
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null)));
tasks.put(task, null);
task.addOperation(new Operation<classType>() { // this gives an error
#Override
public void onNewInput(classType input) { // this gives an error
System.out.println(input)
}
});
As you can see from the comments, the surrounding infrastructure and the intention are not entirely clear. However, you can achieve a certain degree of type-safety with a "helper" method that captures the type of the given Task, and allows you to work with this type internally:
public class RuntimeType
{
public static void main(String[] args) throws Exception
{
String className = "";
final Class<?> classType = Class.forName(className);
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null));
addOperation(task);
}
private static <T> void addOperation(Task<T> task)
{
task.addOperation(new Operation<T>()
{
#Override
public void onNewInput(T input)
{
System.out.println(input);
}
});
}
}
class TaskFactory
{
public static Task<?> createTask(String string)
{
return null;
}
}
class Task<T>
{
public void addOperation(Operation<T> operation)
{
}
}
interface Operation<T>
{
void onNewInput(T input);
}

GSON-Deserialization doesn't map correct classes

I'm using GSON for deserializing JSON-Strings in my Android-App. My problem is, that I don't retrieve the expected objects, but LinkedTreeMap-objects, what causes an exception:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to sis.hierrein.android.DDL.Core.Uniqueidentifier
My classes:
public class Uniqueidentifier {
public String OID;
}
public class Element extends Uniqueidentifier {
(...)
}
The methods, that implement the deserialization:
public void Set(String data) throws Exception {
HashSet<T> objList = loadObject(data);
if(objList == null) {
throw new Exception("Es wurden keine Daten übergeben.");
}
for(T obj: objList) {
// Trying to iterate over each element causes the exception
}
}
// T is in this example Element
protected HashSet<T> loadObject(String data) {
Gson parser = new Gson();
HashSet<T> obj;
Type t = new TypeToken<HashSet<T>>(){}.getType();
try {
obj = parser.fromJson(data, t);
}
catch(Exception e) {
return null;
}
return obj;
}
The class
public class Handler<T extends Uniqueidentifier> {
(...)
}
which contains this methods, is used as the superclass of
public class ElementHandler extends Handler<Element> {
(...)
}
ElementHandler itself only contains a constructor, so the methods of the superclass are called.
Background: all these classes are instantiated / methods are called via reflection.

Unable to instantiate sub class with parameter

I keep getting the error: java.lang.NoSuchMethodException: com.production.workflow.MyWorkflow.<init>(com.production.model.entity.WorkflowEntity)
I have a constructor that is expecting WorkflowEntity so I'm not able to figure out why it's saying NoSuchMethod. Is there something about constructor inheritance that is preventing this from instantiating?
My instantiation factory:
public static Workflow factory(WorkflowEntity workflowEntity) {
try {
Class<?> clazz = Class.forName(workflowEntity.getClassName()).asSubclass(Workflow.class);
Constructor c = clazz.getConstructor(WorkflowEntity.class);
Object workflowClass = c.newInstance(clazz);
return (Workflow) workflowClass;
} catch (Exception e) {
e.printStackTrace();
logger.severe("Unable to instantiate "+workflowEntity.getClassName()+" class: " + e.getLocalizedMessage());
}
return null;
}
Workflow class:
public class MyWorkflow extends Workflow {
//no constructors
Extended class:
abstract public class Workflow {
protected static final Logger logger = Logger.getLogger(Workflow.class.getName());
private WorkflowEntity entity;
protected WorkflowProcess workflowProcess;
#Autowired
private WorkflowProcessService workflowProcessService;
/* Don't use this one */
public Workflow() { }
/* Default constructor */
public Workflow (WorkflowEntity entity) {
this.entity = entity;
//get first workflow process
//#todo this should factor in rule, for multiple starting points
for (WorkflowProcessEntity workflowProcessEntity : entity.getWorkflowProcesses()) {
workflowProcess = WorkflowProcess.factory(workflowProcessEntity);
break;
}
}
There are two problems in your code:
Constructors are not automatically inherited by subclasses. You need to add the MyWorkflow(WorkflowEntity) constructor to the MyWorkflow class.
Your new instance call needs to be made with the workflowEntity instance (and not the class instance you are giving it now)
Here:
class MyWorkflow extends Workflow {
public MyWorkflow() {
super();
}
public MyWorkflow(WorkflowEntity entity) {
super(entity);
}
}
public static Workflow factory(WorkflowEntity workflowEntity) {
try {
Class<?> clazz = Class.forName(workflowEntity.getClassName())
.asSubclass(Workflow.class);
Constructor<?> c = clazz.getConstructor(WorkflowEntity.class);
Object workflowClass = c.newInstance(workflowEntity);
return (Workflow) workflowClass;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Consider the builder pattern instead of the factory pattern. Here is an example that builds a WorkFlow that takes a WorkflowEntity constructor parameter and builds a workFlow that does not take a WorkFlowEntity pattern (just showing multiple options available via a builder).
public class WorkFlowBuilder
{
private WorkflowEntity constructorParameter;
private Class workflowClass;
public WorkFlowBuilder(Class desiredWorkflowClass)
{
if (desiredWorkflowClass != null)
{
workflowClass = desiredWorkflowClass;
}
else
{
throw new IllegalArgumentException("blah blah blah");
}
}
public void setConstructorParameter(final WorkflowEntity newValue)
{
constructorParameter = newValue;
}
public WorkFlow build()
{
Object workflowObject;
if (constructorParameter != null)
{
Constructor constructor = workflowClass.getConstructor(WorkflowEntity.class);
Object workflowObject;
workflowObject = constructor.newInstance(workflowEntity);
}
else
{
workflowObject = workflowClass.newInstance();
}
return (WorkFlow)workflowObject;
}
}
Use this as follows:
WorkFlowBuilder builder = new WorkFlowBuilder(MyWorkFlow.class);
WorkflowEntity entity = new WorkFlowEntity();
WorkFlow item;
entity... set stuff.
builder.setConstructerParameter(entity)
item = builder.build();
I think you just want to pass in the workflowEntity into the constructor on the newInstance call, instead of the typed Class.
Constructors lost their outside visibility during inheritance.
You need to redefine it in MyWorkflow.
This is done so because sub classes may not support the super class creation process. So super object constructors does not make sense to sub classes and it's even unsafe if they were visible outside.
You should also remove the default constructor if your class can be used if instantiated without WorkflowEntity. Just remove it from Workflow and do not add to MyWorkflow.
UPD
You should also consider using generics to avoid class casting.
public Workflow create(WorkflowEntity workflowEntity) throws
ClassNotFoundException, NoSuchMethodException, SecurityException
, InstantiationException, IllegalAccessException
, IllegalArgumentException, InvocationTargetException {
Class<? extends Workflow> clazz = Class.forName(workflowEntity.getClassName()).asSubclass(Workflow.class);
Constructor<? extends Workflow> c = clazz.getConstructor(WorkflowEntity.class);
Workflow workflowClass = c.newInstance(clazz);
return workflowClass;
}
class WorkflowEntity {
public String getClassName() {
return "className";
};
}
class Workflow {
Workflow(WorkflowEntity entity) {
};
}
class MyWorkflow extends Workflow {
MyWorkflow(WorkflowEntity entity) {
super(entity);
}
}

Categories