I am trying to simplify the number of lines for my codes in the repository.
Currently there is a lot of repetition in my codes.
Many of the solutions online only involves inserting once into the table.
I need to do insert() on many tables. I want to reduce the repetition for writing the same inner AsyncTask for inserting different data into different table
This is the codes for the repository class
public class CharacterRepository {
private UserDao rUserDao;
private CharacterDao rCharacterDao;
private EquipementDao rEquipementDao;
private LiveData<List<UserDao>> rUserLD;
private LiveData<List<CharacterDao>> rCharacterLD;
private LiveData<List<EquipmentDao>> rEquipmentLD;
// Constructor that handles the database and initialise the member variables
CharacterRepository(Application application){
MyDatabase db = MyDatabase.getDatabase(application);
rUserDao = db.userDao();
rCharacterDao = db.characterDao();
rEquipementDao = db.EquipmentDao();
rUserLD = rUserDao.getAllUser();
rCharacterLD = rCharacterDao.getAllChar();
rEquipmentLD = rEquipementDao.getAllEquip();
}
// Wrapper method that returns cached entities as LiveData
public LiveData<List<UserEntity>> getAllUser(){return rUserLD;}
public LiveData<List<CharEntity>> getAllChar(){return rCharacterLD;}
public LiveData<List<EquipEntity>> getAllEquip(){return rEquipmentLD;}
/*---------------------the start of the problem-------------------*/
//Wrapper method: calling insert on non-UI Thread
public void insert(UserEntity userEntity){new insertUserAsyncTask(rUserDao).execute(userEntity);}
public void insert(CharacterEntity characterEntity){new insertCharacterAsyncTask(rCharacterDao).execute(characterEntity);}
public void insert(EquipmentEntity equipmentEntity){new insertEquipAsyncTask(rCharacterDao).execute(equipmentEntity);}
/*-------------------THIS IS THE PART WHERE I WANT TO REDUCE THE CODE REDUNDANCY THE CODES ARE DOING THE SAME THING-------------------*/
private static class insertUserAsyncTask extends AsyncTask<UserEntity, Void, Void> {
private UserDao mAsyncTaskDao;
insertUserAsyncTask(UserDao dao) {mAsyncTaskDao = dao;}
#Override
protected Void doInBackground(UserEntity... userEntities) {
mAsyncTaskDao.save(params[0]);
return null;
}
}
private static class insertCharacterAsyncTask extends AsyncTask<CharacterEntity, Void, Void> {
private CharacterDao mAsyncTaskDao;
insertCharacterAsyncTask(CharacterDao dao) {mAsyncTaskDao = dao; }
#Override
protected Void doInBackground(CharacterEntity... characterEntities) {
mAsyncTaskDao.save(params[0]);
return null;
}
}
private static class insertEquipAsyncTask extends AsyncTask<, Void, Void> {
private EquipmentDao mAsyncTaskDao;
insertEquipAsyncTask(EquipmentDao dao) {mAsyncTaskDao = dao;}
#Override
protected Void doInBackground(EquipmentEntity... equipmentEntities) {
mAsyncTaskDao.save(params[0]);
return null;
}
}
}
I still have other insert methods and I need to call delete and update as well. I do not want the codes to so repetitive
so, #notTdar came up with this solution
Have a class call ThreadPoolExecutor.
Call this class to execute all the DAO from the Android Room Database
Call cleanResource(); in onDestroy
Call shut(); in onPause
ThreadPoolExecutorHelper.java
public class ThreadPoolExecutorHelper {
private static final String TAG = ThreadPoolExecutorHelper.class.getSimpleName() + " : ";
private static final boolean LOG_DEBUG = false;
private static volatile ThreadPoolExecutorHelper INSTANCE;
private ThreadPoolExecutor mThreadPoolExecutor;
private BlockingQueue<Runnable> mBlockingQueue;
private static final int TASK_QUEUE_SIZE = 12;
//core size, keeps thread : along with running + idle
private static final int CORE_POOL_SIZE = 5;
// pool size
private static final int MAX_POOL_SIZE = 5;
// core pool size exceeds, idle thread will wait for this time before termination.
private static final long KEEP_ALIVE_TIME = 20L;
public static ThreadPoolExecutorHelper getInstance() {
if (LOG_DEBUG) Log.e(TAG, "getInstance: ");
if (INSTANCE == null) {
synchronized (ThreadPoolExecutorHelper.class) {
if (INSTANCE == null) {
INSTANCE = new ThreadPoolExecutorHelper();
}
}
}
return INSTANCE;
}
private ThreadPoolExecutorHelper() {
if (LOG_DEBUG) Log.d(TAG, "ctor: ");
initBlockingQueue();
initThreadPoolExecutor();
}
// submit Runnables
public void submitRunnable(Runnable task) {
if (LOG_DEBUG) Log.d(TAG, "submitRunnable: " + task.getClass().getSimpleName());
//in case, init again, if null.
initBlockingQueue();
initThreadPoolExecutor();
mThreadPoolExecutor.execute(task);
}
// shut the threadpool
public synchronized void shut() {
if (LOG_DEBUG) Log.d(TAG, "shut: ");
if (mThreadPoolExecutor != null) {
mThreadPoolExecutor.shutdown();
try {
mThreadPoolExecutor.awaitTermination(6000L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
if (LOG_DEBUG) Log.w(TAG, "shut: InterruptedException");
mThreadPoolExecutor.shutdownNow();
}
} else {
Log.e(TAG, "shut: mThreadPoolExecutor instance NULL");
}
}
//clean up
public void cleanResources() {
if (LOG_DEBUG) Log.e(TAG, "cleanResources: ");
if (INSTANCE != null) {
if (mThreadPoolExecutor != null) {
mThreadPoolExecutor = null;
}
if (mBlockingQueue != null) {
mBlockingQueue = null;
}
nullifyHelper();
}
}
private static void nullifyHelper() {
if (INSTANCE != null) {
INSTANCE = null;
}
}
private void initBlockingQueue() {
if (mBlockingQueue == null) {
mBlockingQueue = new LinkedBlockingQueue<>(TASK_QUEUE_SIZE);
}
}
private void initThreadPoolExecutor() {
if (mThreadPoolExecutor == null) {
mThreadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
KEEP_ALIVE_TIME, TimeUnit.SECONDS, mBlockingQueue);
}
}
}
Add this codes in onCreate (activity) or onViewCreated(Fragment)
This will initialise the ThreadPoolExecutorHelper by calling getInstance()
private void initExecutorHelper() {
if (LOG_DEBUG) Log.d(TAG, "initExecutorHelper: ");
if (mExecutorHelper == null) {
mExecutorHelper = ThreadPoolExecutorHelper.getInstance();
}
}
This is the insert(); method to start a Thread
You can change this to do insert, query, delete task from the DAO in Room Database
public void insert() {
if (LOG_DEBUG) Log.d(TAG, "requestQREntityList: whatKind= " + whatKind);
mExecutorHelper.submitRunnable(() -> {
if (!Thread.interrupted()) {
//request a list or inset something, write your logic.
} else {
if (LOG_DEBUG) Log.e(TAG, "run: Thread is interrupted");
}
});
}
Related
I'm dealing with something that I don't understand at all.
If I delete the observer before updating my Room database and then put the observer back on, I have notifications for each update, and the recyclerview is updated as many times.
My partial code:
public class ArticlesListFragment extends Fragment {
private LiveData<List<Article>> mLDgetAllArticle;
private long mClistId;
private ArrayList<Article> mArticles;
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.mLDgetAllArticle = this.articleViewModel.getAllArticle(mclistId);
this.setArticlesObserver();
...
}
private void setArticlesObserver() {
if (this.mLDgetAllArticle != null && !this.mLDgetAllArticle.hasObservers())
this.mLDgetAllArticle.observe(getViewLifecycleOwner(), this::updateArticlesList);
}
private void updateArticlesList(List<Article> articles) {
this.mArticles = new ArrayList<>(articles);
this.mArticlesRecyclerViewAdapter.setAdapterDatas(this.mArticles);
}
private void removeArticlesObserver() {
if (this.mLDgetAllArticle != null && this.mLDgetAllArticle.hasObservers())
this.mLDgetAllArticle.removeObservers(getViewLifecycleOwner());
}
private void updateArticle(Article article) {
this.articleViewModel.updateArticle(article);
}
...
}
Everything is fine so far.
But, elsewhere, I have to update all my Articles, like:
for (int i = 0; i < this.mArticles.size(); i++) {
this.mArticles.get(i).setOrd(i);
this.updateArticle(this.mArticles.get(i));
}
Also, I thought I should delete the observer before, and put it back later:
this.removeArticlesObserver();
for (int i = 0; i < this.mArticles.size(); i++) {
this.mArticles.get(i).setOrd(i);
this.updateArticle(this.mArticles.get(i));
}
this.setArticlesObserver();
but I still get after all the updates notifications. updateArticlesList is called as many times as there were updateArticles, after setArticlesObserver.
What am I missing?
Is there any way to flush all that waiting results before setting observer again?
ViewModelFactory.java:
public class ViewModelFactory implements ViewModelProvider.Factory {
private final ArticleDataRepository articleDataSource;
private final Executor executor;
private static volatile ViewModelFactory factory;
public static ViewModelFactory getInstance(Context context) {
if (factory == null) {
synchronized (ViewModelFactory.class) {
if (factory == null) {
factory = new ViewModelFactory(context);
}
}
}
return factory;
}
private ViewModelFactory(Context context) {
getDB database = getDB.getInstance(context);
this.articleDataSource = new ArticleDataRepository(database.articleDao());
this.executor = Executors.newSingleThreadExecutor();
}
#Override
#NotNull
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass.isAssignableFrom(ArticleViewModel.class)) {
return (T) new ArticleViewModel(articleDataSource, executor);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
ArticleViewModel.java:
public class ArticleViewModel extends ViewModel {
private final ArticleDataRepository articleDataSource;
private final Executor executor;
public ArticleViewModel(
ArticleDataRepository articleDataSource,
Executor executor
) {
this.articleDataSource = articleDataSource;
this.executor = executor;
}
public LiveData<List<Article>> getAllArticle(long clistId) {
return articleDataSource.getAll(clistId);
}
}
ArticleDataRepository:
public class ArticleDataRepository {
private final ArticleDao articleDao;
public ArticleDataRepository(ArticleDao articleDao) {
this.articleDao = articleDao;
}
public LiveData<List<Article>> getAll(long clistId) {
return this.articleDao.getAll(clistId);
}
}
I used this documentation
I took two steps to solve my problem:
I'm updating all the articles instead of doing a part one I didn't know I could do
#Update(onConflict = OnConflictStrategy.REPLACE)
void updateAll(ArrayList articles);
for (int i = 0; i < this.mArticles.size(); i++) {
this.mArticles.get(i).setOrd(i);
this.updateArticle(this.mArticles.get(i));
}
becomes:
for (int i = 0; i < this.mArticles.size(); i++) {
this.mArticles.get(i).setOrd(i);
}
this.updateAllArticles(this.mArticles);
I put a flag to launch the notifyDataSetChanged() only during specific events and not each time the data is updated.
I have been trying to code an app but I am stuck with a problem. I have a function checkexistence which checks weather column with column name as the current month is present or not and another function that inserts rows in the sql database but I am not able to figure out how can use both of them to check that weather current month is present or not and if not add the current month
Here is the class with the code.
public class Repository {
private MonthDetailsDAO monthDetailsDAO;
private static Repository instance;
private YearDetailsDAO yearDetailsDAO;
private LiveData<List<MonthDetailsEntity>> liveMonthData;
private LiveData<List<YearDetailsEntity>> liveYearData;
private static final String LOG_TAG = "Database>>Repository";
public Repository(Application application){
Database database = Database.getInstance(application);
yearDetailsDAO = database.yearDetailsDAO();
liveYearData = yearDetailsDAO.getdata();
}
public void add_year(YearDetailsEntity yearDetailsEntity){
new InsertAsyncTask(yearDetailsDAO).execute(yearDetailsEntity);
}
public void checkExistance(YearDetailsEntity yearDetailsEntity){
new CheckExistanceAsyncTask(yearDetailsDAO).equals(yearDetailsEntity);
}
public LiveData<List<YearDetailsEntity>> getLiveYearData(){
return liveYearData;
}
private static class CheckExistanceAsyncTask extends AsyncTask<YearDetailsEntity , Void , Void>{
private YearDetailsDAO yearDetailsDAO;
public CheckExistanceAsyncTask(YearDetailsDAO yearDetailsDAO){
this.yearDetailsDAO = yearDetailsDAO;
}
#Override
protected Void doInBackground(YearDetailsEntity... yearDetailsEntities) {
int m = yearDetailsEntities[0].getMonth();
int y = yearDetailsEntities[0].getYear();
YearDetailsEntity yearDetailsEntity1 = yearDetailsDAO.checkExistance(m , y);
if(yearDetailsEntity1 == null) {
//insert now
}
else
Log.d(LOG_TAG , "Month already present");
return null;
}
}
private static class InsertAsyncTask extends AsyncTask<YearDetailsEntity, Void, Void> {
private YearDetailsDAO yearDetailsDAO;
public InsertAsyncTask(YearDetailsDAO yearDetailsDAO){
this.yearDetailsDAO = yearDetailsDAO;
}
#Override
protected Void doInBackground(YearDetailsEntity... yearDetailsEntities) {
yearDetailsDAO.add_year(yearDetailsEntities[0]);
return null;
}
}
}
I believe I've seen variants of this question, but no "definitive answer". In the code below, I understand that SomeEventManager holds a reference to someImplClassTwo.myEventListenerA and someImplClassTwo.myEventListenerB, and that this does not allow for someImplClassTwo to be garbage collected, and this results in the output generated the second time someEventManager.notifyListeners() is invoked.
But, I'd really like for users of SomeImplClass not to have to know that there are listeners involved in the implementation, and that these listeners need to be manually un-registered (i.e., SomeImplClass.releaseListeners()) before releasing the SomeImplClass object.
Is there a clean/accepted way of doing this?
p.s. I've already played with finalize(), just for fun, and confirmed that GC is not even attempted in this case, for either instance of SomeImplClass. So, that seems to be a non-starter as a potential solution.
Test Driver
public class TestDriver {
public static void main(String[] args) {
SomeEventManager someEventManager = SomeEventManager.getInstance();
SomeImplClass someImplClassOne = new SomeImplClass("One");
SomeImplClass someImplClassTwo = new SomeImplClass("Two");
someEventManager.notifyListeners();
someImplClassOne.releaseListeners();
someImplClassOne = null;
someImplClassTwo = null;
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
}
someEventManager.notifyListeners();
}
}
Event Interface
public interface SomeEventListener {
public void handleSomeEvent();
}
Event Manager
import java.util.ArrayList;
import java.util.List;
public class SomeEventManager {
private static SomeEventManager eventManager = null;
private List<SomeEventListener> listeners = null;
private SomeEventManager() {
listeners = new ArrayList<SomeEventListener>();
}
public static SomeEventManager getInstance() {
if (eventManager == null) {
eventManager = new SomeEventManager();
}
return eventManager;
}
public void addListener(SomeEventListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeListener(SomeEventListener listener) {
listeners.remove(listener);
}
public void notifyListeners() {
for(SomeEventListener listener : listeners) {
listener.handleSomeEvent();
}
}
}
Event Listener Implementation
public class SomeImplClass {
private InnerEventListener myEventListenerA = null;
private InnerEventListener myEventListenerB = null;
private String id = null;
public SomeImplClass(String id) {
this.id = id;
myEventListenerA = new InnerEventListener(id + "_A");
myEventListenerB = new InnerEventListener(id + "_B");
}
public void releaseListeners() {
myEventListenerA.unregisterListener();
myEventListenerB.unregisterListener();
}
private class InnerEventListener implements SomeEventListener {
private SomeEventManager someEventManager = null;
private String id = null;
public InnerEventListener(String id) {
someEventManager = SomeEventManager.getInstance();
this.id = id;
registerListener();
}
public void registerListener() {
someEventManager.addListener(this);
}
public void unregisterListener() {
someEventManager.removeListener(this);
}
public void handleSomeEvent() {
System.out.println("InnerEventListener->" + id);
}
}
}
The solution we use is to have the listener automatically unregister itself if it gets called and the thing it's updating has been collected.
It looks a bit like this:
private static class InnerEventListener implements SomeEventListener {
private final WeakReference<ThingToUpdate> thingRef;
public InnerEventListener(ThingToUpdate thing) {
thingRef = new WeakReference<>(thing);
}
#Override
public void handleSomeEvent(SomeEvent event) {
ThingToUpdate thing = thingRef.get();
if (thing != null) {
thing.updateSomehow();
} else {
((SomeEventedThing) event.getSource())
.removeSomeEventListener(this);
}
}
}
//...
SomeEventedThing eventedThing;
ThingToUpdate thingToUpdate;
//...
eventedThing.addListener(new InnerEventListener(thingToUpdate));
I wouldn't say it's a perfect solution because the listener sticks around until it gets an event, and it's still somewhat dependent on garbage collection. We've been trying to replace it with explicit removal where possible, usually on addNotify/removeNotify on GUI components.
I have a separate thread, which is calling a method from DLL in a while/call/sleep loop, which on it's turn calls mapped callback from my java app. The first call of cabinet.ProcessCallbacks.apply() is successful, but all subsequent calls fail with java.lang.Error: Invalid Memory Access, which is very odd. Do you have any idea why is this happening and how to fix it?
private void registerCallbacksCycle(final int rate) {
Thread buttonsCycle = new Thread() {
#Override
public void run() {
while(true) {
try {
System.out.println("Refreshing Callbacks ...");
try {
System.out.println(" ");
byte b = cabinet.ProcessCallbacks.apply();
System.out.println("result " + b);
} catch(Error e) {
e.printStackTrace();
}
System.out.println("Refreshing Callbacks END ...");
sleep(rate);
System.out.println("Refreshing Callbacks AFTER SLEEP ...");
} catch (Exception e1) {
System.out.println("Refreshing Callbacks EXCEPTION ");
e1.printStackTrace();
}
}
}
};
buttonsCycle.start();
System.out.println("Callback Cycles started successfully ... ");
}
[UPDATE]
Java mapped to DLL:
public class OGAPI_CabinetButtons_Interface_v1 extends Structure {
/** C type : Initialize_callback* */
public OGAPI_CabinetButtons_Interface_v1.Initialize_callback Initialize;
/** C type : ProcessCallbacks_callback* */
public OGAPI_CabinetButtons_Interface_v1.ProcessCallbacks_callback ProcessCallbacks;
/** C type : Shutdown_callback* */
public OGAPI_CabinetButtons_Interface_v1.Shutdown_callback Shutdown;
/** C type : SetButtonCallback_callback* */
public OGAPI_CabinetButtons_Interface_v1.SetButtonCallback_callback SetButtonCallback;
/** C type : SetLights_callback* */
public OGAPI_CabinetButtons_Interface_v1.SetLights_callback SetLights;
public interface Initialize_callback extends Callback {
byte apply();
};
public interface ProcessCallbacks_callback extends Callback {
byte apply();
};
public interface Shutdown_callback extends Callback {
void apply();
};
public interface SetButtonCallback_callback extends Callback {
void apply(ButtonCallback cb, Pointer userData);
};
public interface SetLights_callback extends Callback {
void apply(int lights);
};
public OGAPI_CabinetButtons_Interface_v1() {
super();
}
protected List<? > getFieldOrder() {
return Arrays.asList("Initialize", "ProcessCallbacks", "Shutdown", "SetButtonCallback", "SetLights");
}
/**
* #param Initialize C type : Initialize_callback*<br>
* #param ProcessCallbacks C type : ProcessCallbacks_callback*<br>
* #param Shutdown C type : Shutdown_callback*<br>
* #param SetButtonCallback C type : SetButtonCallback_callback*<br>
* #param SetLights C type : SetLights_callback*
*/
public OGAPI_CabinetButtons_Interface_v1(OGAPI_CabinetButtons_Interface_v1.Initialize_callback Initialize, OGAPI_CabinetButtons_Interface_v1.ProcessCallbacks_callback ProcessCallbacks, OGAPI_CabinetButtons_Interface_v1.Shutdown_callback Shutdown, OGAPI_CabinetButtons_Interface_v1.SetButtonCallback_callback SetButtonCallback, OGAPI_CabinetButtons_Interface_v1.SetLights_callback SetLights) {
super();
this.Initialize = Initialize;
this.ProcessCallbacks = ProcessCallbacks;
this.Shutdown = Shutdown;
this.SetButtonCallback = SetButtonCallback;
this.SetLights = SetLights;
}
public OGAPI_CabinetButtons_Interface_v1(Pointer peer) {
super(peer);
read();
}
public static class ByReference extends OGAPI_CabinetButtons_Interface_v1 implements Structure.ByReference {
};
public static class ByValue extends OGAPI_CabinetButtons_Interface_v1 implements Structure.ByValue {
};
}
It seems that this is happening only when i make calls to the DLL from separate threads. Event Native.synchronizedLibrary doesn't help.
[UPDATE 2]
public class IKernel {
private static final Logger logger = LoggerFactory.getLogger(IKernel.class);
private static final String OGAPI_DLL = "ogapiDLL_Release";
private static final String ADMIN_V = "admin_v1";
private static final String LEGION_CORE_V = "legion_core_v1";
private static final String DEBUG_V = "debug_v1";
private static final String CABINET_V = "cabinet_buttons_v1";
private static Object HANDLE = new Object();
public static String CONTENT_ID = "999406";
public static Integer LOG_LEVEL = 3;
public static Integer CALLBACK_REFRESH_RATE = 2000;
protected static IKernel kernel;
private OGAPI_Admin_Interface_v1 admin;
private OGAPI_LegionCore_Interface_v1 core;
private OGAPI_CabinetButtons_Interface_v1 cabinet;
public static IKernel instance() {
if (kernel == null) {
synchronized (HANDLE) {
kernel = new IKernel(CONTENT_ID, LOG_LEVEL, CALLBACK_REFRESH_RATE);
}
}
return kernel;
}
protected IKernel(String contentId, int logLevel, int rate) {
initialize(contentId, logLevel);
registerCallbacksCycle();
}
private void initialize(String contentId, int logLevel) {
logger.info("OGAPI Initialization started ...");
OGAPILibrary ogapi = (OGAPILibrary) Native
.synchronizedLibrary((OGAPILibrary) Native.loadLibrary(
OGAPI_DLL, OGAPILibrary.class));
admin = ogapi.RequestAdminInterface(ADMIN_V);
OGAPI_Admin_Interface_v1.RequestInterface_callback request = admin.RequestInterface;
Pointer coreIntPointer = request.apply(LEGION_CORE_V);
core = new OGAPI_LegionCore_Interface_v1(coreIntPointer);
Pointer debugIntPointer = request.apply(DEBUG_V);
OGAPI_Debug_Interface_v1 debug = new OGAPI_Debug_Interface_v1(debugIntPointer);
if (contentId != null) {
debug.SetContentID.apply(contentId);
}
admin.SetLoggingCallback.apply(new LoggingCallback() {
public void apply(Pointer userData, String msg, int level) {
logger.info("Remote Logger: " + msg);
}
}, null);
admin.SetLoggingLevel.apply(logLevel);
Pointer cabinetIntPointer = request.apply(CABINET_V);
cabinet = new OGAPI_CabinetButtons_Interface_v1(cabinetIntPointer);
boolean success = admin.Initialize.apply() == 1
&& cabinet.Initialize.apply() == 1;
if (!success) {
throw new RuntimeException("ikernel initialization failed");
}
logger.info("OGAPI Initialization completed successfully ...");
}
private void registerCallbacksCycle() {
Thread buttonsCycle = new Thread() {
#Override
public void run() {
while (true) {
try {
logger.info("Refreshing Callbacks ...");
try {
logger.info(" ");
byte b = cabinet.ProcessCallbacks.apply();
logger.info("result " + b);
} catch (Error e) {
e.printStackTrace();
}
logger.info("Refreshing Callbacks END ...");
sleep(CALLBACK_REFRESH_RATE);
logger.info("Refreshing Callbacks AFTER SLEEP ...");
} catch (Exception e1) {
logger.info("Refreshing Callbacks EXCEPTION ");
e1.printStackTrace();
}
}
}
};
buttonsCycle.start();
logger.info("Callback Cycles started successfully ... ");
}
}
Header for the CabinetInterface method:
typedef void (__cdecl *ButtonCallback )( void* userData, int buttonIndex, int pressed );
typedef struct tag_OGAPI_CabinetButtons_Interface_v1 {
bool (__cdecl *Initialize)();
bool (__cdecl *ProcessCallbacks)();
void (__cdecl *Shutdown)();
void (__cdecl *SetButtonCallback)( ButtonCallback cb, void* userData );
void (__cdecl *SetLights)( unsigned int lights );
} OGAPI_CabinetButtons_Interface_v1;
Task definition: I need to test custom concurrent collection or some container which manipulates with collections in concurrent environment. More precisely - I've read-API and write-API. I should test if there is any scenarios where I can get inconsistent data.
Problem: All concurrent test frameworks (like MultiThreadedTC, look at MultiThreadedTc section of my question) just provides you an ability to control the asynchronous code execution sequence. I mean you should suppose a critical scenarios by your own.
Broad question: Is there frameworks that can take annotations like #SharedResource, #readAPI, #writeAPI and check if your data will always be consistent? Is that impossible or I just leak a startup idea?
Annotation: If there is no such framework, but you find the idea attractive, you are welcome to contact me or propose your ideas.
Narrow question: I'm new in concurrency. So can you suggest which scenarios should I test in the code below? (look at PeerContainer class)
PeerContainer:
public class PeersContainer {
public class DaemonThreadFactory implements ThreadFactory {
private int counter = 1;
private final String prefix = "Daemon";
#Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, prefix + "-" + counter);
thread.setDaemon(true);
counter++;
return thread;
}
}
private static class CacheCleaner implements Runnable {
private final Cache<Long, BlockingDeque<Peer>> cache;
public CacheCleaner(Cache<Long, BlockingDeque<Peer>> cache) {
this.cache = cache;
Thread.currentThread().setDaemon(true);
}
#Override
public void run() {
cache.cleanUp();
}
}
private final static int MAX_CACHE_SIZE = 100;
private final static int STRIPES_AMOUNT = 10;
private final static int PEER_ACCESS_TIMEOUT_MIN = 30;
private final static int CACHE_CLEAN_FREQUENCY_MIN = 1;
private final static PeersContainer INSTANCE;
private final Cache<Long, BlockingDeque<Peer>> peers = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.removalListener(new RemovalListener<Long, BlockingDeque<Peer>>() {
public void onRemoval(RemovalNotification<Long, BlockingDeque<Peer>> removal) {
if (removal.getCause() == RemovalCause.EXPIRED) {
for (Peer peer : removal.getValue()) {
peer.sendLogoutResponse(peer);
}
}
}
})
.build();
private final Striped<Lock> stripes = Striped.lock(STRIPES_AMOUNT);
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new DaemonThreadFactory());
private PeersContainer() {
scheduledExecutorService.schedule(new CacheCleaner(peers), CACHE_CLEAN_FREQUENCY_MIN, TimeUnit.MINUTES);
}
static {
INSTANCE = new PeersContainer();
}
public static PeersContainer getInstance() {
return INSTANCE;
}
private final Cache<Long, UserAuthorities> authToRestore = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.build();
public Collection<Peer> getPeers(long sessionId) {
return Collections.unmodifiableCollection(peers.getIfPresent(sessionId));
}
public Collection<Peer> getAllPeers() {
BlockingDeque<Peer> result = new LinkedBlockingDeque<Peer>();
for (BlockingDeque<Peer> deque : peers.asMap().values()) {
result.addAll(deque);
}
return Collections.unmodifiableCollection(result);
}
public boolean addPeer(Peer peer) {
long key = peer.getSessionId();
Lock lock = stripes.get(key);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(key);
if (userPeers == null) {
userPeers = new LinkedBlockingDeque<Peer>();
peers.put(key, userPeers);
}
UserAuthorities authorities = restoreSession(key);
if (authorities != null) {
peer.setAuthorities(authorities);
}
return userPeers.offer(peer);
} finally {
lock.unlock();
}
}
public void removePeer(Peer peer) {
long sessionId = peer.getSessionId();
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(sessionId);
if (userPeers != null && !userPeers.isEmpty()) {
UserAuthorities authorities = userPeers.getFirst().getAuthorities();
authToRestore.put(sessionId, authorities);
userPeers.remove(peer);
}
} finally {
lock.unlock();
}
}
void removePeers(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
peers.invalidate(sessionId);
authToRestore.invalidate(sessionId);
} finally {
lock.unlock();
}
}
private UserAuthorities restoreSession(long sessionId) {
BlockingDeque<Peer> activePeers = peers.getIfPresent(sessionId);
return (activePeers != null && !activePeers.isEmpty()) ? activePeers.getFirst().getAuthorities() : authToRestore.getIfPresent(sessionId);
}
public void resetAccessedTimeout(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> deque = peers.getIfPresent(sessionId);
peers.invalidate(sessionId);
peers.put(sessionId, deque);
} finally {
lock.unlock();
}
}
}
MultiThreadedTC test case sample: [optional section of question]
public class ProducerConsumerTest extends MultithreadedTestCase {
private LinkedTransferQueue<String> queue;
#Override
public void initialize() {
super.initialize();
queue = new LinkedTransferQueue<String>();
}
public void thread1() throws InterruptedException {
String ret = queue.take();
}
public void thread2() throws InterruptedException {
waitForTick(1);
String ret = queue.take();
}
public void thread3() {
waitForTick(1);
waitForTick(2);
queue.put("Event 1");
queue.put("Event 2");
}
#Override
public void finish() {
super.finish();
assertEquals(true, queue.size() == 0);
}
}
Sounds like a job for static analysis, not testing, unless you have time to run multiple trillions of test cases. You pretty much can't test multithreaded behaviour - test behaviour in a single thread, then prove the abscence of threading bugs.
Try:
http://www.contemplateltd.com/threadsafe
http://checkthread.org/