modules/core/domain/intentional-api-changes-since-4.8.0.xml | 8 + modules/core/domain/src/main/java/org/rhq/core/domain/operation/OperationHistory.java | 21 +++- modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationJob.java | 22 ++++ modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java | 48 +++++++--- modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/ResourceOperationJob.java | 26 ++++- modules/integration-tests/rest-api/pom.xml | 6 + modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java | 2 modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java | 23 +++- modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/JBossProductType.java | 2 9 files changed, 134 insertions(+), 24 deletions(-)
New commits: commit 1cd277b34e9acd277d5aafebde987b1103c660e0 Author: Heiko W. Rupp hwr@redhat.com Date: Fri Aug 23 13:25:52 2013 +0200
BZ 972756 - Create history items before scheduling in quartz. This prevents the time window without history after scheduling and before quartz starts working, which was breaking fast clients.
diff --git a/modules/core/domain/intentional-api-changes-since-4.8.0.xml b/modules/core/domain/intentional-api-changes-since-4.8.0.xml index 6a22ea5..4a23253 100644 --- a/modules/core/domain/intentional-api-changes-since-4.8.0.xml +++ b/modules/core/domain/intentional-api-changes-since-4.8.0.xml @@ -17,4 +17,12 @@ The new method will clear only the specified status. </justification> </difference> + <difference> + <className>org/rhq/core/domain/operation/OperationHistory</className> + <differenceType>7012</differenceType><!-- method added --> + <method>void setStartedTime(long)</method> + <justification> + We are adding a method to explicitly set the start time. + </justification> + </difference> </differences> diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/operation/OperationHistory.java b/modules/core/domain/src/main/java/org/rhq/core/domain/operation/OperationHistory.java index f81ec9a..f3beb14 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/operation/OperationHistory.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/operation/OperationHistory.java @@ -342,10 +342,27 @@ public abstract class OperationHistory implements Serializable { }
/** + * This method MUST be called when the corresponding operation is triggered, but before the request is sent down to + * the agent. The started time is used in the calculation of {@link #getDuration()}, which is in turn used by the + * business layer to reason whether an operation has timed out. If this method is never called, and if there are + * any issues executing the corresponding operation, this history element will never time out and will forever stay + * in the {@link OperationRequestStatus#INPROGRESS} state. + * + * @throws IllegalArgumentException if an attempt is made to start this object more than once + * @see #getCreatedTime() + * @since RHQ 4.9 + */ + public void setStartedTime(long startedTime) { + if (this.startedTime != 0) { + throw new IllegalArgumentException("Can only start an operation once"); + } + this.startedTime = startedTime; + } + + /** * The time when corresponding operation was started. If the corresponding operation has not yet been started, * this method will return 0. - * - * @return started time, in epoch millis + * * @return started time, in epoch millis */ public long getStartedTime() { return this.startedTime; diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationJob.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationJob.java index ad8fb59..ceabb56 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationJob.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationJob.java @@ -19,6 +19,7 @@ package org.rhq.enterprise.server.operation;
import java.util.Date; +import java.util.List;
import org.apache.commons.logging.LogFactory; import org.quartz.Job; @@ -26,7 +27,9 @@ import org.quartz.JobDetail;
import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.configuration.Configuration; +import org.rhq.core.domain.criteria.ResourceOperationHistoryCriteria; import org.rhq.core.domain.operation.GroupOperationHistory; +import org.rhq.core.domain.operation.JobId; import org.rhq.core.domain.operation.OperationDefinition; import org.rhq.core.domain.operation.ResourceOperationHistory; import org.rhq.core.domain.operation.ScheduleJobId; @@ -122,4 +125,23 @@ public abstract class OperationJob implements Job {
return persisted; } + + protected ResourceOperationHistory findOperationHistory(String name, String group, + OperationManagerLocal operationManager, + ResourceOperationSchedule schedule) { + + JobId jobId = new JobId(name,group); + + ResourceOperationHistoryCriteria criteria = new ResourceOperationHistoryCriteria(); + criteria.addFilterJobId(jobId); + + ResourceOperationHistory history ; + List<ResourceOperationHistory> list = operationManager.findResourceOperationHistoriesByCriteria(schedule.getSubject(),criteria); + if (list==null || list.isEmpty()) { + return null; + } + + history = list.get(0); + return history; + } } \ No newline at end of file diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java index f5ceae6..8ac6af4 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2011 Red Hat, Inc. + * Copyright (C) 2005-2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -13,8 +13,8 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.operation;
@@ -232,7 +232,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
ensureControlPermission(subject, resource);
- validateOperationNameAndParameters(resource.getResourceType(), operationName, parameters); + OperationDefinition opDef = validateOperationNameAndParameters(resource.getResourceType(), operationName, parameters);
String uniqueJobId = createUniqueJobName(resource, operationName);
@@ -254,7 +254,8 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
JobDetail jobDetail = new JobDetail(); jobDetail.setName(uniqueJobId); - jobDetail.setGroup(createJobGroupName(resource)); + String jobGroupName = createJobGroupName(resource); + jobDetail.setGroup(jobGroupName); jobDetail.setDescription(notes); jobDetail.setVolatility(false); // we want it persisted jobDetail.setDurability(false); @@ -282,6 +283,13 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
LOG.debug("Scheduled Resource operation [" + newSchedule + "] - next fire time is [" + next + "]");
+ // Create an IN_PROGRESS item + ResourceOperationHistory history; + history = new ResourceOperationHistory(uniqueJobId, jobGroupName, subject.getName(), opDef, parameters, + schedule.getResource(), null); + + updateOperationHistory(subject,history); + return newSchedule; }
@@ -896,7 +904,22 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan ensureControlPermission(subject, history); }
- // we do not cascade add the param config (we probably can add that but), so let's persist it now + + // The history item may have been created already, so find it in the database and + // set the new state from our input + if (history.getId()!=0) { + OperationHistory existingHistoryItem =entityManager.find(OperationHistory.class,history.getId()); + existingHistoryItem.setStatus(history.getStatus()); + existingHistoryItem.setErrorMessage(history.getErrorMessage()); + if (existingHistoryItem.getStartedTime()==0) { + existingHistoryItem.setStartedTime(history.getStartedTime()); + } + if (history instanceof ResourceOperationHistory) { + ((ResourceOperationHistory)existingHistoryItem).setResults(((ResourceOperationHistory) history).getResults()); + } + history = existingHistoryItem; + } + // we do not cascade add the param config (we probably can add that but), so let's persist it now if needed Configuration parameters = history.getParameters(); if ((parameters != null) && (parameters.getId() == 0)) { entityManager.persist(parameters); @@ -1358,7 +1381,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan boolean neverStarted = ((System.currentTimeMillis() - createdTime) > (1000 * 60 * 60 * 24)); if (timedOut || neverStarted) { if (timedOut) { - // the operation started, but the agent never told us how it finished prior to exceeding the timeout + // the operation started, but the agent never told us how it finished prior to exceeding the timeout LOG.info("Operation execution seems to have been orphaned - timing it out: " + history); history.setErrorMessage("Timed out : did not complete after " + duration + " ms" + " (the timeout period was " + timeout + " ms)"); @@ -2051,7 +2074,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan return triggers[0]; }
- private static JobTrigger convertToJobTrigger(Trigger trigger) { + private JobTrigger convertToJobTrigger(Trigger trigger) { JobTrigger schedule; if (trigger instanceof SimpleTrigger) { SimpleTrigger simpleTrigger = (SimpleTrigger) trigger; @@ -2106,7 +2129,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan return schedule; }
- private static Trigger convertToTrigger(JobTrigger jobTrigger) { + private Trigger convertToTrigger(JobTrigger jobTrigger) { Trigger trigger; if (jobTrigger.getRecurrenceType() == JobTrigger.RecurrenceType.CRON_EXPRESSION) { CronTrigger cronTrigger = new CronTrigger(); @@ -2146,8 +2169,9 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan return trigger; }
- private static void validateOperationNameAndParameters(ResourceType resourceType, String operationName, - Configuration parameters) { + private OperationDefinition validateOperationNameAndParameters(ResourceType resourceType, + String operationName, + Configuration parameters) { Set<OperationDefinition> operationDefinitions = resourceType.getOperationDefinitions(); OperationDefinition matchingOperationDefinition = null; for (OperationDefinition operationDefinition : operationDefinitions) { @@ -2167,6 +2191,8 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan throw new IllegalArgumentException("Parameters for [" + operationName + "] on Resource of type [" + resourceType.getName() + "] are not valid: " + errors); } + + return matchingOperationDefinition; }
} \ No newline at end of file diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/ResourceOperationJob.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/ResourceOperationJob.java index 004929f..c7d0a58 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/ResourceOperationJob.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/ResourceOperationJob.java @@ -24,6 +24,7 @@ import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; +import org.quartz.SimpleTrigger;
import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.operation.ResourceOperationHistory; @@ -74,6 +75,13 @@ public class ResourceOperationJob extends OperationJob {
try { JobDetail jobDetail = context.getJobDetail(); + int triggerTimes = 1; + if (context.getTrigger() instanceof SimpleTrigger) { + SimpleTrigger trigger = (SimpleTrigger) context.getTrigger(); + triggerTimes = trigger.getTimesTriggered(); + } else { + // Cron Trigger + } OperationManagerLocal operationManager = LookupUtil.getOperationManager();
updateOperationScheduleEntity(jobDetail, context.getNextFireTime(), operationManager); @@ -86,16 +94,24 @@ public class ResourceOperationJob extends OperationJob { loggedInSubject = getUserWithSession(schedule.getSubject(), false); schedule.setSubject(loggedInSubject);
- // for the security check, can the user who scheduled the operation in the first + // for the security check, can the user who scheduled the operation in the first // place still have the authority to execute it against the resource in question operationManager.getResourceOperationSchedule(schedule.getSubject(), jobDetail);
- ResourceOperationHistory resourceHistory = createOperationHistory(jobDetail.getName(), - jobDetail.getGroup(), schedule, null, operationManager); + ResourceOperationHistory resourceHistory = null; + if (triggerTimes==1) { // On 1st fire use the already provided history. + resourceHistory = findOperationHistory(jobDetail.getName(),jobDetail.getGroup(),operationManager, schedule); + if (resourceHistory.getStartedTime()>0) { + resourceHistory=null; + } + } + if (resourceHistory==null) { + resourceHistory = createOperationHistory(jobDetail.getName(), + jobDetail.getGroup(), schedule, null, operationManager); + }
invokeOperationOnResource(schedule, resourceHistory, operationManager); - } catch (Exception e) { - if (e instanceof CancelJobException) { + } catch (Exception e) { if (e instanceof CancelJobException) { // if a cancel job exception was thrown we do not need to do anything else. // we can just rethrow the exception. throw (CancelJobException) e; diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java index b12eea3..690ce19 100644 --- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java +++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java @@ -235,6 +235,8 @@ public class ContentTest extends AbstractBase { given() .header(acceptJson) .log().everything() + .redirects().follow(false) + .redirects().allowCircular(true) .expect() .statusCode(isOneOf(200, 201, 302)) .log().everything() diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java index 7789f9e..e73cab9 100644 --- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java +++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java @@ -387,13 +387,13 @@ public class OperationsTest extends AbstractBase { }
private void waitAndCheckStatus(int platformId, String historyId) throws InterruptedException { - Thread.sleep(15000); // we need to wait a little as the execution may take time +// Thread.sleep(15000); // we need to wait a little as the execution may take time
given() .pathParam("hid", historyId) .expect() .statusCode(200) - .log().ifError() + .log().everything() .when() .get("/operation/history/{hid}");
@@ -404,7 +404,7 @@ public class OperationsTest extends AbstractBase { .header(acceptJson) .expect() .statusCode(200) - .log().ifError() + .log().everything() .when() .get("/operation/history");
@@ -422,16 +422,22 @@ public class OperationsTest extends AbstractBase { private void waitForTerminationAndDelete(String historyId) throws InterruptedException { boolean done = false; int count = 0; + String status = "bla"; + JsonPath jsonPath = null; + while (!done) { Response response = given() .header(acceptJson) .pathParam("hid", historyId) + .expect() + .log().everything() .when() .get("/operation/history/{hid}");
- JsonPath jsonPath = response.jsonPath(); - String status= jsonPath.getString("status"); + + jsonPath = response.jsonPath(); + status = jsonPath.getString("status"); int code = response.statusCode();
if (code==200 && (status.equals("Success") || status.equals("Failed"))) { @@ -443,6 +449,13 @@ public class OperationsTest extends AbstractBase { assert count < 10 :"Waited for 20sec -- something is wrong"; }
+ if (done && status.equals("Success")) { + Object result = jsonPath.get("result"); // Can not test on result.operationResult, as not every op has this. + assert result != null; + String errorMessage = jsonPath.getString("errorMessage"); + assert errorMessage == null; + } + // Delete the history item given() .pathParam("hid", historyId)
commit cf2584b5e804bab46fe1f80a93a9b161c56ea642 Author: Heiko W. Rupp hwr@redhat.com Date: Fri Aug 23 13:23:46 2013 +0200
Explicitly add commons-codec as this was all of a sudden no longer visible to the runtime class path, so that all tests failed.
diff --git a/modules/integration-tests/rest-api/pom.xml b/modules/integration-tests/rest-api/pom.xml index 5d7bde5..72bf856 100644 --- a/modules/integration-tests/rest-api/pom.xml +++ b/modules/integration-tests/rest-api/pom.xml @@ -64,6 +64,12 @@ <version>${jackson.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-codec</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency>
</dependencies>
commit 309916e0463a55221de3762c34c88626bff840f7 Author: Heiko W. Rupp hwr@redhat.com Date: Fri Aug 23 13:13:27 2013 +0200
Fix a typo wrt name of JPP
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/JBossProductType.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/JBossProductType.java index ea2c3ed..9cb9e5a 100644 --- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/JBossProductType.java +++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/JBossProductType.java @@ -35,7 +35,7 @@ public enum JBossProductType { ISPN("ISPN", "Infinispan Server", "Infinispan Server", "ISPN"), JDG("JDG", "JBoss JDG 6", "JBoss Data Grid 6", "Data Grid"), EPP("EPP", "JBoss EAP 6", "JBoss Enterprise Portal Platform 6", "Portal Platform"), - JPP("JPP", "JBoss EAP 6", "JBoss Portal Platform 6", "Portal Platform"), + JPP("JPP", "JBoss JPP 6", "JBoss Portal Platform 6", "Portal Platform"), // EWP("EWP", "JBoss EWP 6", "JBoss Enterprise Web Platform 6", "EWP"), SOA("SOA-P", "JBoss SOA-P 6", "Red Hat JBoss Fuse Service Works", "SOAP"), WILDFLY8("WildFly","WildFly 8" ,"WildFly Application Server 8" , "WildFly");
rhq-commits@lists.fedorahosted.org