Using custom built-ins in external Java Project - java

I have created new custom built-ins inside my Java program. They work fine and if I execute them into a ".txt" file making a rule I get the result I expect to .
The problem is that I can use this built-ins only where I have implemented them (in the same file).
For example, I have a java class file which is called "RulesOntology_DiffYear.java" where I have created a built-in called "myDiffDate" which does the difference between two dates and it return the number of years of difference.
If I try to write the rule (using the built-in "myDiffDate") and I try to execute it inside the file "RulesOntology_DiffYear.java", the rule recognises the new built-in and it runs it without problems.
If I try to write the same rule in another file ".java", the built-in I have created in "RulesOntology_DiffYear.java", it is not recognised, of course.
How can I use my custom built-ins in another files ? Like the default built-ins ? Need I to create a library or there is something else to do ?
I have found this Create a library for new built-ins Jena but I do not really know how it works (even if I tried it) and I do not know if it is the easiest solution for my purpose.
In my file "DiffDateLib.java" I have defined my custom built-in "myDiffDateYear".
I have created another file named "MyReasonerFactory.java" with the following code :
public class MyReasonerFactory implements ReasonerFactory {
/** The single global static registry */
public static BuiltinRegistry theRegistry;
private static final String RULE_LOC = "????";
static {
BuiltinRegistry.theRegistry.register(new DiffDateLib());
}
#Override
public Reasoner create(Resource r) {
final GenericRuleReasoner reasoner = new GenericRuleReasoner(this, r);
reasoner.setRules(FBRuleReasoner.loadRules(RULE_LOC));
return reasoner;
}
#Override
public Model getCapabilities() {
return GenericRuleReasonerFactory.theInstance().getCapabilities();
}
#Override
public String getURI() {
return RULE_LOC;
}
}
What can I write into the variable "RULE_LOC" ? Which filepath ?
I have made also another class called "tryNoBuiltin.java" which contains the main of the program :
public class tryNoBuiltin {
public static void main(String[] args) throws OWLOntologyStorageException,
OWLOntologyCreationException, IOException {
String percorsoFile ="./prova_rules_M_rdf.owl";
String ruleFile= "./prova_rules_M_rdf_7_diffDate.txt";
Model rawModel = ModelFactory.createDefaultModel();
MyReasonerFactory MyReas = new MyReasonerFactory();
//create a resource (empty model)
Resource configuration = rawModel.createResource();
// set engine mode
configuration.addProperty(ReasonerVocabulary.PROPruleMode, "hybrid");
// set the rules file
configuration.addProperty(ReasonerVocabulary.PROPruleSet, ruleFile);
List<Rule> rules = Rule.rulesFromURL(ruleFile);
GenericRuleReasoner reasonerRULE = (GenericRuleReasoner) GenericRuleReasonerFactory.theInstance().create(configuration);
reasonerRULE.setRules(rules);
Model modelRULE= FileManager.get().loadModel(percorsoFile);
//create the inference model
InfModel infModelRULE = ModelFactory.createInfModel(reasonerRULE, modelRULE);
//force starting the rule execution
infModelRULE.prepare();
//write down the result in RDFXML form
infModelRULE.write(System.out);
}
}
In the main I can see the output so, the new built-in (which I have used into the rule file "prova_rules_M_rdf_7_diffDate.txt") is recognised .
But I think I did not use correctly the class "MyReasonerFactory". Can you provide me an example ? Where can I use its methods ?
Thank you all!

This should probably work for you. The idea is that the MyReasonerFactory is acting as a way to retrieve a reasoner for your domain. Not only does it register the builtins, but it also gets/sets the rules.
public class MyReasonerFactory implements ReasonerFactory {
private static final String RULE_LOC = "./prova_rules_M_rdf_7_diffDate.txt";
static {
BuiltinRegistry.theRegistry.register(new DiffDateLib());
}
#Override
public Reasoner create(Resource r) {
final GenericRuleReasoner reasoner = new GenericRuleReasoner(this, r);
reasoner.setRules(Rule.rulesFromURL(RULE_LOC));
return reasoner;
}
#Override
public Model getCapabilities() {
return GenericRuleReasonerFactory.theInstance().getCapabilities();
}
#Override
public String getURI() {
// TODO NOTE this is just a suggestion
return "urn:ex:provaRuleReasoner";
}
}
You did a few redundant things while loading the rules. ie: you set it in the config as well as manually retrieved them and parsed them. I removed the extra stuff.
public class tryNoBuiltin {
public static void main(String[] args) throws OWLOntologyStorageException,
OWLOntologyCreationException, IOException {
//Create a configuration model
Resource configuration = ModelFactory.createDefaultModel().createResource();
configuration.addProperty(ReasonerVocabulary.PROPruleMode, "hybrid");
MyReasonerFactory MyReas = new MyReasonerFactory();
GenericRuleReasoner reasonerRULE = (GenericRuleReasoner)MyReas.create(configuration);
Model modelRULE= FileManager.get().loadModel("./prova_rules_M_rdf.owl");
InfModel infModelRULE = ModelFactory.createInfModel(reasonerRULE, modelRULE);
infModelRULE.prepare();
//write down the result in RDFXML form
infModelRULE.write(System.out);
}
}

Related

Is it appropriate to use reflection to create a list of objects of all subclasses?

In my program, different features are divided into different modules, potentially hundreds of modules,
each module is a subclass of an abstract Module class
which look something like this
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
with subclasses that look like this
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
#Override
public void execute() {
//Do something
}
}
and I need to make a list of instances of all modules but doing it like this is kinda tedious since my program might have hundreds of modules and it might be hard to debug(I might miss a few lol)
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
MODULES.add(new Mod1());
MODULES.add(new Mod2());
MODULES.add(new Mod3());
MODULES.add(new Mod4());
MODULES.add(new Mod5());
}
so I think using reflection might be the way to go but after a quick google search, I see many people don't like reflection in production code, so I come here to ask if this is an appropriate case to use reflection or is there any reason or design change to avoid using reflection in this particular case
edit: the list will be used stuff like rendering modules in gui or calling execute method of the module from other user interface like command(simply find the module with matching name and execute it)
note: this is not how my code actually look like but a highly simplified version that give a similar idea
For a minimal-effort approach, java provides the built-in class java.util.ServiceLoader.
With this class you can obtain all available implementations of a service class (in this case Module) like
ServiceLoader moduleLoader = ServiceLoader.load(Module.class);
for (Module mod : moduleLoader) {
System.out.println(mod.name);
}
Note that I highlighted the term available in the previous sentence. In fact, the ServiceLoader implementation does not perform any advanced class-hierarchy lookup, instead it relies on a specific resource file named equal to the canonical name of the Module class.
META-INF/services/com.example.project.Module
com.example.project.impl1.Module1
com.example.project.impl2.Module2
Where each line of the file references an available implementation of Module.
Now, ServiceLoader is an implementation of the java SPI specification, which does not necessarily have to qualify perfectly for your use case. However, since its idea of service loading and providing is rather simple, you can easily build your own variant.
public class JsonServiceLoader<S> {
private Class<S> service;
private Map<String, String> serviceIdentifiers;
public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
Map<String, String> serviceIdentifiers = new HashMap<>();
String name = "META-INF/json-services/" + service.getCanonicalName();
// TODO check for null references where necessary
for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
try (InputStream resource = resources.next().openStream()) {
for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
serviceIdentifers.merge(
identifer.getKey(),
identifer.getValue(),
(value1, value2) -> throw new ServiceConfigurationError(
"duplicate service identifier '" + identifier.getKey() + "'"
);
);
}
}
}
return new JsonServiceLoader<>(service, serviceIdentifiers);
}
private static Map<String, String> parseJson(InputStream resource) {
// TODO parse JSON data from the given stream using your favourite JSON facility
/*
* If you want to use a different style of resources, e.g. XML, this is the only
* location you have to change (you might want to rename 'json-services' though).
*/
return new HashMap<>();
}
private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
this.service = service;
this.serviceIdentifiers = serviceIdentifiers;
}
public Set<String> getServiceIdentifiers() {
return Collections.unmodifiableSet(serviceIdentifiers.keySet());
}
public S getService(String name) {
String className = serviceIdenfiers.get(name);
if (null == className) {
throw new IllegalArgumentException("invalid service identifier '" + name + "'");
}
// TODO improve error handling
try {
return service.cast(Class.forName(className).newInstance());
} catch(Exception exc) {
throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
}
}
}
Your JSON resources could look like
META-INF/json-services/com.example.project.Module
{
"Mod1" : {
"class" : "com.example.project.impl1.Module1"
},
"Mod2" : {
"class" : "com.example.project.impl2.Module2"
}
}
allowing future extensions.
At this point, the Module class also does not need to know its name (the class member name) anymore, since you can always ask the service loader for an appropriate instance. (If you do so at some place in your code, you will already know for which name you just asked.) If so desired, you can also add more logic to this JsonServiceLoader, such as caching.
Ultimately, it all depends on how much information you want to process around your Module and how much of this information you want the Module implementations to handle and how much of it you want the service framework to handle.
Perhaps you can pass the list into the constructor of the parent class and add subclass itself to the list in the constructor method.
Just like this
public abstract class Module {
public final String name;
public Module(String name, List<Module> list) {
this.name = name;
list.add(this);
}
public abstract void execute();
}
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
new Mod1(MODULES);
}

Apache Drill: Write general-purpose array_agg UDF

I would like to create an array_agg UDF for Apache Drill to be able to aggregate all values of a group to a list of values.
This should work with any major types (required, optional) and minor types (varchar, dict, map, int, etc.)
However, I get the impression that Apache Drill's UDF API does not really make use of inheritance and generics. Each type has its own writer and handler, and they cannot be abstracted to handle any type. E.g., the ValueHolder interface seems to be purely cosmetic and cannot be used to have type-agnostic hooking of UDFs to any type.
My current implementation
I tried to solve this by using Java's reflection so I could use the ListHolder's write function independent of the holder of the original value.
However, I then ran into the limitations of the #FunctionTemplate annotation.
I cannot create a general UDF annotation for any value (I tried it with the interface ValueHolder: #param ValueHolder input.
So to me it seems like the only way to support different types to have separate classes for each type. But I can't even abstract much and work on any #Param input, because input is only visible in the class where its defined (i.e. type specific).
I based my implementation on https://issues.apache.org/jira/browse/DRILL-6963
and created the following two classes for required and optional varchars (how can this be unified in the first place?)
#FunctionTemplate(
name = "array_agg",
scope = FunctionScope.POINT_AGGREGATE,
nulls = NullHandling.INTERNAL
)
public static class VarChar_Agg implements DrillAggFunc {
#Param org.apache.drill.exec.expr.holders.VarCharHolder input;
#Workspace ObjectHolder agg;
#Output org.apache.drill.exec.vector.complex.writer.BaseWriter.ComplexWriter out;
#Override
public void setup() {
agg = new ObjectHolder();
}
#Override
public void reset() {
agg = new ObjectHolder();
}
#Override public void add() {
if (agg.obj == null) {
// Initialise list object for output
agg.obj = out.rootAsList();
}
org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter listWriter =
(org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj;
listWriter.varChar().write(input);
}
#Override
public void output() {
((org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj).endList();
}
}
#FunctionTemplate(
name = "array_agg",
scope = FunctionScope.POINT_AGGREGATE,
nulls = NullHandling.INTERNAL
)
public static class NullableVarChar_Agg implements DrillAggFunc {
#Param NullableVarCharHolder input;
#Workspace ObjectHolder agg;
#Output org.apache.drill.exec.vector.complex.writer.BaseWriter.ComplexWriter out;
#Override
public void setup() {
agg = new ObjectHolder();
}
#Override
public void reset() {
agg = new ObjectHolder();
}
#Override public void add() {
if (agg.obj == null) {
// Initialise list object for output
agg.obj = out.rootAsList();
}
if (input.isSet != 1) {
return;
}
org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter listWriter =
(org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj;
org.apache.drill.exec.expr.holders.VarCharHolder outHolder = new org.apache.drill.exec.expr.holders.VarCharHolder();
outHolder.start = input.start;
outHolder.end = input.end;
outHolder.buffer = input.buffer;
listWriter.varChar().write(outHolder);
}
#Override
public void output() {
((org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj).endList();
}
}
Interestingly, I can't import org.apache.drill.exec.vector.complex.writer.BaseWriter to make the whole thing easier because then Apache Drill would not find it.
So I have to put the entire package path for everything in org.apache.drill.exec.vector.complex.writer in the code.
Furthermore, I'm using the depcreated ObjectHolder. Any better solution?
Anyway: These work so far, e.g. with this query:
SELECT
MIN(tbl.`timestamp`) AS start_view,
MAX(tbl.`timestamp`) AS end_view,
array_agg(tbl.eventLabel) AS label_agg
FROM `dfs.root`.`/path/to/avro/folder` AS tbl
WHERE tbl.data.slug IS NOT NULL
GROUP BY tbl.data.slug
however, when I use ORDER BY, I get this:
org.apache.drill.common.exceptions.UserRemoteException: SYSTEM ERROR: UnsupportedOperationException: NULL
Fragment 0:0
Additionally, I tried more complex types, namely maps/dicts.
Interestingly, when I call SELECT sqlTypeOf(tbl.data) FROM tbl, I get MAP.
But when I write UDFs, the query planner complains about having no UDF array_agg for type dict.
Anyway, I wrote a version for dicts:
#FunctionTemplate(
name = "array_agg",
scope = FunctionScope.POINT_AGGREGATE,
nulls = NullHandling.INTERNAL
)
public static class Map_Agg implements DrillAggFunc {
#Param MapHolder input;
#Workspace ObjectHolder agg;
#Output org.apache.drill.exec.vector.complex.writer.BaseWriter.ComplexWriter out;
#Override
public void setup() {
agg = new ObjectHolder();
}
#Override
public void reset() {
agg = new ObjectHolder();
}
#Override public void add() {
if (agg.obj == null) {
// Initialise list object for output
agg.obj = out.rootAsList();
}
org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter listWriter =
(org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter) agg.obj;
//listWriter.copyReader(input.reader);
input.reader.copyAsValue(listWriter);
}
#Override
public void output() {
((org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj).endList();
}
}
#FunctionTemplate(
name = "array_agg",
scope = FunctionScope.POINT_AGGREGATE,
nulls = NullHandling.INTERNAL
)
public static class Dict_agg implements DrillAggFunc {
#Param DictHolder input;
#Workspace ObjectHolder agg;
#Output org.apache.drill.exec.vector.complex.writer.BaseWriter.ComplexWriter out;
#Override
public void setup() {
agg = new ObjectHolder();
}
#Override
public void reset() {
agg = new ObjectHolder();
}
#Override public void add() {
if (agg.obj == null) {
// Initialise list object for output
agg.obj = out.rootAsList();
}
org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter listWriter =
(org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter) agg.obj;
//listWriter.copyReader(input.reader);
input.reader.copyAsValue(listWriter);
}
#Override
public void output() {
((org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter)agg.obj).endList();
}
}
But here, I get an empty list in the field data_agg for my query:
SELECT
MIN(tbl.`timestamp`) AS start_view,
MAX(tbl.`timestamp`) AS end_view,
array_agg(tbl.data) AS data_agg
FROM `dfs.root`.`/path/to/avro/folder` AS tbl
GROUP BY tbl.data.viewSlag
Summary of questions
Most importantly: How do I create an array_agg UDF for Apache Drill?
How to make UDFs type-agnostic/general purpose? Do I really have to implement an entire class for each Nullable, Required and Repeated version of all types? That's a lot to do and quite tedious. Isn't there a way to handle values in an UDF agnostic to the underlying types?
I wish Apache Drill would just use what Java offers here with function generic types, specialised function overloading and inheritence of their own type system. Am I missing something on how to do that?
How can I fix the NULL problem when I use ORDER BY on my varchar version of the aggregate?
How can I fix the problem where my aggregate of maps/dicts is an empty list?
Is there an alternative to using the deprecated ObjectHolder?
To answer your question, unfortunately you've run into one of the limits of the Drill Aggregate UDF API which is that it can only return simple data types.1 It would be a great improvement to Drill to fix this, but that is the current status. If you're interested in discussing that further, please start a thread on the Drill user group and/or slack channel. I don't think it is impossible, but it would require some modification to the Drill internals. IMHO it would be well worth it because there are a few other UDFs that I'd like to implement that need this feature.
The second part of your question is how to make UDFs type agnostic and once again... you've found yet another bit of ugliness in the UDF API. :-) If you do some digging in the codebase, you'll see that most of the Math functions have versions that accept FLOAT, INT etc..
Regarding the aggregate of null or empty lists. I actually have some good news here... The current way of doing that is to provide two versions of the function, one which accepts regular holders and the second which accepts nullable holders and returns an empty list or map if the inputs are null. Yes, this sucks, but the additional good news is that I'm working on cleaning this up and hopefully will have a PR submitted soon that will eliminate the need to do this.
Regarding the ObjectHolder, I wrote a median function that uses a few Stacks to compute a streaming median and I used the ObjectHolder for that. I think it will be with us for some time as there is no alternative at the moment.
I hope this answers your questions.

Design - Global Settings from Consumer Module

I am attempting to make a library. Had some constants in my code which were some kind of different time delays and certain String values across the code. I could extract them to a separate constant class to get something like
Delays.LENGTH_SHORT (seems cleaner)
They are linked to many different classes inside the library code.
Now, the problem in hand is to make it configurable, from the calling consumer (i.e., if the consumer provides the values, then use those, otherwise use the preset values). From the consumer end, I decided to use Builder pattern for creating the config and passing it in the init of the library module (this only happens once in the lifecycle of it).
Is there a way to keep the above syntax and yet accept the config from consumer (the settings are only configured once during init, for all other times, it completely behaves as constant)?
Reading from a file, appeared to be costly.
For constants that can take a value from only a fixed set of values then it is always better to use Java enums instead of integers or strings or other raw data types. They are much better to understand and maintain over the period of time. Default values should ideally be read from a property file to initialize them. But as you have mentioned in your case, you want to avoid the cost of reading from a file for performance reasons. The design question is always open ended and can have multiple approaches. One approach that I recommend can be as below:
public interface Configuration {
public Continent getContinent(); //For fixed set of values use enum
public Integer getPoolSize(); //If the config can take any value then use the corresponding data type directly
public String getDefaultLabel();
}
public enum Continent {
ANTARTICA, AFRICA, ASIA, AUSTRALIA, EUROPE, NORTH_AMERICA, SOUTH_AMERICA;
}
public class ConfigurationBuilder {
private DefaultConfiguration configurationInstance;
private class DefaultConfiguration implements Configuration {
//Ideally the below values should be read from a property file, instead of hard coding it here.
private Integer poolSize = Integer.valueOf(50);
private String defaultLabel = "DEFAULT";
private Continent continent = Continent.ASIA;
#Override
public Continent getContinent() {
return continent;
}
#Override
public Integer getPoolSize() {
return poolSize;
}
#Override
public String getDefaultLabel() {
return defaultLabel;
}
}
public ConfigurationBuilder withContinent(Continent continent) {
this.configurationInstance.continent = continent;
return this;
}
public ConfigurationBuilder withPoolSize(Integer poolSize) {
this.configurationInstance.poolSize = poolSize;
return this;
}
public ConfigurationBuilder withDefaultLabel(String defaultLabel) {
this.configurationInstance.defaultLabel = defaultLabel;
return this;
}
public Configuration build() {
return this.configurationInstance;
}
public ConfigurationBuilder() {
this.configurationInstance = new DefaultConfiguration();
}
public static Configuration buildDefaultConfiguration() {
return new ConfigurationBuilder().build();
}
}
public class Library {
private Configuration configuration;
public void init(Configuration configuration) {
this.configuration = configuration;
}
public void init() {
this.configuration = ConfigurationBuilder.buildDefaultConfiguration();
}
private Library(Configuration config) {
this.init(config);
}
private Library() {
this.init();
}
/**
* Library is not singleton here.
*
*/
public static Library getInstance(Configuration configuration) {
return new Library(configuration);
}
public static Library getInstance() {
return new Library();
}
}
public class Client {
public static void main(String args[]) {
Configuration config = new ConfigurationBuilder()
.withContinent(Continent.AFRICA)
.withPoolSize(20)
.withDefaultLabel("Label")
.build();
Library lib = Library.getInstance();
lib.init(config);
}
}
Please check the Library and Client classes for usage.
- It uses the Builder pattern.
- It has init() and init(Configuration) methods to allow completely relying on Library defaults.
- ConfigurationBuilder supports supplying some or all configuration values to override
- Currently all three config options are overridable - continent, poolSize and defaultLabel. However if some configuration is private to Library then just remove the withXXX method for that property from the Builder.
Hope this fits your need. Good question!

Java Simple Factory with constructors using different parameters

I have two ways of saving data in my application: save to database and save to file. Since I don't want client code dealing with construction of objects I created a class that (to my understanding) is simple factory with a factory method. Code below:
public static DataPersister createDataPersister(Boolean saveToDb, Session session, String filename) {
if (saveToDb) {
return new DatabaseDataPersister(session);
} else {
return new FileDataPersister(filename);
}
}
With this setup client code doesn't have to deal with constructing anything or deciding whether to save to DB or file - it can just call a save() method on an object returned by the factory like so:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
dataPersister.save(this.data);
My question is - is this solution breaking SOLID principles? In order to create e.g. a DatabaseDataPersister client code needs to pass on a filename parameter, and this implementation of DataPersister won't have any use of it. I feel like it doesn't sit right with something similar to Interface-segregation principle but not quite that.
And if the solution is indeed a code smell - how do I go about cleaning it?
The SOLID principle I think is in violation is DIP.
Your client classes, by having to depend on the static factory directly, have a compile-time dependency on actual implementations, DatabaseDataPersister and FileDataPersister, rather than just the abstraction DataPersister.
To solve, supply to the client the DataPersister you want them to use. The constructor is usually a good place for this:
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
This code compiles without the concrete implementations, i.e. it has no dependency on them. The client also doesn't need to know the filename or session so it solves that code smell too.
We can decide which concrete implementation to give it at construction time, here I use your existing method:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
ExampleClient example = new ExampleClient(dataPersister);
This is a perfect opportunity to use the factory pattern
interface DataPersister {
void persist(String s);
}
private class DatabasePersister implements DataPersister {
final Session session;
public DatabasePersister(Session session) {
this.session = session;
}
#Override
public void persist(String s) {
System.out.println("Persist to database: " + s);
}
}
private class FilePersister implements DataPersister {
final String filename;
public FilePersister(String filename) {
this.filename = filename;
}
#Override
public void persist(String s) {
System.out.println("Persist to file: " + s);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
public void test(String[] args) {
DataPersister databasePersister = new PersisterFactory().createDatabasePersister(new Session());
databasePersister.persist("Hello");
DataPersister filePersister = new PersisterFactory().createFilePersister("Hello");
filePersister.persist("Hello");
}
You already pass a bunch of stuff irrelevant to various persisters.
As it stands you need a method that takes a Session and one that takes a String and you're done. No need for a boolean, no need for useless params. That handles your decision making with no cruft.
Whether or not that's a good idea... I'm ambivalent. You're not saving much; might as well just have a static factory in each type so it's explicit in the code what type you're creating.
Consider what happens when you add a new persister, like a REST endpoint, that would take a URL (could be a string, could be an actual URL). You now need even more useless parameters etc. Or you could pass in a URI from the beginning, e.g., file:// or http:// and get around that problem.
There are any number of ways this could be done–I'm not convinced there's a "clearly correct" answer, and it may boil down to opinion.
Well the right solution here is combining the dependency injection from weston and the factory pattern from OldCurmudgeon.
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
The upper level code:
PersisterFactory = new PersisterFactory();
DataPersister dataPersister;
if (saveToDb)
dataPersister = PersisterFactory.createDatabasePersister(new Session());
else
dataPersister = PersisterFactory.createFilePersister("Hello");
ExampleClient example = new ExampleClient(dataPersister);
Usually the dataPersister comes from the DI container and the saveToDb comes from the config, but of course testing can be an exception.

Generic type invocation using string class names

Hope you can help me with this:
I have ...
a string list of class names called classNameList
a generic class Geography<T>
a static generic method <T> void read(Class<T> cl, Geography<T> geo)
I want to loop through the string class name list and call the generic method for each of these classes.
What I tried but obviously did not work:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Error: myClass.newInstance cannot be resolved to a type
My code runs perfectly for a single call of the generic function:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Any ideas how I could do this?
UPDATE:
Thanks for the helpful input, still it's hard for me to adopt it to my real code.
So this is the non simplyfied problem:
I do ready in shapefile-Data with a shapefileLoader, for each feature of the Shapefile a class (GuadAgent) is initialized with a predifined class (PlantWind). I have shapefiles in my input-directory with the names of the Classes their features do represent. I want Java to read in the shapefiles and create the respective agent class. (the agents are also placed in a context and a geography..)
Used classes are: ShapefileLoader, Geography, the other classes can be find at the same website
This part is in the main-method:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The static Method that reads in the names:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
How can I get rid of the switch? Although their number is finit, there are very many different agents...
Unfortunately, there's no syntax close to your intention (nice idea though).
The basic problem is that Class.forName() returns an unknown Class<?>, so you need a cast somewhere. It's just a mater of where you put it.
I suggest this approach (which compiles) that bundles up doing a read() based on a class name:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
May I also suggest using the foreach loop syntax, for tidier code:
for (String className : classNameList) {
readForName(className.getName());
}
Creating instances from Generic Types at Runtime
I am not entirely clear on what you are trying to accomplish, but at first look it looks like the simplest solution is the best solution.
It could be solved with using a scripting environment ( Groovy, JavaScript, JRuby, Jython ) that could dynamically evaluate and execute arbitrary code to create the objects, but that got extremely convoluted and overly complex, just to create an object.
But unfortunately I think it has a very pedestrian solution.
As long as there is a predefined set of supported types, you can use a Factory pattern. Here I just leverage the Provider<>T interface from the javax.inject/com.google.inject package.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(#Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
#Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Disclaimer:
This is just a proof of concept example, I would never use a switch
statement like that in production code I would use a Strategy
Pattern or Chain of Responsibility Pattern to encapsulate the logic
of what type to create based on the ClassName key.
This initially looked like a generics problem, it isn't, it is a creation problem.
That said, you don't need to pass around instances of Class<?> you can get Generic Type information off of Parameterized classes at runtime with TypeToken from Guava.
You can even create instances of any generic type at runtime with TypeToken from the Guava library.
The main problem is this syntax isn't supported: Geography<myClass.newInstance()> geo; and I can't think of anyway to fake it other than the Provider implementation above.
Here is a straw man example of how to use TypeToken so that your
parameterized classes will always know their types!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Notes:
Works great for classes that have a default no arg constructor.
Works better than using straight reflection if there are no default no arg constructors.
Should play well with Guice allowing you to use the ".getRawType()generatedClassto pass togetInstance()` of an Injector. have not tried this yet, I just thought of it!
You can use Class<T>.cast() to do casting that doesn't need #SuppressWarning("unchecked") all over the place.`
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
Update: I'm not sure what the Geography class is meant to do. If it needs a generically typed object, it might look like this:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
If it needs a class, the constructor might look like this:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}

Categories

Resources