In my project, I have used as a code base the Lightbend activator template. It works perfect but the Actor in example is not created with parameters.
I need to create a new Actor and pass to it a parameter during construction such as :
getContext().actorOf(SpringExtProvider.get(actorSystem).props("ControllerActor",type), "controller_" + type)
In this use case, the controller needs to be able to be created with a props paremeter type which is used to typed (obviously) the controller. Each Actor is specifically design to handle and control a specific king of object depending on its type.
But I can't add a new parameter in the props method to pass this parameter. It is not working.
This is my code :
SpringExtension.java
package com.orange.spectre.core.akka.configuration;
import akka.actor.AbstractExtensionId;
import akka.actor.ExtendedActorSystem;
import akka.actor.Extension;
import akka.actor.Props;
import com.orange.spectre.core.config.SpringActorProducer;
import org.springframework.context.ApplicationContext;
/**
* Created by Hervé Darritchon on 04/04/2016.
* <p>
* An Akka Extension to provide access to Spring managed Actor Beans.
*/
public class SpringExtension extends AbstractExtensionId<SpringExtension.SpringExt> {
/**
* The identifier used to access the SpringExtension.
*/
public static SpringExtension SpringExtProvider = new SpringExtension();
/**
* Is used by Akka to instantiate the Extension identified by this
* ExtensionId, internal use only.
*/
#Override
public SpringExt createExtension(ExtendedActorSystem system) {
return new SpringExt();
}
/**
* The Extension implementation.
*/
public static class SpringExt implements Extension {
private volatile ApplicationContext applicationContext;
/**
* Used to initialize the Spring application context for the extension.
*
* #param applicationContext
*/
public void initialize(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Create a Props for the specified actorBeanName using the
* SpringActorProducer class.
*
* #param actorBeanName The name of the actor bean to create Props for
* #return a Props that will create the named actor bean using Spring
*/
public Props props(String actorBeanName) {
return Props.create(SpringActorProducer.class,
applicationContext, actorBeanName);
}
public Props props(String actorBeanName, String type) {
return Props.create(SpringActorProducer.class,
applicationContext, actorBeanName,type);
}
}
}
SpringActorProducer
package com.orange.spectre.core.config;
import akka.actor.Actor;
import akka.actor.IndirectActorProducer;
import org.springframework.context.ApplicationContext;
/**
* Created by Hervé Darritchon on 21/03/2016.
*/
public class SpringActorProducer implements IndirectActorProducer {
private final ApplicationContext applicationContext;
private final String actorBeanName;
private final String type;
public SpringActorProducer(ApplicationContext applicationContext,
String actorBeanName) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.type = null;
}
public SpringActorProducer(ApplicationContext applicationContext,
String actorBeanName, String type) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.type = type;
}
#Override
public Actor produce() {
return (Actor) applicationContext.getBean(actorBeanName);
}
#Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext.getType(actorBeanName);
}
}
I can't create an actor with a props parameter as it is possible basically with Akka like (Documentation) :
public class DemoActor extends UntypedActor {
/**
* Create Props for an actor of this type.
* #param magicNumber The magic number to be passed to this actor’s constructor.
* #return a Props for creating this actor, which can then be further configured
* (e.g. calling `.withDispatcher()` on it)
*/
public static Props props(final int magicNumber) {
return Props.create(new Creator<DemoActor>() {
private static final long serialVersionUID = 1L;
#Override
public DemoActor create() throws Exception {
return new DemoActor(magicNumber);
}
});
}
final int magicNumber;
public DemoActor(int magicNumber) {
this.magicNumber = magicNumber;
}
#Override
public void onReceive(Object msg) {
// some behavior here
}
}
system.actorOf(DemoActor.props(42), "demo");
If you can help me, it should be great !
Thanks.
I'm agree with "nickebbitt". Not sure that it is spossible. And one of the way is to inject an implementation of magic number generator into the actor.
Furthermore, I wouldlike to suggest following IndirectActorProducer implementation:
public class SpringDIActor implements IndirectActorProducer {
private static final Logger LOG = LoggerFactory.getLogger(SpringDIActor.class);
private Class<? extends Actor> type;
private Actor actorInstance = null;
public SpringDIActor(Class<? extends Actor> type) {
this.type = type;
}
public SpringDIActor(Actor actorInstance) {
this.actorInstance = actorInstance;
}
/**
* This factory method must produce a fresh actor instance upon each
* invocation. <b>It is not permitted to return the same instance more than
* once.</b>
*/
#Override
public Actor produce() {
Actor newActor = actorInstance;
actorInstance = null;
if (newActor == null) {
try {
newActor = type.newInstance();
} catch (InstantiationException e) {
LOG.error("Unable to create actor of type:{}", type, e);
} catch (IllegalAccessException e) {
LOG.error("Unable to create actor of type:{}", type, e);
}
}
ApplicationContextProvider.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(newActor);
return newActor;
}
/**
* This method is used by [[Props]] to determine the type of actor which will
* be created. This means that an instance of this `IndirectActorProducer`
* will be created in order to call this method during any call to
* [[Props#actorClass]]; it should be noted that such calls may
* performed during actor set-up before the actual actor’s instantiation, and
* that the instance created for calling `actorClass` is not necessarily reused
* later to produce the actor.
*/
#Override
public Class<? extends Actor> actorClass() {
return type;
}}
This allows you co create actors without direct accesing to SpringContext from any code as follows:
ActorSystem.create("system").actorOf(Props.create(SpringDIActor.class, DemoActor.class))
Then just to use #Autowired annotation into the DemoActor.
No additional annotation on DemoActor is not required.
Related
The following Guice module binds a property file to the #Named annotation.
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
// Omitted: other imports
public class ExampleModule extends AbstractModule {
#Override
protected void configure() {
Names.bindProperties(binder(), getProperties());
}
private Properties getProperties() {
// Omitted: return the application.properties file
}
}
I can now inject properties directly into my classes.
public class Example {
#Inject
#Named("com.example.title")
private String title;
#Inject
#Named("com.example.panel-height")
private int panelHeight;
}
The values read from a properties file are strings but, as you can see in the example above, Guice is capable of doing type conversion for int fields.
Now, given the property com.example.background-color=0x333333 I would like to able to get the same type conversion for an arbitrary class, like:
public class Example {
#Inject
#Named("com.example.background-color")
private Color color;
}
Let's say that the Color class contains a static method decode() and I can obtain a new Color instance by calling Color.decode("0x333333").
How can I configure Guice to do this automatically and behind the scenes for me?
I found a solution by myself looking into the Guice sources, although I have to say it's not the prettiest (more on this later on).
First of all, we need to create a TypeConverter.
import com.google.inject.TypeLiteral;
import com.google.inject.spi.TypeConverter;
// Omitted: other imports
public class ColorTypeConverter implements TypeConverter {
#Override
public Object convert(String value, TypeLiteral<?> toType) {
if (!toType.getRawType().isAssignableFrom(Color.class)) {
throw new IllegalArgumentException("Cannot convert type " + toType.getType().getTypeName());
}
if (value == null || value.isBlank()) {
return null;
}
return Color.decode(value);
}
}
Then, a Matcher. I generalized.
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.AbstractMatcher;
// Omitted: other imports
public class SubclassMatcher extends AbstractMatcher<TypeLiteral<?>> {
private final Class<?> type;
public SubclassMatcher(Class<?> type) {
this.type = type;
}
#Override
public boolean matches(TypeLiteral<?> toType) {
return toType.getRawType().isAssignableFrom(type);
}
}
Finally, add the following line to the Guice module.
import com.google.inject.AbstractModule;
// Omitted: other imports
public class ExampleModule extends AbstractModule {
#Override
protected void configure() {
binder().convertToTypes(new SubclassMatcher(Color.class), new ColorTypeConverter());
// Omitted: other configurations
}
}
Now, the following injection works.
public class Example {
#Inject
#Named("com.example.background-color")
private Color backgroundColor;
}
It could be prettier. There exists a com.google.inject.matcher.Matchers API which I wasn't able use and could have solved my problem without constructing my personal SubclassMatcher class. See, Matchers.subclassesOf(Class<?>). It's for sure my fault as I don't believe Google wouldn't think of this pretty common use-case. If you find a way to make it work, please leave a comment.
Guice can't do that for you.
I suppose the conversion from String to int happens upon injection and not when you call Names.bindProperties(...)
See the bindProperties methods:
/** Creates a constant binding to {#code #Named(key)} for each entry in {#code properties}. */
public static void bindProperties(Binder binder, Map<String, String> properties) {
binder = binder.skipSources(Names.class);
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
binder.bind(Key.get(String.class, new NamedImpl(key))).toInstance(value);
}
}
/**
* Creates a constant binding to {#code #Named(key)} for each property. This method binds all
* properties including those inherited from {#link Properties#defaults defaults}.
*/
public static void bindProperties(Binder binder, Properties properties) {
binder = binder.skipSources(Names.class);
// use enumeration to include the default properties
for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); ) {
String propertyName = (String) e.nextElement();
String value = properties.getProperty(propertyName);
binder.bind(Key.get(String.class, new NamedImpl(propertyName))).toInstance(value);
}
}
They are just binding strings.
You could just copy one of them and create your own binding. If the property value is in a color format, bind it additionally as Color.
As an example:
public class GuiceColors {
public static class GameModule extends AbstractModule {
#Override
protected void configure() {
Properties props = new Properties();
try {
props.load(getClass().getResourceAsStream("application.properties"));
} catch (IOException e) {
e.printStackTrace();
}
bindPropertiesWithColors(props);
}
private void bindPropertiesWithColors(Properties properties) {
Binder binder2 = binder().skipSources(Names.class);
// use enumeration to include the default properties
for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) {
String propertyName = (String) e.nextElement();
String value = properties.getProperty(propertyName);
try {
Color decodedColor = Color.decode(value);
binder2.bind(Key.get(Color.class, Names.named(propertyName)))
.toInstance(decodedColor);
} catch (NumberFormatException ex) {
// property value cannot be decoded as color, ignore the exception
}
binder2.bind(Key.get(String.class, Names.named(propertyName))).toInstance(value);
}
}
}
public static class Example {
#Inject
#Named("com.example.background-color")
private Color color;
#Inject
#Named("com.example.background-color")
private String colorString;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new GameModule());
System.out.println(injector.getInstance(Example.class).color);
System.out.println(injector.getInstance(Example.class).colorString);
}
}
with application.properties being:
com.example.background-color = 0x333333
I am designing a validation framework that will handle many different types of validations based on the object that I pass to my Validator class. Below is the Factory pattern based iplementation of the same.
Validator.java
import java.io.Serializable;
import java.util.Map;
/**
* Validator interface
*
* #param <T> generic type
* #param <M> generic type
*
*/
#FunctionalInterface
public interface Validator<T, M extends Serializable> {
/**
* Validates target object
*
* #param object target object
* #return map of errors (empty if valid)
*/
Map<String, M> validate(T object);
/**
* Validates target object and throws exception in case
* if list of errors is not empty
*
* #param object target object
* #throws ValidationException if any validation errors exist
*/
default void validateAndThrow(T object) throws ValidationException {
Map<String, M> errors = validate(object);
if (!errors.isEmpty()) {
throw new ValidationException(errors);
}
}
/**
* Allows to configure validator if necessary
*
* #param visitor Validator visitor
*/
default void configure(ValidatorVisitor visitor) {
visitor.visit(this);
}
/**
* Validator visitor functional interface
*/
#FunctionalInterface
interface ValidatorVisitor {
/**
* Action to be performed on a validator
*
* #param validator target validator
*/
void visit(Validator validator);
}
}
ValidatorFactory.java
package com.rbs.fsap.aap.apricot.validator;
import com.rbs.fsap.aap.apricot.exception.GenericRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import static com.rbs.fsap.aap.apricot.constants.MessageSource.ERROR_TYPE_VALIDATOR_NOT_FOUND;
import static java.util.Optional.ofNullable;
/**
* Implementation of validation factory
*
* Created by Saransh Bansal on 15/05/2020
*/
#Component
public class ValidatorFactory {
#Autowired
List<Validator> validators;
/**
* Returns specific validator based on object's class.
*
* #param object target object
* #return instance of {#link Validator}
*/
public Validator getValidatorForObject(Object object) {
return validators.stream()
.filter(v -> {
System.out.println("....." + v.getClass());
// return v.getClass().isAssignableFrom(object.getClass()); - ???
})
.findFirst()
.orElseThrow(() -> new GenericRuntimeException(ERROR_TYPE_VALIDATOR_NOT_FOUND.getText(
object == null ? "null" : object.getClass())));
}
}
Custom validators - DocumentValidator.java
import com.rbs.fsap.aap.apricot.web.dto.DocumentDto;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.rbs.fsap.aap.apricot.constants.GeneralConstants.FIELD_DOCUMENT_FILE;
import static com.rbs.fsap.aap.apricot.constants.MessageSource.*;
import static com.rbs.fsap.aap.apricot.util.MessageBuilder.buildMessage;
import static java.util.Objects.isNull;
import static org.apache.commons.lang.Validate.notNull;
import static org.springframework.util.CollectionUtils.isEmpty;
/**
* Document upload Request validator
*
*/
#Component
public class DocumentValidator implements Validator<DocumentDto, Serializable> {
private static final long DEFAULT_MAX_FILE_SIZE = 5 * 1024L * 1024;
#Value("${aap.apricot.document.allowedContentTypes:}#{T(java.util.Collections).emptyList()}")
private List<String> allowedContentTypes;
#Value("${aap.apricot.document.allowedFileNames:}#{T(java.util.Collections).emptyList()}")
private List<String> allowedFileNames;
#Value("${aap.apricot.document.max.size:" + DEFAULT_MAX_FILE_SIZE + "}")
private long maxFileSize;
#Override
public Map<String, Serializable> validate(DocumentDto documentData) {
notNull(documentData, ERROR_MSG_DOCUMENT_MISSED.getText());
Map<String, Serializable> errors = new HashMap<>();
if (isNull(documentData.getFile())) {
errors.put(FIELD_DOCUMENT_FILE.getText(), ERROR_MSG_DOCUMENT_FILE_MISSED.getText());
} else {
String contentType = documentData.getFile().getContentType();
String fileName = documentData.getFile().getOriginalFilename();
if (isNull(documentData.getBusinessDate())) {
errors.put(FIELD_DOCUMENT_FILE.getText(), buildMessage(ERROR_MSG_DOCUMENT_BUSINESS_DATE_MISSED.getText()));
}
if (documentData.getFile().getSize() > maxFileSize) {
errors.put(FIELD_DOCUMENT_FILE.getText(), buildMessage(ERROR_MSG_DOCUMENT_FILE_SIZE_EXCEEDED.getText(), maxFileSize));
}
if (!isEmpty(allowedContentTypes) && contentType != null &&
allowedContentTypes.stream().noneMatch(contentType::equalsIgnoreCase)) {
errors.put(FIELD_DOCUMENT_FILE.getText(), buildMessage(ERROR_MSG_DOCUMENT_RESTRICTED_CONTENT_TYPE.getText(), contentType));
}
if (!isEmpty(allowedFileNames) && fileName != null &&
allowedFileNames.stream().noneMatch(fileName::startsWith)) {
errors.put(FIELD_DOCUMENT_FILE.getText(), buildMessage(ERROR_MSG_DOCUMENT_RESTRICTED_FILE_NAME.getText(), fileName));
}
}
return errors;
}
}
I cannot seem to figure out how to correctly return a validator from my Factory class. (See the code commented with ??? in ValidatorFactory.java)
Can anyone help with the same.
Based on my understanding your Factory needs to return based on a specific type.
Rough Code to give you an idea:
The ValidatorFactory is going to filter from its list of Validators, based on whether they support a given Object or not.
Keep in mind if >1 Validator that supports a given Type, then your filter will return 2 or more. You will only return findFirst().
Option 1:
Public class ValidatorFactory {
validators.stream().filter(v -> v.supports(object.getClass()).findFirst();
}
public class DocumentValidator{
public boolean supports(Class clazz){
return true;
}
}
Option 2:
public class ValidatorFactory{
private Map<Class, Validator> validatorAssociations = new HashMap<>();
public Validator getValidator(Object object){
return validatorAssociations.get(object.class);
}
}
You will need some sort of mapping, whether it is the responsibility of the ValidatorFactory (Which is generally the case for the FactoryPattern), or you push the responsibility down to the validator to know its own capabilities. Up to you.
I have an object in use throughout my codebase, UnsecureObject. This object is auto-generated with no getters/setters, and all member fields are public. So editing is done by doing something like the following:
unsecureObjInstance.firstName = "Jane";
This is not desirable for numerous reasons that I probably don't have to explain here. But using this generated class is required for some other technical details with our messaging pipeline that I won't go into.
I have a desire is to leverage a mapping utility written by someone else on my team to convert this UnsecureObject to a pojo that I am writing.
An example of the mapper in action (with two normal classes w/ getters/setters) would be something like:
new MapperBuilder<>(PojoOne.class, PojoTwo.class)
.from(PojoOne::getName).to(PojoTwo::getFirstName)
.build();
This will map the PojoOne#name field to the PojoTwo#firstName field.
Is there a way to translate this to input my UnsecureObject here? I have tried something like the following:
new MapperBuilder<>(UnsecureObject.class, SecureObject.class)
.from(u -> u.firstName).to(SecureObject::getFirstName)
.build();
But get an error here, something along the lines of 'u -> u.firstName' could not be invoked.
So the question is:
Is there a way to essentially "construct" a getter on the fly using these public members? So in the .from() method, I can construct the call to look like a standard method that will yield my u.firstName?
Thanks for the help!
EDIT:
this is approx what the MapperBuilder class looks like (attempted to edit a bit to take away project specific wrappers/simplify)
/**
* This class is used to convert between POJO getter method references to the corresponding field names.
* #param <B> type
*/
public interface PojoProxy<B> {
/**
* Invokes the given getter method and returns information about the invocation.
* #param getter the getter to invoke
* #return information about the method invoked
*/
<T> GetterInvocation<T> invokeGetter(Function<B, T> getter);
}
/**
* Stores information about a method invocation.
* #param <T> method return type
*/
public interface GetterInvocation<T> {
public Class<T> getReturnType();
public String getFieldName();
}
/**
* A builder class to create {#link Mapper} instances.
* #param <FROM> source type
* #param <TO> target type
*/
public class MapperBuilder<FROM, TO> {
private final Class<FROM> _fromClass;
private final Class<TO> _toClass;
private final PojoProxy<FROM> _fromProxy;
private final PojoProxy<TO> _toProxy;
public MapperBuilder(Class<FROM> fromClass, Class<TO> toClass) {
_fromClass = fromClass;
_toClass = toClass;
//We will pretend there is an impl that provides the proxy.
//Proxies wrap the from and to classes in order to get reflection information about their getter calls.
_fromProxy = PojoProxy.of(fromClass);
_toProxy = PojoProxy.of(toClass);
}
public <FROM_VALUE> ToFieldBuilder<FROM_VALUE> from(Function<FROM, FROM_VALUE> getter) {
GetterInvocation<FROM_VALUE> methodInvocation = _fromProxy.invokeGetter(getter);
return new ToFieldBuilder<>(methodInvocation.getFieldName(), methodInvocation.getReturnType());
}
public class ToFieldBuilder<FROM_VALUE> {
private final String _fromFieldPath;
private final Class<FROM_VALUE> _fromClass;
public ToFieldBuilder(String fromFieldPath, Class<FROM_VALUE> fromClass) {
_fromFieldPath = fromFieldPath;
_fromClass = fromClass;
}
public <TO_VALUE> FromFieldBuilder<FROM_VALUE, TO_VALUE> to(Function<TO, TO_VALUE> getter) {
//similar to above, but now using a FromFieldBuilder.
}
}
public class FromFieldBuilder<FROM_VALUE, TO_VALUE> {
//impl..
}
}
I dont see MapperBuilder.from() method details, you can try this implementation of MapperBuilder.java Function (getter) -> (BiConsumer) setter
public class MapperBuilder<S, D> {
private final S src;
private final D dest;
public MapperBuilder(S src, Class<D> dest) {
this.src = src;
try {
this.dest = dest.newInstance();
} catch (Exception e) {
throw new RuntimeException("Required default constructor for: " + dest);
}
}
//getter - function to get value from source instance
//setter - biConsumer to set value to destination instance
//example - map(SrcClass::getSrcValue, DestClass::setDestValue)
public <V> MapperBuilder<S, D> map(Function<S, V> getter, BiConsumer<D, V> setter) {
setter.accept(dest, getter.apply(src));
return this;
}
public D build() {
return dest;
}
}
SrcClass.java some source class:
public class SrcClass {
private String srcValue;
public String getSrcValue() {
return srcValue;
}
public void setSrcValue(String srcValue) {
this.srcValue = srcValue;
}
}
DestClass.java some destination class:
package com.example.demo;
public class DestClass {
private String destValue;
public String getDestValue() {
return destValue;
}
public void setDestValue(String destValue) {
this.destValue = destValue;
}
}
DemoApplication.java demo:
public class DemoApplication {
public static void main(String[] args) {
SrcClass src = new SrcClass();
src.setSrcValue("someValue");
DestClass dest = new MapperBuilder<>(src, DestClass.class)
.map(SrcClass::getSrcValue, DestClass::setDestValue)
// map another fields
.build();
// for your UnsecureObject case
UnsecureObject unsecureObject = new MapperBuilder<>(src, UnsecureObject.class)
.map(SrcClass::getSrcValue,
(unsecure, srcValue) -> unsecure.unsecureValue = srcValue)
.build();
}
}
I am using the factory pattern to create objects of different connections in java version "1.7.0_60"
The problem I am facing is that each concrete class will have unique properties for that particular class. As the factory will use polymorpthism when it return the instance of the concrete class, I cannot access the unique properties. i.e. getHostType() is unique only for SqlServerConnection.
The workaround I have done is to declare getHostType() abstract in the super class and implement it in each concrete class. However, I don't really want to do it that way as the more concrete classes I add that have their unique properties the more abstract methods I will have to include in the super class, and then implement them in each concrete class.
I want to keep my factory pattern and the abstract super class. I am just wondering if there is any other way instead of having the abstract methods in the super class? Any design patterns I can include to get around this?
public abstract class Connection {
private int port;
private int ipAddress;
public Connection() {}
public String description() {
return "Generic";
}
/* Implement in every concrete class, even if the concrete type doesn't have that property */
public abstract int getHostType();
}
public class SqlServerConnection extends Connection {
private int sqlHostType;
public SqlServerConnection() {
sqlHostType = 5060;
}
#Override
public String description() {
return "Created a Sql Server connection type";
}
#Override
public int getHostType() {
return sqlHostType;
}
}
public class OracleConnection extends Connection {
public OracleConnection() {}
#Override
public String description() {
return "Created an Oracle connection type";
}
}
final public class ConnectionFactory {
protected String mType;
public ConnectionFactory(String type) {
mType = type;
}
/* Create the connection we want to use */
public Connection createConnection() {
if(mType.equals("Oracle")) {
return new OracleConnection();
}
else if(mType.equals("SQLServer")) {
return new SqlServerConnection();
}
else {
return null;
}
}
}
public class TestConnection {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
conn = factory.createConnection();
System.out.println(conn.description());
/* need to access the getHostType() */
System.out.println(conn.getHostType());
}
}
You should have a look at the visitor pattern. You need to declare an interface ConnectionVisitor and add an method visit for each of your connection class in your hierarchy.
public interface ConnectionVisitor {
public int visit (Connection connection);
public int visit (SqlServerConnection sqlconnection);
public int visit (OracleConnection oracleConnection)
}
Now you need to add an accept method in your base class connection and that accepts a ConnectionVisitor and then calls visit on it. Your new Connection class will look something like
public abstract class Connection {
private int port;
private int ipAddress;
public Connection() {}
public String description() {
return "Generic";
}
public int accept(ConnectionVisitor visitor){
return visitor.visit(this);
}
}
Notice that the accept method does a dual dispatch. It dispatches on the base of the object on which it is called and the parameter that is passed to this method. This is at the heart of visitor pattern.
You can then implement the ConnectionVisitor interface to define any new functionality without changing your base class.
class DemoVisitor implements ConnectionVisitor{
public int visit(Connection connection){
System.out.println("Visiting Connection");
return 1;
}
public int visit(SqlServerConnection sqlServerConnection){
System.out.println("Visiting SqlServerConnection");
return 1;
}
public int visit(OracleConnection oracleConnection){
System.out.println("Visiting Oracle Connection");
return 1;
}
}
In your TestConnection class you can simply create a new connection object and then call accept method on that object passing a visitor object.
public class TestConnection {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
conn = factory.createConnection();
System.out.println(conn.description());
ConnectionVisitor visitor = new DemoVisitor();
System.out.println(conn.accept(visitor));
}
}
So now any child class specific functionality must not reside in connection class hierarchy instead they must be implemented in new visitors.
Note that this pattern is not going to fit as such in your scenario. One of the limitation of this pattern in that the return type for all the methods in visitor interface must be same. This pattern may or may not fit your needs but it is worth looking into your case as such. You will probably need to modify this pattern to fit your needs. And that is what patterns are all about looking into some common solutions and then modifying those solutions to fit into your problem.
Why do you want that? I mean, I use a Factory to hide specific implementation of factorized object, returning a common abstract class (or an interface).
I wonder why you may want to do something like:
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
if(conn.getHostType() == 1 ) {
doSomethingLogic();
}
if(conn.getHostType() == 2) {
doSomethingElseLogic();
}
shouldn't all the ifs be inside the factory?
You need to have the getHostType() method in your Connection class for this method to be called polymorphically.
The only other solution is to type cast the object returned by the factory to the one expected which is not at all a good approach. Reason for this is you will have to keep check on the Class type of the returned object whether it is of ORACLE or MySQL etc through if else statements (unnecessary we have ploymorphism to prevent this). If you just define the method in your Connection class you dont need to worry about the getHostType() method, as it will be called from the correct class polymorphically.
In the oracleConnection class you can just add the method getHostType() that returns null object with an error message in the code you wrote.
I don't know how applicable this approach will be to your specific situation, but you could try grouping related, optional, subclass-specific connection behavior into its own interfaces and then let each concrete class implement the interfaces that are appropriate for it.
In your example, both connection classes implement description() so you could create an Interface called Descriptor and have a method on your abstract class called getDescriptor():
public Descriptor getDescriptor() throws HasNoDescriptorException {
if (self instanceof Descriptor) {
return self;
}
throw new HasNoDescriptorException();
}
And then let interface Descriptor provide the description() method.
You'd get a connector's description like this:
String desc = "";
try {
desc = connector.getDescriptor().description();
} catch (HasNoDescriptorException e) {
// connector doesn't have a description() method;
}
If you don't like exceptions you could just return and test for nulls.
For that matter, your code could simply test to see if the connection instance is an instance of the Descriptor interface and if it is then you know you have full access to any of the methods appropriate for a Descriptor.
To continue the example, you could have a TypedHost interface whose implementing connection classes provide a getSqlHost() method.
I'd do it like this:
Remove the getHostTypeCreate method from the abstract class since not all of the connections have this property. Then add a new interface IHostTypeProvider(you might pick a better name though):
public interface IHostTypeProvider {
int getHostType();
}
Now, make some of the sub-classes implement this interface:
public SqlServerConnection extends Connection implements IHostTypeProvider {
....
public int getHostType() {
return 5060;
}
}
When you actually need access to the property, you fist need to check if it's available for this type of connection:
Connection con = ...;
//Check if host type is available
if (connection instanceof IHostTypeProvider) {
System.out.println(((IHostTypeProvider)con).getHostType());
}
Hope that helps.
Sounds like a use case for typesafe heterogeneous container. I'll just post my example, I think it's pretty much explaining itself. If there are any questions left, I'll answer them.
The Pros are that it is easily extensible and it supports multiple types.
import java.util.HashMap;
import java.util.Map;
public abstract class Connection
{
/* typesafe heterogeneous container */
private final Map<Property<?>, Object> properties = new HashMap<Property<?>, Object>();
public Connection(String ip, int port)
{
addProperty(ConnectionProperties.IP_ADDRESS, ip);
addProperty(ConnectionProperties.PORT, port);
}
/**
* Gets property in its respective type.
*
* #param p property
* #return value of property.
*/
public <T> T getProperty(Property<T> p)
{
Object obj = properties.get(p);
if (obj == null)
return null;
Class<T> clazz = p.getClazz();
return clazz.cast(obj);
}
/**
* Checks whether property is available
*
* #param p property to check for
* #return <code>true</code>, if property is available
*/
public boolean hasProperty(Property<?> p)
{
return properties.get(p) != null;
}
/* helper method to add properties */
protected <T> void addProperty(Property<T> p, T value)
{
properties.put(p, value);
}
}
class SqlServerConnection extends Connection
{
public SqlServerConnection(String ip, int port)
{
super(ip, port);
addProperty(ConnectionProperties.DESCRIPTION, "Created a Sql Server connection type");
addProperty(ConnectionProperties.SQL_HOST_TYPE, 5090);
}
}
/* all properties are stored here (...there could be more classes if needed) */
final class ConnectionProperties
{
private ConnectionProperties()
{
// private contructor to prevent instantiation of utility class
}
public static final Property<String> IP_ADDRESS = new Property<String>("IP_ADDRESS", String.class);
public static final Property<Integer> PORT = new Property<Integer>("PORT", Integer.class);
public static final Property<String> DESCRIPTION = new Property<String>("DESCRIPTION", String.class);
public static final Property<Integer> SQL_HOST_TYPE = new Property<Integer>("SQL_HOST_TYPE", Integer.class);
}
/* property class that serves as key for typesafe heterogeneous container */
final class Property<T>
{
/* has to be unique */
private final String name;
private final Class<T> clazz;
public Property(String name, Class<T> clazz)
{
this.name = name;
this.clazz = clazz;
}
public String getName()
{
return name;
}
public Class<T> getClazz()
{
return clazz;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Property<?> other = (Property<?>) obj;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}
You could also define a interface for Property<T> and for Connection, that would have the future benefit of having the possibility to exchange the implementations, but I left it out to spaer some space here.
If the instantion of properties is to complex, there are also other alternatives. For example following
public final class PropertyV2<T>
{
private static final AtomicInteger KEY_SUPPLY = new AtomicInteger();
/* unique key for property distinction */
private final int key;
private final Class<T> clazz;
private PropertyV2(Class<T> clazz)
{
this.key = KEY_SUPPLY.getAndIncrement();
this.clazz = clazz;
}
/* factory method for string properties */
public static PropertyV2<String> string()
{
return new PropertyV2<String>(String.class);
}
/* factory method for integer properties */
public static PropertyV2<Integer> integer()
{
return new PropertyV2<Integer>(Integer.class);
}
public Class<T> getClazz()
{
return clazz;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + key;
return result;
}
#Override
public boolean equals(Object obj)
{
if (obj == null || getClass() != obj.getClass())
return false;
PropertyV2<?> other = (PropertyV2<?>) obj;
if (key != other.key)
return false;
return true;
}
}
class ConnectionPropertiesV2
{
private ConnectionPropertiesV2()
{
// private constructor to prevent instatiation of utiltiy class
}
PropertyV2<String> IP_ADDRESS = PropertyV2.string();
PropertyV2<Integer> PORT = PropertyV2.integer();
}
The problem hereby is, that you lose the name attribute, which can be useful if you want to use the property name at runtime - let's say in an exception.
The more OO approach is to push the implementation details into the child class, rather than expose them in abstract methods (which may not have a well-defined implementation for some children).
For example rather than writing,
System.out.println(conn.description());
/* need to access the getHostType() */
System.out.println(conn.getHostType());
Instead, write
conn.printTo(System.out);
Then provide a printTo method for each child. In this way, you've refactored the code to hide all of the implementation details of the Connection objects and how it prints itself to a stream.
Getting an error when trying to set up a unit test using Jooq mocking with custom types (Joda Time). I have registered a converter during code generation. It almost looks like Jooq can't find / doesn't see the converter and is trying to fall back on ConvertAll, which doesn't work.
Querying the mock result throws the exception (below).
Converter:
import org.joda.time.DateTime;
import java.sql.Timestamp;
import org.joda.time.DateTimeZone;
import org.jooq.Converter;
public class DateTimeConverter implements Converter<Timestamp, DateTime> {
#Override
public DateTime from(Timestamp databaseObject) {
return new DateTime(databaseObject.getTime()).withZone(DateTimeZone.UTC);
}
#Override
public Timestamp to(DateTime userObject) {
return new Timestamp(userObject.getMillis());
}
#Override
public Class<Timestamp> fromType() {
return Timestamp.class;
}
#Override
public Class<DateTime> toType() {
return DateTime.class;
}
}
The class is generated successfully:
/**
* This class is generated by jOOQ
*/
package redacted.generated.jooq.tables;
/**
* This class is generated by jOOQ.
*/
#javax.annotation.Generated(value = {"http://www.jooq.org", "3.0.0"},
comments = "This class is generated by jOOQ")
#java.lang.SuppressWarnings({ "all", "unchecked" })
public class Bug extends org.jooq.impl.TableImpl<redacted.generated.jooq.tables.records.BugRecord> {
private static final long serialVersionUID = 1992533553;
/**
* The singleton instance of <code>public.bug</code>
*/
public static final redacted.generated.jooq.tables.Bug BUG = new redacted.generated.jooq.tables.Bug();
/**
* The class holding records for this type
*/
#Override
public java.lang.Class<redacted.generated.jooq.tables.records.BugRecord> getRecordType() {
return redacted.generated.jooq.tables.records.BugRecord.class;
}
/**
* The column <code>public.bug.testdate</code>.
*/
public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, org.joda.time.DateTime> TESTDATE = createField("testdate", org.jooq.impl.SQLDataType.TIMESTAMP.asConvertedDataType(new name.benjaminAbbitt.jooqJodaTime.DateTimeConverter()), this);
/**
* The column <code>public.bug.id</code>.
*/
public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> ID = createField("id", org.jooq.impl.SQLDataType.INTEGER, this);
/**
* Create a <code>public.bug</code> table reference
*/
public Bug() {
super("bug", redacted.generated.jooq.Public.PUBLIC);
}
/**
* Create an aliased <code>public.bug</code> table reference
*/
public Bug(java.lang.String alias) {
super(alias, redacted.generated.jooq.Public.PUBLIC, redacted.generated.jooq.tables.Bug.BUG);
}
/**
* {#inheritDoc}
*/
#Override
public org.jooq.Identity<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> getIdentity() {
return redacted.generated.jooq.Keys.IDENTITY_BUG;
}
/**
* {#inheritDoc}
*/
#Override
public java.util.List<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>> getKeys() {
return java.util.Arrays.<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>>asList(redacted.generated.jooq.Keys.BUG_ID_KEY);
}
/**
* {#inheritDoc}
*/
#Override
public redacted.generated.jooq.tables.Bug as(java.lang.String alias) {
return new redacted.generated.jooq.tables.Bug(alias);
}
}
Exception is:
org.jooq.exception.DataTypeException: Cannot convert from 2014-03-05T17:57:24.668Z (class org.joda.time.DateTime) to class java.sql.Timestamp
at org.jooq.tools.Convert$ConvertAll.fail(Convert.java:809)
at org.jooq.tools.Convert$ConvertAll.from(Convert.java:747)
at org.jooq.tools.Convert.convert0(Convert.java:296)
at org.jooq.tools.Convert.convert(Convert.java:288)
at org.jooq.tools.Convert.convert(Convert.java:349)
at org.jooq.impl.AbstractRecord.getValue(AbstractRecord.java:219)
at org.jooq.tools.jdbc.MockResultSet.getValue(MockResultSet.java:383)
at org.jooq.tools.jdbc.MockResultSet.getTimestamp(MockResultSet.java:566)
at org.jooq.impl.Utils.getTimestamp(Utils.java:2195)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1952)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1881)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.setValue(CursorImpl.java:1464)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1447)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1439)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:119)
at org.jooq.impl.CursorImpl$CursorIterator.fetchOne(CursorImpl.java:1412)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1389)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1353)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:202)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:176)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:268)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:321)
at org.jooq.impl.AbstractResultQuery.fetch(AbstractResultQuery.java:324)
at org.jooq.impl.SelectImpl.fetch(SelectImpl.java:1034)
at org.jooq.ResultQuery$fetch.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at redacted.bug.BugTest.getTest(BugTest.groovy:47)
BugTest.groovy:
public class BugTest {
#Test
public void getTest() {
DSLContext testContext = setupDSL(new MockDataProvider() {
#Override
public MockResult[] execute(MockExecuteContext ctx) throws SQLException {
DSLContext create = DSL.using(SQLDialect.POSTGRES)
def result = create.newResult(BUG)
result.add(create.newRecord(BUG, [id: 0, testdate: new DateTime()]))
[new MockResult(result.size(), result)]
}
})
testContext.select().from(BUG).fetch() //this line fails
}
private DSLContext setupDSL(MockDataProvider provider) {
MockConnection connection = new MockConnection(provider)
return DSL.using(connection, SQLDialect.POSTGRES)
}
}
This is bug #5771. As of jOOQ 3.9.0, MockResultSet doesn't use your converter to revert your user-defined type <U> (DateTime) to the JDBC/database type <T> (Timestamp) prior to exposing it through the JDBC API. Without using your converter, jOOQ doesn't know how to perform this conversion, hence the exception.
The correct behaviour should be:
You create a result using <U> (DateTime) values.
jOOQ's MockResultSet uses your converter to convert <U> to <T> (Timestamp) prior to passing that value through the JDBC API
jOOQ's ResultQuery fetches <T> from the JDBC API
jOOQ uses your converter to convert <T> back to <U> again
Workaround:
For the time being, you shouldn't put any user-defined types in your MockResult, but use the JDBC Timestamp type only (and no generated columns that refer to your user-defined type)