Mocking constructor and a private function inside it using Mockito - java

I am in a scenario where I need to Unit test on class which is involving some bean formation and it require real data which I dont have, for more reference Below is the code.
the adapter class which I want to Mock
public class TIBCOAdapter {
public TIBCOAdapter(final GIAFProperties giafProperties) throws Exception {
if (giafProperties != null) {
this.giafProperties = giafProperties;
} else {
LOG.info("Error: No properties found");
}
init();
}
public void init() throws IOException {
factory = initializeQueueConnectionFactory();
requestQueue = initializeRequestQueue();
}
private QueueConnectionFactory initializeQueueConnectionFactory() {
final DurationRecord start = DurationLog.logBefore();
final JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiTemplate(new JndiTemplate(giafProperties.getProperties()));
bean.setJndiName(GIAFPropertyUtil.getPropertyString(giafProperties, "externalJndiName"));
try {
bean.afterPropertiesSet();
} catch (Exception e) {
throw new GIAFRuntimeException(e);
}
final ConnectionFactory targetConnectionFactory = (ConnectionFactory) bean
.getObject();
LOG.info("Got target connection factory: " + targetConnectionFactory);
final MultiCachingConnectionFactory factoryLocal = new MultiCachingConnectionFactory(
targetConnectionFactory, giafProperties);
DurationLog.logAfter(start);
return factoryLocal;
}
private Queue initializeRequestQueue() {
final JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiTemplate(new JndiTemplate(giafProperties.getProperties()));
bean.setJndiName(GIAFPropertyUtil.getPropertyString(giafProperties,
"request-queue"));
try {
bean.afterPropertiesSet();
} catch (Exception e) {
throw new GIAFRuntimeException(e);
}
return (Queue) bean.getObject();
}
}
The actual class where its object is created, which I don't want and that's why I want to mock creation of TIBCOAdapter
public class SomeClass {
public String getResponse(TestClientFilter testClientFilter) throws ICAException {
if (!filterValid(testClientFilter)) {
return null;
}
try {
Properties properties = new Properties(); // Sucess
GIAFProperties giafProperties = new GIAFProperties(properties, null); // sucess
addProperties(properties, testClientFilter); // sucess
TIBCOAdapter tibcoAdapter = new TIBCOAdapter(giafProperties); // ERROR This is the line which I want to mock
return (String) tibcoAdapter.invokeRequestResponse(testClientFilter.getMessage());
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new ICAException(e);
}
}
}
and this is my TEST
public class TestClientBusinessTest {
#Mock
private TIBCOAdapter tibco;
#InjectMocks
#Autowired
private SomeClass test;
#BeforeClass
public void setUp() throws NamingException {
MockitoAnnotations.initMocks(this);
}
private String returnStatement;
#Test(dataProvider = "getTestClientResponseBusiness", dataProviderClass = StaticDataProvider.class)
public void getResponse(TestClientFilter testClientFilter) throws Exception {
when(tibco.invokeRequestResponse(Matchers.any(TestClientFilter.class))).thenReturn(new Object());
test.getResponse(testClientFilter);
tibco.invokeRequestResponse(testClientFilter.getMessage());
}
}
These lines of code are making problem from TIBCOAdapters internal functions.
bean.setJndiTemplate(new JndiTemplate(giafProperties.getProperties()));
bean.setJndiName(GIAFPropertyUtil.getPropertyString(giafProperties, "externalJndiName"));

Related

Code executes actual instance instead of Powermock'ed instance?

I have a PowerMock
#Test
public void testDueSoonQueryOperation () {
try {
PowerMockito.whenNew(InitialContext.class).withNoArguments().
thenReturn((InitialContext) mockInitialContext);
} catch (Exception e) { e.printStackTrace(); }
try {
PowerMockito.when(lookup(anyString())).thenReturn(mockDataSource);
} catch (NamingException e) { e.printStackTrace(); }
try {
PowerMockito.when(mockDataSource.getConnection(anyString(), anyString())).thenReturn(mockDataConnection);
} catch (SQLException e) { e.printStackTrace(); }
QueryWorkItemHandler workItemHandler = new QueryWorkItemHandler();
BatchWorkItemHandler mockBatchHandler = PowerMockito.mock(BatchWorkItemHandler.class);
String dueIn7Days = "dueIn7Days";
HashMap<String, Object> taskDue7DaysMap = new HashMap<>();
taskDue7DaysMap.put(dueIn7Days,dueIn7Days);
PowerMockito.when(mockBatchHandler.handleMessageType(any(),
eq("SOON"), anyBoolean(), anyBoolean())).thenReturn(taskDue7DaysMap);
Map<String, Object> results = workItemHandler.doQueryOperation("SOON");
...
to test a class that uses JDBC to call a database. I mocked the Connection, DataSource, and InitialContext, but from the log messages the test seems to be using the real Connection, DataSource, and InitialContext. - why is it not using the mocked datasource, connection, and context? What do I need to add to have it use the mock's for these calls? If I replace a mock instance with (class of mock).class the compiler complains that the method does not exist for the class, e.g.,
PowerMockito.when((Context.class).lookup(Mockito.anyString()))
so I doubt it is simply a problem with my "when" clauses. The method being called in the class being test is
public class QueryWorkItemHandler extends AbstractLogOrThrowWorkItemHandler {
final static String dbJndiDataSource = System.getProperty("persistence.ds");
final static String dbUsername = System.getProperty("database.username");
final static String dbPassword = System.getProperty("database.password");
static BatchWorkItemHandler queryHelper = new BatchWorkItemHandler ();
public HashMap<String, Object> doQueryOperation(String messageType) {
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(dbJndiDataSource);
Connection conn = ds.getConnection(dbUsername, dbPassword);
(up through the line that throws a null pointer exception because dbUsername is a property not set up in the mock.) If the Mock were being used it seems like the call to getConnection would be covered by
PowerMockito.when(mockDataSource.getConnection(anyString(),anyString())).thenReturn(mockDataConnection);

Hibernate: Error Could not locate SessionFactory in JNDI

I'm a newbie user of Hibernate. I've designed a database in MySQL and generated automatically the classes using HibernateTools.
For each table, HibernateTools has created 2 classes, one representing the table itself, and the other the Home object with the persist. For instance the Users table has the Users.java and UsersHome.java
User.java
public class User implements java.io.Serializable {
private Integer idUsuario;
private String nomUsuario;
private String mailUsuario;
private String pass;
private Date ultConexion;
private Date fechaAlta;
private Date ultimoIngreso;
private Date ultimoGasto;
#SuppressWarnings("rawtypes")
private Set avisoses = new HashSet(0);
#SuppressWarnings("rawtypes")
private Set conceptoses = new HashSet(0);
public User() {
}
public User(String nomUsuario, String mailUsuario, String pass, Date ultConexion, Date fechaAlta) {
this.nomUsuario = nomUsuario;
this.mailUsuario = mailUsuario;
this.pass = pass;
this.ultConexion = ultConexion;
this.fechaAlta = fechaAlta;
}
#SuppressWarnings("rawtypes")
public User(String nomUsuario, String mailUsuario, String pass, Date ultConexion, Date fechaAlta,
Date ultimoIngreso, Date ultimoGasto, Set avisoses, Set conceptoses) {
this.nomUsuario = nomUsuario;
this.mailUsuario = mailUsuario;
this.pass = pass;
this.ultConexion = ultConexion;
this.fechaAlta = fechaAlta;
this.ultimoIngreso = ultimoIngreso;
this.ultimoGasto = ultimoGasto;
this.avisoses = avisoses;
this.conceptoses = conceptoses;
}
public Integer getIdUsuario() {
return this.idUsuario;
}
public void setIdUsuario(Integer idUsuario) {
this.idUsuario = idUsuario;
}
public String getNomUsuario() {
return this.nomUsuario;
}
public void setNomUsuario(String nomUsuario) {
this.nomUsuario = nomUsuario;
}
public String getMailUsuario() {
return this.mailUsuario;
}
public void setMailUsuario(String mailUsuario) {
this.mailUsuario = mailUsuario;
}
public String getPass() {
return this.pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public Date getUltConexion() {
return this.ultConexion;
}
public void setUltConexion(Date ultConexion) {
this.ultConexion = ultConexion;
}
public Date getFechaAlta() {
return this.fechaAlta;
}
public void setFechaAlta(Date fechaAlta) {
this.fechaAlta = fechaAlta;
}
public Date getUltimoIngreso() {
return this.ultimoIngreso;
}
public void setUltimoIngreso(Date ultimoIngreso) {
this.ultimoIngreso = ultimoIngreso;
}
public Date getUltimoGasto() {
return this.ultimoGasto;
}
public void setUltimoGasto(Date ultimoGasto) {
this.ultimoGasto = ultimoGasto;
}
#SuppressWarnings("rawtypes")
public Set getAvisoses() {
return this.avisoses;
}
#SuppressWarnings("rawtypes")
public void setAvisoses(Set avisoses) {
this.avisoses = avisoses;
}
#SuppressWarnings("rawtypes")
public Set getConceptoses() {
return this.conceptoses;
}
#SuppressWarnings("rawtypes")
public void setConceptoses(Set conceptoses) {
this.conceptoses = conceptoses;
}
UserHome.java
public class UserHome {
private static final Log log = LogFactory.getLog(UserHome.class);
private final SessionFactory sessionFactory = getSessionFactory();
protected SessionFactory getSessionFactory() {
try {
return (SessionFactory) new InitialContext().lookup("SessionFactory");
} catch (Exception e) {
log.error("Could not locate SessionFactory in JNDI", e);
throw new IllegalStateException("Could not locate SessionFactory in JNDI");
}
}
public void persist(User transientInstance) {
log.debug("persisting User instance");
try {
sessionFactory.getCurrentSession().persist(transientInstance);
log.debug("persist successful");
} catch (RuntimeException re) {
log.error("persist failed", re);
throw re;
}
}
public void attachDirty(User instance) {
log.debug("attaching dirty User instance");
try {
sessionFactory.getCurrentSession().saveOrUpdate(instance);
log.debug("attach successful");
} catch (RuntimeException re) {
log.error("attach failed", re);
throw re;
}
}
public void attachClean(User instance) {
log.debug("attaching clean User instance");
try {
sessionFactory.getCurrentSession().lock(instance, LockMode.NONE);
log.debug("attach successful");
} catch (RuntimeException re) {
log.error("attach failed", re);
throw re;
}
}
public void delete(User persistentInstance) {
log.debug("deleting User instance");
try {
sessionFactory.getCurrentSession().delete(persistentInstance);
log.debug("delete successful");
} catch (RuntimeException re) {
log.error("delete failed", re);
throw re;
}
}
public User merge(User detachedInstance) {
log.debug("merging User instance");
try {
User result = (User) sessionFactory.getCurrentSession().merge(detachedInstance);
log.debug("merge successful");
return result;
} catch (RuntimeException re) {
log.error("merge failed", re);
throw re;
}
}
public User findById(java.lang.Integer id) {
log.debug("getting User instance with id: " + id);
try {
User instance = (User) sessionFactory.getCurrentSession().get("Usuario", id);
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
#SuppressWarnings("rawtypes")
public List findByExample(User instance) {
log.debug("finding User instance by example");
try {
List results = sessionFactory.getCurrentSession().createCriteria("User").add(Example.create(instance))
.list();
log.debug("find by example successful, result size: " + results.size());
return results;
} catch (RuntimeException re) {
log.error("find by example failed", re);
throw re;
}
}
}
When I do this as a tryout to see if everything goes as supposed, I get "Could not locate SessionFactory in JNDI"
UserHome uh = new UserHome();
User u = new User("nom", "Mail1#mail.com", "chsss", new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis()));
uh.persist(u);
What am I doing wrong?
Edit: The complete stacktrace of the exception
abr 21, 2016 8:47:09 AM hibernate.UsuarioHome getSessionFactory
SEVERE: Could not locate SessionFactory in JNDI
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source)
at javax.naming.InitialContext.lookup(Unknown Source)
at hibernate.UserHome.getSessionFactory(UserHome.java:27)
at hibernate.UserHome.<init>(UserHome.java:23)
at bbdd.FirstHibernate.main(FirstHibernate.java:11)
Exception in thread "main" java.lang.IllegalStateException: Could not locate SessionFactory in JNDI
at hibernate.UserHome.getSessionFactory(UserHome.java:30)
at hibernate.UserHome.<init>(UserHome.java:23)
at bbdd.FirstHibernate.main(FirstHibernate.java:11)
Do you have a initial context? Initial context will only exists when you are running your code on application server. If you are running this code on application server, please add your config files so that I can verify it is configured properly. If there is no application server and the code is being run from POJO, you need to get session factory in a different manner.Looking at your classes, I think you are running this in a POJO.
It will also help if you can paste the exception or stacktrace. Makes it easier to guess whats going wrong.

JMX threads are not closing

when I am doing JMX connection through a thread by following code,
private JMXConnector initConnection() throws Exception{
JMXServiceURL serviceURL = null;
try {
String URL = MessageFormat.format(connectorURL, new Object[]{hostName, port});
serviceURL = new JMXServiceURL(URL);
final Map<String, String[]> environment = new HashMap<String, String[]>();
environment.put(JMXConnector.CREDENTIALS, new String[]{userName, password});
return JMXConnectorFactory.connect(serviceURL, environment);
}
catch (Exception e)
{
throw e;
}
}
following threads are not destroying, even though i closed the connection and destroyed the thread that creates jmx connection
GC Daemon,RMI RenewClean, RMI Scheduler(0) these threads are not destroying in java JMX connection.
code in connection close
public void closeConnection() {
if(jmxc != null){
try{
jmxc.close();
}
catch (IOException e) {
jmxc = null;
}
}
}
public void createMBeanServerConnection() throws Exception
{
try
{
jmxc = initConnection();
mbServerConnection = jmxc.getMBeanServerConnection();
}
catch (Exception e)
{
throw e;
}
}
This is the full context
public class Test1
{
public static void main(String[] args) throws Exception
{
Thread.sleep(120000);
Test t = new Test();
t.main(args);
Thread.sleep(120000);
}
}
public class Test
{
private String hostName = "";
private String port = "";
private String userName = "";
private String password = "";
private String connectorURL = "service:jmx:rmi:///jndi/rmi://{0}:{1}/jmxrmi";
private JMXConnector jmxc = null;
public static void main(String []args) throws Exception
{
Test t = new Test();
t.hostName = args[0];
System.out.println(args[1]);
t.port = args[1];
t.jmxc = t.initConnection();
MBeanServerConnection mbsc = t.jmxc.getMBeanServerConnection();
mbsc.queryMBeans(new ObjectName("*.*:*"), null);
t.closeConnection();
}
private JMXConnector initConnection()
{
JMXServiceURL serviceURL = null;
try
{
String URL = MessageFormat.format(connectorURL, new Object[]{hostName, port});
System.out.println(URL);
serviceURL = new JMXServiceURL(URL);
final Map<String, String[]> environment = new HashMap<String, String[]>();
environment.put(JMXConnector.CREDENTIALS, new String[]{userName, password});
System.out.println(serviceURL);
return JMXConnectorFactory.connect(serviceURL, environment);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public void closeConnection()
{
if(jmxc != null)
{
try
{
jmxc.close();
}
catch (IOException e) {
jmxc = null;
}
}
}
}
You should do your task, then close the connection.
public void createMBeanServerConnection() throws Exception
{
try
{
jmxc = initConnection();
mbServerConnection = jmxc.getMBeanServerConnection();
doYourThing();
}
catch (Exception e)
{
throw e;
} finally {
closeConnection();
}
}

Retrying Method calls in generic way

My Java application requires a retry logic on remote calls failures.
These remote calls are:
scattered all over the application
pertain to different Remote Service classes.
Also, the retry logic may have varying retry interval and varying retry attempts.
I need a generic retry() implementation which can make appropriate method calls depending on from where it is called. Below is a simple code illustration of I am looking for. I know we can attempt to do this using java reflection, but, is there a framework or an open source available somewhere which is read-to-use?
try {
ClassA objA = remoteServiceA.call(paramA1, paramA2, ...);
} catch (Exception e){
ClassA objA = (ClassA)retry(remoteService, listOfParams, ..); // generic method call
}
..
try {
ClassB objB = remoteServiceB.call(paramB1, paramB2, ...);
} catch (Exception e){
ClassA objB = (ClassB)retry(remoteService, listOfParams, ..); // generic method call
}
As already suggested, you should use AOP and Java annotations. I would recommend a read-made mechanism from jcabi-aspects (I'm a developer):
#RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
return url.openConnection().getContent();
}
Read also this blog post: http://www.yegor256.com/2014/08/15/retry-java-method-on-exception.html
Update: Check RetryFunc from Cactoos.
This is a book example of where aspectj (or aop in general) can be used, see 8.2.7 Example in Spring documentation and 5 Reasons Java Developers Should Learn and Use AspectJ.
Basically an aspect intercepts all calls to given methods (specified using annotation, naming convention, whatever) and retries.
Assume you have a method, that need to retied at every 500ms and upto 5 times.
Current class:
public class RemoteCaller{
Service serviceCaller;
public void remoteCall(String message) {
serviceCaller.updateDetails( this.message);
return null;
}
}
Modified approach:
public class RetriableHelper<T> implements Callable<T> {
private Callable<T> task;
private int numberOfRetries;
private int numberOfTriesLeft;
private long timeToWait;
public RetriableHelper(int numberOfRetries, long timeToWait, Callable<T> task) {
this.numberOfRetries = numberOfRetries;
numberOfTriesLeft = numberOfRetries;
this.timeToWait = timeToWait;
this.task = task;
}
public T call() throws Exception {
while (true) {
try {
return task.call();
} catch (InterruptedException e) {
throw e;
} catch (CancellationException e) {
throw e;
} catch (Exception e) {
numberOfTriesLeft--;
if (numberOfTriesLeft == 0) {
throw e;
}
Thread.sleep(timeToWait);
}
}
}
}
Backend system/remote call class:
public class RemoteCaller{
Service serviceCaller;
public void remoteCall(String message) {
class RemoteCallable implements Callable<Void> {
String message;
public RemoteCallable( String message)
{
this.message = message;
}
public Void call() throws Exception{
serviceCaller.updateDetails( this.message);
return null;
}
}
RetriableHelper<Void> retriableHelper = new RetriableHelper<Void>(5, 500, new RemoteCallable( message));
try {
retriableHelper.call();
} catch (Exception e) {
throw e;
}
}
}
enter link description here Spring has a retry annotation which servers the purpose
Step 1: Add following dependency to your POM
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
Step 2: Enabling Spring Retry
To enable Spring Retry in an application, we need to add the #EnableRetry annotation to our #Configuration class:
Ex:
#Configuration
#EnableRetry
public class AppConfig { ... }
Step 3: To add retry functionality to methods, #Retryable can be used:
Ex:
#Service
public interface MyService {
#Retryable(
value = { SQLException.class },
maxAttempts = 2,
backoff = #Backoff(delay = 5000))
void retryService(String sql) throws SQLException;
...
}
Step 4.The #Recover annotation is used to define a separate recovery method when a #Retryable method fails with a specified exception:
Ex:
#Service
public interface MyService {
...
#Recover
void recover(SQLException e, String sql);
}
See Url for more details : http://www.baeldung.com/spring-retry
where do you get the services from? use a factory to Proxy the service you get from the original factory. The proxy can then implement the retry transparently. See the java Proxy/ProxyGenerators in reflection.
If you are using spring , then better go with Aspects.
Otherwise, below sample solution can work:
public class Test
{
public static void main(String[] args) throws Exception
{
Test test = new Test();
test.toRunFirst("Hello! This is normal invocation");
runWithRetry(test, "toRunFirst", "Hello! This is First, called with retry");
runWithRetry(test, "toRunSecond", "Hello! This is Second, called with retry");
}
public void toRunFirst(String s) {
System.out.println(s);
}
public void toRunSecond(String s) {
System.out.println(s);
}
public static Object runWithRetry(Object obj, String methodName, Object... args) throws Exception
{
Class<?>[] paramClass = new Class<?>[args.length];
for(int i=0; i< args.length; i++) {
paramClass[i] = args[i].getClass();
}
Method method = obj.getClass().getDeclaredMethod(methodName, paramClass);
int retryCount = 2;
for(int i=0; i< retryCount; i++) {
try {
return method.invoke(obj, args);
}
catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
I did not find what I needed so there is mine.
The main feature is that it throws the type of Exception you need when maxRetries is reached so you can catch it in the call.
import org.apache.log4j.Logger;
public class TaskUtils {
public static <E extends Throwable> void retry(int maxRetries, Task<E> task) throws E {
retry(maxRetries, 0, null, task);
}
public static <E extends Throwable> void retry(int maxRetries, long waitTimeMs, Logger logger, Task<E> task) throws E {
while (maxRetries > 0) {
maxRetries--;
try {
task.run();
} catch (Exception e) {
if (maxRetries == 0) {
try {
throw e;
} catch (Exception ignored) { // can't happen but just in case we wrap it in
throw new RuntimeException(e);
}
}
if (logger != null)
logger.warn("Attempt " + maxRetries + " failed", e);
try {
Thread.sleep(waitTimeMs);
} catch (InterruptedException ignored) {
}
}
}
}
public interface Task<E extends Throwable> {
void run() throws E;
}
}
Usage :
TaskUtils.retry(3, 500, LOGGER, () -> stmClickhouse.execute(
"ALTER TABLE `" + database + "`.`" + table.getName() + "` ON CLUSTER " + clusterName + allColumnsSql
));
add it into pom.xml
<dependency>
<groupId>org.deking.utils</groupId>
<artifactId>retry</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
new Retry<String>()
.maxOperationWaitTime(30_000)//Max operation wait time during a single operation
.retryIntervalTime(1_000)//Interval time between two operations
.maxRetryTimes(3)//Retry times when operation failed(or timeout) at the first time
.operation(() -> {
//your operation
return "success!";
})
.judgement(t -> (t == null || t.isEmpty()))//add your judgement whether the operation should be retry(Operation should return a value)
.execute();
If you want add retry config annotation on method,and call it:
class RetryTests{
#RetryConfig( maxRetryTimes=1)
public static String TestAnnotation() {
return "aaa";
}
public static void main(String[] args) {
try {
new Retry<String>()
.of(RetryTest.class.getMethod("TestAnnotation"),null)
.judgement(r -> r.equals("aaa"))
.execute();
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

java customize ObjectOutputStream to write additional data for each instance of a specific class

There're some library classes that although implement Serializable, fail to serialize correctly. I can't fix them, but I can extend ObjectOutputStream and ObjectInputStream for some workaround.
I want my ObjectOutputStream to write additional data for each instance of class A and ObjectInputStream to read and apply that data after A is deserialized.
Currently I have a mid-workaround that requires explicit additional calls to writeObject() and readObject(). I'd prefer to manage without these calls.
Uncomment /* */ blocks to see how it works.
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class CloneSerializableTest2 {
// library classes
public static class A implements Serializable {
public transient String s1;
}
public static class MyA extends A {
public String s2;
}
/*
private static class AHolder implements Serializable {
private static final Field s1Fld;
static {
try {
s1Fld = A.class.getDeclaredField("s1");
s1Fld.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Unexpected error", e);
}
}
private String s1;
private A a;
public AHolder(A m) {
this.a = m;
try {
s1 = (String)s1Fld.get(m);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unexpected error", e);
}
}
public void restoreA() {
try {
s1Fld.set(a, s1);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unexpected error", e);
}
}
}
*/
#SuppressWarnings("unchecked")
public static <T> T cloneSerializable(T o) {
try {
/*
final List<AHolder> accumSrc = new ArrayList<AHolder>();
*/
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout)
/*
{
{
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException
{
if (obj instanceof A) {
accumSrc.add(new AHolder((A)obj));
}
return super.replaceObject(obj);
}
}
*/
;
out.writeObject(o);
/*
out.writeObject(accumSrc);
*/
out.close();
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin);
Object copy = in.readObject();
/*
List<AHolder> accumDst = (List<AHolder>)in.readObject();
for (AHolder r : accumDst) {
r.restoreA();
}
*/
in.close();
return (T)copy;
} catch (IOException e) {
throw new RuntimeException("Unexpected error", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unexpected error", e);
}
}
#Test
public void testIt() throws Exception {
try {
MyA m1 = new MyA();
m1.s1 = "a";
m1.s2 = "b";
m1 = cloneSerializable(m1);
assertEquals("a", m1.s1);
assertEquals("b", m1.s2);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Answering to myself
import java.io.*;
import java.lang.reflect.Field;
import org.junit.Test;
import static org.junit.Assert.*;
public class CloneSerializableTest2 {
// library classes
public static class A implements Serializable {
public transient String s1;
}
public static class MyA extends A {
public String s2;
}
private static class AHolder implements Serializable, Externalizable {
private static final Field s1Fld;
static {
try {
s1Fld = A.class.getDeclaredField("s1");
s1Fld.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Unexpected error", e);
}
}
private String s1;
private A a;
#SuppressWarnings("unused")
public AHolder() {
}
public AHolder(A m) {
this.a = m;
try {
s1 = (String)s1Fld.get(m);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unexpected error", e);
}
}
private Object readResolve() {
try {
s1Fld.set(a, s1);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unexpected error", e);
}
return a;
}
#Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(s1);
ObjectOutputStream out2 = ((ObjectOutputStream)out);
out2.writeUnshared(a);
}
#Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
s1 = (String)in.readObject();
a = (A)in.readObject();
}
}
#SuppressWarnings("unchecked")
public static <T> T cloneSerializable(T o) {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout)
{
{
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException
{
if (obj instanceof A) {
obj = new AHolder((A) obj);
} else if (obj instanceof AHolder) {
obj = ((AHolder)obj).a;
}
return super.replaceObject(obj);
}
};
out.writeObject(o);
out.close();
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin);
Object copy = in.readObject();
in.close();
return (T)copy;
} catch (IOException e) {
throw new RuntimeException("Unexpected error", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unexpected error", e);
}
}
#Test
public void testIt() throws Exception {
try {
MyA m1 = new MyA();
m1.s1 = "a";
m1.s2 = "b";
m1 = cloneSerializable(m1);
assertEquals("a", m1.s1);
assertEquals("b", m1.s2);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}

Categories

Resources