I'm doing the multiple transaction in thread using spring transaction template, as per the spring transaction template it's thread-safe, however it's not releasing the connection for the long time. I'm using c3p0 connection pool configured by tomcat jndi context.
Here is the code snippet:
for (SearchProcessorType[] processorTypes : getProcessorChainTypes()) {
if (processorTypes.length > 1) {
ExecutorService doerService = Executors.newFixedThreadPool(processorTypes.length);
for (final SearchProcessorType processorType : processorTypes) {
if(searchData.getException() != null) {
if(searchData.getException() instanceof SearchException) {
throw (SearchException)searchData.getException();
}
throw new RuntimeException(searchData.getException().getMessage());
}
doerService.submit(new Runnable() {
#Override
public void run() {
try {
if (!searchData.isSkipType(processorType)) {
final SearchProcessor processsor = SearchProcessor.get(processorType);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
if (processsor.isTransactional()) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
processsor.process(searchData, (SearchResponse<Object>) response);
}
});
} else {
processsor.process(searchData, (SearchResponse<Object>) response);
}
}
} catch (Exception ex) {
searchData.setException(ex);
}
}
});
}
doerService.shutdown();
while (!doerService.isTerminated()) {
// Wait for processors to get completed
}
if(searchData.getException() != null) {
if (searchData.getException() instanceof SearchException) {
throw (SearchException) searchData.getException();
} else {
throw new RuntimeException(searchData.getException());
}
}
} else if (!searchData.isSkipType(processorTypes[0])) {
SearchProcessor.get(processorTypes[0]).process(searchData, (SearchResponse<Object>) response);
}
}
Related
The fact is that I need to simultaneously pull in data from the local database, from the server, while checking the connection to the Internet.
Without checking the internet is easy. But when I turn off mobile data, crashes.
I do not understand how to combine and decided to do this:
private void getCategories() {
composite.add(getDataFromLocal(context)
.observeOn(AndroidSchedulers.mainThread()).flatMap(new Function<PromoFilterResponse, ObservableSource<List<FilterCategory>>>() {
#Override
public ObservableSource<List<FilterCategory>> apply(PromoFilterResponse promoFilterResponse) throws Exception {
if (promoFilterResponse != null) {
PreferencesHelper.putObject(context, PreferencesKey.FILTER_CATEGORIES_KEY, promoFilterResponse);
return combineDuplicatedCategories(promoFilterResponse);
} else {
return Observable.empty();
}
}
})
.subscribe(new Consumer<List<FilterCategory>>() {
#Override
public void accept(List<FilterCategory> categories) throws Exception {
if (mView != null) {
mView.hideConnectingProgress();
if (categories != null && categories.size() > 0) {
mView.onCategoriesReceived(categories);
}
}
}
}));
composite.add(InternetUtil.isConnectionAvailable().subscribe(isOnline -> {
if (isOnline) {
composite.add(
getDataFromServer(context)
.flatMap(new Function<PromoFilterResponse, ObservableSource<List<FilterCategory>>>() {
#Override
public ObservableSource<List<FilterCategory>> apply(PromoFilterResponse promoFilterResponse) throws Exception {
if (promoFilterResponse != null) {
PreferencesHelper.putObject(context, PreferencesKey.FILTER_CATEGORIES_KEY, promoFilterResponse);
return combineDuplicatedCategories(promoFilterResponse);
} else {
return Observable.empty();
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(categories -> {
if (mView != null) {
mView.hideConnectingProgress();
if (categories != null && categories.size() > 0) {
mView.onCategoriesReceived(categories);
} else {
mView.onCategoriesReceivingFailure(errorMessage[0]);
}
}
}, throwable -> {
if (mView != null) {
if (throwable instanceof HttpException) {
ResponseBody body = ((HttpException) throwable).response().errorBody();
if (body != null) {
errorMessage[0] = body.string();
}
}
mView.hideConnectingProgress();
mView.onCategoriesReceivingFailure(errorMessage[0]);
}
}));
} else {
mView.hideConnectingProgress();
mView.showOfflineMessage();
}
}));
}
private Single<Boolean> checkNetwork(Context context) {
return InternetUtil.isConnectionAvailable()
.subscribeOn(Schedulers.io())
.doOnSuccess(new Consumer<Boolean>() {
#Override
public void accept(Boolean aBoolean) throws Exception {
getDataFromServer(context);
}
});
}
private Observable<PromoFilterResponse> getDataFromServer(Context context) {
return RetrofitHelper.getApiService()
.getFilterCategories(Constants.PROMO_FILTER_CATEGORIES_URL)
.subscribeOn(Schedulers.io())
.retryWhen(BaseDataManager.isAuthException())
.publish(networkResponse -> Observable.merge(networkResponse, getDataFromLocal(context).takeUntil(networkResponse)))
.doOnNext(new Consumer<PromoFilterResponse>() {
#Override
public void accept(PromoFilterResponse promoFilterResponse) throws Exception {
PreferencesHelper.putObject(context, PreferencesKey.FILTER_CATEGORIES_KEY, promoFilterResponse);
}
})
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
LogUtil.e("ERROR", throwable.getMessage());
}
});
}
private Observable<PromoFilterResponse> getDataFromLocal(Context context) {
PromoFilterResponse response = PreferencesHelper.getObject(context, PreferencesKey.FILTER_CATEGORIES_KEY, PromoFilterResponse.class);
if (response != null) {
return Observable.just(response)
.subscribeOn(Schedulers.io());
} else {
return Observable.empty();
}
}
As you can see, connect the local database separately, simultaneously check the Internet and upload data from the server.
But it seems to me not quite right. Moreover, the subscriber is duplicated and so on.
I saw a lot of tutorials, where the combination of the local database with the API is described, but I did not see it at the same time processing the connection error with the Internet.
I think many people faced such a problem and how did you solve it?
Suppose You have two Obsevable: one from server and another from database
You can merge them into one stream like below:
public Observable<Joke> getAllJokes() {
Observable<Joke> remote = mRepository.getAllJokes()
.subscribeOn(Schedulers.io());
Observable<Joke> local = mRepository.getAllJokes().subscribeOn(Schedulers.io());
return Observable.mergeDelayError(local, remote).filter(joke -> joke != null);
}
Im' not android developer, but in my mind methods return types should be something like this:
//just for demonstration
static boolean isOnline = false;
static class NoInternet extends RuntimeException {
}
private static Completable ensureOnline() {
if (isOnline)
return Completable.complete();
else
return Completable.error(new NoInternet());
}
private static Single<String> getDataFromServer() {
return Single.just("From server");
}
private static Maybe<String> getDataFromLocal() {
return Maybe.just("From local");//or Maybe.never()
}
We can run all in parallel with Observable.merge. But what if error NoIternet happens? Merged observable will fail. We can use materialisation - transform all emission and errors to onNext value.
private static void loadData() {
Observable<Notification<String>> fromServer = ensureOnline().andThen(getDataFromServer()).toObservable().materialize();
Observable<Notification<String>> fromLocaldb = getDataFromLocal().toObservable().materialize();
Observable.merge(fromLocaldb, fromServer)
.subscribe(notification -> {
if (notification.isOnNext()) {
//calls one or two times(db+server || db || server)
//show data in ui
} else if (notification.isOnError()) {
if (notification.getError() instanceof NoInternet) {
//show no internet
} else {
//show another error
}
} else if (notification.isOnComplete()){
//hide progress bar
}
});
}
I'm trying to use AsyncTransaction for inserting some object, but for the moment it is a failed...
I tried to debug, but without success either ...
See my code :
realm.executeTransactionAsync(
new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
Log.i("Insert", "Insert start");
realm.insert(character);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.i("Insert", "Insert complete");
finish();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.i("Insert","Error " + error.getMessage());
}
});
When i debug, i see that I didn't go on any callback of the async-transaction, there is no log, there is nothing that can help me.
Thank in advance,
EDIT :
public RealmAsyncTask executeTransactionAsync(final Transaction transaction,
#Nullable final Realm.Transaction.OnSuccess onSuccess,
#Nullable final Realm.Transaction.OnError onError) {
checkIfValid();
//noinspection ConstantConditions
if (transaction == null) {
throw new IllegalArgumentException("Transaction should not be null");
}
// Avoid to call canDeliverNotification() in bg thread.
final boolean canDeliverNotification = sharedRealm.capabilities.canDeliverNotification();
// If the user provided a Callback then we have to make sure the current Realm has an events looper to deliver
// the results.
if ((onSuccess != null || onError != null)) {
sharedRealm.capabilities.checkCanDeliverNotification("Callback cannot be delivered on current thread.");
}
// We need to use the same configuration to open a background SharedRealm (i.e Realm)
// to perform the transaction
final RealmConfiguration realmConfiguration = getConfiguration();
// We need to deliver the callback even if the Realm is closed. So acquire a reference to the notifier here.
final RealmNotifier realmNotifier = sharedRealm.realmNotifier;
final Future<?> pendingTransaction = asyncTaskExecutor.submitTransaction(new Runnable() {
#Override
public void run() {
if (Thread.currentThread().isInterrupted()) {
return;
}
SharedRealm.VersionID versionID = null;
Throwable exception = null;
final Realm bgRealm = Realm.getInstance(realmConfiguration);
bgRealm.beginTransaction();
// NOTHING IS DONE AFTER IS POINT .....
try {
transaction.execute(bgRealm);
if (Thread.currentThread().isInterrupted()) {
return;
}
bgRealm.commitTransaction();
// The bgRealm needs to be closed before post event to caller's handler to avoid concurrency
// problem. This is currently guaranteed by posting callbacks later below.
versionID = bgRealm.sharedRealm.getVersionID();
} catch (final Throwable e) {
exception = e;
} finally {
try {
if (bgRealm.isInTransaction()) {
bgRealm.cancelTransaction();
}
} finally {
bgRealm.close();
}
}
final Throwable backgroundException = exception;
final SharedRealm.VersionID backgroundVersionID = versionID;
// Cannot be interrupted anymore.
if (canDeliverNotification) {
if (backgroundVersionID != null && onSuccess != null) {
realmNotifier.post(new Runnable() {
#Override
public void run() {
if (isClosed()) {
// The caller Realm is closed. Just call the onSuccess. Since the new created Realm
// cannot be behind the background one.
onSuccess.onSuccess();
return;
}
if (sharedRealm.getVersionID().compareTo(backgroundVersionID) < 0) {
sharedRealm.realmNotifier.addTransactionCallback(new Runnable() {
#Override
public void run() {
onSuccess.onSuccess();
}
});
} else {
onSuccess.onSuccess();
}
}
});
} else if (backgroundException != null) {
realmNotifier.post(new Runnable() {
#Override
public void run() {
if (onError != null) {
onError.onError(backgroundException);
} else {
throw new RealmException("Async transaction failed", backgroundException);
}
}
});
}
} else {
if (backgroundException != null) {
// FIXME: ThreadPoolExecutor will never throw the exception in the background.
// We need a redesign of the async transaction API.
// Throw in the worker thread since the caller thread cannot get notifications.
throw new RealmException("Async transaction failed", backgroundException);
}
}
}
});
return new RealmAsyncTaskImpl(pendingTransaction, asyncTaskExecutor);
}
I found the trick.
In my constructor, i add some RealmObject to a another, that create a error
(Can't not write on a non write transaction)
The second point, was i used beginTransaction() on the parent, but it block on the other part for asynctransaction
I change my code for using the RealmRecyclerView on the firstPart and i didn't have the problem anymore
Thanks
The Future object is never able to obtain access to the synchronized block of code so it never finishes and never returns. There isn't anything accessing writeOut() besides the thread so I am not sure why it is block.
public class FileManager {
private void writeOut() throws BusinessException {
if(f.exists() && f.canWrite()) {
synchronized (this) {
try (FileWriter fileWriter = new FileWriter(f)) {
String endLine = "\n";
fileWriter.write("");
for (Entry entry : directory) {
fileWriter.append(entry.getLastName());
fileWriter.append(CSV_DELIMITER);
fileWriter.append(entry.getFirstName());
fileWriter.append(CSV_DELIMITER);
fileWriter.append(entry.getPhoneNumber());
fileWriter.append(CSV_DELIMITER);
fileWriter.append(entry.getAddress());
fileWriter.append(endLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
System.out.println(f.getAbsolutePath() + " doesn't exist or can't be written to");
}
}
public void addEntry(Entry entryModel, boolean notify) {
assert entryModel != null;
synchronized (this) {
AddEntryAction addAction = new AddEntryAction(entryModel, notify);
AddEntryActor actor = new AddEntryActor(addAction);
actor.execute();
deleteActor(actor);
}
}
Here is the method that is called by execute():
private void executeAsynchronously() {
Callable<String> asyncTask = () -> {
try {
ServiceFw.fileManager.writeBack();
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BusinessException e) {
notifyFailure();
}
return "write back operation";
};
future = executor.submit(asyncTask);
Runnable poll = () -> {
if(future!=null) {
notifySuccess();
} else {
notifyFailure();
}
};
poll.run();
}
I am currently developing an Android app for existing IOS app, so using Session object to store some data on Parse is crucial for me.
As for creating and uploading sessions to Parse I have no problems.
public static void syncUser() {
ParseUser.getCurrentUser().fetchInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
if (e != null) {
Log.d("", "SYNC ERROR");
e.printStackTrace();
}
if (object != null) {
syncSessions();
}
}
});
}
private static void syncSessions() {
ParseQuery<ParseSession> query = ParseQuery.getQuery(ParseSession.class);
query.fromLocalDatastore();
query.findInBackground(new FindCallback<ParseSession>() {
#Override
public void done(List<ParseSession> objects, ParseException e) {
for (ParseSession session : objects) {
fetchSession(session, null);
}
}
});
}
public static void fetchSession(final ParseSession session, final OnResultCallback cb) {
session.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e != null) {
if (cb != null)
cb.onResult(false);
e.printStackTrace();
} else {
if (cb != null)
cb.onResult(true);
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
relation.add(session);
syncUser();
}
}
});
}
public static void addNewSession(Date date, String link, int successValue) {
final ParseSession session = new ParseSession();
session.put("date", date);
session.put("link", link);
session.put("successValue", successValue);
session.pinInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e != null)
e.printStackTrace();
else {
fetchSession(session, new ParseManager.OnResultCallback() {
#Override
public void onResult(boolean success) {
if (success) {
try {
session.unpin();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
});
}
}
});
}
public interface OnResultCallback {
void onResult(boolean success);
}
For creating new Session with my parameters and uploading it I use addNewSession() method, and it displays in Parse dashboard correctly, they have columns for my fields (date, link, successValue) and are stored as the default Session object.
But when I try to load them from Parse to my client, it doesn't work. I load them with this method:
public static void getSessions(final OnResultCallback cb) {
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
ParseQuery<ParseSession> query = relation.getQuery();
query.setLimit(1000);
query.findInBackground(new FindCallback<ParseSession>() {
#Override
public void done(List<ParseSession> objects, ParseException e) {
if (e != null) {
e.printStackTrace();
if (cb != null)
cb.onResult(false);
} else {
cb.onResult(true);
}
//if (objects != null)
//USE SESSIONS
}
});
}
I catch an exception:
W/System.err: com.parse.ParseRequest$ParseRequestException: wrong type of relation. Expecting: , but received: _Session
W/System.err: at com.parse.ParseRequest.newPermanentException(ParseRequest.java:270)
Quite similar code on IOS works fine with "sessions" relation. What mistakes have I done?
UPD. I have noticed that I get this exception only when I have allready send some custom created Session to Parse with addNewSession() and then trying to get them using getSessions(). Maybe creating and sending the Session is the problem?
I found the sollution. After I add my newly created Session to User relations with
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
relation.add(session);
I tried to save them with fetchInBackground, which was a mistake. I changed syncUser() to ParseUser.getCurrentUser().saveInBackground() and everything worked.
I have this code that is executed when a file is modified
public class WatchQueueReaderTask<Void> extends Task {
...
protected Object call() throws Exception {
try {
// get the first event before looping
WatchKey key = myWatcher.take();
while (key != null) {
// we have a polled event, now we traverse it and
// receive all the states from it
for (WatchEvent event : key.pollEvents()) {
WatchEvent.Kind eventType = event.kind();
if (eventType == OVERFLOW) {
continue;
}
process(event);
if (isCancelled()) {
System.out.println("WatchQueueReaderTask::call isCancelled");
return null;
}
}
key.reset();
key = myWatcher.take();
}
} catch (InterruptedException e) {
ArrayList<WatchFileItem> auxList = threadFileToWatch;
for (WatchFileItem obj : auxList) {
if (obj.file != null) {
try {
obj.file.close();
} catch (IOException ex) {
System.out.println("errore in file close");
Logger.getLogger(WatchQueueReaderTask.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
//e.printStackTrace();
}
System.out.println("Stopping thread");
return null;
}
...
private synchronized void process(WatchEvent evt) {
... }
}
I call it using:
ThreadFactory processingThreadFactoryForRealtime = null;
ExecutorService processingThreadFactoryForRealtimeExecutor = null;
WatchQueueReaderTask mywatchQueueReader = null;
processingThreadFactoryForRealtime = new ThreadFactory() {
#Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("FileWatcherTask");
t.setDaemon(true);
return t;
}
};
processingThreadFactoryForRealtimeExecutor = Executors.newSingleThreadExecutor(processingThreadFactoryForRealtime);
mywatchQueueReader = new WatchQueueReaderTask(realtimePath, watcherServiceForRealtime, fileToWatchForRealtime);
processingThreadFactoryForRealtimeExecutor.execute(mywatchQueueReader);
The problem is that execute simultaneously multiple methods process() without waiting for the previous is completed.
Where am I wrong?