How to persist JobDataMap changes from remote client - quartz-scheduler

I'm working on a basic web client for Quartz.NET that among other things supports the modification of a job's JobDataMap at runtime.
My job is decorated with the following attributes which I believe is all that is necessary to make the job stateful:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class SampleTenantJob : IJob { ... }
At runtime, I execute the following code but the JobDataMap is not persisted:
public void UpdateJobProperties(string jobName, string groupName, IDictionary<string, object> properties)
{
IJobDetail job;
if (!TryGetJob(jobName, groupName, out job))
return;
foreach (var property in properties)
{
if (job.JobDataMap.ContainsKey(property.Key) && property.Value != null)
{
job.JobDataMap.Put(property.Key, property.Value);
}
}
}
I thought initially this was because I was using the XMLSchedulingDataProcessorPlugin for jobs but I've tried both the in memory (RAMStore) and AdoJobStore and still can not persist JobDataMap changes made by the remote client.

PersistJobDataAfterExecution (as the name implies) only applies when the job has finished executing, so the following job will track the number of times it is executed
[PersistJobDataAfterExecution]
public class HelloJob : IJob
{
public void Execute(IJobExecutionContext context)
{
int count = (int?) context.MergedJobDataMap["Count"] ?? 1;
Console.WriteLine("hello " + count.ToString() );
context.JobDetail.JobDataMap.Put("Count" , ++count);
}
}
Without the PersistJobDataAfterExecution attributes, count is always the same.
Since you aren't running the job, this doesn't help you, and I think you have to delete and re-create the job with the new JobDataMap.
Of course, you aren't forced to use JobDataMap and can always to read and store information for your job from somewhere else.

Related

AspNet Boilerplate Parallel DB Access through Entity Framework from an AppService

We are using ASP.NET Zero and are running into issues with parallel processing from an AppService. We know requests must be transactional, but unfortunately we need to break out to slow running APIs for numerous calls, so we have to do parallel processing.
As expected, we are running into a DbContext contingency issue on the second database call we make:
System.InvalidOperationException: A second operation started on this context
before a previous operation completed. This is usually caused by different
threads using the same instance of DbContext, however instance members are
not guaranteed to be thread safe. This could also be caused by a nested query
being evaluated on the client, if this is the case rewrite the query avoiding
nested invocations.
We read that a new UOW is required, so we tried using both the method attribute and the explicit UowManager, but neither of the two worked.
We also tried creating instances of the referenced AppServices using the IocResolver, but we are still not able to get a unique DbContext per thread (please see below).
public List<InvoiceDto> CreateInvoices(List<InvoiceTemplateLineItemDto> templateLineItems)
{
List<InvoiceDto> invoices = new InvoiceDto[templateLineItems.Count].ToList();
ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(templateLineItems, async (templateLineItem) =>
{
try
{
XAppService xAppService = _iocResolver.Resolve<XAppService>();
InvoiceDto invoice = await xAppService
.CreateInvoiceInvoiceItem();
invoices.Insert(templateLineItems.IndexOf(templateLineItem), invoice);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
if (exceptions.Count > 0) throw new AggregateException(exceptions);
return invoices;
}
How can we ensure that a new DbContext is availble per thread?
I was able to replicate and resolve the problem with a generic version of ABP. I'm still experiencing the problem in my original solution, which is far more complex. I'll have to do some more digging to determine why it is failing there.
For others that come across this problem, which is exactly the same issue as reference here, you can simply disable the UnitOfWork through an attribute as illustrated in the code below.
public class InvoiceAppService : ApplicationService
{
private readonly InvoiceItemAppService _invoiceItemAppService;
public InvoiceAppService(InvoiceItemAppService invoiceItemAppService)
{
_invoiceItemAppService = invoiceItemAppService;
}
// Just add this attribute
[UnitOfWork(IsDisabled = true)]
public InvoiceDto GetInvoice(List<int> invoiceItemIds)
{
_invoiceItemAppService.Initialize();
ConcurrentQueue<InvoiceItemDto> invoiceItems =
new ConcurrentQueue<InvoiceItemDto>();
ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(invoiceItemIds, (invoiceItemId) =>
{
try
{
InvoiceItemDto invoiceItemDto =
_invoiceItemAppService.CreateAsync(invoiceItemId).Result;
invoiceItems.Enqueue(invoiceItemDto);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
if (exceptions.Count > 0) {
AggregateException ex = new AggregateException(exceptions);
Logger.Error("Unable to get invoice", ex);
throw ex;
}
return new InvoiceDto {
Date = DateTime.Now,
InvoiceItems = invoiceItems.ToArray()
};
}
}
public class InvoiceItemAppService : ApplicationService
{
private readonly IRepository<InvoiceItem> _invoiceItemRepository;
private readonly IRepository<Token> _tokenRepository;
private readonly IRepository<Credential> _credentialRepository;
private Token _token;
private Credential _credential;
public InvoiceItemAppService(IRepository<InvoiceItem> invoiceItemRepository,
IRepository<Token> tokenRepository,
IRepository<Credential> credentialRepository)
{
_invoiceItemRepository = invoiceItemRepository;
_tokenRepository = tokenRepository;
_credentialRepository = credentialRepository;
}
public void Initialize()
{
_token = _tokenRepository.FirstOrDefault(x => x.Id == 1);
_credential = _credentialRepository.FirstOrDefault(x => x.Id == 1);
}
// Create an invoice item using info from an external API and some db records
public async Task<InvoiceItemDto> CreateAsync(int id)
{
// Get db record
InvoiceItem invoiceItem = await _invoiceItemRepository.GetAsync(id);
// Get price
decimal price = await GetPriceAsync(invoiceItem.Description);
return new InvoiceItemDto {
Id = id,
Description = invoiceItem.Description,
Amount = price
};
}
private async Task<decimal> GetPriceAsync(string description)
{
// Simulate a slow API call to get price using description
// We use the token and credentials here in the real deal
await Task.Delay(5000);
return 100.00M;
}
}

Android Mobile Apps query from the azure database returns last row only

There's more than 15 items in my azure database table called Events.
I've tried to run most of the commands found on
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-android-how-to-use-client-library such as :
List<Events> results = eventsTable.execute().get()
and
List<Events> results = eventsTable.select("Events").execute().get();
and
List<Events> results = eventsTable.top(20).execute().get();
to return all the row items in the table. The queries seem to run on the last row of the table only and returns the last row or nothing at all when query is executed.
Though the ToDoItem Quickstart from Azure works perfectly with all the queries - which is odd.
Here's some of the code
ArrayList<Events> events = new ArrayLists<Events>();
private void EventsFromTable() {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
final List<Events> results = EventsTable.execute().get();
runOnUiThread(new Runnable() {
#Override
public void run() {
for (Events event : results) {
Events ev = new Events(event.getName(), event.getVenue(), event.getDate());
events.add(ev);
System.out.println("size is " +events.size());
<======This returns "size is 1"======>
}
}
});
} catch (final Exception e){
createAndShowDialogFromTask(e, "Error");
}
return null;
}
};
runAsyncTask(task);
}
Might any one know what the matter is?
Thanks
According to your code, the variable events seems to be a public shared instance of ArraryList in your Android app, so I don't know whether exists the case which multiple threads access it concurrently. The implementation of ArrayList class is not synchronized, please see here.
So please use the code below instead of the code ArrayList<Events> events = new ArrayLists<Events>(); when you shared the variable between UI thread and data async task thread.
List<Events> events = Collections.synchronizedList(new ArrayLists<Events>());
And I think it's better for copying data retrieved from table via addAll method, not add method for each, as the code below.
#Override
public void run() {
events.addAll(results);
}

refreshLocal on IResource throws exception

I am trying to programmatically refresh a folder inside the project for my Eclipse plugin. I have also included a simple semaphore rule in the Job. Here is the code:
public class MutexRule implements ISchedulingRule {
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
}
public boolean contains(ISchedulingRule rule) {
return rule == this;
}
}
...
Job refreshJob = new Job("Refreshing...") {
public void run() {
IResource myFolder = ...
if(myFolder != null)
myFolder.refreshLocal(DEPTH_INFINITE, null);
}
};
refreshJob.setRule(
When I schedule two jobs, and run the refresh, I get the following exception:
Attempted to beginRule: P/test, does not match outer scope rule: ...
What am I missing?
What is a possible solution / workaround?
The error you are getting is because the IResource you are refreshing also has a scheduling rule (IResource implements ISchedulingRule). When there are nested scheduling rules the outer rule must 'contain' the inner rule - your MutexRule doesn't do this.
Because the resource already has a scheduling rule there is no need to set a rule on your job.
refreshLocal does not have to in a Job but if it is run from the UI thread a progress monitor should be provided using IProgressService or similar.
Update: refreshLocal can generate many resource changed events, to minimize these use a WorkspaceModifyOperation or a WorkspaceJob:
Job job = new WorkspaceJob("Refreshing") {
public IStatus runInWorkspace(IProgressMonitor monitor) {
monitor.beginTask("", IProgressMonitor.UNKNOWN);
try {
resource.refreshLocal(monitor);
}
finally {
monitor.done();
}
return Status.OK_STATUS;
}
});
or
WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
#Override
protected void execute(IProgressMonitor monitor) throws CoreException {
resource.refreshLocal(monitor);
}
};
If you are in a wizard page you can run the WorkspaceModifyOperation with:
getContainer().run(true, true, op);
elsewhere you can use:
IProgressService service = PlatformUI.getWorkbench().getProgressService();
service.run(true, true, op);
Even better is to use the Eclipse IFile and IFolder APIs to create files and folders, in this case refreshLocal is not required.

class member returns null after osgi bind method

My problem is that in the main class I have some osgi references that work just fine when the class is call. But after that all the references became null. When I close the main windows and call shutdown method, the hubService reference returns null. What do I do wrong here?
private void shutdown() {
if(hubService == null) {
throw new NullPointerException();
}
hubService.shutdownHub(); // why is hubService null?
}
// bind hub service
public synchronized void setHubService(IHubService service) {
hubService = service;
try {
hubService.startHub(PORT, authenticationHandler);
} catch (Exception e) {
JOptionPane.showMessageDialog(mainFrame, e.toString(), "Server", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
// remove hub service
public synchronized void unsetHubService(IHubService service) {
hubService.shutdownHub();
hubService = null;
}
If a field can be read and written by multiple threads, you must protect access to read as well as write. Your first method, shutdown, does not protect the read of hubService so that the value of hubService can change between the first read and the second read. You don't show the declaration of the hubService field. You could make it volatile or only read when synchronized (on the same object used to synchronized when writing the field). Then your shutdown implementation could look like:
private volatile IHubService hubService;
private void shutdown() {
IHubService service = hubService; // make a copy of the field in a local variable
if (service != null) // use local var from now on since the field could have changed
service.shutdownHub();
}
I assume your shutdown method is the DS deactivate method? If so, why do you shutdown in the unset method as well in the shutdown method?
Overall the design does not seem very sound. The IHubService is used as a factory and should return some object that is then closed in the deactivate method. You made the IHubService effectively a singleton. Since it must come from another bundle, it should handle its life cycle itself.
Since you also do not use annotations, it is not clear if your set/unset methods are static/dynamic and/or single/multiple. The following code should not have your problems (exammple code with bnd annotations):
#Component public class MyImpl {
IHubService hub;
#Activate
void activate() {
hubService.startHub(PORT, authenticationHandler);
}
#DeActivate
void deactivate() {
hubService.shutdown();
}
#Reference
void setHub(IHubService hub) { this.hub = hub; }
}

Is it possible to kill a current running Quartz Job?

I remember that we cannot kill the current running Quartz Job but we can interrupt and have a boolean check wherever is necessary whether we need to proceed further with the subsequent operations or not.
Even when we implement the InterruptableJob and call the scheduler.interrupt to interrupt the Job, the current executed job will be still running in the server.
Ex:
A named SQL query has been triggered by the job via Hibernate which takes a long time
A call has been made to a third party server where the third party server takes a long time to respond
http://neopatel.blogspot.in/2011/05/quartz-stop-job.html
http://forums.terracotta.org/forums/posts/list/3191.page
Could someone corrects my understanding and explain me how we can kill or stop the "currently" executing Job ?
you can create new abstract class called JobBase for example that implements IJob interface and insert abstract method:
public abstract void ExecuteJob(IJobExecutionContext context);
On JobBase you can implements method Execute like this
public abstract class JobBase : IJob,IInterruptableJob
{
private Thread currentThread;
private ILog logger;
public JobBase(ILog logger)
{
this.logger=logger;
}
public void Execute(IJobExecutionContext context)
{
var thread = new Thread(()=>
{
try
{
this.ExecuteJob(context);
}
catch(Exception ex)
{
this.logger.ErrorFormat("Unhandled exception {0}",ex.ToString());
}
});
thread.Start();
this.currentThread = thread;
this.currentThread.Join();
}
public abstract void ExecuteJob(IJobExecutionContext context);
public void Interrupt()
{
currentThread.Abort();
}
}
Each Job will implements JobExecute method.
public class TestJob :JobBase
{
private ILog logger;
public TeJob(ILog logger):base(logger)
{
}
public override ExecuteJob(IJobExecutionContext context)
{
}
}
Assumes that use some factory for creating a Job
For Stopping a Job you will call method scheduler.Interrupt(new JobKey(jobName));
As you told, there is no way to interrupt "brutally" a job in quartz, neither in JAVA.
You can encapsulate your job's logic in a separate Thread and run it with the ExecutorService.
Take a look to this example: https://stackoverflow.com/a/2275596/1517816
Assume your QuartzJob is the Test class and move your business logic in the Task class.
Hope it helps
I don't know why nobody mentioned this, or maybe this was not available at the time the question was asked.
There is a method called shutdown for a Scheduler instance.
SchedulerFactory factory = new StdSchedulerFactor();
Scheduler scheduler = factory.getScheduler();
The above is used to start a job like
scheduler.start();
Use a flag or something to know when to stop the job from running. Then use
scheduler.shutdown();
How I implemented my requirement:
if(flag==true)
{
scheduler.start();
scheduler.scheduleJob(jobDetail, simpleTrigger);
}
else if(flag==false)
{
scheduler.shutdown();
}
Where jobDetail and simpleTrigger are self explanatory.
Hope it helps. :)