activity retry job after 503 error - java

I have many service tasks that call rest services and sometimes the service is not available. I want in my JavaDelegate to be able to infinitely retry the job:
#Override
public void execute(DelegateExecution execution)
{
try
{
//call_rest_service
}
catch (Exception503 error)
{
CommandContext commandContext = Context.getCommandContext();
JobEntity jobEntity = commandContext.getJobEntityManager().findById(job.getId());
jobEntity.setRetries(10);
//then throw original error
}
}
But this does not seem to work!

I think this is a SLOPPY way of doing things, but if you are sure that is what you want to do, I suggest you do something like this :
#Override
public void execute(DelegateExecution execution)
{
int retryMax = 10, retryCount =0;
while (retryCount++ < retryMax){
try
{
//call_rest_service
}
catch (Exception503 error)
{
// sleep to not DDoS the server
Thread.sleep(500);
}
}
}

Related

How to programatically (java) prevent specific errors messages to be sent to Sentry

How to programatically (java) prevent specific errors messages to be sent to Sentry? I want, for example, do not send to Sentry errors with the word "any_example_word". It's important to know that filtering by error message is not enabled in the User Interface.
I'm using Sentry 1.7.23, but all examples I can find use latest version (4.*), which are tottaly different. They use classes and methods that do not exist in this old version.
I don't know if this is relevant, but my application runs over thorntail and it uses jdk 8.
Edit:
I'm trying to do this:
#WebListener
public class MyContextListener implements ServletContextListener {
private static SentryClient sentryClient = SentryClientFactory.sentryClient();
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
Sentry.init();
String testStrings = "ipsis litteris;some_error_message";
String[] messagesToIgnore = StringUtils.split(testStrings, ';');
sentryClient.addShouldSendEventCallback(new ShouldSendEventCallback() {
#Override
public boolean shouldSend(Event event) {
for (Map.Entry<String, SentryInterface> interfaceEntry : event.getSentryInterfaces().entrySet()) {
if (interfaceEntry.getValue() instanceof ExceptionInterface) {
ExceptionInterface i = (ExceptionInterface) interfaceEntry.getValue();
for (SentryException sentryException : i.getExceptions()) {
for (String msgToIgnore : messagesToIgnore) {
if (StringUtils.contains(sentryException.getExceptionMessage(), msgToIgnore)) {
return false;
}
}
}
}
}
return true;
}
});
Sentry.setStoredClient(sentryClient);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
Question 1) Is this the correct place to initialize Sentry?
Question 2) Why ShouldSendEventCallback is lost? Looking at
io.sentry.SentryClient:
public void sendEvent(Event event) {
for (ShouldSendEventCallback shouldSendEventCallback : shouldSendEventCallbacks) {
if (!shouldSendEventCallback.shouldSend(event)) {
logger.trace("Not sending Event because of ShouldSendEventCallback: {}", shouldSendEventCallback);
return;
}
}
try {
connection.send(event);
} catch (LockedDownException | TooManyRequestsException e) {
logger.debug("Dropping an Event due to lockdown: " + event);
} catch (Exception e) {
logger.error("An exception occurred while sending the event to Sentry.", e);
} finally {
getContext().setLastEventId(event.getId());
}
}
In some point during app execution, sentryClient is reinitialized and shouldSendEventCallbacks becomes empty, what causes my messages not being filtered.
So I get back to question 1, since apparently sentry configuration is not being persistent.

Handle multiple user at the same time with TelegramLongPollingBot and thread

I'm building my first telegram bot. It send one message every 5 seconds to the user.
While it sends it to one user it cannot receive update from other chat.
public void foo(msg, Update update){
msg.setChatId(update.getMessage().getChatId());
for (int i = 1; i < links.size(); i++){
msg.setText(links.get(i));
execute(msg);
}
Thread.sleep(wait * 1000);
}
How can I use Thread? I've tried creating multiple thread here
public static void bot(){
ApiContextInitializer.init();
TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
try {
telegramBotsApi.registerBot(new myBot());
} catch (TelegramApiException e) {
e.printStackTrace();
}
But he tries to create multiple bots and fails. Same if this is the runnable function:
How can I do it? I'm Stuck. I cannot create this function in different thread
public void onUpdateReceived(Update update) {
leggi(new SendMessage(), update.getMessage().getText(), update);
//.setChatId(update.getMessage().getChatId())
public void leggi(SendMessage msg, String command, Update update){
if(command.equals("test") {
foo( msg, update);
}
Here the full code... https://github.com/siamoInPochi/Ilsottomarinobot/tree/prova/src/main/java/Ilsottomarinobot
If you spawn a thread for every bot user who wants to receive messages, you will quickly be out of computer's resources in case of high number of users. So I think threads is not a good idea for your task.
In my mind more natural approach is the following:
Find a library with an HTTP server.
Switch from GetUpdates to webhooks.
Schedule send-message-to-user-every-5-seconds tasks to server's event loop.
Send messages every 5 seconds asynchronously.
You can make it with this library https://github.com/pengrad/java-telegram-bot-api
<dependency>
<groupId>com.github.pengrad</groupId>
<artifactId>java-telegram-bot-api</artifactId>
<version>4.2.0</version>
</dependency>
Subscribe to new updates via bot.setUpdatesListener
Send messages via bot.execute(new SendMessage(chatId, link), callback)
Full working example:
static String[] links = {"1", "2", "3"};
static Callback emptyCallback = new Callback() {
#Override
public void onResponse(BaseRequest request, BaseResponse response) {
}
#Override
public void onFailure(BaseRequest request, IOException e) {
e.printStackTrace();
}
};
static void foo(TelegramBot bot, Update update) {
Message message = update.message();
if (message == null) return;
Long chatId = message.chat().id();
for (String link : links) {
bot.execute(new SendMessage(chatId, link), emptyCallback);
}
}
public static void main(String[] args) {
TelegramBot bot = new TelegramBot(TOKEN);
bot.setUpdatesListener(updates -> {
for (Update update : updates) {
foo(bot, update);
}
return UpdatesListener.CONFIRMED_UPDATES_ALL;
});
}

Execute CompletableFuture which depends on an other task

I wrote a Play framework web application and I have the following problem. I have some DAOs that access the database, and sometimes one request to the database relies on information from another request.
Here is one of the problems: I execute the getLicenseByKey method in an asynchronous way and get the result. Now I can execute version_dao.getVersionUntilX() with the result of the license_dao request. The problem here is that the .get() function of CompletableFuture is executed on the HttpExecutionContext (blocking one of the HTTP threads of Akka (Play framework)), and if this database request takes a long time, the thread is blocked.
So how can I execute asynchronously the license_dao.getLicenseByKey() and then, with the result of this method, execute the version_dao.getVersionUntilX() method also asynchronously? And if both are finished, I want to return the HTTP result from the HttpExecutionContext of Play.
public CompletionStage<Result> showDownloadScreen(String license_key) {
return license_dao.getLicenseByKey(license_key).thenApplyAsync(license -> {
try {
if(license!=null) {
if(license.isRegistered()) {
return ok(views.html.download.render(license,
version_dao.getVersionUntilX(license.getUpdates_until())
.toCompletableFuture().get()));
}else {
return redirect(routes.LicenseActivationController.showActivationScreen(license_key));
}
}else {
return redirect(routes.IndexController.index("Insert Key can not be found!"));
}
} catch (InterruptedException | ExecutionException e) {
return redirect(routes.IndexController.index("Error while checking the Verison"));
}
}, httpExecutionContext.current());
}
Use thenComposeAsync() instead of thenApplyAsync(), and have your inner lambda also return a CompletableFuture:
public CompletionStage<Result> showDownloadScreen(String license_key) {
return license_dao.getLicenseByKey(license_key).thenComposeAsync(license -> {
try {
if(license!=null) {
if(license.isRegistered()) {
return version_dao.getVersionUntilX(license.getUpdates_until())
.toCompletableFuture()
.thenApply(v -> ok(views.html.download.render(license, v)));
} else {
return CompletableFuture.completedFuture(redirect(routes.LicenseActivationController.showActivationScreen(license_key)));
}
} else {
return CompletableFuture.completedFuture(redirect(routes.IndexController.index("Insert Key can not be found!")));
}
} catch (InterruptedException | ExecutionException e) {
return CompletableFuture.completedFuture(redirect(routes.IndexController.index("Error while checking the Verison")));
}
}, httpExecutionContext.current());
}
Since that lambda is quite complex, it would also be worth extracting it to a separate method, and do some more cleanup.

retrofit2.HttpException thrown even though it was handled in onError

I'm always getting an unhandled exception when google+ responses with error json
retrofit2.HttpException: HTTP 404
at retrofit2.RxJavaCallAdapterFactory$SimpleCallAdapter$1.call(RxJavaCallAdapterFactory.java:159)
at retrofit2.RxJavaCallAdapterFactory$SimpleCallAdapter$1.call(RxJavaCallAdapterFactory.java:154)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at retrofit2.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:109)
at retrofit2.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:88)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
....
In that code:
Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> strSub) {
// Getting ID
strSub.onNext(AccountUtils.getAccountId(appContext));
strSub.onCompleted();})
.subscribeOn(Schedulers.io())
// Get Google+ Image through Retrofit2
.flatMap(str -> createGPlusUserObservable(str, AccountUtils.ANDROID_API_KEY))
.map(this::setprofileImage) // I don't see Timber.d message inside that method!
.compose(binder)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
In createGPlusUserObservable I use Retrofit 2 to get google+ image
private Observable<GPlusUser> createGPlusUserObservable(String userId, String apiKey) {
//try {
GoogleApiService service = ServiceFactory.getInstance().createJsonRetrofitService(
GoogleApiService.class,
GoogleApiService.SERVICE_ENDPOINT
);
Observable<GPlusUser> result = service.getGPlusUserInfo(userId, apiKey);
Timber.d("Here1!"); // I see that in console!
return result; // It always returns result!
/*} catch (Throwable e) { - it doesn't catch anything!
Timber.d("Here!");
}*/
}
And subscriber is:
new Subscriber<GPlusUser>() {
#Override
public void onCompleted() {
Timber.d("GPlusUserSubscriber ON COMPLETED");
}
#Override
public void onError(Throwable e) {
if (e instanceof HttpException) {
Timber.d("RETROFIT!"); // I see that in console!
}
}
#Override
public void onNext(GPlusUser gPlusUser) {
setupAccountBox();
}
};
UPDATE: setprofileImage method
private GPlusUser setprofileImage(GPlusUser gPlusUser) {
Timber.d("FOUR"); // As I've said, it doesn't appear in console
AccountUtils.setProfileImage(appContext, gPlusUser.image.url);
Timber.d("Setting profile image: %s", gPlusUser.image.url);
return gPlusUser;
}
So the question is - why I'm getting unhandled exception if I handle it in subscriber's onError(Throwable e)
Thanks!
I think it is because error happens in retrofit factory logic, while converting from pure html string to my GPlusUser class.
I've eliminated that annoying exception in console log by working with pure html through Observable<Response<ResponseBody>> response and it's response.isSuccess()

How to send custom graph data to MCStats every hour?

I have been working on a plugin and have gotten some really interesting data with it, I am trying to add a custom graph and have succeeded on getting the graph to appear with the name I set in code on MCStats.
My plugin is here and recreates the Dense Ores Mod.
I would like to send block mined data on an hourly basis. This is what I have in my onEnable so far:
try {
Metrics metrics = new Metrics(this);
Graph blocksMinedGraph = metrics.createGraph("Extra items from blocks");
blocksMinedGraph.addPlotter(new Metrics.Plotter("Coal Ore") {
#Override
public int getValue() {
return coalMined;
}
});
blocksMinedGraph.addPlotter(new Metrics.Plotter("Iron Ore") {
#Override
public int getValue() {
return ironMined;
}
});
metrics.start();
} catch (IOException e) {
getLogger().info(ANSI_RED + "Metrics have been unable to load for: DenseOres" + ANSI_RESET);
}
This has successfully created a new graph on my MCStats page called 'Extra items from blocks' although I have been unable to populate it thus far. I have tried but cannot work out how to send the data.
Connected to this question, when sending the data, will I have to keep a count of the values in a file somewhere so they persist between reloads and server restarts?
I appear to have solved it by placing the blocksMinedGraph.addPlotter(...) parts in an async repeating task.
Here is the code with the repeating task in place, the graphs on MCStats take forever to update though.
try {
Metrics metrics = new Metrics(this);
if (!metrics.isOptOut()) {
final Graph blocksMinedGraph = metrics.createGraph("Extra items from blocks (Since v2.3)");
Bukkit.getScheduler().runTaskTimerAsynchronously(this, new Runnable() {
public void run() {
getLogger().info("Graph data sent");
blocksMinedGraph.addPlotter(new Metrics.Plotter("Coal Ore") {
#Override
public int getValue() {
return coalMined;
}
});
blocksMinedGraph.addPlotter(new Metrics.Plotter("Iron Ore") {
#Override
public int getValue() {
return ironMined;
}
});
}
}, DELAY, INCREMENT);
getLogger().info("Metrics started");
metrics.start();
}
} catch (IOException e) {
getLogger().info(ANSI_RED + "Metrics have been unable to load for: DenseOres" + ANSI_RESET);
}

Categories