In the Walk-through page we documented the most common scenarios where you would want to use PODAM. These include:
In this page I will show how PODAM handles some corner cases, such as:
PODAM supports inheritance. It will fill parent attributes if an accessible setter is available to the POJOs which you are filling with data.
The following example illustrates a parent class, uk.co.jemos.podam.test.dto.AbstractOneDimensionalPojo which is extended by another POJO, uk.co.jemos.podam.test.dto.OneDimensionalChildPojo.
/** * */ package uk.co.jemos.podam.test.dto; import java.util.Calendar; import uk.co.jemos.podam.annotations.PodamIntValue; /** * Abstract POJO to test inheritance. * * @author mtedone * */ public abstract class AbstractOneDimensionalPojo { @PodamIntValue(maxValue = 10) private int parentIntField; private Calendar parentCalendarField; public int getParentIntField() { return parentIntField; } protected void setParentIntField(int parentIntField) { this.parentIntField = parentIntField; } public Calendar getParentCalendarField() { return parentCalendarField; } protected void setParentCalendarField(Calendar parentCalendarField) { this.parentCalendarField = parentCalendarField; } @Override public String toString() { final String TAB = " "; StringBuilder retValue = new StringBuilder(); retValue.append("AbstractOneDimensionalPojo ( ") .append("parentIntField = ").append(parentIntField).append(TAB) .append("parentCalendarField = ").append(parentCalendarField.getTime()) .append(TAB).append(" )"); return retValue.toString(); } }
The class above defines two private attributes, parentIntField and parentCalendarField. It also provides two protected setters which are available to sub-classes. The presence of accessible setter methods is a pre-requisite for this inheritance to be handled properly by PODAM.
This is the child class:
/** * */ package uk.co.jemos.podam.test.dto; /** * A child class to test inheritance * @author mtedone * */ public class OneDimensionalChildPojo extends AbstractOneDimensionalPojo { private int intField; private String strField; public OneDimensionalChildPojo() { super(); } public int getIntField() { return intField; } public void setIntField(int intField) { this.intField = intField; } public String getStrField() { return strField; } public void setStrField(String strField) { this.strField = strField; } /** * Constructs a <code>String</code> with all attributes in name = value * format. * * @return a <code>String</code> representation of this object. */ @Override public String toString() { final String TAB = " "; StringBuilder retValue = new StringBuilder(); retValue.append(super.toString()).append(TAB); retValue.append("OneDimensionalChildPojo ( ").append("intField = ") .append(intField).append(TAB).append("strField = ") .append(strField).append(TAB).append(" )"); return retValue.toString(); } }
Running PODAM on the OneDimensionalChildPojo class will produce the following toString() result:
AbstractOneDimensionalPojo ( parentIntField = 3 parentCalendarField = Sat Apr 30 13:49:49 BST 2011 ) OneDimensionalChildPojo ( intField = -516418302 strField = ﶖ藾冋荬馫콈愁㵼藙酬 )
As you can see Podam has set both the parent attributes values.
A Singleton is a class which provides only an instance of itself. One of the implementation details of a Singleton is that, unless one used an Enum, the class should forbid clients from creating new instances. This is typically achieved by declaring the constructor private and by providing a public static method (e.g. getInstance()) which returns a singleton instance of the class.
PODAM handles Singleton-like classes, as the following example demonstrates:
/** * */ package uk.co.jemos.podam.test.dto; import java.io.Serializable; import java.util.Calendar; import java.util.List; import java.util.Map; /** * A POJO to test a Singleton-like scenario where the public static Singleton * method contains parameters. * * @author mtedone * */ public class SingletonWithParametersInStaticFactoryPojo implements Serializable { private static final long serialVersionUID = 1L; private final Calendar createDate; private final List<OneDimensionalTestPojo> pojoList; private final Map<String, OneDimensionalTestPojo> pojoMap; private final String firstName; private static SingletonWithParametersInStaticFactoryPojo singleton; // ------------------->> Constructors /** * A private constructor to enforce the Singleton pattern * * @param createDate * The creation date * @param pojoList * A list * @param pojoMap * A map * @param firstName * The first name */ private SingletonWithParametersInStaticFactoryPojo(Calendar createDate, List<OneDimensionalTestPojo> pojoList, Map<String, OneDimensionalTestPojo> pojoMap, String firstName) { super(); this.createDate = createDate; this.pojoList = pojoList; this.pojoMap = pojoMap; this.firstName = firstName; } /** * Singleton method * * @param createDate * The creation date * @param pojoList * A list * @param pojoMap * A map * @param firstName * The first name * @return a singleton instance of this class */ public static SingletonWithParametersInStaticFactoryPojo getInstance( Calendar createDate, List<OneDimensionalTestPojo> pojoList, Map<String, OneDimensionalTestPojo> pojoMap, String firstName) { if (null == singleton) { singleton = new SingletonWithParametersInStaticFactoryPojo( createDate, pojoList, pojoMap, firstName); } return singleton; } public Calendar getCreateDate() { return createDate; } public List<OneDimensionalTestPojo> getPojoList() { return pojoList; } public Map<String, OneDimensionalTestPojo> getPojoMap() { return pojoMap; } public String getFirstName() { return firstName; } }
The class above shows an interesting scenario: a Singleton-like class with the Singleton method which accepts parameters. To make the example even more palatable, some arguments are container-like (collections, maps).
Running the test on the class above produces the following result:
SingletonWithParametersInStaticFactoryPojo ( createDate = Sat Apr 30 14:05:21 BST 2011 pojoList = [ OneDimensionalTestPojo ( booleanField = true booleanObjectField = true byteField = 116 byteObjectField = 10 shortField = 77 shortObjectField = 4 charField = 㐘 charObjectField = ꪁ intField = 1339494839 intObjectField = 980291811 longField = -3119222069530856219 longObjectField = 4260197822662475631 floatField = 0.24369287 floatObjectField = 0.7073253 doubleField = 0.7151146071422815 doubleObjectField = 0.8253745751884359 stringField = ౠ᮷᳃┸㯪쭵Ὼాꎐᆫ objectField = java.lang.Object@57a7ddcf calendarField = Sat Apr 30 14:05:21 BST 2011 dateField = Sat Apr 30 14:05:21 BST 2011 randomArray = [Ljava.util.Random;@4dd36dfe )] pojoMap = { 研䷢쫗㛗á룸ꁧ╝楫=OneDimensionalTestPojo ( booleanField = false booleanObjectField = true byteField = 8 byteObjectField = 39 shortField = 17 shortObjectField = 24 charField = 끰 charObjectField = 뿩 intField = 1412793167 intObjectField = -1521479114 longField = -5054977360881301041 longObjectField = 8252315121779248953 floatField = 0.13922673 floatObjectField = 0.6835188 doubleField = 0.7345653869074095 doubleObjectField = 0.7172732709165366 stringField = 英訆ฤ뭧㜲펮憷ᮻ焜懪 objectField = java.lang.Object@73da669c calendarField = Sat Apr 30 14:05:21 BST 2011 dateField = Sat Apr 30 14:05:21 BST 2011 randomArray = [Ljava.util.Random;@786c730 )} firstName = 㪜됰찄狇브穊厣텾ꧠ )
All three arguments to the Singleton method have been filled with data.
PODAM support classes with generic attributes by solving its currently generic types by the owner setter declaration.
The following example illustrates a generic class, uk.co.jemos.podam.test.dto.pdm45.GenericPojo<F, S>, which is used several times as attributes of another POJO, uk.co.jemos.podam.test.dto.pdm45.GenericAttributePojo.
package uk.co.jemos.podam.test.dto.pdm45; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Generic Pojo. * * @author marciocarmona * * @param <F> * the first generic type * @param <S> * the second generic type */ public class GenericPojo<F, S> { private F firstValue; private S secondValue; @PodamCollection(nbrElements = 2) private List<F> firstList; @PodamCollection(nbrElements = 2) private S[] secondArray; @PodamCollection(nbrElements = 2) private Map<F, S> firstSecondMap; public F getFirstValue() { return firstValue; } public void setFirstValue(F firstValue) { this.firstValue = firstValue; } public S getSecondValue() { return secondValue; } public void setSecondValue(S secondValue) { this.secondValue = secondValue; } public List<F> getFirstList() { return firstList; } public void setFirstList(List<F> firstList) { this.firstList = firstList; } public S[] getSecondArray() { return secondArray; } public void setSecondArray(S[] secondArray) { this.secondArray = secondArray; } public Map<F, S> getFirstSecondMap() { return firstSecondMap; } public void setFirstSecondMap(Map<F, S> firstSecondMap) { this.firstSecondMap = firstSecondMap; } @Override public String toString() { return "GenericPojo [firstValue=" + firstValue + ", secondValue=" + secondValue + ", firstList=" + firstList + ", secondArray=" + Arrays.toString(secondArray) + ", firstSecondMap=" + firstSecondMap + "]"; } }
Notice all the generic types (F and S) used by the class attributes, for each instance of this class we may use different types. It is very important to use the generic notation on the setters because this is where the current types are solved to create the instance that will be supplied for the setter.
Now this is a POJO using the generic class as an attribute, defining F and S as String and Long respectively.
package uk.co.jemos.podam.test.dto.pdm45; /** * Pojo with generic attribute. * * @author marciocarmona * */ public class GenericAttributePojo { private GenericPojo<String, Long> genericPojo; public GenericPojo<String, Long> getGenericPojo() { return genericPojo; } public void setGenericPojo(GenericPojo<String, Long> genericPojo) { this.genericPojo = genericPojo; } @Override public String toString() { return "GenericAttributePojo [genericPojo=" + genericPojo + "]"; } }
As before, it is necessary to specify the types to be used on the setter, because these will be the types used during the generic class instantiation.
Running the test on the class above produces the following result:
GenericAttributePojo [ genericPojo=GenericPojo [ firstValue=mJdAWTgzkv, secondValue=1340162143705, firstList=[EMZeLCj8qf, VJZbWl8Xyo], secondArray=[1340162143705, 1340162143705], firstSecondMap={sSb8TCcOZB=1340162143700, EqVTVO78RR=1340162143700} ] ]
As you can notice all the generic types have been solved properly as String or Long according to the types defined on GenericAttributePojo class. Also the collections, the array and the map have 2 items as specified by the @PodamCollection annotation.
If you want to manufacture a generic class directly, i.e., that is not wrapped by a non-generic class like on the example above, please refer to "Manufacturing generic types" section in the Introduction page.
There are some classes for which PODAM is not able to instantiate an object. Typically these classes are not POJOs, but require sophisticate instantiation. PODAM will leave values for such classes as null, but you still can manufacture them with second level factory registered to the PODAM factory.
Define your own external factory implementing PodamFactory interface.
package uk.co.jemos.podam.example; /** * External factory */ public class MyExternalFactory implements PodamFactory { private static final Logger LOG = LoggerFactory .getLogger(MyExternalFactory.class); private static final Type[] NO_TYPES = new Type[0]; @Override public <T> T manufacturePojo(Class<T> pojoClass) { return this.manufacturePojo(pojoClass, NO_TYPES); } @Override public <T> T manufacturePojo(Class<T> pojoClass, Type... genericTypeArgs) { if (pojoClass.equals(InputStream.class)) { byte[] buffer = new byte[256]; @SuppressWarnings("unchecked") T result = (T) new ByteArrayInputStream(buffer); return result; } else { LOG.warn("Cannot instantiate {} with arguments {}. Returning null.", pojoClass, Arrays.toString(genericTypeArgs)); return null; } } @Override public DataProviderStrategy getStrategy() { return null; } }
Set the external factory, when instantiation PODAM factory.
PodamFactory factory = new PodamFactoryImpl(externalFactory); InputStream pojo = factory.manufacturePojo(InputStream.class);
InputStream class was given just for an example.