I found this class that I really want to use:
org.hibernate.action.spi.AfterTransactionCompletionProcess -
http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/action/AfterTransactionCompletionProcess.html
Basically, I'd like some custom logic to happen after the transaction is committed. But I cannot for the life of me figure out how to use this thing.
Where do I specify this interface? Any examples would be awesome.
Found an example in the Hibernate 4.3's unit test code base:
org.hibernate.envers.test.integration.basic.RegisterUserEventListenersTest
Shows exactly what I was looking for:
package org.hibernate.envers.test.integration.basic;
import org.hibernate.Session;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.envers.internal.tools.MutableInteger;
import org.hibernate.envers.test.BaseEnversFunctionalTestCase;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.persister.entity.EntityPersister;
import org.junit.Assert;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
/**
* #author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class RegisterUserEventListenersTest extends BaseEnversFunctionalTestCase {
#Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {StrTestEntity.class};
}
#Test
#TestForIssue(jiraKey = "HHH-7478")
public void testTransactionProcessSynchronization() {
final EventListenerRegistry registry = sessionFactory().getServiceRegistry()
.getService( EventListenerRegistry.class );
final CountingPostInsertTransactionBoundaryListener listener = new CountingPostInsertTransactionBoundaryListener();
registry.getEventListenerGroup( EventType.POST_INSERT ).appendListener( listener );
Session session = openSession();
session.getTransaction().begin();
StrTestEntity entity = new StrTestEntity( "str1" );
session.save( entity );
session.getTransaction().commit();
session.close();
// Post insert listener invoked three times - before/after insertion of original data,
// revision entity and audit row.
Assert.assertEquals( 3, listener.getBeforeCount() );
Assert.assertEquals( 3, listener.getAfterCount() );
}
private static class CountingPostInsertTransactionBoundaryListener implements PostInsertEventListener {
private final MutableInteger beforeCounter = new MutableInteger();
private final MutableInteger afterCounter = new MutableInteger();
#Override
public void onPostInsert(PostInsertEvent event) {
event.getSession().getActionQueue().registerProcess(
new BeforeTransactionCompletionProcess() {
#Override
public void doBeforeTransactionCompletion(SessionImplementor session) {
beforeCounter.increase();
}
}
);
event.getSession().getActionQueue().registerProcess(
new AfterTransactionCompletionProcess() {
#Override
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
afterCounter.increase();
}
}
);
}
#Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return true;
}
public int getBeforeCount() {
return beforeCounter.get();
}
public int getAfterCount() {
return afterCounter.get();
}
}
}
Related
I am new at the room database in android and I want to insert data into the room, but before inserting I want to check if the item already exists in the database or not. I am using these codes(News and Article are the same it is just naming problem):
My Dao
package com.example.newsapp.models;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.example.newsapp.operations.Article;
import java.util.List;
#Dao
public interface SavedNewsDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Article article);
#Query("DELETE FROM saved_articles")
void deleteAll();
#Query("DELETE FROM saved_articles where id =:id ")
void removeNews(int id);
#Query("SELECT * FROM saved_articles")
LiveData<List<Article>> getAll();
#Query("select Count() from saved_articles where title =:title and content =:content and description =:description")
int checkArticle(String title, String content, String description);
}
I want to get number of items in the database with checkArticle method.
My Repository
package com.example.newsapp.models;
import android.app.Application;
import androidx.lifecycle.LiveData;
import com.example.newsapp.operations.Article;
import java.util.List;
public class SavedArticlesRepository {
private final LiveData<List<Article>> allArticles;
private final SavedNewsDAO savedNewsDAO;
SavedArticlesRepository(Application application) {
SavedNewsRoomDatabase db = SavedNewsRoomDatabase.getDatabase(application);
savedNewsDAO = db.savedNewsDAO();
allArticles = savedNewsDAO.getAll();
}
LiveData<List<Article>> getAllArticles() {
return allArticles;
}
void insert(Article article) {
SavedNewsRoomDatabase.databaseWriterExecutor.execute(() -> savedNewsDAO.insert(article));
}
void checkItem(Article article) {
SavedNewsRoomDatabase.databaseWriterExecutor.execute(() -> {
savedNewsDAO.checkArticle(article.title, article.content, article.description);
});
}
}
and this is my View Model
package com.example.newsapp.models;
import android.app.Application;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.example.newsapp.operations.Article;
import java.util.List;
public class SavedNewsViewModel extends AndroidViewModel {
private final SavedArticlesRepository savedArticlesRepository;
private final LiveData<List<Article>> allArticles;
public SavedNewsViewModel(Application application) {
super(application);
savedArticlesRepository = new SavedArticlesRepository(application);
allArticles = savedArticlesRepository.getAllArticles();
}
public LiveData<List<Article>> getAllArticles() {
return allArticles;
}
public void insert(Article article) {
savedArticlesRepository.insert(article);
}
public void checkArticle(Article article) {
savedArticlesRepository.checkItem(article);
}
}
The problem is that I can't imagine how can I use checkArticle method in ViewModel and Repository classes like getAll method. As I understand I must chain these methods to each-others. And I want to check in this method:
savedNewsViewModel = new ViewModelProvider(this).get(SavedNewsViewModel.class);
saveArticleButton.setOnClickListener(v -> {
Article article = new Article();
article.title = intent.getStringExtra("title");
article.description = intent.getStringExtra("description");
article.content = intent.getStringExtra("content");
article.urlToImage = intent.getStringExtra("image");
savedNewsViewModel.insert(article);
Toast.makeText(this, "Article saved successfully", Toast.LENGTH_SHORT).show();
});
I have looked other questions, but I couldn't get it
You need to observe the value which you get from your checkArticle method.
Use MutableLiveData in the following way:
public class SavedArticlesRepository {
....
LiveData<Integer> checkItem(Article article) {
MutableLiveData<Integer> data = MutableLiveData();
SavedNewsRoomDatabase.databaseWriterExecutor.execute(() -> {
int count = savedNewsDAO.checkArticle(article.title, article.content, article.description);
data.postValue(count);
});
return data;
}
}
Now in your viewmodel, also extend the checkArticle with LiveData
public LiveData<Integer> checkArticle(Article article) {
return savedArticlesRepository.checkItem(article);
}
In your fragment or activity observe this method:
saveArticleButton.setOnClickListener(v -> {
Article article = new Article();
article.title = intent.getStringExtra("title");
article.description = intent.getStringExtra("description");
article.content = intent.getStringExtra("content");
article.urlToImage = intent.getStringExtra("image");
savedNewsViewModel.checkArticle.observe(getViewLifeCycleOwner(), new Observer{ count ->
if(count > 0){
Toast.makeText(this, "Article already exists", Toast.LENGTH_SHORT).show();
} else {
savedNewsViewModel.insert(article);
Toast.makeText(this, "Article saved successfully", Toast.LENGTH_SHORT).show();
}
});
});
I haven't tested this code, as I don't work with Java anymore. But should work fine.
If I can suggest, I'd say learn Kotlin and coroutines. This stuff is so much easier when you're using Kotlin and coroutines.
Leave a comment if you need any more help with this.
I am a newbie to lagom and dgraph. And I got stuck to how to use lagom's read-side processor with Dgraph. Just to give you an idea following is the code which uses Cassandra with lagom.
import akka.NotUsed;
import com.lightbend.lagom.javadsl.api.ServiceCall;
import com.lightbend.lagom.javadsl.persistence.cassandra.CassandraSession;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import akka.stream.javadsl.Source;
public class FriendServiceImpl implements FriendService {
private final CassandraSession cassandraSession;
#Inject
public FriendServiceImpl(CassandraSession cassandraSession) {
this.cassandraSession = cassandraSession;
}
//Implement your service method here
}
Lagom does not provide out-of-the-box support for Dgraph. If you have to use Lagom's Read-Side processor with Dgraph, then you have to use Lagom's Generic Read Side support. Like this:
/**
* Read side processor for Dgraph.
*/
public class FriendEventProcessor extends ReadSideProcessor<FriendEvent> {
private static void createModel() {
//TODO: Initialize schema in Dgraph
}
#Override
public ReadSideProcessor.ReadSideHandler<FriendEvent> buildHandler() {
return new ReadSideHandler<FriendEvent>() {
private final Done doneInstance = Done.getInstance();
#Override
public CompletionStage<Done> globalPrepare() {
createModel();
return CompletableFuture.completedFuture(doneInstance);
}
#Override
public CompletionStage<Offset> prepare(final AggregateEventTag<FriendEvent> tag) {
return CompletableFuture.completedFuture(Offset.NONE);
}
#Override
public Flow<Pair<FriendEvent, Offset>, Done, ?> handle() {
return Flow.<Pair<FriendEvent, Offset>>create()
.mapAsync(1, eventAndOffset -> {
if (eventAndOffset.first() instanceof FriendCreated) {
//TODO: Add Friend in Dgraph;
}
return CompletableFuture.completedFuture(doneInstance);
}
);
}
};
}
#Override
public PSequence<AggregateEventTag<FriendEvent>> aggregateTags() {
return FriendEvent.TAG.allTags();
}
}
For FriendEvent.TAG.allTags(), you have to add following code in FriendEvent interface:
int NUM_SHARDS = 20;
AggregateEventShards<FriendEvent> TAG =
AggregateEventTag.sharded(FriendEvent.class, NUM_SHARDS);
#Override
default AggregateEventShards<FriendEvent> aggregateTag() {
return TAG;
}
I hope this helps!
I'm trying to implement unit tests for Storm bolts (Java). The code below works fine and ends with a success on Storm 1.0.3:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.887 sec
However, when I change BaseBasicParrotBolt on line 46 to BaseRichParrotBolt, the assertions never run and it ends with the following exception:
13610 [main] ERROR o.a.s.testing4j - Error in cluster java.lang.AssertionError: Test timed out (10000ms) (not (every? exhausted? (spout-objects spouts)))
If you step through it with a debugger you'll see that the bolt does receive and emit tuples, but it seems like Testing.completeTopology never returns. I find this really odd because the bolts are virtually the same. All my bolts extend from BaseRichBolt so I'd really like to make it work for those as well. Any ideas?
import java.util.Map;
import org.apache.storm.Config;
import org.apache.storm.ILocalCluster;
import org.apache.storm.Testing;
import org.apache.storm.generated.StormTopology;
import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.testing.CompleteTopologyParam;
import org.apache.storm.testing.MkClusterParam;
import org.apache.storm.testing.MockedSources;
import org.apache.storm.testing.TestJob;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.topology.base.BaseRichSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
import java.util.Arrays;
import java.util.List;
import static junit.framework.Assert.*;
import org.junit.Test;
public class StormTestExample {
private final static String EVENT = "event";
private final static String SPOUT_ID = "spout";
private final static String BOLT_ID = "parrot";
private final static List<String> COMPONENT_IDS = Arrays.asList(SPOUT_ID, BOLT_ID);
#Test
public void testBasicTopology() {
MkClusterParam mkClusterParam = new MkClusterParam();
mkClusterParam.setSupervisors(4);
Config daemonConf = new Config();
daemonConf.put(Config.STORM_LOCAL_MODE_ZMQ, false);
mkClusterParam.setDaemonConf(daemonConf);
Testing.withSimulatedTimeLocalCluster(mkClusterParam, new TestJob() {
#Override
public void run(ILocalCluster cluster) {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout(SPOUT_ID, new TestSpout());
builder.setBolt(BOLT_ID, new BaseBasicParrotBolt()).shuffleGrouping(SPOUT_ID);
StormTopology topology = builder.createTopology();
MockedSources mockedSources = new MockedSources();
mockedSources.addMockData(SPOUT_ID,
new Values("nathan"),
new Values("bob"),
new Values("joey"),
new Values("nathan"));
Config conf = new Config();
conf.setNumWorkers(2);
CompleteTopologyParam completeTopologyParam = new CompleteTopologyParam();
completeTopologyParam.setMockedSources(mockedSources);
completeTopologyParam.setStormConf(conf);
final Map result = Testing.completeTopology(cluster, topology, completeTopologyParam);
final Values expected = new Values(new Values("nathan"), new Values("bob"), new Values("joey"),
new Values("nathan"));
for (String component : COMPONENT_IDS) {
assertTrue("Error in " + component + " output",
Testing.multiseteq(expected, Testing.readTuples(result, component)));
}
}
});
}
private static class TestSpout extends BaseRichSpout {
#Override
public void declareOutputFields(OutputFieldsDeclarer ofd) {
ofd.declare(new Fields(EVENT));
}
#Override
public void open(Map map, TopologyContext tc, SpoutOutputCollector soc) {
throw new UnsupportedOperationException(); // Don't need an implementation to run the test.
}
#Override
public void nextTuple() {
throw new UnsupportedOperationException(); // Don't need an implementation to run the test.
}
}
private static class BaseBasicParrotBolt extends BaseBasicBolt {
#Override
public void declareOutputFields(OutputFieldsDeclarer ofd) {
ofd.declare(new Fields(EVENT));
}
#Override
public void execute(Tuple tuple, BasicOutputCollector boc) {
boc.emit(new Values(tuple.getValue(0)));
}
}
private static class BaseRichParrotBolt extends BaseRichBolt {
private OutputCollector oc;
#Override
public void declareOutputFields(OutputFieldsDeclarer ofd) {
ofd.declare(new Fields(EVENT));
}
#Override
public void prepare(Map map, TopologyContext tc, OutputCollector oc) {
this.oc = oc;
}
#Override
public void execute(Tuple tuple) {
oc.emit(new Values(tuple.getValue(0)));
}
}
}
If using BaseRichBolt , you should call ack() yourself in execute(), which is handled by BaseBasicBolt.
A user has asked that I preserve the state of the JFileChooser across application restarts. Specifically, he has asked that I preserve the state of the Details / List view type selection. Two applicable questions:
How can I start the JFileChooser in the Details view?
Start a JFileChooser with files ordered by date
These both show methods of starting the JFileChooser with specific default behavior. The piece that is missing is a way to determine what behavior the user had active (view type, sort order) when the JFileChooser window is closed, so that it can be saved and restored later. Any ideas?
You can use the Properties API or Preferences API to save/restore user data.
At start up you would read the users data and set the file chooser property.
To listener for user changes to the view type you can add a PropertyChangeListener to the file chooser and listen for the viewType event. Then you would update the user data with the new value.
You can add a RowSorterListener to the RowSorter to listen for changes in the sort order. You would then need to save the sort order. I don't know the best way to store the sort data.
Based on feedback, I have created the following class. I believe that this gives all the functionality I am looking for. This class depends on this SwingUtils class.
You will also need to update access restriction rules (at least in Eclipse) to allow access to sun/swing/FilePane as described in
Access restriction: Is not accessible due to restriction on required library ..\jre\lib\rt.jar
Edit: Cleanup resource leak
After coming back to work from a long vacation, I realized that the code I initially provided could leak JFileChooser instances, and so I have reworked it to provide an AutoCloseable entity that can be used in a try-with-resources statement. Sorry for the churn.
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.swing.FilePane;
import darrylbu.util.SwingUtils;
#SuppressWarnings("restriction")
public class JFileChooserPersisterFactory {
private static final String VIEW_TYPE_LIST = "viewTypeList"; //$NON-NLS-1$
private static final String VIEW_TYPE_DETAILS = "viewTypeDetails"; //$NON-NLS-1$
private static final String CHOOSER_CLOSING_PROPERTY = "JFileChooserDialogIsClosingProperty"; //$NON-NLS-1$
private static final String VIEW_TYPE_PROPERTY = "viewType"; //$NON-NLS-1$
private static final String IS_DETAILS = "isDetails"; //$NON-NLS-1$
private static final String SORT_ORDER = "sortOrder"; //$NON-NLS-1$
private JFileChooserPersisterFactory() {
}
public static JFileChooserPersister createJFileChooserPersister() {
JFileChooserPersisterImpl persister = new JFileChooserPersisterImpl();
persister.init();
return persister;
}
public interface JFileChooserPersister extends AutoCloseable {
JFileChooser getJFileChooser();
#Override
void close();
}
private static class JFileChooserPersisterImpl implements JFileChooserPersister {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Preferences persistentPrefs = Preferences.userNodeForPackage(getClass());
private final JFileChooser chooser;
private boolean isDetails;
private OnChooserClosing chooserClosingListener;
private FilePane filePane;
private OnViewTypeChanged viewTypeChangedListener;
public JFileChooserPersisterImpl() {
chooser = new JFileChooser();
}
public void init() {
restoreSettings();
registerForViewTypeChangeEvents();
chooserClosingListener = new OnChooserClosing();
chooser.addPropertyChangeListener(CHOOSER_CLOSING_PROPERTY, chooserClosingListener);
}
#Override
public JFileChooser getJFileChooser() {
return chooser;
}
private void persistSettings() {
persistentPrefs.putBoolean(IS_DETAILS, isDetails);
if (isDetails) persistSortOrder();
}
private void persistSortOrder() {
byte[] serializedSortOrder = serializeSortOrder();
if (serializedSortOrder != null)
persistentPrefs.putByteArray(SORT_ORDER, serializedSortOrder);
}
private byte[] serializeSortOrder() {
List<? extends SortKey> keys = getRowSorter().getSortKeys();
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(byteStream)) {
out.writeObject(new SortOrderInfo(keys));
return byteStream.toByteArray();
} catch (IOException e) {
logger.error("Could not serialize JFileChooser row sort order.", e); //$NON-NLS-1$
}
return null;
}
private void restoreSettings() {
isDetails = persistentPrefs.getBoolean(IS_DETAILS, false);
if (isDetails) {
setToDetailsView();
applyInitialSortOrder();
} else {
setToListView();
}
}
private void setToDetailsView() {
Action details = chooser.getActionMap().get(VIEW_TYPE_DETAILS);
details.actionPerformed(null);
}
private void setToListView() {
Action details = chooser.getActionMap().get(VIEW_TYPE_LIST);
details.actionPerformed(null);
}
private void applyInitialSortOrder() {
byte[] serializedSortOrder = persistentPrefs.getByteArray(SORT_ORDER, null);
if (serializedSortOrder == null) return;
ByteArrayInputStream byteStream = new ByteArrayInputStream(serializedSortOrder);
try (ObjectInputStream in = new ObjectInputStream(byteStream)) {
setSortInfo((SortOrderInfo) in.readObject());
} catch (IOException | ClassNotFoundException e) {
logger.error("Could not deserialize JFileChooser row sort order.", e); //$NON-NLS-1$
}
}
private void setSortInfo(SortOrderInfo info) {
info.setSortOrder(getRowSorter());
}
private RowSorter<?> getRowSorter() {
JTable table = SwingUtils.getDescendantsOfType(JTable.class, chooser).get(0);
RowSorter<?> rowSorter = table.getRowSorter();
return rowSorter;
}
private void registerForViewTypeChangeEvents() {
filePane = SwingUtils.getDescendantsOfType(FilePane.class, chooser).get(0);
viewTypeChangedListener = new OnViewTypeChanged();
filePane.addPropertyChangeListener(VIEW_TYPE_PROPERTY, viewTypeChangedListener);
}
private final class OnChooserClosing implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
persistSettings();
}
}
private class OnViewTypeChanged implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
isDetails = ((int) evt.getNewValue()) == FilePane.VIEWTYPE_DETAILS;
}
}
public static class SortOrderInfo implements Serializable {
private static final long serialVersionUID = -5393878644049680645L;
private final List<ColumnSortInfo> keyInfo = new ArrayList<>();
public SortOrderInfo(List<? extends SortKey> keys) {
for (SortKey sortKey : keys) {
keyInfo.add(new ColumnSortInfo(sortKey));
}
}
public void setSortOrder(RowSorter<?> rowSorter) {
rowSorter.setSortKeys(makeSortKeys());
}
private List<SortKey> makeSortKeys() {
List<SortKey> keys = new ArrayList<>();
for (ColumnSortInfo info : keyInfo) {
keys.add(info.makeSortKey());
}
return keys;
}
public static class ColumnSortInfo implements Serializable {
private static final long serialVersionUID = 5406885180955729893L;
private final SortOrder sortOrder;
private final int column;
public ColumnSortInfo(SortKey sortKey) {
column = sortKey.getColumn();
sortOrder = sortKey.getSortOrder();
}
public SortKey makeSortKey() {
return new SortKey(column, sortOrder);
}
}
}
#Override
public void close() {
chooser.removePropertyChangeListener(CHOOSER_CLOSING_PROPERTY, chooserClosingListener);
filePane.removePropertyChangeListener(VIEW_TYPE_PROPERTY, viewTypeChangedListener);
}
}
}
I also need to store the time stamp inside the list when the log message is pushed into the list.So how can we get the timestamp and push into the list.I am pushing the logs into the list as follows
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.RollingFileAppender;
//import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.LoggingEvent;
public class Listappender extends RollingFileAppender {
private List<LoggingEvent> events = new ArrayList<LoggingEvent>();
static List<String> clone = new ArrayList<String>();
#Override
public synchronized void doAppend(LoggingEvent event) {
//System.out.println("hi....");
//events.clear();
events.add(event);
super.doAppend(event);
clone.clear();
clone.add((String) event.getMessage());
//getEvents();
/*
for (int j = 0; j < events.size(); j++){
System.out.println(events.get(j));
System.out.println(j);
}*/
}
public List<String> getEntries()
{
return clone;
}
}
See how to create timestamp Java Timestamp - How can I create a Timestamp with the date 23/09/2007?
And you can simply do this
clone.add(timestamp.toString() + event.getMessage());
Just keep the LoggingEvent s in a list. You don't have to get the message and save it in a different list. The LoggingEvent has a public final long getTimeStamp() method to supply the time. Try using this code:
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;
public class ListAppender extends RollingFileAppender {
private final List<LoggingEvent> events = new ArrayList<LoggingEvent>();
#Override
public synchronized void doAppend(LoggingEvent event) {
events.add(event);
super.doAppend(event);
}
public synchronized List<LoggingEvent> getLoggingEvents() {
List<LoggingEvent> clone = new ArrayList<>(events);
events.clear();
return clone;
}
}