diff --git a/core/src/com/cloud/async/AsyncJobVO.java b/core/src/com/cloud/async/AsyncJobVO.java
index 0e5fed8bf0a..eecfb410da5 100644
--- a/core/src/com/cloud/async/AsyncJobVO.java
+++ b/core/src/com/cloud/async/AsyncJobVO.java
@@ -28,6 +28,7 @@ import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
+import javax.persistence.Transient;
import com.cloud.utils.db.GenericDao;
@@ -106,7 +107,13 @@ public class AsyncJobVO {
@Column(name=GenericDao.REMOVED_COLUMN)
private Date removed;
-
+
+ @Transient
+ private SyncQueueItemVO syncSource = null;
+
+ @Transient
+ private boolean fromPreviousSession = false;
+
public AsyncJobVO() {
}
@@ -302,6 +309,22 @@ public class AsyncJobVO {
this.cmdOriginator = cmdOriginator;
}
+ public SyncQueueItemVO getSyncSource() {
+ return syncSource;
+ }
+
+ public void setSyncSource(SyncQueueItemVO syncSource) {
+ this.syncSource = syncSource;
+ }
+
+ public boolean isFromPreviousSession() {
+ return fromPreviousSession;
+ }
+
+ public void setFromPreviousSession(boolean fromPreviousSession) {
+ this.fromPreviousSession = fromPreviousSession;
+ }
+
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("AsyncJobVO {id:").append(getId());
diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java
index 0c9bb35a674..45936301551 100644
--- a/server/src/com/cloud/api/ApiDispatcher.java
+++ b/server/src/com/cloud/api/ApiDispatcher.java
@@ -1,3 +1,20 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
package com.cloud.api;
import java.lang.reflect.Field;
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index a23a9d87677..e220c5394e7 100644
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -357,6 +357,16 @@ public class ApiServer implements HttpRequestHandler {
Gson gson = GsonHelper.getBuilder().create();
+ UserContext ctx = UserContext.current();
+ Long userId = ctx.getUserId();
+ Account account = (Account)ctx.getAccountObject();
+ if (userId != null) {
+ params.put("ctxUserId", userId.toString());
+ }
+ if (account != null) {
+ params.put("ctxAccountId", account.getId().toString());
+ }
+
AsyncJobVO job = new AsyncJobVO();
job.setUserId(UserContext.current().getUserId());
job.setCmd(cmdObj.getClass().getName());
diff --git a/server/src/com/cloud/api/BaseAsyncCmd.java b/server/src/com/cloud/api/BaseAsyncCmd.java
index d8ce6a8db1a..b4aa79eee37 100644
--- a/server/src/com/cloud/api/BaseAsyncCmd.java
+++ b/server/src/com/cloud/api/BaseAsyncCmd.java
@@ -1,5 +1,7 @@
package com.cloud.api;
+import com.cloud.async.AsyncJobManager;
+import com.cloud.async.AsyncJobVO;
import com.cloud.serializer.SerializerHelper;
/**
@@ -7,13 +9,24 @@ import com.cloud.serializer.SerializerHelper;
* serialized to the queue (currently the async_job table) and a response will be immediately returned with the
* id of the queue object. The id can be used to query the status/progress of the command using the
* queryAsyncJobResult API command.
- *
- * TODO: Create commands are often async and yet they need to return the id of object in question, e.g. deployVirtualMachine
- * should return a virtual machine id, createPortForwardingServiceRule should return a rule id, and createVolume should return
- * a volume id.
*/
public abstract class BaseAsyncCmd extends BaseCmd {
+ private AsyncJobManager _asyncJobMgr = null;
+ private AsyncJobVO _job = null;
+
public String getResponse(long jobId) {
return SerializerHelper.toSerializedString(Long.valueOf(jobId));
}
+
+ public void setAsyncJobManager(AsyncJobManager mgr) {
+ _asyncJobMgr = mgr;
+ }
+
+ public void synchronizeCommand(String syncObjType, long syncObjId) {
+ _asyncJobMgr.syncAsyncJobExecution(_job, syncObjType, syncObjId);
+ }
+
+ public void setJob(AsyncJobVO job) {
+ _job = job;
+ }
}
diff --git a/server/src/com/cloud/async/AsyncCommandQueued.java b/server/src/com/cloud/async/AsyncCommandQueued.java
new file mode 100644
index 00000000000..7894fa63907
--- /dev/null
+++ b/server/src/com/cloud/async/AsyncCommandQueued.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.async;
+
+import com.cloud.utils.SerialVersionUID;
+
+public class AsyncCommandQueued extends RuntimeException {
+ private static final long serialVersionUID = SerialVersionUID.AsyncCommandQueued;
+
+ private SyncQueueVO _queue = null;
+
+ public AsyncCommandQueued(SyncQueueVO queue, String msg) {
+ super(msg);
+ _queue = queue;
+ }
+
+ public SyncQueueVO getQueue() {
+ return _queue;
+ }
+}
diff --git a/server/src/com/cloud/async/AsyncJobManager.java b/server/src/com/cloud/async/AsyncJobManager.java
index 525b5f703df..a293d764449 100644
--- a/server/src/com/cloud/async/AsyncJobManager.java
+++ b/server/src/com/cloud/async/AsyncJobManager.java
@@ -33,6 +33,6 @@ public interface AsyncJobManager extends Manager {
public void updateAsyncJobStatus(long jobId, int processStatus, Object resultObject);
public void updateAsyncJobAttachment(long jobId, String instanceType, Long instanceId);
- public void syncAsyncJobExecution(long jobId, String syncObjType, long syncObjId);
+ public void syncAsyncJobExecution(AsyncJobVO job, String syncObjType, long syncObjId);
public void releaseSyncSource(AsyncJobExecutor executor);
}
diff --git a/server/src/com/cloud/async/AsyncJobManagerImpl.java b/server/src/com/cloud/async/AsyncJobManagerImpl.java
index 9121defe4f3..22ce5de66cd 100644
--- a/server/src/com/cloud/async/AsyncJobManagerImpl.java
+++ b/server/src/com/cloud/async/AsyncJobManagerImpl.java
@@ -35,13 +35,16 @@ import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import com.cloud.api.ApiDispatcher;
-import com.cloud.api.BaseCmd;
+import com.cloud.api.BaseAsyncCmd;
import com.cloud.async.dao.AsyncJobDao;
import com.cloud.cluster.ClusterManager;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.maid.StackMaid;
import com.cloud.serializer.GsonHelper;
import com.cloud.serializer.SerializerHelper;
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+import com.cloud.user.dao.AccountDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ComponentLocator;
@@ -68,6 +71,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
private AsyncJobExecutorContext _context;
private SyncQueueManager _queueMgr;
private ClusterManager _clusterMgr;
+ private AccountDao _accountDao;
private AsyncJobDao _jobDao;
private long _jobExpireSeconds = 86400; // 1 day
private ApiDispatcher _dispatcher;
@@ -95,37 +99,30 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
public long submitAsyncJob(AsyncJobVO job) {
return submitAsyncJob(job, false);
}
-
+
@Override @DB
public long submitAsyncJob(AsyncJobVO job, boolean scheduleJobExecutionInContext) {
if(s_logger.isDebugEnabled())
s_logger.debug("submit async job-" + job.getId() + ", details: " + job.toString());
-// AsyncJobExecutor executor = getJobExecutor(job);
-// if(executor == null) {
-// s_logger.error("Unable to find executor to execute command " + job.getCmd() + " for job-" + job.getId());
-// } else {
- Transaction txt = Transaction.currentTxn();
- try {
- txt.start();
- job.setInitMsid(getMsid());
- _jobDao.persist(job);
- txt.commit();
- // no sync source originally
- // FIXME: sync source for commands? how does new API framework handle that?
-// executor.setSyncSource(null);
-// executor.setJob(job);
-// scheduleExecution(executor, scheduleJobExecutionInContext);
- scheduleExecution(job, scheduleJobExecutionInContext);
- return job.getId();
- } catch(Exception e) {
- s_logger.error("Unexpected exception: ", e);
- txt.rollback();
- }
-// }
+ Transaction txt = Transaction.currentTxn();
+ try {
+ txt.start();
+ job.setInitMsid(getMsid());
+ _jobDao.persist(job);
+ txt.commit();
+
+ // no sync source originally
+ job.setSyncSource(null);
+ scheduleExecution(job, scheduleJobExecutionInContext);
+ return job.getId();
+ } catch(Exception e) {
+ s_logger.error("Unexpected exception: ", e);
+ txt.rollback();
+ }
return 0L;
}
-
+
@Override @DB
public void completeAsyncJob(long jobId, int jobStatus, int resultCode, Object resultObject) {
if(s_logger.isDebugEnabled())
@@ -219,10 +216,19 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
}
@Override
- public void syncAsyncJobExecution(long jobId, String syncObjType, long syncObjId) {
- if(s_logger.isDebugEnabled())
- s_logger.debug("Sync job-" + jobId + " execution on object " + syncObjType + "." + syncObjId);
-
+ public void syncAsyncJobExecution(AsyncJobVO job, String syncObjType, long syncObjId) {
+ // This method is re-entrant. If an API developer wants to synchronized on an object, e.g. the router,
+ // when executing business logic, they will call this method (actually a method in BaseAsyncCmd that calls this).
+ // This method will get called every time their business logic executes. The first time it exectues for a job
+ // there will be no sync source, but on subsequent execution there will be a sync souce. If this is the first
+ // time the job executes we queue the job, otherwise we just return so that the business logic can execute.
+ if (job.getSyncSource() != null) {
+ return;
+ }
+
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Sync job-" + job.getId() + " execution on object " + syncObjType + "." + syncObjId);
+
SyncQueueVO queue = null;
// to deal with temporary DB exceptions like DB deadlock/Lock-wait time out cased rollbacks
@@ -230,7 +236,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
Random random = new Random();
for(int i = 0; i < 5; i++) {
- queue = _queueMgr.queue(syncObjType, syncObjId, "AsyncJob", jobId);
+ queue = _queueMgr.queue(syncObjType, syncObjId, "AsyncJob", job.getId());
if(queue != null)
break;
@@ -239,12 +245,20 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
} catch (InterruptedException e) {
}
}
-
- if(queue != null) {
- checkQueue(queue.getId());
+
+ if (queue == null) {
+ throw new CloudRuntimeException("Unable to insert queue item into database, DB is full?");
} else {
- throw new CloudRuntimeException("Unable to insert queue item into database, DB is full?");
+ throw new AsyncCommandQueued(queue, "job-" + job.getId() + " queued");
}
+
+ /*
+ if (queue != null) {
+ checkQueue(queue.getId());
+ } else {
+ throw new CloudRuntimeException("Unable to insert queue item into database, DB is full?");
+ }
+ */
}
@Override @DB
@@ -388,7 +402,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
}
private void scheduleExecution(final AsyncJobVO job, boolean executeInContext) {
- Runnable runnable = getExecutorRunnable(job);
+ Runnable runnable = getExecutorRunnable(this, job);
if (executeInContext) {
runnable.run();
} else {
@@ -396,7 +410,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
}
}
- private Runnable getExecutorRunnable(final AsyncJobVO job) {
+ private Runnable getExecutorRunnable(final AsyncJobManager mgr, final AsyncJobVO job) {
return new Runnable() {
public void run() {
long jobId = 0;
@@ -411,38 +425,65 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
}
Class> cmdClass = Class.forName(job.getCmd());
- BaseCmd cmdObj = (BaseCmd)cmdClass.newInstance();
+ BaseAsyncCmd cmdObj = (BaseAsyncCmd)cmdClass.newInstance();
+ cmdObj.setAsyncJobManager(mgr);
+ cmdObj.setJob(job);
+
Type mapType = new TypeToken